您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add P-Stream links to Letterboxd film pages
// ==UserScript== // @name Letterboxd to P-Stream // @namespace http://tampermonkey.net/ // @version 1.2 // @description Add P-Stream links to Letterboxd film pages // @author archiivv // @match https://letterboxd.com/film/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @license MIT // ==/UserScript== (function() { 'use strict'; // Wait for page to load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { // Wait a bit for dynamic content to load setTimeout(addPstreamButton, 1000); } function addPstreamButton() { // Get film data from the page let filmTitle, filmYear; // Try to get from filmData global variable (if available) if (typeof window.filmData !== 'undefined') { filmTitle = window.filmData.name; filmYear = window.filmData.releaseYear; } else { // Fallback: scrape from page elements const titleElement = document.querySelector('h1.headline-1'); const yearElement = document.querySelector('.number a'); if (titleElement) { filmTitle = titleElement.textContent.trim(); } if (yearElement) { filmYear = yearElement.textContent.trim(); } } if (!filmTitle) { console.log('Could not find film title'); return; } console.log('Film:', filmTitle, filmYear); // Look for TMDB link on the page const tmdbLink = document.querySelector('a[data-track-action="TMDb"], a[href*="themoviedb.org"]'); if (tmdbLink) { const tmdbMatch = tmdbLink.href.match(/themoviedb\.org\/movie\/(\d+)/); if (tmdbMatch) { const tmdbId = tmdbMatch[1]; console.log('Found TMDB ID:', tmdbId); createPstreamButton(tmdbId, filmTitle); return; } } // If no TMDB link found, search via API searchTMDBForId(filmTitle, filmYear); } function searchTMDBForId(title, year) { // Free TMDB API - you need to get a key from https://www.themoviedb.org/settings/api const apiKey = 'YOUR_API_KEY_HERE'; // Replace with your actual API key if (apiKey === 'YOUR_API_KEY_HERE') { console.log('Please add your TMDB API key to the script'); // For now, let's try to construct URL without API createPstreamButtonFallback(title, year); return; } const searchUrl = `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&query=${encodeURIComponent(title)}&year=${year}`; GM_xmlhttpRequest({ method: 'GET', url: searchUrl, onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.results && data.results.length > 0) { const tmdbId = data.results[0].id; console.log('Found TMDB ID via API:', tmdbId); createPstreamButton(tmdbId, title); } else { console.log('No TMDB results found'); createPstreamButtonFallback(title, year); } } catch (error) { console.error('Error parsing TMDB response:', error); createPstreamButtonFallback(title, year); } }, onerror: function(error) { console.error('Error fetching TMDB data:', error); createPstreamButtonFallback(title, year); } }); } function createPstreamButton(tmdbId, title) { // Create slug from title const slug = title.toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); const pstreamUrl = `https://pstream.mov/media/tmdb-movie-${tmdbId}-${slug}`; addButtonToPage(pstreamUrl, 'Watch on P-Stream'); } function createPstreamButtonFallback(title, year) { // Create a generic search URL or best guess const slug = title.toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); const pstreamUrl = `https://pstream.mov/search?q=${encodeURIComponent(title + ' ' + year)}`; addButtonToPage(pstreamUrl, 'Search on P-Stream'); } function addButtonToPage(url, text) { // Find the actions panel ul const actionsList = document.querySelector('.actions-panel ul, .js-actions-panel ul'); if (!actionsList) { console.log('Could not find actions panel list'); return; } // Create li element to match Letterboxd structure const listItem = document.createElement('li'); listItem.className = 'pstream-action'; // Create the link const link = document.createElement('a'); link.href = url; link.target = '_blank'; link.textContent = text; // Add the link to the list item listItem.appendChild(link); // Add to the actions list actionsList.appendChild(listItem); console.log('Added P-Stream button:', url); } // Add CSS to match Letterboxd's design GM_addStyle(` .pstream-action { color: var(--content-color) !important; background-color: #456 !important; text-align: center !important; border-bottom: 1px solid #2C3440 !important; padding: 10px 0 !important; box-sizing: border-box !important; } .pstream-action a { color: var(--content-color) !important; text-decoration: none !important; display: block !important; width: 100% !important; height: 100% !important; } .pstream-action a:hover { color: var(--content-color) !important; } `); })();