Video time tracker (Firestore)

Save and restore video playback time using Firestore

目前为 2025-03-04 提交的版本。查看 最新版本

// ==UserScript==
// @name         Video time tracker (Firestore)
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Save and restore video playback time using Firestore
// @author       Bui Quoc Dung
// @match        *://*/*
// @exclude      *://*.facebook.com/*
// @exclude      *://*.chatgpt.com/*
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function () {
    "use strict";

    // Firestore API endpoint
    const FIRESTORE_URL = "PASTE YOUR FIRESTORE LINK";
    // Controls how often playback progress is saved to Firestore
    const SAVE_INTERVAL = 30 * 1000; // seconds *1000
    // Filters out short videos/clips that don't need progress tracking
    const MIN_TRACK_TIME = 20 * 60; // minutes * 60

    let video = null;        // Video element reference
    let VIDEO_ID = "";       // Unique video identifier
    let lastSaveTime = 0;    // Last save timestamp

    // Generate video ID from URL (special handling for YouTube)
    function getVideoID() {
        const url = new URL(window.location.href);
        if (url.hostname.includes("youtube.com")) {
            return "youtube_" + url.searchParams.get("v");
        }
        return window.location.pathname.replace(/[^a-zA-Z0-9]/g, "");
    }

    // Find video element, retry if not found
    function findVideo() {
        video = document.querySelector("video") || detectPlyrVideo();
        if (video) {
            if (video.duration < MIN_TRACK_TIME) return;
            initializeVideo();
        } else setTimeout(findVideo, 1000);
    }

    // Check for Plyr.js video player
    function detectPlyrVideo() {
        if (typeof Plyr !== "undefined" && Plyr.instances.length > 0) {
            return Plyr.instances[0].elements.container.querySelector("video");
        }
        return null;
    }

    // Retrieve saved time from Firestore
    function loadSavedProgress() {
        GM_xmlhttpRequest({
            method: "GET",
            url: `${FIRESTORE_URL}/${VIDEO_ID}`,
            onload: function (response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data?.fields?.time?.integerValue) {
                        video.currentTime = parseInt(data.fields.time.integerValue);
                    }
                } catch (error) {}
            }
        });
    }

    // Save current time with throttling
    function savePlaybackProgress() {
        if (!video || video.paused || video.ended || video.duration < MIN_TRACK_TIME) return;

        const currentTime = Math.floor(video.currentTime);
        const currentDate = new Date().toISOString().split('T')[0];

        if (Date.now() - lastSaveTime >= SAVE_INTERVAL) {
            GM_xmlhttpRequest({
                method: "PATCH",
                url: `${FIRESTORE_URL}/${VIDEO_ID}`,
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify({
                    fields: {
                        time: { integerValue: currentTime },
                        date: { stringValue: currentDate }
                    }
                }),
                onload: () => lastSaveTime = Date.now()
            });
        }
    }

    // Track URL changes to handle page navigation
    function monitorUrlChanges() {
        let previousUrl = location.href;
        setInterval(() => {
            if (location.href !== previousUrl) {
                previousUrl = location.href;
                VIDEO_ID = getVideoID();
                findVideo();
            }
        }, 1000);
    }

    // Set up video event listeners and load saved time
    function initializeVideo() {
        loadSavedProgress();
        video.addEventListener("timeupdate", savePlaybackProgress);
        video.addEventListener("seeked", savePlaybackProgress);
    }

    // Main initialization entry point
    function init() {
        VIDEO_ID = getVideoID();
        findVideo();
        monitorUrlChanges();
    }

    init();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址