您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add Debrid Media Manager buttons to Letterboxd movie pages
// ==UserScript== // @name Letterboxd DMM Integration // @namespace letterboxd-dmm // @version 1.1 // @description Add Debrid Media Manager buttons to Letterboxd movie pages // @author You // @match https://letterboxd.com/film/* // @match https://www.letterboxd.com/film/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; console.log('Letterboxd DMM script loaded'); // Configuration - Update these URLs to match your DMM setup const DMM_BASE_URL = 'https://debridmediamanager.com'; // Function to extract movie information from Letterboxd page function getMovieInfo() { console.log('Attempting to extract movie info...'); // First, try to find IMDb ID from page links let imdbId = ''; // Method 1: Look for IMDb link in the page const imdbLink = document.querySelector('a[href*="imdb.com/title/"]'); if (imdbLink) { const imdbMatch = imdbLink.href.match(/\/title\/(tt\d+)/); if (imdbMatch) { imdbId = imdbMatch[1]; console.log(`Found IMDb ID from link: ${imdbId}`); } } // Method 2: Check for IMDb link in the film details/external links section if (!imdbId) { const detailsLinks = document.querySelectorAll('a[href*="imdb.com"]'); for (const link of detailsLinks) { const imdbMatch = link.href.match(/\/title\/(tt\d+)/); if (imdbMatch) { imdbId = imdbMatch[1]; console.log(`Found IMDb ID from details: ${imdbId}`); break; } } } // Method 3: Check meta tags for IMDb ID if (!imdbId) { const imdbMeta = document.querySelector('meta[property*="imdb"]'); if (imdbMeta && imdbMeta.content) { const imdbMatch = imdbMeta.content.match(/(tt\d+)/); if (imdbMatch) { imdbId = imdbMatch[1]; console.log(`Found IMDb ID from meta: ${imdbId}`); } } } // Get movie title - try multiple selectors const titleSelectors = [ 'h1.headline-1.primaryname', // Updated based on debug output 'h1.headline-1.filmtitle', 'h1.filmtitle', 'h1.headline-1', 'h1.js-widont', 'h1.prettify', '.film-title h1', 'section.film-header h1', 'h1', // Last resort - any h1 '[data-film-name]' // In case they use data attributes ]; let titleElement = null; let title = ''; // Debug: Log all h1 elements found const allH1s = document.querySelectorAll('h1'); console.log(`Found ${allH1s.length} h1 elements on page:`); allH1s.forEach((h1, index) => { console.log(`H1 ${index}: class="${h1.className}", text="${h1.textContent.trim()}"`); }); for (const selector of titleSelectors) { titleElement = document.querySelector(selector); if (titleElement) { title = titleElement.textContent.trim(); console.log(`Found title with selector "${selector}": ${title}`); break; } } if (!title) { console.log('Could not find movie title'); return null; } // Get year from the URL or page const urlMatch = window.location.pathname.match(/\/film\/([^\/]+)/); const movieSlug = urlMatch ? urlMatch[1] : ''; // Try to extract year from various sources let year = ''; // Method 1: From the release year link const yearSelectors = [ '.releaseyear a', '.film-title .releaseyear', 'small.number a', '.film-poster img' ]; for (const selector of yearSelectors) { const yearElement = document.querySelector(selector); if (yearElement) { if (selector === '.film-poster img') { // Extract from alt text const altText = yearElement.alt; const yearMatch = altText.match(/\((\d{4})\)/); if (yearMatch) { year = yearMatch[1]; console.log(`Found year from poster alt: ${year}`); break; } } else { year = yearElement.textContent.trim(); console.log(`Found year with selector "${selector}": ${year}`); break; } } } // Method 2: From meta tags if (!year) { const metaYear = document.querySelector('meta[property="video:release_date"]'); if (metaYear) { year = new Date(metaYear.content).getFullYear().toString(); console.log(`Found year from meta: ${year}`); } } // Method 3: Extract from page title if (!year) { const pageTitle = document.title; const yearMatch = pageTitle.match(/\((\d{4})\)/); if (yearMatch) { year = yearMatch[1]; console.log(`Found year from page title: ${year}`); } } console.log(`Extracted movie info: Title="${title}", Year="${year}", Slug="${movieSlug}", IMDb ID="${imdbId}"`); return { title, year, slug: movieSlug, imdbId: imdbId }; } // Function to create DMM button function createDMMButton(movieInfo) { const button = document.createElement('a'); // Use IMDb ID format if available, otherwise fall back to title search if (movieInfo.imdbId) { button.href = `${DMM_BASE_URL}/movie/${movieInfo.imdbId}`; button.textContent = '📁 Add to DMM'; } else { // Fallback to hash search if no IMDb ID found button.href = `${DMM_BASE_URL}/hash/${encodeURIComponent(movieInfo.title + (movieInfo.year ? ' ' + movieInfo.year : ''))}`; button.textContent = '📁 Search DMM'; } button.target = '_blank'; button.rel = 'noopener noreferrer'; button.className = 'dmm-button'; // Style the button to match Letterboxd's design button.style.cssText = ` display: inline-block; background: #00c030; color: white !important; padding: 8px 12px; border-radius: 3px; text-decoration: none !important; font-weight: 600; font-size: 11px; margin: 4px 4px 4px 0; transition: background-color 0.2s; border: none; cursor: pointer; text-transform: uppercase; letter-spacing: 0.5px; `; // Add hover effect button.addEventListener('mouseenter', function() { this.style.backgroundColor = '#00a028'; }); button.addEventListener('mouseleave', function() { this.style.backgroundColor = '#00c030'; }); return button; } // Function to create search DMM button function createSearchDMMButton(movieInfo) { const button = document.createElement('a'); // Use the correct DMM search URL format button.href = `${DMM_BASE_URL}/search?query=${encodeURIComponent(movieInfo.title + (movieInfo.year ? ' ' + movieInfo.year : ''))}`; button.target = '_blank'; button.rel = 'noopener noreferrer'; button.className = 'dmm-search-button'; button.textContent = '🔍 Search DMM'; // Style the button button.style.cssText = ` display: inline-block; background: #0078d4; color: white !important; padding: 8px 12px; border-radius: 3px; text-decoration: none !important; font-weight: 600; font-size: 11px; margin: 4px 4px 4px 0; transition: background-color 0.2s; border: none; cursor: pointer; text-transform: uppercase; letter-spacing: 0.5px; `; // Add hover effect button.addEventListener('mouseenter', function() { this.style.backgroundColor = '#106ebe'; }); button.addEventListener('mouseleave', function() { this.style.backgroundColor = '#0078d4'; }); return button; } // Function to insert DMM buttons function insertDMMButtons() { console.log('insertDMMButtons called'); // Check if buttons already exist if (document.querySelector('.dmm-button')) { console.log('DMM buttons already exist, skipping'); return; } const movieInfo = getMovieInfo(); if (!movieInfo) { console.log('Could not extract movie info from Letterboxd page'); return; } // Find a good location to insert the buttons const possibleContainers = [ 'section.film-header', '.film-header-group', '.film-header', '.film-title-wrapper', 'h1.headline-1.primaryname', // Updated based on debug output 'h1.headline-1.filmtitle', 'h1.filmtitle', '.film-title', '.primarytext' ]; let container = null; for (const selector of possibleContainers) { container = document.querySelector(selector); if (container) { console.log(`Found container with selector: ${selector}`); break; } } if (!container) { console.log('Could not find suitable container, trying to insert after title'); const titleElement = document.querySelector('h1.headline-1.primaryname, h1.headline-1.filmtitle, h1.filmtitle, h1.headline-1'); if (titleElement) { container = titleElement.parentElement; console.log('Using title parent as container:', container.tagName, container.className); } } if (!container) { console.log('Could not find any suitable container for DMM buttons'); console.log('Available sections:', Array.from(document.querySelectorAll('section')).map(s => s.className)); console.log('Available divs with classes:', Array.from(document.querySelectorAll('div[class]')).slice(0, 10).map(d => d.className)); return; } // Create button container const buttonContainer = document.createElement('div'); buttonContainer.className = 'dmm-buttons-container'; buttonContainer.style.cssText = ` margin: 12px 0 8px 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; `; // Create buttons const addButton = createDMMButton(movieInfo); const searchButton = createSearchDMMButton(movieInfo); // Add buttons to container buttonContainer.appendChild(addButton); buttonContainer.appendChild(searchButton); // Insert the button container if (container.tagName === 'H1') { container.parentNode.insertBefore(buttonContainer, container.nextSibling); } else if (container.tagName === 'SECTION') { // For section containers, append at the end container.appendChild(buttonContainer); } else { // For other containers, try to insert after the title const titleInContainer = container.querySelector('h1'); if (titleInContainer) { titleInContainer.parentNode.insertBefore(buttonContainer, titleInContainer.nextSibling); } else { container.appendChild(buttonContainer); } } console.log('DMM buttons added successfully'); } // Function to wait for elements and then insert buttons function waitAndInsert() { let attempts = 0; const maxAttempts = 30; function tryInsert() { attempts++; console.log(`Attempt ${attempts} to insert DMM buttons`); // Check for multiple possible title elements const possibleTitles = [ document.querySelector('h1.headline-1.filmtitle'), document.querySelector('h1.filmtitle'), document.querySelector('h1.headline-1'), document.querySelector('h1.js-widont'), document.querySelector('h1.prettify'), document.querySelector('h1'), document.querySelector('[data-film-name]'), document.querySelector('.film-title h1') ].filter(Boolean); console.log(`Found ${possibleTitles.length} potential title elements`); // Also check if the page has loaded enough content const hasContent = document.querySelector('.film-poster') || document.querySelector('.film-header') || document.querySelector('section') || document.querySelector('.col-17'); if (possibleTitles.length > 0 && hasContent) { console.log('Page appears to be loaded, attempting to insert buttons'); insertDMMButtons(); } else if (attempts < maxAttempts) { console.log(`Not ready yet (titles: ${possibleTitles.length}, content: ${!!hasContent}), retrying...`); setTimeout(tryInsert, 800); } else { console.log('Max attempts reached. Final debug info:'); console.log('Page HTML snippet:', document.body.innerHTML.substring(0, 500)); console.log('All H1 elements:', Array.from(document.querySelectorAll('h1')).map(h1 => ({ text: h1.textContent.trim(), class: h1.className, id: h1.id }))); } } tryInsert(); } // Initialize the script function init() { console.log('Initializing DMM script'); console.log('Current URL:', window.location.href); // Check if we're on a movie page if (!window.location.pathname.startsWith('/film/')) { console.log('Not on a film page, skipping'); return; } // Wait for page to load and try to insert buttons waitAndInsert(); } // Run when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Also run on URL changes (for single page app navigation) let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; console.log('URL changed to:', url); setTimeout(init, 1000); } }).observe(document, { subtree: true, childList: true }); })();