// ==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();
}
})();