影巢Emby助手

影巢显示emby已入库以及未入库,支持主页,详情页,用户页面,合集页面,支持并适配移动端与pc端

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         影巢Emby助手
// @version      0.5
// @description  影巢显示emby已入库以及未入库,支持主页,详情页,用户页面,合集页面,支持并适配移动端与pc端
// @author       楠
// @match        *://hdhive.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @icon         https://hdhive.com/apple-touch-icon.png
// @namespace https://greasyfork.org/users/1514724
// ==/UserScript==

(function() {
    'use strict';

    let EMBY_CONFIG = {
        HOST: GM_getValue("embyHost", ""),
        API_KEY: GM_getValue("embyApiKey", "")
    };

    const state = {
        processingItems: new Set(),
        processedItems: new Set(),
        embyCache: new Map()
    };

    const BUTTON_STYLES = {
        posterBtn: {
            size: '25px',
            position: { top: '10px', right: '10px' },
            has: {
                bg: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
                icon: '✓',
                border: '2px solid rgba(255,255,255,0.8)'
            },
            notHas: {
                bg: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
                icon: '✗',
                border: '2px solid rgba(255,255,255,0.8)'
            },
            hoverEffect: 'scale(1.1)'
        },
        nameBtn: {
            padding: '3px 10px',
            marginTop: '5px',
            fontSize: '11px',
            has: {
                bg: 'rgba(76, 175, 80, 0.15)',
                text: '已入库',
                textColor: '#4CAF50',
                border: '1px solid rgba(76, 175, 80, 0.3)'
            },
            notHas: {
                bg: 'rgba(244, 67, 54, 0.15)',
                text: '未入库',
                textColor: '#F44336',
                border: '1px solid rgba(244, 67, 54, 0.3)'
            },
            hoverEffect: 'translateY(-1px)'
        },
        detailBtn: {
            posterBtn: {
                size: '30px',
                position: { top: '15px', right: '15px' },
                has: {
                    bg: 'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)',
                    icon: '✓',
                    border: '2px solid rgba(255,255,255,0.9)'
                },
                notHas: {
                    bg: 'linear-gradient(135deg, #F44336 0%, #C62828 100%)',
                    icon: '✗',
                    border: '2px solid rgba(255,255,255,0.9)'
                },
                hoverEffect: 'scale(1.15)'
            },
            titleBtn: {
                padding: '5px 15px',
                marginLeft: '10px',
                fontSize: '12px',
                has: {
                    bg: 'rgba(76, 175, 80, 0.15)',
                    text: '已入库',
                    textColor: '#4CAF50',
                    border: '1px solid rgba(76, 175, 80, 0.4)'
                },
                notHas: {
                    bg: 'rgba(244, 67, 54, 0.15)',
                    text: '未入库',
                    textColor: '#F44336',
                    border: '1px solid rgba(244, 67, 54, 0.4)'
                },
                hoverEffect: 'translateY(-1px)'
            }
        },
        searchYearBtn: {
            padding: '3px 10px',
            marginLeft: '10px',
            fontSize: '11px',
            has: {
                bg: 'rgba(76, 175, 80, 0.15)',
                text: '已入库',
                textColor: '#4CAF50',
                border: '1px solid rgba(76, 175, 80, 0.3)'
            },
            notHas: {
                bg: 'rgba(244, 67, 54, 0.15)',
                text: '未入库',
                textColor: '#F44336',
                border: '1px solid rgba(244, 67, 54, 0.3)'
            },
            hoverEffect: 'translateY(-1px)'
        },
        userPageBtn: {
            padding: '3px 10px',
            marginLeft: '8px',
            fontSize: '11px',
            has: {
                bg: 'rgba(76, 175, 80, 0.35)',
                text: '已入库',
                textColor: '#4CAF50',
                border: '1px solid rgba(76, 175, 80, 0.4)'
            },
            notHas: {
                bg: 'rgba(244, 67, 54, 0.35)',
                text: '未入库',
                textColor: '#F44336',
                border: '1px solid rgba(244, 67, 54, 0.4)'
            },
            hoverEffect: 'translateY(-1px)'
        },
        collectionBtn: {
            padding: '3px 10px',
            marginLeft: '10px',
            fontSize: '11px',
            has: {
                bg: 'rgba(76, 175, 80, 0.15)',
                text: '已入库',
                textColor: '#4CAF50',
                border: '1px solid rgba(76, 175, 80, 0.3)'
            },
            notHas: {
                bg: 'rgba(244, 67, 54, 0.15)',
                text: '未入库',
                textColor: '#F44336',
                border: '1px solid rgba(244, 67, 54, 0.3)'
            },
            hoverEffect: 'translateY(-1px)'
        },
        settingBtn: {
            padding: '3px 10px',
            marginRight: '10px',
            fontSize: '11px',
            has: {
                bg: 'rgba(100, 181, 246, 0.35)',
                text: 'Emby设置',
                textColor: '#64B5F6',
                border: '1px solid rgba(100, 181, 246, 0.4)'
            },
            hoverEffect: 'translateY(-1px)',
            iconSize: '16px'
        }
    };

    const style = document.createElement('style');
    style.textContent = `
        .emby-poster-btn {
            position: absolute;
            width: ${BUTTON_STYLES.posterBtn.size};
            height: ${BUTTON_STYLES.posterBtn.size};
            top: ${BUTTON_STYLES.posterBtn.position.top};
            right: ${BUTTON_STYLES.posterBtn.position.right};
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            z-index: 100;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            text-shadow: 0 1px 2px rgba(0,0,0,0.2);
        }
        .emby-poster-btn.has {
            background: ${BUTTON_STYLES.posterBtn.has.bg};
            color: white;
            border: ${BUTTON_STYLES.posterBtn.has.border};
        }
        .emby-poster-btn.not-has {
            background: ${BUTTON_STYLES.posterBtn.notHas.bg};
            color: white;
            border: ${BUTTON_STYLES.posterBtn.notHas.border};
        }
        .emby-poster-btn:hover {
            transform: ${BUTTON_STYLES.posterBtn.hoverEffect};
            box-shadow: 0 6px 20px rgba(0,0,0,0.3);
        }
        .emby-name-btn {
            display: inline-flex;
            align-items: center;
            margin-top: ${BUTTON_STYLES.nameBtn.marginTop};
            padding: ${BUTTON_STYLES.nameBtn.padding};
            border-radius: 12px;
            font-size: ${BUTTON_STYLES.nameBtn.fontSize};
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            background: transparent;
        }
        .emby-name-btn.has {
            background: ${BUTTON_STYLES.nameBtn.has.bg};
            color: ${BUTTON_STYLES.nameBtn.has.textColor};
            border: ${BUTTON_STYLES.nameBtn.has.border};
        }
        .emby-name-btn.not-has {
            background: ${BUTTON_STYLES.nameBtn.notHas.bg};
            color: ${BUTTON_STYLES.nameBtn.notHas.textColor};
            border: ${BUTTON_STYLES.nameBtn.notHas.border};
        }
        .emby-name-btn:hover {
            transform: ${BUTTON_STYLES.nameBtn.hoverEffect};
            box-shadow: 0 2px 6px rgba(0,0,0,0.15);
        }
        .emby-detail-poster-btn {
            position: absolute;
            width: ${BUTTON_STYLES.detailBtn.posterBtn.size};
            height: ${BUTTON_STYLES.detailBtn.posterBtn.size};
            top: ${BUTTON_STYLES.detailBtn.posterBtn.position.top};
            right: ${BUTTON_STYLES.detailBtn.posterBtn.position.right};
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            font-weight: bold;
            cursor: pointer;
            z-index: 100;
            box-shadow: 0 6px 20px rgba(0,0,0,0.3);
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
        }
        .emby-detail-poster-btn.has {
            background: ${BUTTON_STYLES.detailBtn.posterBtn.has.bg};
            color: white;
            border: ${BUTTON_STYLES.detailBtn.posterBtn.has.border};
        }
        .emby-detail-poster-btn.not-has {
            background: ${BUTTON_STYLES.detailBtn.posterBtn.notHas.bg};
            color: white;
            border: ${BUTTON_STYLES.detailBtn.posterBtn.notHas.border};
        }
        .emby-detail-poster-btn:hover {
            transform: ${BUTTON_STYLES.detailBtn.posterBtn.hoverEffect};
            box-shadow: 0 8px 25px rgba(0,0,0,0.4);
        }
        .emby-detail-title-btn {
            display: inline-flex;
            align-items: center;
            margin-left: ${BUTTON_STYLES.detailBtn.titleBtn.marginLeft};
            padding: ${BUTTON_STYLES.detailBtn.titleBtn.padding};
            border-radius: 15px;
            font-size: ${BUTTON_STYLES.detailBtn.titleBtn.fontSize};
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            background: transparent;
        }
        .emby-detail-title-btn.has {
            background: ${BUTTON_STYLES.detailBtn.titleBtn.has.bg};
            color: ${BUTTON_STYLES.detailBtn.titleBtn.has.textColor};
            border: ${BUTTON_STYLES.detailBtn.titleBtn.has.border};
        }
        .emby-detail-title-btn.not-has {
            background: ${BUTTON_STYLES.detailBtn.titleBtn.notHas.bg};
            color: ${BUTTON_STYLES.detailBtn.titleBtn.notHas.textColor};
            border: ${BUTTON_STYLES.detailBtn.titleBtn.notHas.border};
        }
        .emby-detail-title-btn:hover {
            transform: ${BUTTON_STYLES.detailBtn.titleBtn.hoverEffect};
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
        }
        .emby-search-year-btn {
            display: inline-flex;
            align-items: center;
            margin-left: ${BUTTON_STYLES.searchYearBtn.marginLeft};
            padding: ${BUTTON_STYLES.searchYearBtn.padding};
            border-radius: 12px;
            font-size: ${BUTTON_STYLES.searchYearBtn.fontSize};
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            background: transparent;
        }
        .emby-search-year-btn.has {
            background: ${BUTTON_STYLES.searchYearBtn.has.bg};
            color: ${BUTTON_STYLES.searchYearBtn.has.textColor};
            border: ${BUTTON_STYLES.searchYearBtn.has.border};
        }
        .emby-search-year-btn.not-has {
            background: ${BUTTON_STYLES.searchYearBtn.notHas.bg};
            color: ${BUTTON_STYLES.searchYearBtn.notHas.textColor};
            border: ${BUTTON_STYLES.searchYearBtn.notHas.border};
        }
        .emby-search-year-btn:hover {
            transform: ${BUTTON_STYLES.searchYearBtn.hoverEffect};
            box-shadow: 0 2px 6px rgba(0,0,0,0.15);
        }
        .emby-user-page-btn {
            display: inline-flex;
            align-items: center;
            margin-left: ${BUTTON_STYLES.userPageBtn.marginLeft};
            padding: ${BUTTON_STYLES.userPageBtn.padding};
            border-radius: 12px;
            font-size: ${BUTTON_STYLES.userPageBtn.fontSize};
            font-weight: 600;
            cursor: default;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            opacity: 0.7;
        }
        .emby-user-page-btn.has {
            background: ${BUTTON_STYLES.userPageBtn.has.bg};
            color: ${BUTTON_STYLES.userPageBtn.has.textColor};
            border: ${BUTTON_STYLES.userPageBtn.has.border};
        }
        .emby-user-page-btn.not-has {
            background: ${BUTTON_STYLES.userPageBtn.notHas.bg};
            color: ${BUTTON_STYLES.userPageBtn.notHas.textColor};
            border: ${BUTTON_STYLES.userPageBtn.notHas.border};
        }
        .emby-user-page-btn:hover {
            transform: ${BUTTON_STYLES.userPageBtn.hoverEffect};
            box-shadow: 0 2px 6px rgba(0,0,0,0.15);
        }
        .emby-collection-btn {
            display: inline-flex;
            align-items: center;
            margin-left: ${BUTTON_STYLES.collectionBtn.marginLeft};
            padding: ${BUTTON_STYLES.collectionBtn.padding};
            border-radius: 12px;
            font-size: ${BUTTON_STYLES.collectionBtn.fontSize};
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            background: transparent;
        }
        .emby-collection-btn.has {
            background: ${BUTTON_STYLES.collectionBtn.has.bg};
            color: ${BUTTON_STYLES.collectionBtn.has.textColor};
            border: ${BUTTON_STYLES.collectionBtn.has.border};
        }
        .emby-collection-btn.not-has {
            background: ${BUTTON_STYLES.collectionBtn.notHas.bg};
            color: ${BUTTON_STYLES.collectionBtn.notHas.textColor};
            border: ${BUTTON_STYLES.collectionBtn.notHas.border};
        }
        .emby-collection-btn:hover {
            transform: ${BUTTON_STYLES.collectionBtn.hoverEffect};
            box-shadow: 0 2px 6px rgba(0,0,0,0.15);
        }
        .emby-setting-btn {
            display: inline-flex;
            align-items: center;
            padding: ${BUTTON_STYLES.settingBtn.padding};
            border-radius: 12px;
            font-size: ${BUTTON_STYLES.settingBtn.fontSize};
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            background: ${BUTTON_STYLES.settingBtn.has.bg};
            color: ${BUTTON_STYLES.settingBtn.has.textColor};
            border: ${BUTTON_STYLES.settingBtn.has.border};
        }
        .emby-setting-btn:hover {
            transform: ${BUTTON_STYLES.settingBtn.hoverEffect};
            box-shadow: 0 2px 6px rgba(0,0,0,0.15);
        }
        .emby-name-btn::before,
        .emby-detail-title-btn::before,
        .emby-search-year-btn::before,
        .emby-user-page-btn::before,
        .emby-collection-btn::before,
        .emby-setting-btn::before {
            content: "";
            display: inline-block;
            width: 16px;
            height: 16px;
            margin-right: 6px;
            background-image: url('https://raw.githubusercontent.com/lige47/QuanX-icon-rule/main/icon/emby.png');
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
            filter: brightness(0.9);
        }
        .emby-detail-title-btn::before {
            width: 18px;
            height: 18px;
            margin-right: 8px;
        }
        .MuiPopover-root .emby-poster-btn,
        .MuiPopover-root .emby-name-btn,
        .MuiPopover-root .emby-detail-poster-btn,
        .MuiPopover-root .emby-detail-title-btn,
        .MuiPopover-root .emby-search-year-btn,
        .MuiPopover-root .emby-user-page-btn,
        .MuiPopover-root .emby-collection-btn,
        .MuiPopover-root .emby-setting-btn {
            z-index: 1500;
        }
        #hdhive-notice {
            position: fixed;
            top: 10px;
            left: 50%;
            transform: translateX(-50%);
            padding: 8px 12px;
            background-color: #ff9800;
            color: #fff;
            font-size: 14px;
            font-weight: bold;
            border-radius: 4px;
            z-index: 9999;
            box-shadow: 0 2px 6px rgba(0,0,0,0.3);
        }
        .emby-settings-modal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 400px;
            max-width: 90%;
            background: #fff;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
            z-index: 99999;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            overflow: hidden;
            display: none;
        }
        .emby-settings-header {
            background: linear-gradient(135deg, #2196F3 0%, #0D47A1 100%);
            color: white;
            padding: 20px;
            text-align: center;
            position: relative;
        }
        .emby-settings-header h2 {
            margin: 0;
            font-size: 22px;
            font-weight: 600;
        }
        .emby-settings-close {
            position: absolute;
            top: 15px;
            right: 20px;
            background: none;
            border: none;
            color: white;
            font-size: 24px;
            cursor: pointer;
            width: 30px;
            height: 30px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s;
        }
        .emby-settings-close:hover {
            background: rgba(255,255,255,0.2);
        }
        .emby-settings-body {
            padding: 25px;
        }
        .emby-settings-form-group {
            margin-bottom: 20px;
        }
        .emby-settings-label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #333;
            font-size: 15px;
        }
        .emby-settings-input {
            width: 100%;
            padding: 12px 15px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 15px;
            transition: all 0.3s;
            box-sizing: border-box;
        }
        .emby-settings-input:focus {
            border-color: #2196F3;
            box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
            outline: none;
        }
        .emby-settings-footer {
            display: flex;
            justify-content: flex-end;
            padding: 15px 25px;
            background: #f9f9f9;
            border-top: 1px solid #eee;
        }
        .emby-settings-btn {
            padding: 10px 20px;
            border-radius: 6px;
            font-size: 15px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
            border: none;
        }
        .emby-settings-save {
            background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
            color: white;
            margin-left: 10px;
        }
        .emby-settings-save:hover {
            background: linear-gradient(135deg, #43A047 0%, #1B5E20 100%);
        }
        .emby-settings-cancel {
            background: #f5f5f5;
            color: #666;
        }
        .emby-settings-cancel:hover {
            background: #e0e0e0;
        }
        .emby-settings-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.6);
            z-index: 99998;
            display: none;
        }
        #emby-save-notice {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 20px;
            background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
            color: white;
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 99999;
            font-size: 14px;
            font-weight: 600;
            display: flex;
            align-items: center;
            opacity: 0;
            transform: translateX(100%);
            transition: all 0.3s ease;
        }
        #emby-save-notice.show {
            opacity: 1;
            transform: translateX(0);
        }
        #emby-save-notice::before {
            content: "✓";
            display: inline-block;
            margin-right: 8px;
            font-size: 16px;
            font-weight: bold;
        }
    `;
    document.head.appendChild(style);

    function isDetailPage() {
        const path = window.location.pathname;
        return /^\/(movie|tv)\/[\w-]+/.test(path);
    }

    function isUserPage() {
        return window.location.pathname.startsWith('/user/');
    }

    function isCollectionPage() {
        return window.location.pathname.startsWith('/collection/');
    }

    function checkEmbyResource(name, year) {
        return new Promise((resolve) => {
            const cacheKey = `${name}-${year}`;
            
            if (state.embyCache.has(cacheKey)) {
                resolve(state.embyCache.get(cacheKey));
                return;
            }
            
            const searchUrl = `${EMBY_CONFIG.HOST}/emby/Items?api_key=${EMBY_CONFIG.API_KEY}&SearchTerm=${encodeURIComponent(name)}&IncludeItemTypes=Movie,Series&Recursive=true&Fields=ProductionYear&Limit=20`;
            
            GM_xmlhttpRequest({
                method: 'GET',
                url: searchUrl,
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        let hasResource = false;
                        if (data.Items && data.Items.length > 0) {
                            const matchedItem = data.Items.find(item => {
                                const itemName = item.Name;
                                const itemYear = item.ProductionYear;
                                return itemName === name && itemYear === year;
                            });
                            hasResource = !!matchedItem;
                        }
                        state.embyCache.set(cacheKey, hasResource);
                        resolve(hasResource);
                    } catch (error) {
                        state.embyCache.set(cacheKey, false);
                        resolve(false);
                    }
                },
                onerror: function(error) {
                    state.embyCache.set(cacheKey, false);
                    resolve(false);
                }
            });
        });
    }

    function extractInfoFromPoster(poster) {
        const nameElement = poster.querySelector('p.mui-1kj6qkz');
        const yearElement = poster.querySelector('p.mui-1mfu778');
        
        if (nameElement && yearElement) {
            const name = nameElement.textContent.trim();
            const year = parseInt(yearElement.textContent.trim(), 10);
            if (name && !isNaN(year)) {
                return { name, year, element: poster };
            }
        }
        return null;
    }
    
    function extractInfoFromDetail() {
        const titleElement = document.querySelector('h1.mui-oqdiq5');
        if (titleElement) {
            const titleText = titleElement.childNodes[0].textContent.trim();
            const yearElement = titleElement.querySelector('p.mui-eo8gqg');
            if (yearElement) {
                const yearText = yearElement.textContent.trim();
                const yearMatch = yearText.match(/\((\d{4})\)/);
                if (yearMatch) {
                    const year = parseInt(yearMatch[1], 10);
                    return { name: titleText, year };
                }
            }
        }
        return null;
    }
    
    function extractInfoFromUserPage(element) {
        const text = element.textContent.trim();
        const match = text.match(/(.+?)\s*\((\d{4})\)/);
        if (match) {
            const name = match[1].trim();
            const year = parseInt(match[2], 10);
            return { name, year, element };
        }
        return null;
    }
    
    function extractInfoFromSearchYear(element) {
        const text = element.textContent.trim();
        const match = text.match(/\((\d{4})\)/);
        if (match) {
            const year = parseInt(match[1], 10);
            const nameElement = element.previousElementSibling;
            if (nameElement) {
                const name = nameElement.textContent.trim();
                return { name, year, element };
            }
        }
        return null;
    }

    function extractInfoFromCollection(element) {
        const nameElement = element.querySelector('p.mui-1kj6qkz');
        const yearElement = element.querySelector('p.mui-1mfu778');
        
        if (nameElement && yearElement) {
            const name = nameElement.textContent.trim();
            const year = parseInt(yearElement.textContent.trim(), 10);
            if (name && !isNaN(year)) {
                return { name, year, element };
            }
        }
        return null;
    }

    function createPosterButton(hasResource) {
        const btn = document.createElement('div');
        btn.className = `emby-poster-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.posterBtn.has.icon : BUTTON_STYLES.posterBtn.notHas.icon;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createNameButton(hasResource) {
        const btn = document.createElement('span');
        btn.className = `emby-name-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.nameBtn.has.text : BUTTON_STYLES.nameBtn.notHas.text;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createDetailPosterButton(hasResource) {
        const btn = document.createElement('div');
        btn.className = `emby-detail-poster-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.detailBtn.posterBtn.has.icon : BUTTON_STYLES.detailBtn.posterBtn.notHas.icon;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createDetailTitleButton(hasResource) {
        const btn = document.createElement('span');
        btn.className = `emby-detail-title-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.detailBtn.titleBtn.has.text : BUTTON_STYLES.detailBtn.titleBtn.notHas.text;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createSearchYearButton(hasResource) {
        const btn = document.createElement('span');
        btn.className = `emby-search-year-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.searchYearBtn.has.text : BUTTON_STYLES.searchYearBtn.notHas.text;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createUserPageButton(hasResource) {
        const btn = document.createElement('span');
        btn.className = `emby-user-page-btn ${hasResource ? 'has' : 'not-has'}`;
        const state = hasResource ? BUTTON_STYLES.userPageBtn.has : BUTTON_STYLES.userPageBtn.notHas;
        btn.textContent = state.text;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        btn.disabled = true;
        return btn;
    }

    function createCollectionButton(hasResource) {
        const btn = document.createElement('span');
        btn.className = `emby-collection-btn ${hasResource ? 'has' : 'not-has'}`;
        btn.textContent = hasResource ? BUTTON_STYLES.collectionBtn.has.text : BUTTON_STYLES.collectionBtn.notHas.text;
        btn.title = hasResource ? 'Emby库中有此资源' : 'Emby库中无此资源';
        return btn;
    }

    function createSettingButton() {
        const btn = document.createElement('span');
        btn.className = 'emby-setting-btn';
        btn.textContent = BUTTON_STYLES.settingBtn.has.text;
        btn.title = '配置Emby服务器设置';
        return btn;
    }

    async function processPoster(poster) {
        const itemKey = `poster-${poster.href}`;
        if (state.processedItems.has(itemKey) || state.processingItems.has(itemKey)) {
            return;
        }
        state.processingItems.add(itemKey);
        const info = extractInfoFromPoster(poster);
        if (!info) {
            state.processingItems.delete(itemKey);
            return;
        }
        try {
            const hasResource = await checkEmbyResource(info.name, info.year);
            if (!poster.querySelector('.emby-poster-btn')) {
                const posterImageContainer = poster.querySelector('div.mui-1daepjq');
                if (posterImageContainer) {
                    const btn = createPosterButton(hasResource);
                    posterImageContainer.style.position = 'relative';
                    posterImageContainer.appendChild(btn);
                }
            }
            if (!poster.querySelector('.emby-name-btn')) {
                const yearElement = poster.querySelector('p.mui-1mfu778');
                if (yearElement) {
                    const btn = createNameButton(hasResource);
                    const buttonContainer = document.createElement('div');
                    buttonContainer.style.display = 'flex';
                    buttonContainer.style.justifyContent = 'center';
                    buttonContainer.style.width = '100%';
                    buttonContainer.appendChild(btn);
                    yearElement.parentNode.insertBefore(buttonContainer, yearElement.nextSibling);
                }
            }
            state.processedItems.add(itemKey);
        } catch (error) {
        } finally {
            state.processingItems.delete(itemKey);
        }
    }

    async function processDetailPage() {
        if (!isDetailPage()) return;
        const detailKey = 'detail-page';
        if (state.processedItems.has(detailKey) || state.processingItems.has(detailKey)) {
            return;
        }
        state.processingItems.add(detailKey);
        const info = extractInfoFromDetail();
        if (!info) {
            state.processingItems.delete(detailKey);
            return;
        }
        try {
            const hasResource = await checkEmbyResource(info.name, info.year);
            if (!document.querySelector('.emby-detail-poster-btn')) {
                const posterContainer = document.querySelector('.mui-ja4wo8, .mui-77cdso');
                if (posterContainer) {
                    const btn = createDetailPosterButton(hasResource);
                    posterContainer.style.position = 'relative';
                    posterContainer.appendChild(btn);
                }
            }
            if (!document.querySelector('.emby-detail-title-btn')) {
                const titleElement = document.querySelector('.mui-oqdiq5');
                if (titleElement) {
                    const btn = createDetailTitleButton(hasResource);
                    titleElement.parentNode.insertBefore(btn, titleElement.nextSibling);
                }
            }
            state.processedItems.add(detailKey);
        } catch (error) {
        } finally {
            state.processingItems.delete(detailKey);
        }
    }

    async function processSearchYearButtons() {
        const resultItems = document.querySelectorAll('a[href*="/tmdb/"]');
        for (const item of resultItems) {
            const itemKey = `search-${item.href}`;
            if (state.processedItems.has(itemKey) || state.processingItems.has(itemKey)) {
                continue;
            }
            state.processingItems.add(itemKey);
            const yearText = item.querySelector('.MuiTypography-body2');
            if (yearText && yearText.textContent.includes('(')) {
                const info = extractInfoFromSearchYear(yearText);
                if (info) {
                    try {
                        const hasResource = await checkEmbyResource(info.name, info.year);
                        if (!yearText.parentNode.querySelector('.emby-search-year-btn')) {
                            const btn = createSearchYearButton(hasResource);
                            yearText.parentNode.insertBefore(btn, yearText.nextSibling);
                        }
                    } catch (error) {
                    }
                }
            }
            state.processedItems.add(itemKey);
            state.processingItems.delete(itemKey);
        }
    }

    async function processUserPageButtons() {
        function showNotice(msg) {
            if (document.getElementById('hdhive-notice')) return;
            const div = document.createElement('div');
            div.id = 'hdhive-notice';
            div.textContent = msg;
            document.body.appendChild(div);
        }

        async function addButtonsToElements(elements) {
            let added = false;
            for (const el of elements) {
                const itemKey = `user-${el.textContent}`;
                if (state.processedItems.has(itemKey) || state.processingItems.has(itemKey)) {
                    continue;
                }
                state.processingItems.add(itemKey);
                const info = extractInfoFromUserPage(el);
                if (info) {
                    try {
                        const hasResource = await checkEmbyResource(info.name, info.year);
                        if (!el.querySelector('.emby-user-page-btn')) {
                            const btn = createUserPageButton(hasResource);
                            el.appendChild(btn);
                            added = true;
                        }
                    } catch (error) {
                    }
                }
                state.processedItems.add(itemKey);
                state.processingItems.delete(itemKey);
            }
            return added;
        }

        function waitForElements(selector, callback, maxTry = 40, intervalTime = 300) {
            let count = 0;
            const timer = setInterval(async () => {
                const elements = Array.from(document.querySelectorAll(selector))
                                      .filter(el => /\(\d{4}\)/.test(el.textContent));
                if (elements.length > 0) {
                    const added = await callback(elements);
                    if (added) {
                        const notice = document.getElementById('hdhive-notice');
                        if (notice) notice.remove();
                    }
                    clearInterval(timer);
                } else if (++count >= maxTry) {
                    showNotice('页面内容正在渲染,按钮稍后显示或请刷新页面');
                    clearInterval(timer);
                }
            }, intervalTime);
        }

        waitForElements('p', addButtonsToElements);
    }

    async function processCollectionButtons() {
        if (!isCollectionPage()) return;
        
        const collectionItems = document.querySelectorAll('a.MuiBox-root.mui-ytumd6, a.mui-r5wu0g');
        for (const item of collectionItems) {
            const itemKey = `collection-${item.href}`;
            if (state.processedItems.has(itemKey) || state.processingItems.has(itemKey)) {
                continue;
            }
            state.processingItems.add(itemKey);
            const info = extractInfoFromCollection(item);
            if (info) {
                try {
                    const hasResource = await checkEmbyResource(info.name, info.year);
                    
                    if (!item.querySelector('.emby-poster-btn')) {
                        const posterImageContainer = item.querySelector('div.mui-1daepjq, div.mui-19aj6fg');
                        if (posterImageContainer) {
                            const btn = createPosterButton(hasResource);
                            posterImageContainer.style.position = 'relative';
                            posterImageContainer.appendChild(btn);
                        }
                    }
                    
                    if (!item.querySelector('.emby-collection-btn')) {
                        const btn = createCollectionButton(hasResource);
                        const yearElement = item.querySelector('p.mui-1mfu778');
                        if (yearElement) {
                            const buttonContainer = document.createElement('div');
                            buttonContainer.style.display = 'flex';
                            buttonContainer.style.justifyContent = 'center';
                            buttonContainer.style.width = '100%';
                            buttonContainer.appendChild(btn);
                            yearElement.parentNode.insertBefore(buttonContainer, yearElement.nextSibling);
                        }
                    }
                } catch (error) {
                }
            }
            state.processedItems.add(itemKey);
            state.processingItems.delete(itemKey);
        }
    }

    async function processAllPosters() {
        const posters = document.querySelectorAll('a.MuiBox-root.mui-ytumd6');
        for (const poster of posters) {
            await processPoster(poster);
        }
        const popoverPosters = document.querySelectorAll('.MuiPopover-root a.MuiBox-root.mui-ytumd6');
        for (const poster of popoverPosters) {
            await processPoster(poster);
        }
        await processDetailPage();
        await processSearchYearButtons();
        await processCollectionButtons();
        if (isUserPage()) {
            await processUserPageButtons();
        }
    }

    function setupSearchListener() {
        const searchInput = document.querySelector('input[type="text"][name="search"]');
        if (searchInput) {
            searchInput.addEventListener('input', () => {
                state.processedItems.clear();
                state.processingItems.clear();
                setTimeout(processAllPosters, 1000);
            });
        }
    }

    function setupUrlChangeListener() {
        let currentUrl = window.location.href;
        const observer = new MutationObserver(() => {
            if (window.location.href !== currentUrl) {
                currentUrl = window.location.href;
                state.processedItems.clear();
                state.processingItems.clear();
                state.embyCache.clear();
                setTimeout(processAllPosters, 1000);
            }
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    function setupSearchDialogListener() {
        const observer = new MutationObserver(() => {
            const searchDialog = document.querySelector('.MuiDialog-paper');
            if (searchDialog) {
                state.processedItems.clear();
                state.processingItems.clear();
                setTimeout(processAllPosters, 500);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    function showSaveSuccessNotice() {
        let notice = document.getElementById('emby-save-notice');
        if (!notice) {
            notice = document.createElement('div');
            notice.id = 'emby-save-notice';
            notice.textContent = '设置保存成功!';
            document.body.appendChild(notice);
        }
        notice.classList.add('show');
        setTimeout(() => {
            notice.classList.remove('show');
            setTimeout(() => {
                notice.remove();
            }, 300);
        }, 2000);
    }
    
    function createSettingsModal() {
        const modal = document.createElement('div');
        modal.className = 'emby-settings-modal';
        modal.innerHTML = `
            <div class="emby-settings-header">
                <h2>Emby 设置</h2>
                <button class="emby-settings-close">×</button>
            </div>
            <div class="emby-settings-body">
                <div class="emby-settings-form-group">
                    <label class="emby-settings-label" for="emby-host">Emby 地址</label>
                    <input type="text" id="emby-host" class="emby-settings-input" placeholder="http/s://emby地址">
                </div>
                <div class="emby-settings-form-group">
                    <label class="emby-settings-label" for="emby-api-key">API 密钥</label>
                    <input type="password" id="emby-api-key" class="emby-settings-input" placeholder="输入您的Emby API密钥">
                </div>
            </div>
            <div class="emby-settings-footer">
                <button class="emby-settings-btn emby-settings-cancel">取消</button>
                <button class="emby-settings-btn emby-settings-save">保存</button>
            </div>
        `;
        const overlay = document.createElement('div');
        overlay.className = 'emby-settings-overlay';
        document.body.appendChild(modal);
        document.body.appendChild(overlay);
        modal.querySelector('.emby-settings-close').addEventListener('click', closeSettingsModal);
        modal.querySelector('.emby-settings-cancel').addEventListener('click', closeSettingsModal);
        modal.querySelector('.emby-settings-save').addEventListener('click', saveSettings);
        document.getElementById('emby-host').value = EMBY_CONFIG.HOST;
        document.getElementById('emby-api-key').value = EMBY_CONFIG.API_KEY;
        return { modal, overlay };
    }
    
    function showSettingsModal() {
        const { modal, overlay } = createSettingsModal();
        modal.style.display = 'block';
        overlay.style.display = 'block';
        overlay.addEventListener('click', closeSettingsModal);
    }
    
    function closeSettingsModal() {
        const modal = document.querySelector('.emby-settings-modal');
        const overlay = document.querySelector('.emby-settings-overlay');
        if (modal && overlay) {
            modal.style.display = 'none';
            overlay.style.display = 'none';
            setTimeout(() => {
                modal.remove();
                overlay.remove();
            }, 300);
        }
    }
    
    function saveSettings() {
        const host = document.getElementById('emby-host').value.trim();
        const apiKey = document.getElementById('emby-api-key').value.trim();
        if (!host || !apiKey) {
            alert('请填写完整的Emby设置信息');
            return;
        }
        GM_setValue("embyHost", host);
        GM_setValue("embyApiKey", apiKey);
        EMBY_CONFIG.HOST = host;
        EMBY_CONFIG.API_KEY = apiKey;
        state.embyCache.clear();
        state.processedItems.clear();
        state.processingItems.clear();
        processAllPosters();
        closeSettingsModal();
        showSaveSuccessNotice();
    }
    
    function addSettingButtons() {
        const checkInterval = setInterval(() => {
            const target1 = document.querySelector('a.mui-1oi34mv');
            const target2 = document.querySelector('button.mui-19y4szv');
            if (target1 && target2) {
                clearInterval(checkInterval);
                const btn1 = createSettingButton();
                btn1.addEventListener('click', () => {
                    showSettingsModal();
                });
                target1.parentNode.insertBefore(btn1, target1);
                const btn2 = createSettingButton();
                btn2.addEventListener('click', () => {
                    showSettingsModal();
                });
                target2.parentNode.insertBefore(btn2, target2);
            }
        }, 500);
    }

    let mutationTimeout;
    const observer = new MutationObserver(mutations => {
        clearTimeout(mutationTimeout);
        mutationTimeout = setTimeout(() => {
            let shouldProcess = false;
            for (const mutation of mutations) {
                if (mutation.addedNodes.length > 0) {
                    shouldProcess = true;
                    break;
                }
            }
            if (shouldProcess) {
                processAllPosters();
            }
        }, 300);
    });

    processAllPosters();
    setupSearchListener();
    setupUrlChangeListener();
    setupSearchDialogListener();
    addSettingButtons();
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();