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
// @connect      imdb.com
// @connect      rottentomatoes.com
// @connect      metacritic.com
// ==/UserScript==

(function() {
    'use strict';

    const ICONS = {
        imdb: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6ElEQVQ4ja2TsQ3CMBBF/0UMgSgYgBkyAANkBEZgBLqUkWgzAiPQMgINI7CBaGgQhbElY4JQPsnS6c7/7/587wB/GAA7ADcAKYAYJ+IL9bV0JoX73I0AKgmqAbQAngBGAE8JagCcdG5MUEQGqT0AGwBHOZ8A7LXxCmCpdR1LyVyqL3rj2mm6AnCQzEbzRZ1dVMWdU5VIHqPFKJc+c5MC0CvTWvPGBE34JV0JwFwL3GfqtKi7WQbVHlXY1Dwp/Gv3w2I6W6MjgK0UzRpHXX1qXQN4fwsITRmAfaSp86bKHNgF8L9kpXEeE/QBWUU9pW4s4WYAAAAASUVORK5CYII=',
        rotten: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABP0lEQVQ4jZWSPUsDQRCGn7vceSpiYSMIAUEQRLCxsLSxFgv/goWNjYU/wcI/YGlhYWdlYyEIFoJgIQiCIBgQBU0uzd3u7FjcJZcYc+oLL8w+M/PO7MwK/hm+5wngxvdcqT1nwRmwD/QkSS7DMLyW1k5cK8ltxJmwNgQuAC0iQ2BDRC6ttdPGUkQi4AfYBHaAHeAd6AFC0zQP1tqRqvzFHSilWiKyBawppUZhGN7XwDmXK6UcMPX9WVFV6jWIyJox5tj3/XPf91/CMHwDMMaMAE9ExsCZMebUWvtVOVBKrQJdYB5fVBTFR3WmtR4Dpz+Ps8YLlNYqpZaAh6IoPqvAOZc75144514rQGayB6K1XhGRO+dc3DRutdYbwKDMZO4EVdtF8gocAQNjzKQEp8AYeAL6ZSZzG4lI2xgzKRtXRG6/AduYkKWrJqV7AAAAAElFTkSuQmCC',
        metacritic: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtUlEQVQ4jWNgoBAwQun/+BSzYBH7j0UNXgW4DPgPBTjlYQrwGYBNDqcBMAOI0YzTAGI1YzcAj2asBhDQjGEAIc3oBox/u3b3PwMDw38GBob/x95s2Y1LM7IBr7+c3//405n9/xkYGP4ffX1i/pH3x3bj0oxiwJsvF/Yfen9s938GBob/h94d2X3o3ZHd2DQjG3Dg3eHdB94d3v2fgYHh/4G3B3cfendkNy7NSAacs7XlGXEpBgDZMJSlUEFhkwAAAABJRU5ErkJggg=='
    };

    async function getIMDbRating(title, year) {
        try {
            const imdbIdMatch = document.querySelector('a[href*="www.imdb.com/title/tt"]');
            if (!imdbIdMatch) return null;
            
            const imdbId = imdbIdMatch.href.match(/tt\d+/)[0];
            
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://www.imdb.com/title/${imdbId}/`,
                    onload: function(response) {
                        const ratingMatch = response.responseText.match(/"ratingValue":\s*"([^"]+)"/);
                        if (!ratingMatch) resolve(null);
                        
                        resolve({
                            rating: ratingMatch[1],
                            url: `https://www.imdb.com/title/${imdbId}/`
                        });
                    },
                    onerror: function(error) {
                        console.error('Error fetching IMDb rating:', error);
                        resolve(null);
                    }
                });
            });
        } catch (error) {
            console.error('Error fetching IMDb rating:', error);
            return null;
        }
    }

    async function getRottenTomatoesRating(title, year) {
        try {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://www.rottentomatoes.com/search?search=${encodeURIComponent(title)}`,
                    onload: async function(response) {
                        try {
                            const rtUrlMatch = response.responseText.match(new RegExp(`href="([^"]+${year}[^"]+)"`));
                            if (!rtUrlMatch) {
                                resolve(null);
                                return;
                            }

                            const rtUrl = 'https://www.rottentomatoes.com' + rtUrlMatch[1];
                            
                            GM_xmlhttpRequest({
                                method: 'GET',
                                url: rtUrl,
                                onload: function(movieResponse) {
                                    const ratingMatch = movieResponse.responseText.match(/tomatometer">(\d+)%/);
                                    if (!ratingMatch) {
                                        resolve(null);
                                        return;
                                    }
                                    
                                    resolve({
                                        rating: ratingMatch[1] + '%',
                                        url: rtUrl
                                    });
                                },
                                onerror: function(error) {
                                    console.error('Error fetching RT movie page:', error);
                                    resolve(null);
                                }
                            });
                        } catch (error) {
                            console.error('Error processing RT response:', error);
                            resolve(null);
                        }
                    },
                    onerror: function(error) {
                        console.error('Error fetching RT search:', error);
                        resolve(null);
                    }
                });
            });
        } catch (error) {
            console.error('Error in RT rating function:', error);
            return null;
        }
    }

    async function getMetacriticRating(title, year) {
        try {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://www.metacritic.com/search/movie/${encodeURIComponent(title)}/results`,
                    onload: async function(response) {
                        try {
                            const mcUrlMatch = response.responseText.match(new RegExp(`href="([^"]+${year}[^"]+)"`));
                            if (!mcUrlMatch) {
                                resolve(null);
                                return;
                            }

                            const mcUrl = 'https://www.metacritic.com' + mcUrlMatch[1];
                            
                            GM_xmlhttpRequest({
                                method: 'GET',
                                url: mcUrl,
                                onload: function(movieResponse) {
                                    const ratingMatch = movieResponse.responseText.match(/metascore_w[^>]+>(\d+)</);
                                    if (!ratingMatch) {
                                        resolve(null);
                                        return;
                                    }
                                    
                                    resolve({
                                        rating: ratingMatch[1],
                                        url: mcUrl
                                    });
                                },
                                onerror: function(error) {
                                    console.error('Error fetching Metacritic movie page:', error);
                                    resolve(null);
                                }
                            });
                        } catch (error) {
                            console.error('Error processing Metacritic response:', error);
                            resolve(null);
                        }
                    },
                    onerror: function(error) {
                        console.error('Error fetching Metacritic search:', error);
                        resolve(null);
                    }
                });
            });
        } catch (error) {
            console.error('Error in Metacritic rating 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() {
        if (!document.querySelector('.filmCoverSection__title')) {
            setTimeout(init, 100);
            return;
        }

        const titleElement = document.querySelector('.filmCoverSection__title');
        if (!titleElement) return;

        const title = titleElement.textContent.trim();
        const year = document.querySelector('.filmCoverSection__year')?.textContent.trim() || '';
        
        const ratingsContainer = document.createElement('div');
        ratingsContainer.style.cssText = `
            margin-top: 15px;
            display: flex;
            justify-content: center;
            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, year),
                getRottenTomatoesRating(title, year),
                getMetacriticRating(title, 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);
        }
    }

    init();
})();