// ==UserScript==
// @name IMDb Watcher
// @namespace https://www.imdb.com/
// @icon https://vidsrc.net/template/vidsrc-ico.png
// @version 2.1.1
// @description Watch movies & series for free on IMDb
// @author cevoj35548
// @license MIT
// @match https://*.imdb.com/title/*
// @grant GM_xmlhttpRequest
// @connect api.themoviedb.org
// ==/UserScript==
(function() {
'use strict';
const sources = [{
name: 'VidLink',
urls: {
movie: 'https://vidlink.pro/movie/{id}?autoplay=true',
tv: 'https://vidlink.pro/tv/{id}/{season}/{episode}?autoplay=true'
},
tmdb: true,
},
{
name: 'Embed.su',
urls: {
movie: 'https://embed.su/embed/movie/{id}/1/1',
tv: 'https://embed.su/embed/tv/{id}/{season}/{episode}'
},
},
{
name: 'SuperEmbed',
urls: {
movie: 'https://multiembed.mov/?video_id={id}',
tv: 'https://multiembed.mov/?video_id={id}&s={season}&e={episode}'
},
},
{
name: '2Embed',
urls: {
movie: 'https://www.2embed.stream/embed/movie/{id}',
tv: 'https://www.2embed.stream/embed/tv/{id}/{season}/{episode}'
},
}
];
const buttonSpacing = 10; // Space between buttons
const tmdbCache = {}; // Cache the imdb -> tmdb resps
// helper to convert IMDb IDs to TMDb IDs
async function getTmdbID(imdbId) {
// check cache
if (tmdbCache[imdbId]) {
console.log(`Cache hit for ${imdbId}`);
return tmdbCache[imdbId];
}
// fetch
console.log(`Fetching TMDb ID for ${imdbId}`);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: `https://api.themoviedb.org/3/find/${imdbId}?api_key=8d6d91941230817f7807d643736e8a49&language=en-US&external_source=imdb_id`,
onload: function(response) {
const respJson = JSON.parse(response.responseText);
const tmdbId =
respJson.tv_results?.[0]?.id ||
respJson.movie_results?.[0]?.id ||
respJson.tv_episode_results?.[0]?.show_id;
if (!tmdbId) {
reject("No TMDb ID found");
return;
}
console.log(`TMDb ID: ${tmdbId}`);
tmdbCache[imdbId] = tmdbId; // cache the result
resolve(tmdbId);
},
});
});
}
// extract season and episode numbers from the IMDb page
function extractSeasonEpisode() {
const seasonEpisodeDiv = document.querySelector('[data-testid="hero-subnav-bar-season-episode-numbers-section"]');
if (seasonEpisodeDiv) {
const seasonEpisodeText = seasonEpisodeDiv.textContent.trim();
const match = seasonEpisodeText.match(/S(\d+).E(\d+)/);
if (match) {
return {
season: match[1],
episode: match[2],
};
}
}
return null;
}
// extract the series ID from the IMDb page
function extractSeriesId() {
const seriesLink = document.querySelector('[data-testid="hero-title-block__series-link"]');
if (seriesLink) {
const href = seriesLink.getAttribute('href');
const match = href.match(/\/title\/(tt\d+)\//);
if (match) {
return match[1];
}
}
return null;
}
// generate the URL for a specific source
async function generateUrl(urls, isMovie, isEpisode, imdbId, seriesId, seasonEpisode, isTmdb) {
imdbId = seriesId || imdbId;
if (isTmdb) {
imdbId = await getTmdbID(imdbId);
}
if (isMovie && isEpisode) {
return urls.movie.replace('{id}', imdbId);
}
if (seasonEpisode && seriesId) {
const {
season,
episode
} = seasonEpisode;
return urls.tv.replace('{id}', imdbId).replace('{season}', season).replace('{episode}', episode);
} else {
return urls.tv.replace('{id}', imdbId).replace('{season}', '1').replace('{episode}', '1');
}
}
// create the iframe only once
const createIframe = (defaultUrl) => {
const mainEl = document.querySelector('main');
const iframe = document.createElement('iframe');
iframe.setAttribute('style', 'height:800px; width:100%;');
iframe.id = "Player";
iframe.allowFullscreen = "true";
iframe.webkitallowfullscreen = "true";
iframe.mozallowfullscreen = "true";
iframe.src = defaultUrl;
mainEl.before(iframe);
return iframe;
};
const imdbId = window.location.pathname.split('/')[2];
const isMovie = document.title.indexOf('TV Series') === -1;
const isEpisode = document.title.indexOf('TV Episode') === -1;
const seasonEpisode = extractSeasonEpisode();
const seriesId = extractSeriesId();
// initialize the iframe with the first source as default
(async () => {
const defaultUrl = await generateUrl(
sources[0].urls,
isMovie,
isEpisode,
imdbId,
seriesId,
seasonEpisode,
!!sources[0].tmdb
);
const iframe = createIframe(defaultUrl);
document.querySelector('section.ipc-page-section').setAttribute('style', 'padding-top: 10px;')
// add buttons to switch sources
const mainEl = document.querySelector('main');
const buttonContainer = document.createElement('div');
buttonContainer.style.position = 'absolute';
buttonContainer.style.top = '10px';
buttonContainer.style.right = '10px';
buttonContainer.style.display = 'flex';
buttonContainer.style.flexDirection = 'column';
buttonContainer.style.zIndex = '1000';
let selectedButton = null;
sources.forEach((source, index) => {
const button = document.createElement('button');
button.textContent = `📽 Source - ${source.name}`;
button.style.fontFamily = 'Arial';
button.style.marginBottom = `${buttonSpacing}px`;
button.style.padding = '10px';
button.style.background = '#3B3A3C';
button.style.color = '#ffffff';
button.style.border = 'none';
button.style.cursor = 'pointer';
button.style.fontWeight = 'bold';
button.style.borderRadius = '6px';
button.style.boxShadow =
'0 10px 8px rgb(0 0 0 / 0.05), 0 4px 3px rgb(0 0 0 / 0.1)';
// highlight selected button
const highlightButton = (btn) => {
if (selectedButton) {
selectedButton.style.border = 'none';
}
selectedButton = btn;
btn.style.border = '2px solid #FFD700';
};
// button click handler
button.addEventListener('click', async () => {
highlightButton(button);
button.textContent = "⏳ Loading...";
button.disabled = true;
try {
const url = await generateUrl(source.urls, isMovie, isEpisode, imdbId, seriesId, seasonEpisode, !!source.tmdb);
console.log('Reloading iframe');
document.querySelector('iframe#Player').remove()
createIframe(url);
} catch (error) {
console.error("Error generating URL:", error);
alert(`Failed to generate URL: ${error}`);
} finally {
button.textContent = `📽 Source - ${source.name}`;
button.disabled = false;
}
});
// set the first button as highlighted
if (index === 0) {
highlightButton(button);
}
buttonContainer.appendChild(button);
});
// Add "Open in Popup" button
const popupButton = document.createElement('button');
popupButton.textContent = "⛶ Open in Popup";
popupButton.style.position = 'absolute';
popupButton.style.top = '10px';
popupButton.style.left = '10px';
popupButton.style.padding = '10px';
popupButton.style.background = '#3B3A3C';
popupButton.style.color = '#ffffff';
popupButton.style.border = 'none';
popupButton.style.cursor = 'pointer';
popupButton.style.fontWeight = 'bold';
popupButton.style.borderRadius = '6px';
popupButton.style.boxShadow =
'0 10px 8px rgb(0 0 0 / 0.02), 0 4px 3px rgb(0 0 0 / 0.1)';
popupButton.addEventListener('click', () => {
window.open(document.querySelector('iframe#Player').src, '_blank', 'width=800,height=600,scrollbars=yes');
});
mainEl.style.position = 'relative';
mainEl.appendChild(buttonContainer);
mainEl.appendChild(popupButton);
})();
})();