// ==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/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iNTEycHgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJfeDMxXzcxLWltZGIiPjxnPjxnPjxnPjxwYXRoIGQ9Ik00MzYuNzE0LDI2LjAwMUg3NS4yODdjLTI3LjIxLDAtNDkuMjg1LDIyLjA3NS00OS4yODUsNDkuMjg2djM2MS40MjcgICAgICBjMCwyNy4yMTEsMjIuMDc1LDQ5LjI4NSw0OS4yODUsNDkuMjg1aDM2MS40MjdjMjcuMjExLDAsNDkuMjg0LTIyLjA3NCw0OS4yODQtNDkuMjg1Vjc1LjI4NyAgICAgIEM0ODUuOTk4LDQ4LjA3Niw0NjMuOTI1LDI2LjAwMSw0MzYuNzE0LDI2LjAwMXoiIHN0eWxlPSJmaWxsOiNGQkJGMTQ7Ii8+PC9nPjwvZz48cmVjdCBoZWlnaHQ9IjEzMS4yMjIiIHN0eWxlPSJmaWxsOiMyNzMyMzg7IiB3aWR0aD0iMzMuODgzIiB4PSI5MS43MTYiIHk9IjE5MC4yODciLz48cGF0aCBkPSJNMjQxLjgzMSwzMjEuNTA5aC0yOS40Njl2LTg4LjcxNGwtMTEuOTEyLDg4LjcxNEgxNzkuM2wtMTIuNTI4LTg2Ljc2M3Y4Ni43NjNoLTI5Ljc3NlYxOTAuMjg3ICAgIGg0My45NDdjMy4zOSwyMC4zMjksNi4xNiw0MC45NjgsOC45MzQsNjEuNTA0bDcuODAzLTYxLjUwNGg0NC4xNTJWMzIxLjUwOXoiIHN0eWxlPSJmaWxsOiMyNzMyMzg7Ii8+PHBhdGggZD0iTTMzMC41NDQsMjM2LjhjMC04LjMxNywwLjMxLTE3LjI1LTEuNDM4LTI1LjA1NWMtNC40MTQtMjMuMTAyLTMyLjI0LTIxLjQ1OC01MC4zMTEtMjEuNDU4aC0yNS4yNjEgICAgdjEzMS4yMjJDMzQxLjk0MiwzMjEuNjEyLDMzMC41NDQsMzI3LjY2OSwzMzAuNTQ0LDIzNi44eiBNMjg3LjUyMiwyOTguNzEzdi04NS45NGMxMi4yMTksMCwxMC41NzYsNi40NywxMC41NzYsMTYuNDI4djUwLjYyMiAgICBDMjk4LjA5OSwyODkuNzgxLDMwMC4wNDksMjk5LjAyMiwyODcuNTIyLDI5OC43MTN6IiBzdHlsZT0iZmlsbDojMjczMjM4OyIvPjxwYXRoIGQ9Ik0zOTUuOTQ5LDIyMy42NTZjLTkuMTM3LDAtMTUuMjk4LDIuNzczLTIxLjQ1Nyw5LjQ0N3YtNDIuODE2aC0zMi41NXYxMzEuMjIyaDMwLjU5N2wxLjk1My04LjMxNyAgICBjNS44NTIsNi45ODIsMTIuMjE4LDEwLjA2MywyMS40NTcsMTAuMDYzYzIwLjMzMSwwLDIyLjc5NS0xNS42MDcsMjIuNzk1LTMxLjcyOXYtMzYuOTYzICAgIEM0MTguNzQ0LDIzNi44LDQxNy45MjMsMjIzLjY1NiwzOTUuOTQ5LDIyMy42NTZ6IE0zNzkuNTIyLDMwNC4zNjJjLTEuNjQyLDAtMy4wODEtMC44MjMtMy45MDItMi40NjUgICAgYy0yLjI2LTUuMjM3LTEuMTI4LTQ1LjI4MS0xLjEyOC00NS44OTdjMC0zLjkwMS0xLjEzMi0xMy4wNCw1LjAzLTEzLjA0YzcuNDk2LDAsNi4zNjQsNy40OTYsNi4zNjQsMTMuMDR2MzMuNTc0ICAgIEMzODUuODg3LDI5NS4xMiwzODcuNTMsMzA0LjM2MiwzNzkuNTIyLDMwNC4zNjJ6IiBzdHlsZT0iZmlsbDojMjczMjM4OyIvPjwvZz48L2c+PGcgaWQ9IkxheWVyXzEiLz48L3N2Zz4=',
rotten: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAGVUExURf///wBdNQBrNQ0oDQ1dNQ1rNRpdNRprNRp4NShDGihQGihQKCh4NTUaDTUoDTUoGjU1GjVDGjVDKDVQKDVdKDVrNTV4NTWGNUM1GkNDGkNDKENQGkNQKENdKENrKENrNUOGNVAAAFBDGlBQKFBdKFBrKFBrNVB4KFB4NVCGNVCTNVCTQ10AAF1DGl1QKF1rKF1rNV14NV2GNV2TNV2TQ12hQ2sAAGs1GmtQKGtrNWt4NWuTNWuTQ3gAAHgoGnh4NXiGNXiTNYYAAIYADYYNDYYaGoYoGoY1GoZDGoZQKIZ4NYaGNZMNDZNDGpNDKJNdKJNrNZN4NZOGNaENDaEaGqEoGqFQKKFrNa4NDa4NGq4aGq4oGq41KK5DKK5dKK5rNa54NbsNGrsaGrsoGrtDKLt4NckNGskaGsk1KMlDKMlQKNYaGtYoKNZdKOQaGuQoGuQoKORDKORQKORdKPEaGvEoGvEoKPE1KPFDKPFQKPFdKPFrKPFrNfF4Nf94Nf+GNf+GQ/+TQ/+TUP+hUP+uUP+uXf+7XRs3W9UAAAABdFJOUwBA5thmAAAD90lEQVRYw6WX61sSQRSHCbMsElC3tFLSxJJSK9exC1FWamaUFywju4mFGka3AVniKtrf3ZmZ3WWWnV03OR/kwWd/77xnLruLy2VT/W1ut6uZQqjN3Vx+4liTgGGPpznABX9zUzDu8yFSRxmb/h3ynkdovOUIY0syIBAKtt8i40tHQEhUPdg+hJDs+u8uSAAUxgHQg4JB59OA6vkJWEICaO/pgW8hp4AQ0mrinOQnLbR7L8pUyJn7aVknoEudMgp6vV6f/wro3NvC2BEhQEYja4D6PV19fp/P7+/wnLwO38NLq9sOGC2nTndJErQy4T7u6ezs6OiVUai7tZ9ZTa1uHQbA82F2bau77cwJj8dzln67rjUWXrRHbD9XL+zuhsbRtdZjZ1BDhec/WA//IayvgvqhyocCkiQNhzQLq/wqvVbqkurL2U9J8ss0xjjxduZygK7TI+Fs7ixBuvdBAi7dGGsUX8Ss0iu3CSMs2Fg/plBg9BXOQGWz32+yXF1lG2uVmu2TUXjbNH1TgdubmIR3Sb2nqXuJUQ0xheuVnumVFxu62Ak+SGGcoekcKZp6lsErg+ru/Ix5xGzfgoGA0zBLGZLPsfpI524rm8F4dpgClrCh0gkeQP+lxhWo359oZmx3lxASQ3TxsKmMAJZXlG9flh8x6as/c4ywSe5LCFsTmADklY/L97Vplwe/KjmVsEbm4Yc9AARyuV8v5ubGxmDTSX13Xufz0AwQAICfAmDLUkHPK0o+XygUisVSqQifhAAORCF9wwmA5SFdLpcpghKYwoYsBOA6gAmQfLlCq1SiBFUB37UGGASKpUqlSgoIxcIfqkAB7xwBCgUYv1qt1WqUwBRYDynZHrCrAVieEkBB7YFc8lAESKd5AOugulfbPzgAQqVc4gELKUFcCKjVDv4e7NdoD9wkrAnzIkAVDPbNBsJ4KmWeg4rFHAjjdQBbhT+FElHYq+1pq6DvJHF+M2nYyXm2jhXRPhAOn0wmDWeRKMBOphuxrO7lnAmgxzeTyfV17m6iKkAXtIxnQWRP8joAZ5kCOJDjSA4jfxqF9hCPx3WAfp4pgsbNeaM9xN/o9yOdQBCsFEW/I3Hxun08Ho/FXI0Eek9laWGet4/FolEDgLstszifN9nH9LxOYM+VnF4knrG2j0anpxsAWHuwscqah+fto08eGx8sWh8MAmnD8Cb76SeRiACA6eOZFra1h3ik8dmGbU6tyT4SmZx0HU6wtp805l3Wcd7+TT0+MtL4goRtDj3XvGpvypsJdvYjgnwDgrdfN9kPDFi85zm0t8rrBHt767iKOMx+4JDX7aPa8wjjxuPnfsDpz66U2X5SvHQ2pe17mre+7B/tWBuL3OYsFQAAAABJRU5ErkJggg==',
metacritic: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAgMAAADXB5lNAAAADFBMVEUAAAACIzD/ygD///+DWqJTAAAAAXRSTlMAQObYZgAAAVFJREFUOMuNk0uOgzAQREcsc5Tcx0HKhj13iOAS7Nkgge/DUby0PFWuTnuG0Uhp5SO/UM8daH99Wl2M82X9m0TW3ta3WGt1cI9HCLykGQJKFiUWgodnGGCN+zuxCDyRUSJYWeZeE0NCxsBGUAoyklTFUErCPnIGgQzJTGdVTIWZsQIpUCetzUnJs4JFgJLH/t6kLwnEQKjgHCiJDeBVEkF3qAtqMhoBUBf6GAkYIKAE4GbgrBKCjb8RBAB0RjCxTX6dP0DmhcEBCqrsIA9Vkgy8plTbRAngV6Syg54AGQc1T9qAdWXgkFAS/Tl11QD7qjfRgSR460nFYBKs7Z5K8nKwSKKp0oOiRGAn2EwSNCBoVRLWuGocJFEbGhhJ1Aa3ocQ3AaAky+ljOSUfZVgpkWL10e6lmC/DD6ck1+PR6ZKRCcuAYB3/HkIvgX8Psh/1D+sbAAmSjR+xz1QAAAAASUVORK5CYII='
};
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}/`,
headers: {
'User-Agent': 'Mozilla/5.0'
},
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)}`,
headers: {
'User-Agent': 'Mozilla/5.0',
'Origin': 'https://www.rottentomatoes.com'
},
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,
headers: {
'User-Agent': 'Mozilla/5.0',
'Origin': 'https://www.rottentomatoes.com'
},
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`,
headers: {
'User-Agent': 'Mozilla/5.0',
'Origin': 'https://www.metacritic.com'
},
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,
headers: {
'User-Agent': 'Mozilla/5.0',
'Origin': 'https://www.metacritic.com'
},
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();
})();