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.0
// @description  Save and restore video playback time using Firestore
// @author       Bui Quoc Dung
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function () {
    "use strict";
    // 🔹 Firestore Configuration
    const FIRESTORE_URL = "PASTE YOUR FIRESTORE LINK";
    const SAVE_INTERVAL = 10*1000; // seconds*1000
    let video = null;
    let VIDEO_ID = "";
    let lastSaveTime = 0;

    // 🔍 1. Generate Video ID
    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, "");
    }

    // 🔍 2. Video Detection
    function findVideo() {
        video = document.querySelector("video") || detectPlyrVideo();
        if (video) {
            console.log("🎥 Video element found");
            initializeVideo();
        } else {
            setTimeout(findVideo, 1000);
            console.log("🔍 Searching for video...");
        }
    }

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

    // ⬇️ 4. Load Saved Progress
    function loadSavedProgress() {
        GM_xmlhttpRequest({
            method: "GET",
            url: `${FIRESTORE_URL}/${VIDEO_ID}`,
            onload: function (response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data?.fields) {
                        // Restore playback time
                        if (data.fields.time?.integerValue) {
                            video.currentTime = parseInt(data.fields.time.integerValue);
                            console.log("⏩ Restored time:", video.currentTime);
                        }

                        // Display last saved date
                        if (data.fields.date?.stringValue) {
                            const savedDate = data.fields.date.stringValue;
                            console.log("📅 Last saved:", savedDate);
                        }
                    }
                } catch (error) {
                    console.error("⚠️ Error loading data:", error);
                }
            }
        });
    }

    // ⬆️ 5. Save Playback Progress
    function savePlaybackProgress() {
        if (!video || video.paused || video.ended) return;

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

        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();
                    console.log("💾 Saved:", currentTime, "| Date:", currentDate);
                }
            });
        }
    }

    // 🔄 6. Handle Page Navigation (SPA)
    function monitorUrlChanges() {
        let previousUrl = location.href;
        setInterval(() => {
            if (location.href !== previousUrl) {
                previousUrl = location.href;
                VIDEO_ID = getVideoID();
                console.log("🔄 Page changed - New video ID:", VIDEO_ID);
                findVideo();
            }
        }, 1000);
    }

    // 🛠️ 7. Initialize Video Settings
    function initializeVideo() {
        loadSavedProgress();
        video.addEventListener("timeupdate", savePlaybackProgress);
        video.addEventListener("seeked", savePlaybackProgress);
    }

    // 🚀 Start Script
    function init() {
        VIDEO_ID = getVideoID();
        console.log("🔑 Video ID:", VIDEO_ID);
        findVideo();
        monitorUrlChanges();
    }

    init();
})();

QingJ © 2025

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