Greasy Fork 还支持 简体中文。

LPSG Video Unlocker

Unlock videos in LPSG posts with the correct URL transformation logic.

// ==UserScript==
// @name         LPSG Video Unlocker
// @namespace    LPSG_Video_UNlocker_HoLyMeo 
// @version      1.3
// @description  Unlock videos in LPSG posts with the correct URL transformation logic.
// @author       AI
// @match        https://www.lpsg.com/*
// @connect      cdn-videos.lpsg.com
// @connect      self
// @grant        GM_xmlhttpRequest
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    console.log("LPSG Video Unlocker v1.3: Script is running with correct path logic.");

    const VIDEO_EXTENSIONS = [ "mp4", "mov", "m4a", "m4v", "webm", "mpeg", "mpg", "ogv" ];

    function setUrlFileExtension(urlString, newExtension) {
        try {
            const url = new URL(urlString);
            const pathWithoutExtension = url.pathname.substring(0, url.pathname.lastIndexOf('.'));
            url.pathname = `${pathWithoutExtension}.${newExtension}`;
            return url.href;
        } catch (e) {
            console.error("LPSG Video Unlocker: Invalid URL to set extension", urlString);
            return urlString;
        }
    }

    function createVideoContainer(videoUrl, imageSrc) {
        const videoSourceTag = document.createElement("source");
        videoSourceTag.src = videoUrl;
        videoSourceTag.type = 'video/mp4';
        const newVideoTag = document.createElement("video");
        newVideoTag.poster = imageSrc;
        newVideoTag.controls = true;
        newVideoTag.setAttribute("controlslist", "nodownload");
        newVideoTag.preload = "metadata";
        newVideoTag.style.width = '100%';
        newVideoTag.appendChild(videoSourceTag);
        newVideoTag.className = "lpsg-vu-unlocked";

        return newVideoTag;
    }

    function probeVideoUrl(url) {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "HEAD",
                url: url,
                headers: { "Accept": "video/*" },
                onload: (response) => {
                    const isSuccess = response.status >= 200 && response.status < 300;
                    if (isSuccess) console.log(`%cLPSG Video Unlocker: Probe SUCCESS for ${url}`, 'color: green; font-weight: bold;');
                    resolve(isSuccess);
                },
                onerror: () => resolve(false),
                ontimeout: () => resolve(false)
            });
        });
    }

    async function findVideoUrl(previewImgSrc) {
        // SỬA LỖI: Áp dụng quy luật chuyển đổi chính xác đã được xác định.
        // Thay thế '/attachments/posters/' bằng '/video/'
        if (!previewImgSrc.includes("/attachments/posters/")) {
             console.warn(`LPSG Video Unlocker: Image URL does not match expected pattern: ${previewImgSrc}`);
             return null;
        }
        const videoBaseUrl = previewImgSrc.replace("/attachments/posters/", "/video/");

        for (const ext of VIDEO_EXTENSIONS) {
            const potentialVideoUrl = setUrlFileExtension(videoBaseUrl, ext);
            if (await probeVideoUrl(potentialVideoUrl)) {
                return potentialVideoUrl;
            }
        }
        console.error(`LPSG Video Unlocker: Could not find a valid video URL even with the correct rule for image: ${previewImgSrc}`);
        return null;
    }

    function processPage() {
        const isGuest = !!document.querySelector("div.p-navgroup.p-account.p-navgroup--guest");

        if (isGuest) {
        } else {
            const wrappers = document.querySelectorAll("div.bbMediaWrapper-inner:not(.lpsg-processed)");

            wrappers.forEach(async (wrapperDiv) => {
                wrapperDiv.classList.add('lpsg-processed');
                if (wrapperDiv.querySelector("video")) return; // Đã có video, bỏ qua

                const previewImg = wrapperDiv.querySelector("img");
                if (previewImg && previewImg.src) {
                    const videoUrl = await findVideoUrl(previewImg.src);
                    if (videoUrl) {
                        console.log("LPSG Video Unlocker: Found valid video URL. Creating player...");
                        wrapperDiv.appendChild(createVideoContainer(videoUrl, previewImg.src));
                        wrapperDiv.querySelector("div.video-easter-egg-poster")?.remove();
                        wrapperDiv.querySelector("div.video-easter-egg-blocker")?.remove();
                        wrapperDiv.querySelector("div.video-easter-egg-overlay")?.remove();
                    }
                }
            });
        }
    }

    processPage();

    const observer = new MutationObserver((mutations) => {
        for (const mutation of mutations) {
            if (mutation.addedNodes.length) {
                processPage();
                return;
            }
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });

})();