Greasy Fork

Filmweb External Ratings

Add IMDb, Rotten Tomatoes, and Metacritic ratings to Filmweb

目前为 2025-03-29 提交的版本。查看 最新版本

// ==UserScript==
// @name         Filmweb External Ratings
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Add IMDb, Rotten Tomatoes, and Metacritic ratings to Filmweb
// @author       mrkkr
// @match          http*://www.filmweb.pl/serial/*
// @match          http*://www.filmweb.pl/film/*
// @match          http*://www.filmweb.pl/tvshow/*
// @match          http*://www.imdb.com/*
// @match          http*://www.rottentomatoes.com/*
// @match          http*://www.metacritic.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM.xmlhttpRequest
// @connect      *
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const ICONS = {
        imdb: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iNTEycHgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJfeDMxXzcxLWltZGIiPjxnPjxnPjxnPjxwYXRoIGQ9Ik00MzYuNzE0LDI2LjAwMUg3NS4yODdjLTI3LjIxLDAtNDkuMjg1LDIyLjA3NS00OS4yODUsNDkuMjg2djM2MS40MjcgICAgICBjMCwyNy4yMTEsMjIuMDc1LDQ5LjI4NSw0OS4yODUsNDkuMjg1aDM2MS40MjdjMjcuMjExLDAsNDkuMjg0LTIyLjA3NCw0OS4yODQtNDkuMjg1Vjc1LjI4NyAgICAgIEM0ODUuOTk4LDQ4LjA3Niw0NjMuOTI1LDI2LjAwMSw0MzYuNzE0LDI2LjAwMXoiIHN0eWxlPSJmaWxsOiNGQkJGMTQ7Ii8+PC9nPjwvZz48cmVjdCBoZWlnaHQ9IjEzMS4yMjIiIHN0eWxlPSJmaWxsOiMyNzMyMzg7IiB3aWR0aD0iMzMuODgzIiB4PSI5MS43MTYiIHk9IjE5MC4yODciLz48cGF0aCBkPSJNMjQxLjgzMSwzMjEuNTA5aC0yOS40Njl2LTg4LjcxNGwtMTEuOTEyLDg4LjcxNEgxNzkuM2wtMTIuNTI4LTg2Ljc2M3Y4Ni43NjNoLTI5Ljc3NlYxOTAuMjg3ICAgIGg0My45NDdjMy4zOSwyMC4zMjksNi4xNiw0MC45NjgsOC45MzQsNjEuNTA0bDcuODAzLTYxLjUwNGg0NC4xNTJWMzIxLjUwOXoiIHN0eWxlPSJmaWxsOiMyNzMyMzg7Ii8+PHBhdGggZD0iTTMzMC41NDQsMjM2LjhjMC04LjMxNywwLjMxLTE3LjI1LTEuNDM4LTI1LjA1NWMtNC40MTQtMjMuMTAyLTMyLjI0LTIxLjQ1OC01MC4zMTEtMjEuNDU4aC0yNS4yNjEgICAgdjEzMS4yMjJDMzQxLjk0MiwzMjEuNjEyLDMzMC41NDQsMzI3LjY2OSwzMzAuNTQ0LDIzNi44eiBNMjg3LjUyMiwyOTguNzEzdi04NS45NGMxMi4yMTksMCwxMC41NzYsNi40NywxMC41NzYsMTYuNDI4djUwLjYyMiAgICBDMjk4LjA5OSwyODkuNzgxLDMwMC4wNDksMjk5LjAyMiwyODcuNTIyLDI5OC43MTN6IiBzdHlsZT0iZmlsbDojMjczMjM4OyIvPjxwYXRoIGQ9Ik0zOTUuOTQ5LDIyMy42NTZjLTkuMTM3LDAtMTUuMjk4LDIuNzczLTIxLjQ1Nyw5LjQ0N3YtNDIuODE2aC0zMi41NXYxMzEuMjIyaDMwLjU5N2wxLjk1My04LjMxNyAgICBjNS44NTIsNi45ODIsMTIuMjE4LDEwLjA2MywyMS40NTcsMTAuMDYzYzIwLjMzMSwwLDIyLjc5NS0xNS42MDcsMjIuNzk1LTMxLjcyOXYtMzYuOTYzICAgIEM0MTguNzQ0LDIzNi44LDQxNy45MjMsMjIzLjY1NiwzOTUuOTQ5LDIyMy42NTZ6IE0zNzkuNTIyLDMwNC4zNjJjLTEuNjQyLDAtMy4wODEtMC44MjMtMy45MDItMi40NjUgICAgYy0yLjI2LTUuMjM3LTEuMTI4LTQ1LjI4MS0xLjEyOC00NS44OTdjMC0zLjkwMS0xLjEzMi0xMy4wNCw1LjAzLTEzLjA0YzcuNDk2LDAsNi4zNjQsNy40OTYsNi4zNjQsMTMuMDR2MzMuNTc0ICAgIEMzODUuODg3LDI5NS4xMiwzODcuNTMsMzA0LjM2MiwzNzkuNTIyLDMwNC4zNjJ6IiBzdHlsZT0iZmlsbDojMjczMjM4OyIvPjwvZz48L2c+PGcgaWQ9IkxheWVyXzEiLz48L3N2Zz4=',
        rotten: 'data:image/png;base64,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',
        metacritic: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAgMAAADXB5lNAAAADFBMVEUAAAACIzD/ygD///+DWqJTAAAAAXRSTlMAQObYZgAAAVFJREFUOMuNk0uOgzAQREcsc5Tcx0HKhj13iOAS7Nkgge/DUby0PFWuTnuG0Uhp5SO/UM8daH99Wl2M82X9m0TW3ta3WGt1cI9HCLykGQJKFiUWgodnGGCN+zuxCDyRUSJYWeZeE0NCxsBGUAoyklTFUErCPnIGgQzJTGdVTIWZsQIpUCetzUnJs4JFgJLH/t6kLwnEQKjgHCiJDeBVEkF3qAtqMhoBUBf6GAkYIKAE4GbgrBKCjb8RBAB0RjCxTX6dP0DmhcEBCqrsIA9Vkgy8plTbRAngV6Syg54AGQc1T9qAdWXgkFAS/Tl11QD7qjfRgSR460nFYBKs7Z5K8nKwSKKp0oOiRGAn2EwSNCBoVRLWuGocJFEbGhhJ1Aa3ocQ3AaAky+ljOSUfZVgpkWL10e6lmC/DD6ck1+PR6ZKRCcuAYB3/HkIvgX8Psh/1D+sbAAmSjR+xz1QAAAAASUVORK5CYII='
    };

    async function getIMDbRating(title, originalTitle, year) {
        try {
            console.log('Getting IMDb rating for:', {title, originalTitle, year});
            
            const imdbLinkElem = document.querySelector('a[href*="www.imdb.com/title/tt"]');
            if (imdbLinkElem) {
                const imdbId = imdbLinkElem.href.match(/tt\d+/)[0];
                console.log('Found IMDb ID from page link:', imdbId);
                
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: `https://www.imdb.com/title/${imdbId}/`,
                        headers: {
                            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                        },
                        onload: function(response) {
                            console.log('IMDb page response:', response.status);
                            const ratingMatch = response.responseText.match(/ratingValue":{"@type":"AggregateRating"[^}]+"ratingValue":"([^"]+)"/);
                            if (ratingMatch) {
                                resolve({
                                    rating: ratingMatch[1] + '/10',
                                    url: `https://www.imdb.com/title/${imdbId}/`
                                });
                            } else {
                                const oldRatingMatch = response.responseText.match(/class="ipc-button__text"[^>]*>([0-9.]+)<\/span>/);
                                if (oldRatingMatch) {
                                    resolve({
                                        rating: oldRatingMatch[1] + '/10',
                                        url: `https://www.imdb.com/title/${imdbId}/`
                                    });
                                } else {
                                    resolve(null);
                                }
                            }
                        },
                        onerror: (error) => {
                            console.error('IMDb page fetch error:', error);
                            resolve(null);
                        }
                    });
                });
            }
            
            const searchTitle = originalTitle || title;
            const searchUrl = `https://www.imdb.com/find?q=${encodeURIComponent(searchTitle)}+${year}&s=tt`;
            console.log('Searching IMDb:', searchUrl);
            
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: searchUrl,
                    headers: {
                        'User-Agent': 'Mozilla/5.0'
                    },
                    onload: function(response) {
                        const match = response.responseText.match(/href="\/title\/(tt\d+)/);
                        if (!match) {
                            console.log('No IMDb search results');
                            resolve(null);
                            return;
                        }

                        const imdbId = match[1];
                        console.log('Found IMDb ID from search:', imdbId);

                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: `https://www.imdb.com/title/${imdbId}/`,
                            headers: {
                                'User-Agent': 'Mozilla/5.0'
                            },
                            onload: function(response) {
                                const ratingMatch = response.responseText.match(/ratingValue":{"@type":"AggregateRating"[^}]+"ratingValue":"([^"]+)"/);
                                if (ratingMatch) {
                                    resolve({
                                        rating: ratingMatch[1] + '/10',
                                        url: `https://www.imdb.com/title/${imdbId}/`
                                    });
                                } else {
                                    resolve(null);
                                }
                            }
                        });
                    }
                });
            });
        } catch (error) {
            console.error('Error in IMDb function:', error);
            return null;
        }
    }

    async function getRottenTomatoesRating(title, originalTitle, year) {
        try {
            const searchTitle = originalTitle || title;
            console.log('Getting RT rating for:', {title: searchTitle, year});
            
            const movieSlug = searchTitle.toLowerCase()
                .replace(/[^a-z0-9]+/g, '_')
                .replace(/^_+|_+$/g, '');
            
            const directUrl = `https://www.rottentomatoes.com/m/${movieSlug}_${year}`;
            console.log('Trying direct RT URL:', directUrl);

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: directUrl,
                    headers: {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            const scoreMatch = response.responseText.match(/"ratingValue":"(\d+)"/);
                            if (scoreMatch) {
                                resolve({
                                    rating: scoreMatch[1] + '%',
                                    url: directUrl
                                });
                                return;
                            }
                        }
                        
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: `https://www.rottentomatoes.com/search?search=${encodeURIComponent(searchTitle)}`,
                            headers: {
                                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                            },
                            onload: function(searchResponse) {
                                try {
                                    const movieMatch = searchResponse.responseText.match(
                                        new RegExp(`href="(/m/[^"]+${year}[^"]*)"[^>]*>([^<]+)</a>`)
                                    );
                                    if (!movieMatch) {
                                        console.log('No RT search results');
                                        resolve(null);
                                        return;
                                    }

                                    const movieUrl = 'https://www.rottentomatoes.com' + movieMatch[1];
                                    console.log('Found RT movie:', movieMatch[2], movieUrl);

                                    GM_xmlhttpRequest({
                                        method: 'GET',
                                        url: movieUrl,
                                        headers: {
                                            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                                        },
                                        onload: function(movieResponse) {
                                            const scoreMatch = movieResponse.responseText.match(/"ratingValue":"(\d+)"/);
                                            if (scoreMatch) {
                                                resolve({
                                                    rating: scoreMatch[1] + '%',
                                                    url: movieUrl
                                                });
                                            } else {
                                                resolve(null);
                                            }
                                        }
                                    });
                                } catch (error) {
                                    console.error('Error processing RT response:', error);
                                    resolve(null);
                                }
                            }
                        });
                    }
                });
            });
        } catch (error) {
            console.error('Error in RT function:', error);
            return null;
        }
    }

    async function getMetacriticRating(title, originalTitle, year) {
        try {
            console.log('Getting Metacritic rating for:', {title, originalTitle, year});
            
            const searchTitle = (originalTitle || title).toLowerCase()
                .replace(/[^\w\s-]/g, '')
                .replace(/\s+/g, '-');
            
            const directUrl = `https://www.metacritic.com/movie/${searchTitle}`;
            console.log('Trying Metacritic URL:', directUrl);

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: directUrl,
                    headers: {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            const scoreMatch = response.responseText.match(/metascore_w[^>]+>(\d+)</);
                            if (scoreMatch) {
                                resolve({
                                    rating: scoreMatch[1],
                                    url: directUrl
                                });
                                return;
                            }
                        }

                        const searchUrl = `https://www.metacritic.com/search/movie/${encodeURIComponent(searchTitle)}/results`;
                        console.log('Searching Metacritic:', searchUrl);

                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: searchUrl,
                            headers: {
                                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                            },
                            onload: function(searchResponse) {
                                try {
                                    const movieMatch = searchResponse.responseText.match(
                                        new RegExp(`href="([^"]+${year}[^"]*)"[^>]*>([^<]+)</a>`)
                                    );
                                    if (!movieMatch) {
                                        console.log('No Metacritic search results');
                                        resolve(null);
                                        return;
                                    }

                                    const movieUrl = 'https://www.metacritic.com' + movieMatch[1];
                                    console.log('Found Metacritic movie:', movieMatch[2], movieUrl);

                                    GM_xmlhttpRequest({
                                        method: 'GET',
                                        url: movieUrl,
                                        headers: {
                                            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                                        },
                                        onload: function(movieResponse) {
                                            const scoreMatch = movieResponse.responseText.match(/metascore_w[^>]+>(\d+)</);
                                            if (scoreMatch) {
                                                resolve({
                                                    rating: scoreMatch[1],
                                                    url: movieUrl
                                                });
                                            } else {
                                                resolve(null);
                                            }
                                        }
                                    });
                                } catch (error) {
                                    console.error('Error processing Metacritic response:', error);
                                    resolve(null);
                                }
                            }
                        });
                    }
                });
            });
        } catch (error) {
            console.error('Error in Metacritic function:', error);
            return null;
        }
    }

    function createRatingElement(icon, rating, url) {
        const container = document.createElement('div');
        container.style.cssText = `
            display: inline-flex;
            align-items: center;
            margin: 0 10px;
            cursor: pointer;
            padding: 5px 10px;
            border-radius: 4px;
            background: rgba(0,0,0,0.05);
            transition: background 0.2s;
        `;

        container.onmouseover = () => container.style.background = 'rgba(0,0,0,0.1)';
        container.onmouseout = () => container.style.background = 'rgba(0,0,0,0.05)';

        const img = document.createElement('img');
        img.src = icon;
        img.style.cssText = `
            width: 16px;
            height: 16px;
            margin-right: 8px;
        `;

        const span = document.createElement('span');
        span.textContent = rating || 'N/A';
        span.style.cssText = `
            font-weight: bold;
            color: #333;
        `;

        container.appendChild(img);
        container.appendChild(span);

        if (url) {
            container.addEventListener('click', () => window.open(url, '_blank'));
        }

        return container;
    }

    async function init() {
        console.log('Initializing script...');
        if (!document.querySelector('.filmCoverSection__title')) {
            console.log('Title element not found, retrying...');
            setTimeout(init, 100);
            return;
        }

        const originalTitle = document.querySelector('.filmCoverSection__originalTitle')?.firstChild?.textContent?.trim();
        const title = document.querySelector('.filmCoverSection__title')?.textContent?.trim();
        const year = document.querySelector('.filmCoverSection__year')?.textContent?.trim() || '';

        console.log('Found titles:', {title, originalTitle, year});

        const ratingsContainer = document.createElement('div');
        ratingsContainer.style.cssText = `
            margin-top: 15px;
            display: flex;
            justify-content: left;
            flex-wrap: wrap;
            gap: 10px;
        `;

        const filmCoverSection = document.querySelector('.filmCoverSection__card');
        if (!filmCoverSection) return;

        const placeholders = {
            imdb: createRatingElement(ICONS.imdb, 'Loading...', null),
            rotten: createRatingElement(ICONS.rotten, 'Loading...', null),
            metacritic: createRatingElement(ICONS.metacritic, 'Loading...', null)
        };

        Object.values(placeholders).forEach(el => ratingsContainer.appendChild(el));
        filmCoverSection.appendChild(ratingsContainer);

        try {
            const [imdb, rotten, metacritic] = await Promise.all([
                getIMDbRating(title, originalTitle, year),
                getRottenTomatoesRating(title, originalTitle, year),
                getMetacriticRating(title, originalTitle, year)
            ]);

            if (imdb) placeholders.imdb.replaceWith(createRatingElement(ICONS.imdb, imdb.rating, imdb.url));
            if (rotten) placeholders.rotten.replaceWith(createRatingElement(ICONS.rotten, rotten.rating, rotten.url));
            if (metacritic) placeholders.metacritic.replaceWith(createRatingElement(ICONS.metacritic, metacritic.rating, metacritic.url));
        } catch (error) {
            console.error('Error fetching ratings:', error);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();