☰

海角天涯-解锁海角社区资源

无限制解锁收费资源,包括钻石贴、金币贴,下载视频,复制视频链接,自动展开帖子

Before you install, Sleazy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script is only fully functional after you make a payment. Sleazy Fork is not involved in the payment, so can't validate you will get anything of value or help you get a refund.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         海角天涯-解锁海角社区资源
// @namespace    tianya365.top
// @homepage     https://vip.tianya365.top
// @version      2.1.9
// @description  无限制解锁收费资源,包括钻石贴、金币贴,下载视频,复制视频链接,自动展开帖子
// @icon         data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAABKCAYAAAAc0MJxAAAKSUlEQVR4Xu2cCZQU1RVA7x8G2VE2WWUbMSiCBDQqiAYVUAkIohIwyCaYnATZHAYwsgnICLLlIMQIRiISECTsgsMSRDGAsguMgkQWCWgiqzow/XPe/OlUdXdNdxXUBKH7nTMHqv5+6/3/33u/uhTRJFWnoGip4D4UtdBUUYoSUctcJolacwrFITSfaUUGAZYxVu3Lq/vKMSFVpyQlMULDLxUkXSZjv6huak22UswOBBjmBCwSVJp+XMHrCopeVMuXaWENZ3Q2nRinFtiHEApqgO6vYKxSOGvaZTp4r93WENABnmWsmhAsawEZoNsoxfx4mWqx4OXA0rTjJfU3yWtApeoUlcRWBcVjVRBP6RpO6SzqM0HtzwGVlKZnAR3jCYKHsc4MpKvOiv66mkpGiMXF7uYBUE5W2Q11FtcrUnXvpCQmeq0gnvIHAjyj1AC9VCkeiqeBex2r1ixSKk3vVlDba+F4yq9hj4A6oaBkPA3c61g1nFRJaVp7LRiP+ROgXD71BKgEKJcEXGZLaFQClEsCLrPlm0Yt6wqbDsGkD+DfZ1325kecLV9AtawNCzuDUnDie5i/A0athgP/+RGTiNE1T6DKFYM/PGwA2GXqBli737rzTid4uE5onknr4cU15l54+bz6KM0cPyOBoUsP2BOo6qVgX1pkp7vPgz9vNvd/Ug529oOkMJjZAbiQ8VZ7EY6eusxAVb0GvhgYHdSoFjCwqX8DqzQS/nXav/outCZPGpUXqKfmweuboVQRODAIil91od2JLFdxJBy70kB1vw1ebecfJKmpwgtmnbrU4ptG/eUTszbVKmsNSdzt9z6DwycjhylpTmtWuIvefwmczrrUmMATqNJFYVzL3E7bBirTLjkJMnqE7mgnv4ca6fDtd/4NtHAyjH7AtCNQZUfM+RdY8zm8m+lfW/aaPIGK1oVl3aDFDaE5vjsHe45HlnI6NAzfJRfshBGrIss+8VOY2T7yvgB7YDrsOgpnz0FWNpzLhuwL2WodBuoLqJQysKMvFEr272lO3wg934msb8Zj0Llh9HaCmiaQzgcMMAGXA++89f8+i2FNnm8bhLbhC6iyRWFia3isLiQX8AfW9E3Qc35oXTK9vxwE5X16TWTwu5C+1l1/PYESy7xvkzDSwNztsOUI3FoFpj8KN1dw13i0XDM2QY8wUM1qwfJu7i37WL148xPoPDdWLpPuCdSN15qdLVw6z4E3t5i7w5rB8/dZOUTtW0yHrPPWvZvKg/zN3ZbbCQVrn4aCNm10AjWyBQwKM2Z3HAXZcZ2kaEEYcn+klxDMu+4LaPrHfABVuxzs6u8NlKwLpYbC9+eh713Q43a4oaxxS6qMtuo6OzJ0jQsHJRtA5gCoWTq0/ZGrYOh7zoPteiu89qhz2spMENfriIPp4lTCk0aJH/dpDFBDm8GQMI0qNQxkB5zUGn7XyHRDdqkaY+DQCXMdC1SDSrCxV+i0kzrqvAyZXzvDmPsEtKsbmiaaPXqNWZvkIboVf0DNhfVfGAh3VoM7qlrNy67T8S3TqTZ1oMutVppEFP4uUQcFczqGTr09x2D1PliyG1ZkQu/GML5V6LAEcpOpzkMtUhA294KiNnfqTBaIASv1iYhWu4XlC6guc2HfN/D+b9w+H/f5hqyE0athax9/Ngl7y794HZbvddcXT6AqlYSJrYzrYXc1pmwACaPkFyjZnfY7RC3cDTHvXGnLYNw6d7V4AhWtykbV8g+UuEgyjfyyn4LjmPkxdH07H0F1rA/1KoIYhZ/lLqSyNq13mHqiebI2BCUvj0JCM06RT5l6EkaufS2s7gElCkORZCuvhGDsRuNd1aHtzVZ7AmPbV+a6ytWhduDGg3DnlHwE9d5TcO/1Zrp9cACmfWRCwdcWh1/fYf6CInZU+RfgbJQIgMA7OSLUPFi4C8RyPnbGOpxoUBnqV4KpbSwPQBb0mmOs9lrfBPM6Wdd9F8OUD8219O/gYAuylJWd102o2fPUK3YVHH4OShQKfRIjMmB4hjHwht4fCqr0MOOoRpNw8yDatPh2uNW+aKx95xKtvMpmuJ4Pc4wlLai5YrKUGQY/uDATPINqmgKiUfZpIk+k8SsgqhwNlGzZMm3vqQkNK0PdCdbT9ALq0GCo6MP7NwL55vHOEY7wh+oZ1Ctt4enbQ6uReNN1o43WiPsibox96pUZbtapEc3huXtNinRS4P7joLn2Amr3s8a690NkMRftjSWeQe3qZxZWu4hT3OEtcycclBicMvUE1OP1YLbtldrnVxgr2SuoTb1A1qugvL0dvrEdsjavBTXLWA9k1hYrSnpLRWMUB+XldTBgWSxMHp3i8sVNmCM8lCIeuNg6TqBkwZ+43qwjZYpCT5s2SsRBfC6R/nebKGlQtn8FUz+Cjw/B1iOhAbg1PeHumlbe5q/Bqs+ta4mApv3cAtVwsrXzhQf+5u2A9vJOdAzxpFGp98CYByNrrD3OMhPCNSpWB2KlyxQVbRF3ZudR4/IMvjc0mmqPXkh98jCmtrVqFm0XrRepVwG29LHSZOo3cmEieAK1qAvIcbld9h43jmnQPvIblBNIgWffTMT0sO9csrPJ7hwUe7qEnK8ubKWJv1d5VKzH5WHqSazoq9+bszu7vJABwzKsO/8PULGH5T6HLA3lRph3JKKJa41qdSMseDLSem7zBizebTUhGidGn12c3hINt9C73Ra6Rn3+NWw+DBJVrVjCmAMlC0GBfPjZQLM/mantCyg5pgoPA4tKy0mul3M3UXsnd2VvKshRVFBkp3pyTmjXBVSN0tCvCfyqgZW27QgsDzum6tIQKuTG1n84D+K4Bw3T+1Lgtuus8qlLYfz7PoHa0tv4d3ZZ+Ck8MtO9mkvOya3ht7nBu2glnUAF80vcS4KAQfnwADSZFlrbWx2g/S3mnrhR8rJHr8YgUdo65eGGclZ+2ZUlTuWLRontJNNPtldpTNas596FMS5PMYKd8AOUABAQQZEwc+mhoQu6PXYvU7/pq+aUyOkhSey+w2yfQAWrkV1D1L9TA/jrVnfmv70LfoASN0pOpe1SfyLIQUNQevwMpj1iXfdeZA4ZJoRFSSVHzgnSZJ9BeZtokbmDoGJ57LO3Rq5RwdpkcX/mLihW0JgBJQubNWbDP632ZHrJSyNBkeiGuFrPNI7sk7he4etheC7Xu97FArrcyydAuXyCCVAJUC4JuMyW0KgEKJcEXGZLaFQClEsCLrMlfirrAlTOT2VVmt6rIOztSxel4yiLhkwBtUJB8zgat+ehaliZ+ECEC2yBAH0UA3V1FWB/vH8KKS9eWqN1NjWCH7GZDnRzATces8wIpKvu5t34fvo6lcx2pbgmHknkqU1wQp+jLuPVQetHBGn6QQVLEl/9MdhyvvKjaEW6Wi7XkZ9uU7wU77Byv0bWl5fU/+KekT9LeVa3VQV4Q3FlfEbS61Ki4bSWj46lq8X2ss4f/UvTVZNglBSIF+3K/bzkrAA8T7r6MiIUHJW4fLA0iYeU5v4r+oOlsArN0mgfLP0vox/zX2L4nPIAAAAASUVORK5CYII=
// @author       tianya365.top
// @match        *://*.haijiao.com/*
// @exclude      https://*.tianya365.top/*
// @run-at       document-start
// @grant        none
// @antifeature  payment
// @charset      UTF-8
// ==/UserScript==

(function () {
    "use strict";

    const CONFIG = {
        VERSION: "2.1.9",
        DEFAULT_SERVER: "https://vip.tianya365.top",
        STORAGE_KEYS: {
            SERVER: "hjty_server",
            USER_TOKEN: "hjty_user_token",
            USER_NAME: "hjty_user_name",
        },
        SELECTORS: {
            TOOLBAR_CONTAINER: ".hjty-toolbar-container",
            UNLOCK_BTN: ".hjty-unlock-btn",
            SETTINGS_BTN: ".hjty-settings-btn",
            USER_BTN: ".hjty-user-btn",
            TOGGLE_BTN: ".hjty-toggle-btn",
        },
        MODAL_IDS: {
            LOADING: "hjty-loading-modal",
            LOGIN: "hjty-login-modal",
            SERVER: "hjty-server-modal",
            USER_INFO: "hjty-user-info-modal",
            LOGOUT_CONFIRM: "hjty-logout-confirm-modal",
            UPDATE_CONFIRM: "hjty-update-confirm-modal",
        },
    };

    let topic = null;
    let newVersion = "";
    let m3u8_url = "";
    let play_url = "";
    let shortVideoUrl = "";
    let shortVideoId = "";
    let video = null;
    let hasPic = false;
    let server =
        localStorage.getItem(CONFIG.STORAGE_KEYS.SERVER) ||
        CONFIG.DEFAULT_SERVER;

    let toolbarCollapsed =
        localStorage.getItem("hjty_toolbar_collapsed") === "true";

    if (!localStorage.getItem(CONFIG.STORAGE_KEYS.SERVER)) {
        localStorage.setItem(CONFIG.STORAGE_KEYS.SERVER, server);
    }

    const Utils = {

        debounce(func, wait, immediate = false) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    timeout = null;
                    if (!immediate) func.apply(this, args);
                };
                const callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
                if (callNow) func.apply(this, args);
            };
        },

        base64: {
            encode: function (str) {
                try {
                    return btoa(
                        encodeURIComponent(str).replace(
                            /%([0-9A-F]{2})/g,
                            function (match, p1) {
                                return String.fromCharCode(parseInt(p1, 16));
                            },
                        ),
                    );
                } catch (error) {
                    return "";
                }
            },

            decode: function (base64Str) {
                try {
                    return decodeURIComponent(
                        Array.prototype.map
                            .call(atob(base64Str), function (c) {
                                return (
                                    "%" +
                                    ("00" + c.charCodeAt(0).toString(16)).slice(
                                        -2,
                                    )
                                );
                            })
                            .join(""),
                    );
                } catch (error) {
                    return "";
                }
            },
        },

        getUserToken() {
            return localStorage.getItem(CONFIG.STORAGE_KEYS.USER_TOKEN);
        },

        getCookieValue(name) {
            const match = document.cookie.match(new RegExp(name + "=([^;]+)"));
            return match ? match[1] : null;
        },

        checkLoginStatus() {
            return !!this.getUserToken();
        },

        getPid() {
            if (/\/post\/details.*pid=\d+/.test(location.href)) {
                const match = location.href.match(
                    /\/post\/details.*pid=(\d+)/i,
                );
                return match ? match[1] : null;
            }
            return null;
        },

        deleteCookie(name) {
            document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
        },

        decrypt(data) {
            return JSON.parse(
                this.base64.decode(
                    this.base64.decode(this.base64.decode(data)),
                ),
            );
        },

        encrypt(data) {
            return this.base64.encode(
                this.base64.encode(this.base64.encode(JSON.stringify(data))),
            );
        },

        async copyText(text) {
            try {

                if (navigator.clipboard && window.isSecureContext) {
                    await navigator.clipboard.writeText(text);
                    return true;
                }
            } catch (error) {

            }

            try {
                const textArea = document.createElement("textarea");
                textArea.value = text;

                textArea.style.position = "fixed";
                textArea.style.top = "0";
                textArea.style.left = "0";
                textArea.style.width = "2em";
                textArea.style.height = "2em";
                textArea.style.padding = "0";
                textArea.style.border = "none";
                textArea.style.outline = "none";
                textArea.style.boxShadow = "none";
                textArea.style.background = "transparent";
                textArea.style.opacity = "0";
                textArea.style.pointerEvents = "none";

                textArea.style.fontSize = "12pt";

                textArea.setAttribute("readonly", "");

                document.body.appendChild(textArea);

                const isIOS = /ipad|iphone|ipod/i.test(navigator.userAgent);
                if (isIOS) {
                    const range = document.createRange();
                    range.selectNodeContents(textArea);
                    const selection = window.getSelection();
                    selection.removeAllRanges();
                    selection.addRange(range);
                    textArea.setSelectionRange(0, text.length);
                } else {
                    textArea.select();
                }

                const success = document.execCommand("copy");

                document.body.removeChild(textArea);

                return success;
            } catch (error) {
                return false;
            }
        },

        smoothScrollTo(element, offset = 0) {
            if (!element) return;

            if (this.isMobile()) {
                this.mobileScrollTo(element, offset);
            } else {

                this.tryMultipleScrollMethods(element, offset);
            }
        },

        isMobile() {
            return (
                /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
                    navigator.userAgent,
                ) || window.innerWidth <= 768
            );
        },

        mobileScrollTo(element, offset = 0) {
            try {

                element.scrollIntoView({
                    behavior: "smooth",
                    block: "start",
                    inline: "nearest",
                });

                if (offset !== 0) {
                    setTimeout(() => {
                        const currentPos = this.getScrollTop();
                        const targetPos = currentPos + offset;

                        window.scrollTo({
                            top: targetPos,
                            behavior: "smooth",
                        });
                    }, 300);
                }
            } catch (error) {

                const targetPosition =
                    element.getBoundingClientRect().top +
                    this.getScrollTop() +
                    offset;
                this.animateScrollTo(targetPosition);
            }
        },

        tryMultipleScrollMethods(element, offset = 0) {
            const targetPosition =
                element.getBoundingClientRect().top +
                this.getScrollTop() +
                offset;

            if (this.tryNativeScroll(targetPosition)) {
                return;
            }

            if (this.tryScrollContainer(targetPosition)) {
                return;
            }

            this.animateScrollTo(targetPosition);
        },

        getScrollTop() {
            return (
                window.pageYOffset ||
                document.documentElement.scrollTop ||
                document.body.scrollTop ||
                0
            );
        },

        tryNativeScroll(targetPosition) {
            try {

                window.scrollTo({
                    top: targetPosition,
                    behavior: "smooth",
                });

                return new Promise((resolve) => {
                    const startPos = this.getScrollTop();
                    setTimeout(() => {
                        const currentPos = this.getScrollTop();
                        resolve(Math.abs(currentPos - startPos) > 5);
                    }, 100);
                });
            } catch (error) {
                return false;
            }
        },

        tryScrollContainer(targetPosition) {

            const containerSelectors = [
                "body",
                "html",
                ".app",
                ".main",
                ".container",
                "#app",
                "[data-scroll]",
                ".scroll-container",
            ];

            for (const selector of containerSelectors) {
                const container = document.querySelector(selector);
                if (
                    container &&
                    this.tryScrollElement(container, targetPosition)
                ) {
                    return true;
                }
            }
            return false;
        },

        tryScrollElement(element, targetPosition) {
            try {
                if (element.scrollTo) {
                    element.scrollTo({
                        top: targetPosition,
                        behavior: "smooth",
                    });
                    return true;
                } else if (element.scrollTop !== undefined) {

                    this.animateElementScroll(element, targetPosition);
                    return true;
                }
            } catch (error) {

            }
            return false;
        },

        animateElementScroll(element, targetPosition, duration = 500) {
            const startPosition = element.scrollTop || 0;
            const distance = targetPosition - startPosition;
            let startTime = null;

            function animation(currentTime) {
                if (startTime === null) startTime = currentTime;
                const timeElapsed = currentTime - startTime;
                const progress = Math.min(timeElapsed / duration, 1);

                const ease =
                    progress < 0.5
                        ? 2 * progress * progress
                        : 1 - Math.pow(-2 * progress + 2, 2) / 2;

                element.scrollTop = startPosition + distance * ease;

                if (timeElapsed < duration) {
                    requestAnimationFrame(animation);
                }
            }

            requestAnimationFrame(animation);
        },

        animateScrollTo(targetPosition, duration = 500) {
            const startPosition = this.getScrollTop();
            const distance = targetPosition - startPosition;
            let startTime = null;

            const animation = (currentTime) => {
                if (startTime === null) startTime = currentTime;
                const timeElapsed = currentTime - startTime;
                const progress = Math.min(timeElapsed / duration, 1);

                const ease =
                    progress < 0.5
                        ? 2 * progress * progress
                        : 1 - Math.pow(-2 * progress + 2, 2) / 2;

                const currentPosition = startPosition + distance * ease;

                try {
                    window.scrollTo(0, currentPosition);
                } catch (e) {

                    document.documentElement.scrollTop = currentPosition;
                    document.body.scrollTop = currentPosition;
                }

                if (timeElapsed < duration) {
                    requestAnimationFrame(animation);
                }
            };

            requestAnimationFrame(animation);
        },

        imageDecode(t) {
            const e =
                "ABCD*EFGHIJKLMNOPQRSTUVWX#YZabcdefghijklmnopqrstuvwxyz1234567890";
            let o,
                i,
                a,
                r,
                s,
                c,
                u,
                l = "",
                d = 0;

            function n(e) {
                let t,
                    n = "",
                    o = 0,
                    i = 0,
                    a = 0;
                while (o < e.length)
                    ((i = e.charCodeAt(o)),
                        i < 128
                            ? ((n += String.fromCharCode(i)), o++)
                            : i > 191 && i < 224
                              ? ((a = e.charCodeAt(o + 1)),
                                (n += String.fromCharCode(
                                    ((31 & i) << 6) | (63 & a),
                                )),
                                (o += 2))
                              : ((a = e.charCodeAt(o + 1)),
                                (t = e.charCodeAt(o + 2)),
                                (n += String.fromCharCode(
                                    ((15 & i) << 12) |
                                        ((63 & a) << 6) |
                                        (63 & t),
                                )),
                                (o += 3)));
                return n;
            }

            t = t.replace(/[^A-Za-z0-9*#]/g, "");
            while (d < t.length)
                ((r = e.indexOf(t.charAt(d++))),
                    (s = e.indexOf(t.charAt(d++))),
                    (c = e.indexOf(t.charAt(d++))),
                    (u = e.indexOf(t.charAt(d++))),
                    (o = (r << 2) | (s >> 4)),
                    (i = ((15 & s) << 4) | (c >> 2)),
                    (a = ((3 & c) << 6) | u),
                    (l += String.fromCharCode(o)),
                    64 !== c && (l += String.fromCharCode(i)),
                    64 !== u && (l += String.fromCharCode(a)));

            let reg = new RegExp("", "g");
            l = l.replace(reg, "");
            return ((l = n(l)), l);
        },
    };

    const UI = {

        showToast(message, type = "info") {

            const existingToast = document.getElementById("hjty-toast");
            if (existingToast) {
                existingToast.remove();
            }

            const icons = {
                success: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                    <path d="M9 12L11 14L15 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
                </svg>`,
                error: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                    <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
                    <line x1="15" y1="9" x2="9" y2="15" stroke="currentColor" stroke-width="2"/>
                    <line x1="9" y1="9" x2="15" y2="15" stroke="currentColor" stroke-width="2"/>
                </svg>`,
                warning: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                    <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" stroke="currentColor" stroke-width="2"/>
                    <line x1="12" y1="9" x2="12" y2="13" stroke="currentColor" stroke-width="2"/>
                    <line x1="12" y1="17" x2="12.01" y2="17" stroke="currentColor" stroke-width="2"/>
                </svg>`,
                info: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                    <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
                    <path d="M12 16V12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                    <path d="M12 8H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                </svg>`,
            };

            const toast = document.createElement("div");
            toast.id = "hjty-toast";
            toast.className = `hjty-toast hjty-toast-${type}`;
            toast.innerHTML = `
                <div class="hjty-toast-icon">${icons[type] || icons.info}</div>
                <div class="hjty-toast-message">${message}</div>
            `;

            document.body.appendChild(toast);

            setTimeout(() => toast.classList.add("hjty-toast-show"), 10);

            setTimeout(() => {
                toast.classList.remove("hjty-toast-show");
                setTimeout(() => toast.remove(), 300);
            }, 3000);
        },

        showLoadingModal(
            message = "加载中...",
            showCloseBtn = true,
            onClose = null,
        ) {

            const existingModal = document.getElementById(
                CONFIG.MODAL_IDS.LOADING,
            );
            if (existingModal) {
                existingModal.remove();
            }

            const loadingModal = document.createElement("div");
            loadingModal.id = CONFIG.MODAL_IDS.LOADING;
            loadingModal.className = "hjty-modal-overlay hjty-loading-overlay";

            if (onClose && typeof onClose === "function") {
                loadingModal._onCloseCallback = onClose;
            }

            const closeButtonHtml = showCloseBtn
                ? '<button class="hjty-modal-close hjty-loading-close" title="关闭">×</button>'
                : "";

            loadingModal.innerHTML = `
                <div class="hjty-modal-content hjty-loading-content">
                    <div class="hjty-loading-header">${closeButtonHtml}</div>
                    <div class="hjty-loading-body">
                        <div class="hjty-loading-spinner">
                            <div class="hjty-spinner-ring"></div>
                        </div>
                        <p class="hjty-loading-message">${message}</p>
                    </div>
                </div>
            `;

            document.body.appendChild(loadingModal);

            if (showCloseBtn) {
                const closeBtn = loadingModal.querySelector(
                    ".hjty-loading-close",
                );
                closeBtn.addEventListener("click", () =>
                    this.hideLoadingModal(),
                );
            }

            loadingModal.addEventListener("click", (e) => {

                if (e.target === loadingModal) {
                    e.preventDefault();
                    e.stopPropagation();
                }
            });

            return loadingModal;
        },

        hideLoadingModal(executeCallback = true) {
            const loadingModal = document.getElementById(
                CONFIG.MODAL_IDS.LOADING,
            );
            if (loadingModal) {
                if (executeCallback && loadingModal._onCloseCallback) {
                    try {
                        loadingModal._onCloseCallback();
                    } catch (error) {}
                }
                loadingModal.remove();
            }
        },

        showActions(title = "操作选项", actions = [], onAction = null) {

            const existingDrawer =
                document.getElementById("hjty-action-drawer");
            if (existingDrawer) {
                existingDrawer.remove();
            }

            const drawer = document.createElement("div");
            drawer.id = "hjty-action-drawer";
            drawer.className = "hjty-action-drawer-overlay";

            const actionsHtml = actions
                .map(
                    (action) => `
                <div class="hjty-action-item" data-key="${action.key || ""}" data-url="${action.url || ""}">
                    <div class="hjty-action-content">
                        <div class="hjty-action-name">${action.name || ""}</div>
                        ${action.subname ? `<div class="hjty-action-subname">${action.subname}</div>` : ""}
                    </div>
                    <div class="hjty-action-arrow">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                            <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                        </svg>
                    </div>
                </div>
            `,
                )
                .join("");

            drawer.innerHTML = `
                <div class="hjty-action-drawer">
                    <div class="hjty-action-header">
                        <h3 class="hjty-action-title">${title}</h3>
                    </div>
                    <div class="hjty-action-list">
                        ${actionsHtml}
                    </div>
                    <div class="hjty-action-footer">
                        <button class="hjty-action-close-btn">关闭</button>
                    </div>
                </div>
            `;

            document.body.appendChild(drawer);

            const actionItems = drawer.querySelectorAll(".hjty-action-item");
            const closeBtn = drawer.querySelector(".hjty-action-close-btn");

            actionItems.forEach((item) => {
                item.addEventListener("click", () => {
                    if (onAction && typeof onAction === "function") {
                        onAction(item.dataset);
                    }
                });
            });

            closeBtn.addEventListener("click", () => this.hideActions());

            drawer.addEventListener("click", (e) => {
                if (e.target === drawer) this.hideActions();
            });

            setTimeout(
                () => drawer.classList.add("hjty-action-drawer-show"),
                10,
            );

            return drawer;
        },

        hideActions() {
            const drawer = document.getElementById("hjty-action-drawer");
            if (drawer) {
                drawer.classList.remove("hjty-action-drawer-show");
                setTimeout(() => drawer.remove(), 300);
            }
        },

        showPlayer(src = "") {

            const existingPlayer = document.getElementById("hjty-player-modal");
            if (existingPlayer) {
                existingPlayer.remove();
            }

            const playerModal = document.createElement("div");
            playerModal.id = "hjty-player-modal";
            playerModal.className = "hjty-player-modal-overlay";

            playerModal.innerHTML = `
                <div class="hjty-player-modal-content">
                    <div class="hjty-player-header">
                        <button class="hjty-player-close" title="关闭播放器">×</button>
                    </div>
                    <div class="hjty-player-body">
                        <div id="hjty-play-box">
                            <iframe style="display:block;border:0;width: 100%;height: 500px" allow="autoplay; encrypted-media" allowfullscreen src="${src}"></iframe>
                        </div>
                    </div>
                </div>
            `;

            document.body.appendChild(playerModal);

            const closeBtn = playerModal.querySelector(".hjty-player-close");
            closeBtn.addEventListener("click", () => this.hidePlayer());

            playerModal.addEventListener("click", (e) => {
                if (e.target === playerModal) this.hidePlayer();
            });

            const handleEscKey = (e) => {
                if (e.key === "Escape") {
                    this.hidePlayer();
                    document.removeEventListener("keydown", handleEscKey);
                }
            };
            document.addEventListener("keydown", handleEscKey);

            setTimeout(
                () => playerModal.classList.add("hjty-player-modal-show"),
                10,
            );

            return playerModal;
        },

        hidePlayer() {
            const playerModal = document.getElementById("hjty-player-modal");
            if (playerModal) {
                playerModal.classList.remove("hjty-player-modal-show");
                setTimeout(() => playerModal.remove(), 300);
            }
        },
    };

    const App = {

        getShortVideoLink() {

            UI.showLoadingModal("正在处理视频地址...", true, function () {
                UI.showToast("操作已取消", "warning");
            });

            fetch(server + "/api/video" + "?v=" + CONFIG.VERSION, {
                method: "POST",
                headers: {
                    token: Utils.getUserToken(),
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ id: shortVideoId, url: shortVideoUrl }),
            })
                .then((response) => {
                    if (!response.ok) {
                        UI.hideLoadingModal(false);
                        throw new Error(`HTTP ${response.status}`);
                    }
                    return response.json();
                })
                .then((data) => {
                    UI.hideLoadingModal(false);
                    if (data.code) {
                        if (data.msg === "有新版本") {
                            newVersion = data.data;
                            this.showUpdateConfirm();
                            return;
                        }
                        if (data.data !== "") {
                            m3u8_url = server + data.data;
                            play_url =
                                server +
                                "/play?url=" +
                                Utils.base64.encode(m3u8_url);
                            this.showShortVideoActions();
                        } else {
                            UI.showToast("没有可解锁的内容", "error");
                        }
                    } else {
                        UI.showToast(data.msg, "error");
                        if (data.data["confirmButtonText"] === "重新登录") {
                            localStorage.removeItem(
                                CONFIG.STORAGE_KEYS.USER_TOKEN,
                            );
                            localStorage.removeItem(
                                CONFIG.STORAGE_KEYS.USER_NAME,
                            );
                            this.updateToolbarButtons(false);
                            showLoginModal();
                        } else if (
                            data.data["confirmButtonText"] === "打开官网"
                        ) {
                            setTimeout(() => {
                                window.open(server + "/");
                            }, 1500);
                        }
                    }
                })
                .catch((error) => {
                    UI.hideLoadingModal(false);
                });
        },

        getTopic(pid) {
            return new Promise(async (resolve, reject) => {
                try {
                    const response = await fetch(`/api/topic/${pid}`, {
                        method: "GET",
                        headers: {
                            "Content-Type": "application/json",
                        },
                    });

                    if (!response.ok) {
                        throw new Error(`HTTP ${response.status}`);
                    }

                    const res = await response.json();

                    if (res.isEncrypted) {
                        topic = Utils.decrypt(res.data);
                    } else {
                        topic = res.data;
                    }

                    resolve(topic);
                } catch (error) {
                    reject("网络错误: 3");
                }
            });
        },

        showTopic(data, pid) {
            if (!data["attachments"]) {
                UI.showToast("没有可解锁的内容", "warning");
                return;
            }

            this.getAttachments(data).then((html) => {
                if (html) {
                    const sellBtn = document.querySelector(".sell-btn");
                    const hjsellContainer =
                        document.getElementById("hjsell-container");

                    if (sellBtn) {

                        const newContainer = document.createElement("div");
                        newContainer.id = "hjsell-container";
                        newContainer.className = "hjsell-container";
                        newContainer.innerHTML =
                            '<div class="hssell-content">' + html + "</div>";

                        sellBtn.parentNode.replaceChild(newContainer, sellBtn);
                    } else if (hjsellContainer) {

                        const contentElement =
                            hjsellContainer.querySelector(".hssell-content");
                        if (contentElement) {
                            contentElement.innerHTML = html;
                        }
                    }

                    const finalContainer =
                        document.getElementById("hjsell-container");
                    if (!finalContainer) {
                        UI.showToast("请等待页面加载完成!", "error");
                        return;
                    }

                    Utils.smoothScrollTo(finalContainer, -100);

                    if (hasPic) {
                        this.showImages();
                    }
                }

                if (video !== null) {
                    this.queryTopic(pid);
                } else {
                    UI.showToast("解锁成功", "success");
                    UI.hideLoadingModal(false);
                }
            });
        },

        queryTopic(pid) {
            fetch(server + "/api/topic/" + pid + "?v=" + CONFIG.VERSION, {
                method: "GET",
                headers: {
                    token: Utils.getUserToken(),
                    "Content-Type": "application/json",
                },
            })
                .then((response) => {
                    if (!response.ok) {
                        throw new Error(`HTTP ${response.status}`);
                    }
                    return response.json();
                })
                .then((data) => {
                    UI.hideLoadingModal(false);
                    if (data.code) {
                        if (data.msg === "有新版本") {
                            newVersion = data.data;
                            this.showUpdateConfirm();
                            return;
                        }

                        if (data.data["m3u8_url"] !== "") {
                            m3u8_url = server + data.data["m3u8_url"];
                            play_url =
                                server +
                                "/play?url=" +
                                Utils.base64.encode(m3u8_url);
                            this.showVideoActions();
                        } else {
                            UI.showToast("没有可解锁的内容", "error");
                        }
                    } else {
                        UI.showToast(data.msg, "error");
                        if (data.data["confirmButtonText"] === "重新登录") {
                            localStorage.removeItem(
                                CONFIG.STORAGE_KEYS.USER_TOKEN,
                            );
                            localStorage.removeItem(
                                CONFIG.STORAGE_KEYS.USER_NAME,
                            );
                            this.updateToolbarButtons(false);
                            showLoginModal();
                        } else if (
                            data.data["confirmButtonText"] === "打开官网"
                        ) {
                            setTimeout(() => {
                                window.open(server + "/");
                            }, 1500);
                        }
                    }
                })
                .catch((error) => {});
        },

        showImages() {
            document
                .querySelectorAll("#hjsell-container img")
                .forEach(async (img) => {
                    try {
                        const response = await fetch(img.dataset.url);

                        if (!response.ok) {
                            throw new Error(`HTTP ${response.status}`);
                        }

                        const data = await response.text();
                        img.src = Utils.imageDecode(data);
                    } catch (error) {}
                });
        },

        showShortVideoActions() {
            const actions = [
                {
                    name: "复制视频地址",
                    key: "copy",
                    subname: "可复制视频地址到其他地方播放或下载",
                },
                {
                    name: "在线播放器1",
                    key: "open",
                    subname: "可正常播放",
                    url:
                        "https://m3u8player.app/zh-CN/embed/m3u8/?video_url=" +
                        encodeURIComponent(m3u8_url) +
                        "&autoplay=true",
                },
                {
                    name: "在线播放器2",
                    key: "open",
                    subname: "可正常播放",
                    url:
                        "https://anym3u8player.com/?url=" +
                        encodeURIComponent(m3u8_url),
                },
                {
                    name: "在线播放器3",
                    key: "copy-open",
                    subname: "打开后,会自动复制视频地址,请手动粘贴播放",
                    url: "https://1ktools.com/zh-cn/tools/network/m3u8",
                },
                {
                    name: "视频下载通道1",
                    key: "open",
                    subname: "【推荐】支持自动下载和在线播放",
                    url: "https://getm3u8.com/?source=" + m3u8_url,
                },
                {
                    name: "视频下载通道2",
                    key: "open",
                    subname: "推荐在电脑端操作",
                    url:
                        "https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html?source=" +
                        m3u8_url,
                },
                {
                    name: "视频下载通道3",
                    key: "open",
                    subname: "需要手动点击下载,推荐在电脑端操作",
                    url:
                        "https://tools.thatwind.com/tool/m3u8downloader#m3u8=" +
                        encodeURIComponent(m3u8_url),
                },
                {
                    name: "海角天涯官网",
                    key: "open",
                    subname: "点击访问",
                    url: server + "/",
                },
            ];

            UI.showActions(
                "视频解锁成功,下滑查看更多操作",
                actions,
                (item) => {
                    this.handleVideoAction(item);
                },
            );
        },

        showVideoActions() {
            const actions = [
                {
                    name: "播放视频(站内)",
                    key: "play",
                    subname:
                        "如无法在线播放,可尝试其他在线播放器,或下载后再观看",
                },
                {
                    name: "复制视频地址",
                    key: "copy",
                    subname: "可复制视频地址到其他地方播放或下载",
                },
                {
                    name: "在线播放器1",
                    key: "open",
                    subname: "可正常播放",
                    url:
                        "https://m3u8player.app/zh-CN/embed/m3u8/?video_url=" +
                        encodeURIComponent(m3u8_url) +
                        "&autoplay=true",
                },
                {
                    name: "在线播放器2",
                    key: "open",
                    subname: "可正常播放",
                    url:
                        "https://anym3u8player.com/?url=" +
                        encodeURIComponent(m3u8_url),
                },
                {
                    name: "在线播放器3",
                    key: "copy-open",
                    subname: "打开后,会自动复制视频地址,请手动粘贴播放",
                    url: "https://1ktools.com/zh-cn/tools/network/m3u8",
                },
                {
                    name: "视频下载通道1",
                    key: "open",
                    subname: "【推荐】支持自动下载和在线播放",
                    url:
                        "https://getm3u8.com/?source=" +
                        m3u8_url +
                        (m3u8_url.includes("?") ? "&" : "?") +
                        "title=" +
                        topic.title,
                },
                {
                    name: "视频下载通道2",
                    key: "open",
                    subname: "推荐在电脑端操作",
                    url:
                        "https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html?source=" +
                        m3u8_url,
                },
                {
                    name: "视频下载通道3",
                    key: "open",
                    subname: "需要手动点击下载,推荐在电脑端操作",
                    url:
                        "https://tools.thatwind.com/tool/m3u8downloader#m3u8=" +
                        encodeURIComponent(m3u8_url),
                },
                {
                    name: "海角天涯官网",
                    key: "open",
                    subname: "点击访问",
                    url: server + "/",
                },
            ];

            UI.showActions(
                "帖子解锁成功,下滑查看更多操作",
                actions,
                (item) => {
                    this.handleVideoAction(item);
                },
            );
        },

        handleVideoAction(item) {
            switch (item.key) {
                case "play":

                    UI.showPlayer(play_url);
                    UI.showToast("视频加载中,请稍后...", "info");
                    break;

                case "copy":

                    Utils.copyText(m3u8_url).then((success) => {
                        if (success) {
                            UI.showToast("视频地址已复制", "success");
                        } else {
                            UI.showToast("复制失败", "error");
                        }
                    });
                    break;

                case "open":

                    if (item.url) {
                        UI.showToast("正在打开链接...", "info");
                        setTimeout(() => {
                            window.open(item.url, "_blank");
                        }, 500);
                    }
                    break;

                case "copy-open":

                    if (item.url) {
                        Utils.copyText(m3u8_url).then((success) => {
                            if (success) {
                                UI.showToast("正在打开链接...", "info");
                                setTimeout(() => {
                                    window.open(item.url, "_blank");
                                }, 500);
                            } else {
                                UI.showToast("复制失败", "error");
                            }
                        });
                    }
                    break;

                default:
                    console.log("未知操作:", key);
            }
        },

        getAttachments(data) {
            return new Promise((resolve, reject) => {
                let html = "";

                if (!data || !data.attachments) {
                    resolve(html);
                    return;
                }

                data.attachments.forEach((item) => {
                    switch (item.category) {
                        case "video":
                            video = item;
                            break;
                        case "audio":
                            html +=
                                '<p style="text-align: center"><audio src="' +
                                item.remoteUrl +
                                '" controls="true" id="showaudio"></audio></p>';
                            break;
                        case "images":
                            hasPic = true;
                            if (!data.content.includes(item.remoteUrl)) {
                                html +=
                                    '<p><img data-url="' +
                                    item.remoteUrl +
                                    '" src="/images/common/project/loading.gif" alt="" data-id="' +
                                    item.id +
                                    '" lazy="loaded"></p>';
                            }
                            break;
                    }
                });

                resolve(html);
            });
        },

        async checkUpdate() {
            const updateUrl = `${server}/api/update?v=${CONFIG.VERSION}`;
            const token = Utils.getUserToken();

            try {
                const response = await fetch(updateUrl, {
                    method: "GET",
                    headers: {
                        token: token,
                        "Content-Type": "application/json",
                    },
                });

                const data = await response.json();
                if (data.msg === "有新版本") {
                    newVersion = data.data;
                    this.showUpdateConfirm();
                }
            } catch (error) {
                UI.showToast("检查更新失败,请切换其他服务器", "error");
            }
        },

        showUpdateConfirm() {
            const updateModal = document.createElement("div");
            updateModal.id = CONFIG.MODAL_IDS.UPDATE_CONFIRM;
            updateModal.className = "hjty-modal-overlay";
            updateModal.innerHTML = `
                <div class="hjty-modal-content hjty-update-content">
                    <div class="hjty-modal-header">
                        <h3>脚本升级</h3>
                        <button class="hjty-modal-close" onclick="this.closest('.hjty-modal-overlay').remove()">×</button>
                    </div>
                    <div class="hjty-modal-body">
                        <div class="hjty-update-info">
                            <div class="hjty-update-icon">
                                <svg width="48" height="48" viewBox="0 0 24 24" fill="none">
                                    <circle cx="12" cy="12" r="10" stroke="#3b82f6" stroke-width="2"/>
                                    <path d="M8 12L12 8L16 12" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                                    <path d="M12 8V16" stroke="#3b82f6" stroke-width="2" stroke-linecap="round"/>
                                    <path d="M16 16H8" stroke="#60a5fa" stroke-width="2" stroke-linecap="round"/>
                                </svg>
                            </div>
                            <p class="hjty-update-message">检测到新版本,请更新后再使用!</p>
                            <p class="hjty-update-details">当前版本: ${CONFIG.VERSION}</p>
                            ${newVersion ? `<p class="hjty-update-details">最新版本: ${newVersion}</p>` : ""}
                        </div>
                    </div>
                    <div class="hjty-modal-footer">
                        <button class="hjty-btn hjty-btn-primary hjty-confirm-update-btn">立即更新</button>
                    </div>
                </div>
            `;

            document.body.appendChild(updateModal);

            const confirmBtn = updateModal.querySelector(
                ".hjty-confirm-update-btn",
            );

            confirmBtn.addEventListener("click", () => {
                updateModal.remove();
                window.open(
                    `${server}/js/script.user.js?v=${Math.random()}`,
                    "_blank",
                );
            });

            updateModal.addEventListener("click", (e) => {
                if (e.target === updateModal) updateModal.remove();
            });
        },

        logout() {
            localStorage.removeItem(CONFIG.STORAGE_KEYS.USER_TOKEN);
            localStorage.removeItem(CONFIG.STORAGE_KEYS.USER_NAME);
            this.updateToolbarButtons(false);
            UI.showToast("已退出登录", "info");
        },

        updateToolbarButtons(isLoggedIn) {
            const toolbarContainer = document.querySelector(
                CONFIG.SELECTORS.TOOLBAR_CONTAINER,
            );
            if (!toolbarContainer) return;

            let userBtn = toolbarContainer.querySelector(
                CONFIG.SELECTORS.USER_BTN,
            );

            if (isLoggedIn) {
                if (!userBtn) {
                    userBtn = document.createElement("button");
                    userBtn.className = "hjty-toolbar-btn hjty-user-btn";
                    userBtn.title = "用户信息";
                    userBtn.innerHTML = `
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                            <path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                            <circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2"/>
                        </svg>
                    `;

                    userBtn.addEventListener("click", () =>
                        this.showUserInfoModal(),
                    );

                    const unlockBtn = toolbarContainer.querySelector(
                        CONFIG.SELECTORS.UNLOCK_BTN,
                    );
                    unlockBtn.insertAdjacentElement("afterend", userBtn);
                }
            } else {
                if (userBtn) userBtn.remove();
            }
        },

        showUserInfoModal() {
            const userName =
                localStorage.getItem(CONFIG.STORAGE_KEYS.USER_NAME) ||
                "未知用户";

            const userModal = document.createElement("div");
            userModal.id = CONFIG.MODAL_IDS.USER_INFO;
            userModal.className = "hjty-modal-overlay";
            userModal.innerHTML = `
                <div class="hjty-modal-content hjty-user-info-content">
                    <div class="hjty-modal-header">
                        <h3>用户信息(当前版本:${CONFIG.VERSION})</h3>
                        <button class="hjty-modal-close" onclick="this.closest('.hjty-modal-overlay').remove()">×</button>
                    </div>
                    <div class="hjty-modal-body">
                        <div class="hjty-user-info">
                            <div class="hjty-user-avatar">
                                <svg width="64" height="64" viewBox="0 0 24 24" fill="none">
                                    <circle cx="12" cy="12" r="10" fill="#8b5cf6" opacity="0.1"/>
                                    <path d="M18 19V17C18 15.3431 16.6569 14 15 14H9C7.34315 14 6 15.3431 6 17V19" stroke="#8b5cf6" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
                                    <circle cx="12" cy="8" r="3" stroke="#8b5cf6" stroke-width="1.8"/>
                                </svg>
                            </div>
                            <div class="hjty-user-details">
                                <p class="hjty-user-name">${userName}</p>
                                <p class="hjty-user-status">已登录</p>
                            </div>
                        </div>
                    </div>
                    <div class="hjty-modal-footer">
                        <button class="hjty-btn hjty-btn-secondary hjty-visit-website-btn">访问官网</button>
                        <button class="hjty-btn hjty-btn-danger hjty-logout-btn">退出登录</button>
                    </div>
                </div>
            `;

            document.body.appendChild(userModal);

            const visitWebsiteBtn = userModal.querySelector(
                ".hjty-visit-website-btn",
            );
            const logoutBtn = userModal.querySelector(".hjty-logout-btn");

            visitWebsiteBtn.addEventListener("click", () =>
                window.open(server, "_blank"),
            );
            logoutBtn.addEventListener("click", () => {
                userModal.remove();
                this.showLogoutConfirm();
            });

            userModal.addEventListener("click", (e) => {
                if (e.target === userModal) userModal.remove();
            });
        },

        showLogoutConfirm() {
            const confirmModal = document.createElement("div");
            confirmModal.id = CONFIG.MODAL_IDS.LOGOUT_CONFIRM;
            confirmModal.className = "hjty-modal-overlay";
            confirmModal.innerHTML = `
                <div class="hjty-modal-content hjty-confirm-content">
                    <div class="hjty-modal-header">
                        <h3>确认退出</h3>
                        <button class="hjty-modal-close" onclick="this.closest('.hjty-modal-overlay').remove()">×</button>
                    </div>
                    <div class="hjty-modal-body">
                        <p class="hjty-confirm-message">确定要退出登录吗?</p>
                    </div>
                    <div class="hjty-modal-footer">
                        <button class="hjty-btn hjty-btn-secondary hjty-cancel-btn">取消</button>
                        <button class="hjty-btn hjty-btn-primary hjty-confirm-btn">确认退出</button>
                    </div>
                </div>
            `;

            document.body.appendChild(confirmModal);

            const cancelBtn = confirmModal.querySelector(".hjty-cancel-btn");
            const confirmBtn = confirmModal.querySelector(".hjty-confirm-btn");

            cancelBtn.addEventListener("click", () => confirmModal.remove());
            confirmBtn.addEventListener("click", () => {
                confirmModal.remove();
                this.logout();
            });

            confirmModal.addEventListener("click", (e) => {
                if (e.target === confirmModal) confirmModal.remove();
            });
        },

        formatTitle(data) {
            data.results.forEach((item) => {
                const tips = [];
                tips.push(
                    "money_type" in item
                        ? ["", "💰", "💎"][item.money_type]
                        : "",
                );
                item.hasVideo && tips.push("🎥");
                item.hasAudio && tips.push("🎵");
                item.title = `${tips.join(" ")} ${item.title}`;
            });
            return data;
        },

        getShortVideoUrl(responseText) {
            try {
                let res = JSON.parse(responseText);
                if (res["isEncrypted"]) {
                    let data = Utils.decrypt(res.data);
                    if (
                        data["category"] &&
                        data["category"] === "video" &&
                        data["remoteUrl"]
                    ) {
                        if (!data["remoteUrl"].startsWith("http")) {
                            shortVideoUrl =
                                window.location.origin + data["remoteUrl"];
                        } else {
                            shortVideoUrl = data["remoteUrl"];
                        }
                        shortVideoId = data["id"];
                    }
                }
            } catch (e) {}
        },

        fixShortVideo(responseText) {
            try {
                responseText = JSON.parse(responseText);
                let video = Utils.decrypt(responseText.data);
                video.type = 1;
                video.amount = 0;
                video.money_type = 0;
                video.vip = 0;
                video.message = "";
                responseText.data = Utils.encrypt(video);
                return JSON.stringify(responseText);
            } catch (e) {
                return responseText;
            }
        },
    };

    let buttonInitialized = false;

    function checkTitleAndInitButton() {
        if (buttonInitialized) return true;

        const currentTitle = document.title;
        if (currentTitle === "海角社区") {
            buttonInitialized = true;
            start();
            return true;
        }
        return false;
    }

    function setupTitleObserver() {
        const titleObserver = new MutationObserver((mutations) => {
            if (!buttonInitialized && document.title === "海角社区") {
                buttonInitialized = true;
                start();
                titleObserver.disconnect();
            }
        });

        const titleElement = document.querySelector("title");
        if (titleElement) {
            titleObserver.observe(titleElement, {
                childList: true,
                characterData: true,
                subtree: true,
            });
        }

        let checkCount = 0;
        const maxChecks = 10;
        const checkInterval = setInterval(() => {
            if (buttonInitialized || checkCount >= maxChecks) {
                clearInterval(checkInterval);
                return;
            }

            if (checkTitleAndInitButton()) {
                buttonInitialized = true;
                clearInterval(checkInterval);
            }

            checkCount++;
        }, 1000);
    }

    function start() {
        initStyles();
        initFloatingButton();

        if (Utils.checkLoginStatus()) {
            App.checkUpdate();
        }
    }

    function initStyles() {
        if (document.getElementById("hjty-global-styles")) return;

        const style = document.createElement("style");
        style.id = "hjty-global-styles";
        style.textContent = `
            /* 浮动工具栏样式 */
            #hjty-floating-toolbar {
                position: fixed;
                top: 50%;
                right: 20px;
                transform: translateY(-50%);
                z-index: 10000;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            }

            #hjty-floating-toolbar.hjty-collapsed {
                right: -48px; /* 隐藏主体,只露出切换按钮 */
            }

            .hjty-toolbar-container {
                display: flex;
                flex-direction: column;
                gap: 8px;
                padding: 8px;
                background: rgba(255, 255, 255, 0.95);
                backdrop-filter: blur(10px);
                border-radius: 12px;
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
                border: 1px solid rgba(255, 255, 255, 0.2);
                position: relative;
            }

            /* 切换按钮样式 */
            .hjty-toggle-btn {
                position: absolute;
                left: -20px;
                top: 50%;
                transform: translateY(-50%);
                width: 24px;
                height: 48px;
                border: none;
                border-radius: 12px 0 0 12px;
                background: rgba(255, 255, 255, 0.95);
                backdrop-filter: blur(10px);
                color: #374151;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                z-index: 1;
            }

            .hjty-toggle-btn:hover {
                background: rgba(255, 255, 255, 1);
                transform: translateY(-50%) scale(1.05);
                color: #1f2937;
            }

            .hjty-toggle-btn:active {
                transform: translateY(-50%) scale(0.95);
            }

            .hjty-toggle-btn svg {
                transition: transform 0.3s ease;
            }

            #hjty-floating-toolbar.hjty-collapsed .hjty-toggle-btn svg {
                transform: rotate(180deg);
            }

            .hjty-toolbar-btn {
                width: 32px;
                height: 32px;
                border: none;
                border-radius: 8px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                position: relative;
                overflow: hidden;
            }

            .hjty-toolbar-btn:hover {
                box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
            }

            .hjty-toolbar-btn:active {
                transform: translateY(0);
                box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
            }

            .hjty-unlock-btn {
                background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
            }

            .hjty-unlock-btn:hover {
                box-shadow: 0 8px 25px rgba(17, 153, 142, 0.4);
            }

            .hjty-settings-btn {
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            }

            .hjty-settings-btn:hover {
                box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
            }

            .hjty-user-btn {
                background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
            }

            .hjty-user-btn:hover {
                box-shadow: 0 8px 25px rgba(139, 92, 246, 0.4);
            }

            .hjty-toolbar-btn svg {
                transition: transform 0.3s ease;
            }

            .hjty-toolbar-btn:hover svg {
                transform: scale(1.1);
            }

            /* Modal 通用样式 */
            .hjty-modal-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.5);
                backdrop-filter: blur(5px);
                display: flex;
                align-items: center;
                justify-content: center;
                animation: hjty-fadeIn 0.3s ease;
                z-index: 20000;
            }

            .hjty-modal-content {
                background: white;
                border-radius: 16px;
                box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
                width: 90%;
                max-width: 600px;
                max-height: 80vh;
                overflow: hidden;
                animation: hjty-slideUp 0.3s ease;
            }

            .hjty-modal-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 20px 24px;
                border-bottom: 1px solid #e5e7eb;
            }

            .hjty-modal-header h3 {
                margin: 0;
                font-size: 18px;
                font-weight: 600;
                color: #1f2937;
            }

            .hjty-modal-close {
                background: none;
                border: none;
                font-size: 24px;
                color: #6b7280;
                cursor: pointer;
                padding: 0;
                width: 32px;
                height: 32px;
                display: flex;
                align-items: center;
                justify-content: center;
                border-radius: 8px;
                transition: all 0.2s ease;
            }

            .hjty-modal-close:hover {
                background: #f3f4f6;
                color: #374151;
            }

            .hjty-modal-body {
                padding: 16px 24px;
                max-height: 350px;
                overflow-y: auto;
            }

            .hjty-modal-footer {
                padding: 16px 24px;
                border-top: 1px solid #e5e7eb;
                display: flex;
                justify-content: space-between;
                gap: 12px;
            }

            /* 登录Modal样式 */
            .hjty-login-content {
                max-width: 400px;
                width: 90%;
            }

            .hjty-login-content .hjty-modal-body {
                padding: 24px;
            }

            .hjty-login-form {
                padding: 0;
            }

            .hjty-form-group {
                margin-bottom: 20px;
            }

            .hjty-form-group:last-of-type {
                margin-bottom: 0;
            }

            .hjty-form-group label {
                display: block;
                margin-bottom: 8px;
                font-size: 14px;
                font-weight: 500;
                color: #374151;
            }

            .hjty-form-input {
                display: block;
                width: 100%;
                padding: 12px 16px;
                border: 1px solid #d1d5db;
                border-radius: 8px;
                font-size: 14px;
                transition: all 0.2s ease;
                background: white;
                box-sizing: border-box;
            }

            .hjty-form-input:focus {
                outline: none;
                border-color: #3b82f6;
                box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
            }

            .hjty-form-input::placeholder {
                color: #9ca3af;
            }

            .hjty-form-actions {
                display: flex;
                gap: 12px;
                margin-top: 32px;
            }

            /* 按钮样式 */
            .hjty-btn {
                flex: 1;
                padding: 12px 20px;
                border: none;
                border-radius: 8px;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.2s ease;
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 8px;
            }

            .hjty-btn-primary {
                background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
                color: white;
            }

            .hjty-btn-primary:hover {
                background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
                box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
            }

            .hjty-btn-primary:disabled {
                background: #9ca3af;
                cursor: not-allowed;
                transform: none;
                box-shadow: none;
                min-height: 44px;
            }

            .hjty-btn-secondary {
                background: white;
                color: #374151;
                border: 1px solid #d1d5db;
            }

            .hjty-btn-secondary:hover {
                background: #f9fafb;
                border-color: #9ca3af;
            }

            .hjty-btn-danger {
                background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
                color: white;
            }

            .hjty-btn-danger:hover {
                background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
                box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
            }

            /* 按钮内加载spinner样式 */
            .hjty-btn .hjty-loading-spinner {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                width: 16px;
                height: 16px;
                margin: 0;
                flex-shrink: 0;
            }

            .hjty-btn .hjty-spinner-ring {
                width: 14px;
                height: 14px;
                border: 2px solid rgba(255, 255, 255, 0.3);
                border-top: 2px solid white;
                border-radius: 50%;
                animation: hjty-spin 1s linear infinite;
            }

            /* 加载框样式 */
            #hjty-loading-modal {
                z-index: 40000;
            }

            .hjty-loading-overlay {
                background: rgba(0, 0, 0, 0.6);
                backdrop-filter: blur(8px);
            }

            .hjty-loading-content {
                max-width: 320px;
                width: 90%;
            }

            .hjty-loading-header {
                position: relative;
                height: 20px;
                padding: 16px 16px 0 16px;
            }

            .hjty-loading-close {
                position: absolute;
                top: 16px;
                right: 16px;
                width: 24px;
                height: 24px;
                font-size: 20px;
                border-radius: 6px;
            }

            .hjty-loading-body {
                padding: 20px 24px 32px 24px;
                text-align: center;
            }

            .hjty-loading-spinner {
                margin: 0 auto 20px auto;
                width: 40px;
                height: 40px;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            .hjty-spinner-ring {
                width: 32px;
                height: 32px;
                border: 3px solid #e5e7eb;
                border-top: 3px solid #3b82f6;
                border-radius: 50%;
                animation: hjty-spin 1s linear infinite;
            }

            .hjty-loading-message {
                margin: 0;
                font-size: 16px;
                color: #374151;
                font-weight: 500;
                line-height: 1.5;
            }

            /* 用户信息对话框样式 */
            .hjty-user-info-content {
                max-width: 400px;
                width: 90%;
            }

            .hjty-user-info {
                display: flex;
                align-items: center;
                gap: 16px;
                padding: 20px 0;
            }

            .hjty-user-avatar {
                flex-shrink: 0;
            }

            .hjty-user-details {
                flex: 1;
            }

            .hjty-user-name {
                margin: 0 0 8px 0;
                font-size: 18px;
                font-weight: 600;
                color: #1f2937;
            }

            .hjty-user-status {
                margin: 0;
                font-size: 14px;
                color: #10b981;
                font-weight: 500;
            }

            /* 确认对话框样式 */
            .hjty-confirm-content {
                max-width: 400px;
                width: 90%;
            }

            .hjty-confirm-message {
                margin: 0;
                font-size: 16px;
                color: #374151;
                text-align: center;
                line-height: 1.5;
            }

            /* 服务器切换Modal样式 */
            .hjty-server-list {
                display: flex;
                flex-direction: column;
                gap: 8px;
            }

            .hjty-server-item {
                display: flex;
                align-items: center;
                padding: 12px 16px;
                border: 1px solid #e5e7eb;
                border-radius: 8px;
                transition: all 0.2s ease;
                background: #fafafa;
                cursor: pointer;
            }

            .hjty-server-item:hover {
                border-color: #d1d5db;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
                background: #f9fafb;
            }

            .hjty-server-item.hjty-online {
                border-color: #10b981;
                background: #f0fdf4;
            }

            .hjty-server-item.hjty-offline {
                border-color: #ef4444;
                background: #fef2f2;
            }

            .hjty-server-item.hjty-selected {
                border-color: #3b82f6;
                background: #eff6ff;
            }

            .hjty-server-item:not(.hjty-selected):hover {
                border-color: #3b82f6;
                background: #f1f5f9;
            }

            .hjty-server-info {
                flex: 1;
                min-width: 0;
            }

            .hjty-server-name {
                font-weight: 500;
                color: #1f2937;
                font-size: 14px;
                position: relative;
            }

            .hjty-server-item.hjty-selected .hjty-server-name {
                color: #1e40af;
                font-weight: 600;
            }

            .hjty-radio-indicator {
                width: 16px;
                height: 16px;
                border: 2px solid #d1d5db;
                border-radius: 50%;
                margin-right: 12px;
                flex-shrink: 0;
                position: relative;
                transition: all 0.2s ease;
            }

            .hjty-radio-indicator.hjty-radio-selected {
                border-color: #3b82f6;
                background: #3b82f6;
            }

            .hjty-radio-indicator.hjty-radio-selected::after {
                content: '';
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 6px;
                height: 6px;
                background: white;
                border-radius: 50%;
            }

            .hjty-server-status {
                display: flex;
                align-items: center;
                gap: 6px;
                margin: 0 12px;
                flex-shrink: 0;
            }

            .hjty-status-indicator {
                width: 8px;
                height: 8px;
                border-radius: 50%;
                position: relative;
                flex-shrink: 0;
            }

            .hjty-status-indicator.hjty-checking {
                background: #f59e0b;
            }

            .hjty-status-indicator.hjty-online {
                background: #10b981;
            }

            .hjty-status-indicator.hjty-offline {
                background: #ef4444;
            }

            .hjty-status-text {
                font-size: 11px;
                color: #374151;
                min-width: 40px;
                font-weight: 600;
            }

            .hjty-latency {
                font-size: 11px;
                color: #10b981;
                font-weight: 600;
                min-width: 35px;
            }

            .hjty-refresh-btn {
                padding: 10px 20px;
                border: none;
                border-radius: 8px;
                background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
                color: white;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.2s ease;
            }

            .hjty-refresh-btn:hover:not(:disabled) {
                background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
                box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
            }

            .hjty-refresh-btn:disabled {
                background: #9ca3af;
                cursor: not-allowed;
                transform: none;
                box-shadow: none;
            }

            .hjty-switch-btn {
                padding: 10px 20px;
                border: none;
                border-radius: 8px;
                background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
                color: white;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.2s ease;
            }

            .hjty-switch-btn:hover:not(:disabled) {
                background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
                box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
            }

            .hjty-switch-btn:disabled {
                background: #9ca3af;
                cursor: not-allowed;
                transform: none;
                box-shadow: none;
            }

            /* 升级确认框样式 */
            .hjty-update-content {
                max-width: 450px;
                width: 90%;
            }

            .hjty-update-info {
                text-align: center;
                padding: 20px 0;
            }

            .hjty-update-icon {
                margin-bottom: 16px;
                display: flex;
                justify-content: center;
            }

            .hjty-update-message {
                margin: 0 0 16px 0;
                font-size: 18px;
                font-weight: 600;
                color: #1f2937;
            }

            .hjty-update-details {
                margin: 8px 0;
                font-size: 14px;
                color: #6b7280;
            }

            .hjty-update-description {
                margin: 16px 0 0 0;
                font-size: 14px;
                color: #374151;
                background: #f8fafc;
                padding: 12px;
                border-radius: 8px;
                border-left: 4px solid #3b82f6;
                text-align: left;
            }

            /* Toast 提示样式 */
            .hjty-toast {
                position: fixed;
                top: 20px;
                right: 20px;
                display: flex;
                align-items: center;
                gap: 12px;
                padding: 16px 20px;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
                backdrop-filter: blur(10px);
                z-index: 100000;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-size: 14px;
                font-weight: 500;
                max-width: 400px;
                min-width: 300px;
                transform: translateX(100%);
                opacity: 0;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            }

            .hjty-toast-show {
                transform: translateX(0);
                opacity: 1;
            }

            .hjty-toast-success {
                background: rgba(16, 185, 129, 0.95);
                color: white;
                border: 1px solid rgba(16, 185, 129, 0.3);
            }

            .hjty-toast-error {
                background: rgba(239, 68, 68, 0.95);
                color: white;
                border: 1px solid rgba(239, 68, 68, 0.3);
            }

            .hjty-toast-warning {
                background: rgba(245, 158, 11, 0.95);
                color: white;
                border: 1px solid rgba(245, 158, 11, 0.3);
            }

            .hjty-toast-info {
                background: rgba(59, 130, 246, 0.95);
                color: white;
                border: 1px solid rgba(59, 130, 246, 0.3);
            }

            .hjty-toast-icon {
                flex-shrink: 0;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            .hjty-toast-message {
                flex: 1;
                line-height: 1.4;
            }

            /* 操作抽屉样式 */
            .hjty-action-drawer-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.5);
                backdrop-filter: blur(5px);
                z-index: 30000;
                display: flex;
                align-items: flex-end;
                justify-content: center;
                opacity: 0;
                transition: opacity 0.3s ease;
            }

            .hjty-action-drawer-overlay.hjty-action-drawer-show {
                opacity: 1;
            }

            .hjty-action-drawer {
                background: #f8fafc;
                border-radius: 16px 16px 0 0;
                width: 100%;
                max-height: 80vh;
                display: flex;
                flex-direction: column;
                transform: translateY(100%);
                transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.2);
            }

            .hjty-action-drawer-show .hjty-action-drawer {
                transform: translateY(0);
            }

            .hjty-action-header {
                padding: 20px 24px 16px 24px;
                border-bottom: 1px solid #e5e7eb;
                text-align: center;
                flex-shrink: 0;
                background: white;
                border-radius: 16px 16px 0 0;
            }

            .hjty-action-title {
                margin: 0;
                font-size: 18px;
                font-weight: 600;
                color: #1f2937;
            }

            .hjty-action-list {
                flex: 1;
                overflow-y: auto;
                padding: 8px 0;
                background: white;
            }

            .hjty-action-item {
                display: flex;
                align-items: center;
                padding: 16px 24px;
                cursor: pointer;
                transition: background-color 0.2s ease;
                border-bottom: 1px solid #f3f4f6;
            }

            .hjty-action-item:hover {
                background: #f9fafb;
            }

            .hjty-action-item:active {
                background: #f3f4f6;
            }

            .hjty-action-item:last-child {
                border-bottom: none;
            }

            .hjty-action-content {
                flex: 1;
                min-width: 0;
            }

            .hjty-action-name {
                font-size: 16px;
                font-weight: 500;
                color: #1f2937;
                margin-bottom: 4px;
                line-height: 1.4;
            }

            .hjty-action-subname {
                font-size: 13px;
                color: #6b7280;
                line-height: 1.4;
                word-break: break-all;
            }

            .hjty-action-arrow {
                flex-shrink: 0;
                margin-left: 12px;
                color: #9ca3af;
                transition: transform 0.2s ease;
                display: flex;
                align-items: center;
                align-self: center;
            }

            .hjty-action-item:hover .hjty-action-arrow {
                transform: translateX(2px);
                color: #6b7280;
            }

            .hjty-action-footer {
                padding: 12px 16px;
                border-top: 1px solid #e5e7eb;
                flex-shrink: 0;
                background: #f8fafc;
            }

            .hjty-action-close-btn {
                display: block;
                width: 100%;
                padding: 10px 20px;
                border: none;
                border-radius: 6px;
                background: #f3f4f6;
                color: #374151;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.2s ease;
            }

            .hjty-action-close-btn:hover {
                background: #e5e7eb;
                color: #1f2937;
            }

            .hjty-action-close-btn:active {
                background: #d1d5db;
                transform: translateY(1px);
            }

            /* 播放器弹窗样式 */
            .hjty-player-modal-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.45);
                backdrop-filter: blur(5px);
                z-index: 40000;
                display: flex;
                align-items: center;
                justify-content: center;
                opacity: 0;
                transition: opacity 0.3s ease;
            }

            .hjty-player-modal-overlay.hjty-player-modal-show {
                opacity: 1;
            }

            .hjty-player-modal-content {
                background: #000000;
                border-radius: 12px;
                width: 95%;
                max-width: 1200px;
                height: 90%;
                max-height: 800px;
                display: flex;
                flex-direction: column;
                transform: scale(0.9);
                transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
                overflow: hidden;
            }

            .hjty-player-modal-show .hjty-player-modal-content {
                transform: scale(1);
            }

            .hjty-player-header {
                display: flex;
                justify-content: flex-end;
                align-items: center;
                padding: 16px 20px;
                background: #000000;
                flex-shrink: 0;
            }

            .hjty-player-close {
                background: none;
                border: none;
                font-size: 28px;
                color: #ffffff;
                cursor: pointer;
                padding: 0;
                width: 40px;
                height: 40px;
                display: flex;
                align-items: center;
                justify-content: center;
                border-radius: 8px;
                transition: all 0.2s ease;
                font-weight: 300;
            }

            .hjty-player-close:hover {
                background: rgba(255, 255, 255, 0.1);
                color: #ff4757;
                transform: scale(1.1);
            }

            .hjty-player-close:active {
                transform: scale(0.95);
            }

            .hjty-player-body {
                flex: 1;
                background: #000000;
                display: flex;
                align-items: center;
                justify-content: center;
                overflow: hidden;
            }

            #hjty-play-box {
                width: 100%;
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            /* 动画 */
            @keyframes hjty-fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }

            @keyframes hjty-slideUp {
                from { transform: translateY(20px); opacity: 0; }
                to { transform: translateY(0); opacity: 1; }
            }

            @keyframes hjty-spin {
                from { transform: rotate(0deg); }
                to { transform: rotate(360deg); }
            }

            /* 移动端适配 */
            @media (max-width: 768px) {
                #hjty-floating-toolbar {
                    right: 15px;
                    top: auto;
                    bottom: 180px;
                    transform: none;
                }

                #hjty-floating-toolbar.hjty-collapsed {
                    right: -54px; /* 移动端收起时的位置 */
                }

                .hjty-toggle-btn {
                    left: -20px;
                    width: 20px;
                    height: 40px;
                }

                .hjty-toolbar-btn {
                    width: 36px;
                    height: 36px;
                }

                .hjty-modal-content {
                    width: 95%;
                    margin: 20px;
                }

                .hjty-modal-body {
                    padding: 12px 16px;
                }

                .hjty-server-item {
                    padding: 10px 12px;
                }

                .hjty-server-name {
                    font-size: 13px;
                }

                .hjty-server-status {
                    margin: 0 8px;
                }

                .hjty-login-content {
                    width: 95%;
                    margin: 20px;
                }

                .hjty-form-actions {
                    flex-direction: column;
                }

                .hjty-toast {
                    top: 10px;
                    right: 10px;
                    left: 10px;
                    max-width: none;
                    min-width: auto;
                }

                /* 播放器移动端适配 */
                .hjty-player-modal-content {
                    width: 100%;
                    height: 100%;
                    max-width: none;
                    max-height: none;
                    border-radius: 0;
                }

                .hjty-player-header {
                    padding: 12px 16px;
                }

                .hjty-player-close {
                    width: 36px;
                    height: 36px;
                    font-size: 24px;
                }
            }

            /* 暗色模式适配 */
            @media (prefers-color-scheme: dark) {
                .hjty-toolbar-container {
                    background: rgba(30, 30, 30, 0.95);
                    border: 1px solid rgba(255, 255, 255, 0.1);
                }

                .hjty-toggle-btn {
                    background: rgba(30, 30, 30, 0.95);
                    backdrop-filter: blur(10px);
                    color: #f3f4f6;
                }

                .hjty-toggle-btn:hover {
                    background: rgba(30, 30, 30, 1);
                    color: #ffffff;
                }

                .hjty-modal-content {
                    background: #1f2937;
                }

                .hjty-modal-header {
                    border-bottom-color: #374151;
                }

                .hjty-modal-header h3 {
                    color: #f9fafb;
                }

                .hjty-modal-close {
                    color: #9ca3af;
                }

                .hjty-modal-close:hover {
                    background: #374151;
                    color: #f3f4f6;
                }

                .hjty-modal-footer {
                    border-top-color: #374151;
                }

                .hjty-btn-secondary {
                    background: #374151;
                    color: #f3f4f6;
                    border-color: #4b5563;
                }

                .hjty-btn-secondary:hover {
                    background: #4b5563;
                    border-color: #6b7280;
                }

                .hjty-loading-content {
                    background: #1f2937;
                }

                .hjty-loading-close:hover {
                    background: #374151;
                    color: #f3f4f6;
                }

                .hjty-loading-message {
                    color: #f3f4f6;
                }

                .hjty-user-name {
                    color: #f9fafb;
                }

                .hjty-user-status {
                    color: #34d399;
                }

                .hjty-confirm-message {
                    color: #f3f4f6;
                }

                .hjty-update-message {
                    color: #f9fafb;
                }

                .hjty-update-details {
                    color: #9ca3af;
                }

                .hjty-update-description {
                    color: #f3f4f6;
                    background: #374151;
                    border-left-color: #60a5fa;
                }

                .hjty-server-item {
                    border-color: #374151;
                    background: #1f2937;
                }

                .hjty-server-item:hover {
                    border-color: #4b5563;
                    background: #374151;
                }

                .hjty-server-item.hjty-online {
                    background: #064e3b;
                    border-color: #059669;
                }

                .hjty-server-item.hjty-offline {
                    background: #7f1d1d;
                    border-color: #dc2626;
                }

                .hjty-server-item.hjty-selected {
                    background: #1e3a8a;
                    border-color: #3b82f6;
                }

                .hjty-server-item.hjty-selected .hjty-server-name {
                    color: #93c5fd;
                }

                .hjty-server-name {
                    color: #f9fafb;
                }

                .hjty-radio-indicator {
                    border-color: #4b5563;
                }

                .hjty-radio-indicator.hjty-radio-selected {
                    border-color: #60a5fa;
                    background: #60a5fa;
                }

                .hjty-status-text {
                    color: #d1d5db;
                }

                .hjty-refresh-btn:disabled {
                    background: #4b5563;
                }

                .hjty-switch-btn:disabled {
                    background: #4b5563;
                }

                .hjty-form-group label {
                    color: #f3f4f6;
                }

                .hjty-form-input {
                    background: #374151;
                    border-color: #4b5563;
                    color: #f3f4f6;
                }

                .hjty-form-input:focus {
                    border-color: #60a5fa;
                    box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1);
                }

                .hjty-form-input::placeholder {
                    color: #6b7280;
                }

                /* 操作抽屉暗色模式 */
                .hjty-action-drawer {
                    background: #111827;
                }

                .hjty-action-header {
                    border-bottom-color: #374151;
                    background: #1f2937;
                }

                .hjty-action-title {
                    color: #f9fafb;
                }

                .hjty-action-item {
                    border-bottom-color: #374151;
                }

                .hjty-action-item:hover {
                    background: #374151;
                }

                .hjty-action-item:active {
                    background: #4b5563;
                }

                .hjty-action-name {
                    color: #f9fafb;
                }

                .hjty-action-subname {
                    color: #9ca3af;
                }

                .hjty-action-arrow {
                    color: #6b7280;
                }

                .hjty-action-item:hover .hjty-action-arrow {
                    color: #9ca3af;
                }

                .hjty-action-list {
                    background: #1f2937;
                }

                .hjty-action-footer {
                    border-top-color: #374151;
                    background: #111827;
                }

                .hjty-action-close-btn {
                    background: #374151;
                    color: #f3f4f6;
                }

                .hjty-action-close-btn:hover {
                    background: #4b5563;
                    color: #f9fafb;
                }

                .hjty-action-close-btn:active {
                    background: #6b7280;
                }
            }

            #play-box {height: 100%;display: flex;align-items: center;background-color: #000;}
            .van-popup.van-popup--center.play-box {width: 100%;height: 100%;max-width: 100%;}
            #bbs_float_menu{z-index:1!important;}
            #body.el-popup-parent--hidden{overflow: auto;}
            .van-list .van-list__loading,
            .sell-btn:before,
            .containeradvertising,
            .ishide,
            #tidio-chat{display: none !important;}
        `;

        document.head.appendChild(style);
    }

    function initFloatingButton() {
        if (document.getElementById("hjty-floating-toolbar")) return;

        const floatingToolbar = document.createElement("div");
        floatingToolbar.id = "hjty-floating-toolbar";
        floatingToolbar.innerHTML = `
            <div class="hjty-toolbar-container">
                <button class="hjty-toggle-btn" title="收起工具栏">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
                        <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </button>
                <button class="hjty-toolbar-btn hjty-unlock-btn" title="解锁">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                        <path d="M6 10V8C6 5.79086 7.79086 4 10 4H14C16.2091 4 18 5.79086 18 8V10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                        <rect x="4" y="10" width="16" height="10" rx="2" stroke="currentColor" stroke-width="2" fill="none"/>
                        <circle cx="12" cy="15" r="1" fill="currentColor"/>
                    </svg>
                </button>
                <button class="hjty-toolbar-btn hjty-settings-btn" title="切换服务器">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                        <rect x="2" y="3" width="20" height="4" rx="1" stroke="currentColor" stroke-width="2"/>
                        <rect x="2" y="10" width="20" height="4" rx="1" stroke="currentColor" stroke-width="2"/>
                        <rect x="2" y="17" width="20" height="4" rx="1" stroke="currentColor" stroke-width="2"/>
                        <circle cx="6" cy="5" r="1" fill="currentColor"/>
                        <circle cx="6" cy="12" r="1" fill="currentColor"/>
                        <circle cx="6" cy="19" r="1" fill="currentColor"/>
                        <path d="M16 8L18 10L16 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                        <path d="M8 15L10 17L8 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </button>
            </div>
        `;

        document.body.appendChild(floatingToolbar);

        const unlockBtn = floatingToolbar.querySelector(
            CONFIG.SELECTORS.UNLOCK_BTN,
        );
        const settingsBtn = floatingToolbar.querySelector(
            CONFIG.SELECTORS.SETTINGS_BTN,
        );
        const toggleBtn = floatingToolbar.querySelector(
            CONFIG.SELECTORS.TOGGLE_BTN,
        );

        unlockBtn.addEventListener("click", handleUnlockClick);

        const debouncedSwitchServer = Utils.debounce(() => switchServer(), 300);
        settingsBtn.addEventListener("click", debouncedSwitchServer);

        const debouncedToggleToolbar = Utils.debounce(
            () => toggleToolbar(),
            200,
        );
        toggleBtn.addEventListener("click", debouncedToggleToolbar);

        if (toolbarCollapsed) {
            floatingToolbar.classList.add("hjty-collapsed");
        }

        toggleBtn.title = toolbarCollapsed ? "展开工具栏" : "收起工具栏";

        App.updateToolbarButtons(Utils.checkLoginStatus());

        document.addEventListener("keydown", (e) => {
            if (e.ctrlKey && e.shiftKey && e.key === "H") {
                e.preventDefault();
                toggleToolbar();
            }
        });
    }

    function toggleToolbar() {
        const toolbar = document.getElementById("hjty-floating-toolbar");
        if (!toolbar) return;

        toolbarCollapsed = !toolbarCollapsed;

        if (toolbarCollapsed) {
            toolbar.classList.add("hjty-collapsed");
        } else {
            toolbar.classList.remove("hjty-collapsed");
        }

        const toggleBtn = toolbar.querySelector(CONFIG.SELECTORS.TOGGLE_BTN);
        if (toggleBtn) {
            toggleBtn.title = toolbarCollapsed ? "展开工具栏" : "收起工具栏";
        }

        localStorage.setItem(
            "hjty_toolbar_collapsed",
            toolbarCollapsed.toString(),
        );
    }

    function handleUnlockClick() {
        if (newVersion !== "") {
            App.showUpdateConfirm();
            return;
        }

        if (!Utils.checkLoginStatus()) {
            showLoginModal();
            return;
        }

        Utils.deleteCookie("NOTLOGIN");
        const pid = Utils.getPid();
        if (!pid) {
            if (/\/videoplay/.test(location.href)) {
                App.getShortVideoLink();
                return;
            } else {
                UI.showToast("请进入帖子再解锁!", "error");
            }
            return;
        }

        if (m3u8_url !== "") {
            App.showVideoActions();
            return;
        }

        UI.showLoadingModal("解锁中...", true, function () {
            UI.showToast("解锁已取消", "warning");
        });

        App.getTopic(pid)
            .then((topic) => {
                App.showTopic(topic, pid);
            })
            .catch((err) => {
                UI.showToast(err, "warning");
            });
    }

    function showLoginModal() {
        if (document.getElementById(CONFIG.MODAL_IDS.LOGIN)) return;

        const loginModal = document.createElement("div");
        loginModal.id = CONFIG.MODAL_IDS.LOGIN;
        loginModal.className = "hjty-modal-overlay";
        loginModal.innerHTML = `
            <div class="hjty-modal-content hjty-login-content">
                <div class="hjty-modal-header">
                    <h3>用户登录</h3>
                    <button class="hjty-modal-close" onclick="this.closest('.hjty-modal-overlay').remove()">×</button>
                </div>
                <div class="hjty-modal-body">
                    <form class="hjty-login-form">
                        <div class="hjty-form-group">
                            <label for="hjty-username">账号</label>
                            <input type="text" id="hjty-username" class="hjty-form-input" placeholder="请输入账号" required>
                        </div>
                        <div class="hjty-form-group">
                            <label for="hjty-password">密码</label>
                            <input type="password" id="hjty-password" class="hjty-form-input" placeholder="请输入密码" required>
                        </div>
                        <div class="hjty-form-actions">
                            <button type="submit" class="hjty-btn hjty-btn-primary hjty-login-btn">登录</button>
                            <button type="button" class="hjty-btn hjty-btn-secondary hjty-register-btn">注册</button>
                        </div>
                    </form>
                </div>
            </div>
        `;

        document.body.appendChild(loginModal);

        const loginForm = loginModal.querySelector(".hjty-login-form");
        const loginBtn = loginModal.querySelector(".hjty-login-btn");
        const registerBtn = loginModal.querySelector(".hjty-register-btn");

        loginForm.addEventListener("submit", async (e) => {
            e.preventDefault();
            const username = document
                .getElementById("hjty-username")
                .value.trim();
            const password = document
                .getElementById("hjty-password")
                .value.trim();

            if (!username || !password) {
                UI.showToast("请填写完整信息", "warning");
                return;
            }

            await handleLogin(username, password, loginBtn, loginModal);
        });

        registerBtn.addEventListener("click", () => {
            window.open(`${server}/register`, "_blank");
        });

        loginModal.addEventListener("click", (e) => {
            if (e.target === loginModal) loginModal.remove();
        });
    }

    async function handleLogin(username, password, loginBtn, loginModal) {
        try {
            loginBtn.disabled = true;
            loginBtn.innerHTML =
                '<div class="hjty-loading-spinner"><div class="hjty-spinner-ring"></div></div>登录中...';

            const response = await fetch(`${server}/api/login`, {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ username, password }),
            });

            const result = await response.json();

            if (!result.code) {
                localStorage.setItem(
                    CONFIG.STORAGE_KEYS.USER_TOKEN,
                    result.token,
                );
                localStorage.setItem(CONFIG.STORAGE_KEYS.USER_NAME, username);

                UI.showToast("登录成功", "success");
                loginModal.remove();
                App.updateToolbarButtons(true);
            } else {
                UI.showToast(result.msg || "登录失败", "error");
            }
        } catch (error) {
            UI.showToast("网络错误,请重试", "error");
        } finally {
            loginBtn.disabled = false;
            loginBtn.innerHTML = "登录";
        }
    }

    function switchServer() {

        const servers = [
            { id: "vip", name: "服务器A", url: "https://vip.tianya365.top" },
            { id: "b", name: "服务器B", url: "https://haijiao.tianya365.top" },
            { id: "c", name: "服务器C", url: "https://new.tianya365.top" },
            { id: "d", name: "服务器D", url: "https://hj.tianya365.top" },
        ];

        function getServerIdByUrl(url) {
            const foundServer = servers.find((s) => s.url === url);
            return foundServer ? foundServer.id : "vip";
        }

        const currentServerId = getServerIdByUrl(server);

        const modal = document.createElement("div");
        modal.id = CONFIG.MODAL_IDS.SERVER;
        modal.className = "hjty-modal-overlay";
        modal.style.zIndex = "25000";
        modal.innerHTML = `
            <div class="hjty-modal-content">
                <div class="hjty-modal-header">
                    <h3>服务器节点切换</h3>
                    <button class="hjty-modal-close" onclick="this.closest('.hjty-modal-overlay').remove()">×</button>
                </div>
                <div class="hjty-modal-body">
                    <div class="hjty-server-list">
                        ${servers
                            .map(
                                (srv) => `
                            <div class="hjty-server-item ${srv.id === currentServerId ? "hjty-selected" : ""}" data-server-id="${srv.id}">
                                <div class="hjty-radio-indicator ${srv.id === currentServerId ? "hjty-radio-selected" : ""}"></div>
                                <div class="hjty-server-info">
                                    <div class="hjty-server-name">${srv.name}</div>
                                </div>
                                <div class="hjty-server-status">
                                    <div class="hjty-status-indicator hjty-checking"></div>
                                    <span class="hjty-status-text">检测中</span>
                                    <span class="hjty-latency"></span>
                                </div>
                            </div>
                        `,
                            )
                            .join("")}
                    </div>
                </div>
                <div class="hjty-modal-footer">
                    <button class="hjty-refresh-btn">重新检测</button>
                    <button class="hjty-switch-btn" disabled>立即切换</button>
                </div>
            </div>
        `;

        document.body.appendChild(modal);

        let isChecking = false;
        let selectedServerId = currentServerId;

        async function checkServer(server) {
            const startTime = Date.now();
            try {
                const controller = new AbortController();
                const timeoutId = setTimeout(() => controller.abort(), 60000); 

                await fetch(`${server.url}/api/login`, {
                    method: "HEAD",
                    mode: "no-cors",
                    signal: controller.signal,
                });

                clearTimeout(timeoutId);
                const latency = Date.now() - startTime;
                return { success: true, latency };
            } catch (error) {
                return { online: false, latency: 0 };
            }
        }

        function updateServerStatus(serverId, result) {
            const serverItem = modal.querySelector(
                `[data-server-id="${serverId}"]`,
            );
            const statusIndicator = serverItem.querySelector(
                ".hjty-status-indicator",
            );
            const statusText = serverItem.querySelector(".hjty-status-text");
            const latencySpan = serverItem.querySelector(".hjty-latency");

            statusIndicator.className = `hjty-status-indicator ${result.success ? "hjty-online" : "hjty-offline"}`;
            statusText.textContent = result.success ? "正常" : "异常";

            if (result.success) {
                latencySpan.textContent = `${result.latency}ms`;
                serverItem.classList.add("hjty-online");
                serverItem.classList.remove("hjty-offline");
            } else {
                latencySpan.textContent = "";
                serverItem.classList.add("hjty-offline");
                serverItem.classList.remove("hjty-online");
            }
        }

        function selectServer(serverId) {

            modal.querySelectorAll(".hjty-server-item").forEach((item) => {
                item.classList.remove("hjty-selected");
                item.querySelector(".hjty-radio-indicator").classList.remove(
                    "hjty-radio-selected",
                );
            });

            const selectedItem = modal.querySelector(
                `[data-server-id="${serverId}"]`,
            );
            selectedItem.classList.add("hjty-selected");
            selectedItem
                .querySelector(".hjty-radio-indicator")
                .classList.add("hjty-radio-selected");

            selectedServerId = serverId;

            const switchBtn = modal.querySelector(".hjty-switch-btn");
            switchBtn.disabled = false;
        }

        function switchToServer(serverId) {
            const selectedServer = servers.find((s) => s.id === serverId);
            if (selectedServer) {
                server = selectedServer.url;
                localStorage.setItem(CONFIG.STORAGE_KEYS.SERVER, server);
                UI.showToast(`已切换到:${selectedServer.name}`, "success");
                modal.remove();

                App.updateToolbarButtons(Utils.checkLoginStatus());
            }
        }

        async function checkAllServers() {
            if (isChecking) return;

            isChecking = true;
            const refreshBtn = modal.querySelector(".hjty-refresh-btn");
            refreshBtn.disabled = true;
            refreshBtn.textContent = "检测中...";

            modal.querySelectorAll(".hjty-server-item").forEach((item) => {
                const statusIndicator = item.querySelector(
                    ".hjty-status-indicator",
                );
                const statusText = item.querySelector(".hjty-status-text");
                const latencySpan = item.querySelector(".hjty-latency");

                statusIndicator.className =
                    "hjty-status-indicator hjty-checking";
                statusText.textContent = "检测中";
                latencySpan.textContent = "";
                item.classList.remove("hjty-online", "hjty-offline");
            });

            const checkPromises = servers.map(async (srv) => {
                const result = await checkServer(srv);
                updateServerStatus(srv.id, result);
            });

            await Promise.all(checkPromises);

            refreshBtn.disabled = false;
            refreshBtn.textContent = "重新检测";
            isChecking = false;
        }

        modal.querySelectorAll(".hjty-server-item").forEach((item) => {
            item.addEventListener("click", () => {
                const serverId = item.dataset.serverId;
                selectServer(serverId);
            });
        });

        const refreshBtn = modal.querySelector(".hjty-refresh-btn");
        const switchBtn = modal.querySelector(".hjty-switch-btn");

        refreshBtn.addEventListener("click", checkAllServers);
        switchBtn.addEventListener("click", () =>
            switchToServer(selectedServerId),
        );

        modal.addEventListener("click", (e) => {
            if (e.target === modal) modal.remove();
        });

        checkAllServers();
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", () => {
            buttonInitialized = checkTitleAndInitButton();
            if (!buttonInitialized) {
                setupTitleObserver();
            }
        });
    } else {
        buttonInitialized = checkTitleAndInitButton();
        if (!buttonInitialized) {
            setupTitleObserver();
        }
    }

    (function () {
        const originOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function (method, url) {
            if (
                /\/api\/topic\/(hot\/topics\?|node\/(topics|news)|idol_list)/.test(
                    url,
                )
            ) {
                const xhr = this;
                const getter = Object.getOwnPropertyDescriptor(
                    XMLHttpRequest.prototype,
                    "response",
                ).get;
                Object.defineProperty(xhr, "responseText", {
                    get: () => {
                        let result = getter.call(xhr);

                        try {
                            let res = JSON.parse(result);
                            if (res["isEncrypted"]) {
                                res.data = Utils.encrypt(
                                    App.formatTitle(Utils.decrypt(res.data)),
                                );
                            } else {
                                res.data = App.formatTitle(res.data);
                            }
                            return JSON.stringify(res);
                        } catch (e) {
                            return result;
                        }
                    },
                });
            } else if (
                /\/api\/topic\/\d+/.test(url) &&
                /tianya365/.test(url) === false
            ) {
                m3u8_url = "";
            } else if (/\/api\/attachment/.test(url)) {
                const xhr = this;
                const getter = Object.getOwnPropertyDescriptor(
                    XMLHttpRequest.prototype,
                    "response",
                ).get;
                Object.defineProperty(xhr, "responseText", {
                    get: () => {
                        let result = getter.call(xhr);
                        App.getShortVideoUrl(result);
                        return result;
                    },
                });
            } else if (/\/api\/video\/checkVideoCanPlay/.test(url)) {
                const xhr = this;
                const getter = Object.getOwnPropertyDescriptor(
                    XMLHttpRequest.prototype,
                    "response",
                ).get;
                Object.defineProperty(xhr, "responseText", {
                    get: () => {
                        return App.fixShortVideo(getter.call(xhr));
                    },
                });
            } else if (/\/api\/topic\/like/.test(url)) {
                const xhr = this;
                const getter = Object.getOwnPropertyDescriptor(
                    XMLHttpRequest.prototype,
                    "response",
                ).get;
                Object.defineProperty(xhr, "responseText", {
                    get: () => {
                        let result = getter.call(xhr);
                        let res = JSON.parse(result);
                        if (res.errorCode == 1) {
                            res.errorCode = -1;
                        }
                        return JSON.stringify(res);
                    },
                });
            }

            originOpen.apply(this, arguments);
        };
    })();
})();