Created
November 15, 2024 12:29
-
-
Save HasanAbbadi/f65ea3caca2caff455ee25ebc1b55c0c to your computer and use it in GitHub Desktop.
Tampermonkey userscript to add watch button from Vidsrc on Trakt.tv: dashboard, movie details, season details pages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Vidsrc Trakt.tv Integration | |
| // @namespace http://github.com/HasanAbbadi/ | |
| // @version 2024-11-13 | |
| // @description Stream Movies and TV Shows directly on trakt in upto 1080p! No more shady websites with horrible ads and malware. | |
| // @author You | |
| // @match https://trakt.tv/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=trakt.tv | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| // modal structure | |
| const modal = document.createElement('div'); | |
| modal.style.display = 'none'; | |
| modal.style.position = 'fixed'; | |
| modal.style.zIndex = '10004'; | |
| modal.style.left = '0'; | |
| modal.style.top = '0'; | |
| modal.style.width = '100%'; | |
| modal.style.height = '100%'; | |
| modal.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; | |
| const modalContent = document.createElement('div'); | |
| modalContent.style.position = 'absolute'; | |
| modalContent.style.top = '50%'; | |
| modalContent.style.left = '50%'; | |
| modalContent.style.transform = 'translate(-50%, -50%)'; | |
| modalContent.style.width = '90%'; | |
| modalContent.style.maxWidth = '1111px'; | |
| modalContent.style.height = '90%'; | |
| modalContent.style.maxHeight = '687px'; | |
| modalContent.style.backgroundColor = '#000000'; | |
| modalContent.style.border = '1px solid #000000'; | |
| modalContent.style.borderRadius = '5px'; | |
| modalContent.style.boxShadow = '0 2px 10px rgba(0,0,0,0.3)'; | |
| const modalHeader = document.createElement('div'); | |
| modalHeader.style.padding = '10px'; | |
| modalHeader.style.backgroundColor = '#000000'; | |
| modalHeader.style.color = '#FFFFFF'; | |
| modalHeader.style.borderBottom = '1px solid #000000'; | |
| modalHeader.style.display = 'flex'; | |
| modalHeader.style.justifyContent = 'space-between'; | |
| modalHeader.style.alignItems = 'center'; | |
| const closeButton = document.createElement('span'); | |
| closeButton.innerHTML = '<i class="fas fa-times"></i>'; | |
| closeButton.style.cursor = 'pointer'; | |
| closeButton.style.fontSize = '20px'; | |
| modalHeader.appendChild(closeButton); | |
| const iframe = document.createElement('iframe'); | |
| iframe.style.width = '100%'; | |
| iframe.style.height = 'calc(100% - 41px)'; // Subtract header height | |
| iframe.style.border = 'none'; | |
| iframe.allowFullscreen = true; | |
| modalContent.appendChild(modalHeader); | |
| modalContent.appendChild(iframe); | |
| modal.appendChild(modalContent); | |
| document.body.appendChild(modal); | |
| // Function to create the Watch Now button content | |
| function createWatchButtonContent() { | |
| let c1 = "red"; | |
| let c2 = "rgb(152, 64, 189)"; | |
| let deg = "60"; | |
| let x = 0; | |
| const button = document.createElement('button'); | |
| button.classList.add("vidsrc-watch-btn"); | |
| button.style.background = 'red'; | |
| button.style.background = `linear-gradient(${deg}deg, ${c1}, ${c2} 51%, ${c1}) ${x}/ 200%`; | |
| button.style.transition = '0.5s ease'; | |
| button.style.color = "#fff"; | |
| button.style.border = "none"; | |
| button.style.cursor = "pointer"; | |
| button.style.display = "inline-flex"; | |
| button.style.alignItems = "center"; | |
| button.style.justifyContent = "center"; | |
| button.style.padding = "8px 15px"; | |
| button.style.width = "100%"; | |
| button.style.height = "100%"; | |
| button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" style="overflow:visible;"><path fill="currentColor" d="M8 17.175V6.825q0-.425.3-.713t.7-.287q.125 0 .263.037t.262.113l8.15 5.175q.225.15.338.375t.112.475t-.112.475t-.338.375l-8.15 5.175q-.125.075-.262.113T9 18.175q-.4 0-.7-.288t-.3-.712"/></svg>' | |
| // https://codepen.io/thebabydino/pen/jBbXPP | |
| button.addEventListener("mouseover", () => { | |
| x = "100%"; | |
| button.style.background = `linear-gradient(${deg}deg, ${c1}, ${c2} 51%, ${c1}) ${x}/ 200%`; | |
| }); | |
| button.addEventListener("mouseout",() => { | |
| x = "0" | |
| button.style.background = `linear-gradient(${deg}deg, ${c1}, ${c2} 51%, ${c1}) ${x}/ 200%`; | |
| }); | |
| return button | |
| } | |
| // Function to insert or replace the button | |
| function insertWatchButton() { | |
| const contentType = getContentType(); | |
| if (contentType == 'movie') { | |
| const button = createWatchButton(); | |
| const links = document.querySelector('.streaming-links') | |
| if (links) { | |
| links.parentNode.insertBefore(button, links) | |
| } else { | |
| console.error('element not found') | |
| } | |
| } else if (contentType == 'tv') { | |
| const pathnames = document.location.pathname.split('/') | |
| const seasonNum = pathnames[pathnames.length - 1] | |
| const epEls = document.querySelectorAll('#seasons-episodes-sortable > .row') | |
| epEls.forEach((ep, i) => { | |
| const button = createWatchButton(seasonNum, (i + 1)) | |
| const parentDiv = ep.querySelector(':scope > .grid-item > .quick-icons > .actions') | |
| parentDiv.appendChild(button) | |
| }); | |
| } else if (contentType == 'dashboard') { | |
| const epEls = document.querySelectorAll('#ondeck-shows > .grid-item') | |
| epEls.forEach((ep) => { | |
| const url = ep.querySelector(':scope > a')['href'] | |
| const button = createWatchButton(ep.getAttribute('data-season-number'), ep.getAttribute('data-episode-number'), url) | |
| const existingWatchButton = ep.querySelector(':scope > .quick-icons > .actions > a.watch-now') | |
| existingWatchButton.replaceWith(button) | |
| console.log('replace button') | |
| }); | |
| } else { | |
| console.log('nothing to insert') | |
| } | |
| } | |
| // Function to insert button for TMDB | |
| function createWatchButton(season = null, ep = null, url = null) { | |
| console.log('creating button') | |
| let button | |
| button = document.createElement('a'); | |
| button.classList.add("vidsrc-btn-container"); | |
| button.title = "Watch Now on Vidsrc"; | |
| button.addEventListener('click', () => showModal(season, ep, url)); | |
| button.appendChild(createWatchButtonContent()); | |
| button.style.position = 'relative'; | |
| button.style.zIndex = '2'; | |
| button.style.cursor = 'pointer'; | |
| return button | |
| } | |
| // Function to get ID (TMDB) | |
| function getId() { | |
| const tmdbLink = document.querySelector('#external-link-tmdb').href | |
| const match = tmdbLink.match(/\/(movie|tv)\/(\d+)/); | |
| return match ? match[2] : null; | |
| } | |
| // Function to fetch ID from page (TMDB) | |
| async function fetchId(url) { | |
| const res = await fetch(url) | |
| const body = await res.text(); | |
| const m = body.match(/external-link-tmdb/) | |
| const l = body.slice(m.index, m.index + 70).split('/') | |
| return l[4] | |
| } | |
| // Function to determine if it's a movie or TV show | |
| function getContentType() { | |
| console.log("Current URL:", window.location.href); | |
| console.log("Document title:", document.title); | |
| try { | |
| const isTV = window.location.pathname.includes('/seasons/'); | |
| const isDashboard = window.location.pathname.includes('/dashboard') | |
| const type = isTV ? 'tv' : isDashboard ? 'dashboard' : 'movie'; | |
| console.log("Content Type is: " + type) | |
| return type | |
| } catch (error) { | |
| console.error(error); | |
| return null; | |
| } | |
| } | |
| // Function to show modal | |
| async function showModal(season = null, ep = null, url = null) { | |
| let contentType = getContentType(); | |
| let id; | |
| if (contentType == 'dashboard') { | |
| id = await fetchId(url); | |
| } else { | |
| id = getId(); | |
| } | |
| let baseUrl; | |
| // Set baseUrl based on the contentType | |
| baseUrl = 'https://vidsrc.xyz/embed/'; | |
| if (contentType == 'dashboard') contentType = 'tv' | |
| if (id) { | |
| baseUrl += `${contentType}?tmdb=${id}`; | |
| } | |
| if (season && ep) { | |
| baseUrl += `&season=${season}&episode=${ep}` | |
| } | |
| console.log('.s.....ep...url...id...if') | |
| console.log(season, ep, url, id, baseUrl) | |
| console.log('.........................') | |
| iframe.src = `${baseUrl}`; | |
| modal.style.display = 'block'; | |
| } | |
| // Function to initialize the script | |
| function init() { | |
| insertWatchButton(); | |
| } | |
| // Use a MutationObserver to detect when the relevant elements are added to the DOM | |
| const observer = new MutationObserver((mutations, obs) => { | |
| let contentType = getContentType(); | |
| let relevantElement; | |
| if (contentType == "dashboard") { | |
| relevantElement = document.querySelector('#ondeck-shows > .grid-item') | |
| } else { | |
| relevantElement = document.querySelector('.poster') | |
| } | |
| if (relevantElement) { | |
| init(); | |
| obs.disconnect(); // Stop observing once we've found and modified the relevant element | |
| } | |
| }); | |
| observer.observe(document.body, { | |
| childList: true, | |
| subtree: true | |
| }); | |
| // Close modal on close button click | |
| closeButton.addEventListener('click', () => { | |
| modal.style.display = 'none'; | |
| iframe.src = ''; // Clear the iframe source when closing | |
| }); | |
| // Close modal when clicking outside of it | |
| modal.addEventListener('click', (event) => { | |
| if (event.target === modal) { | |
| modal.style.display = 'none'; | |
| iframe.src = ''; // Clear the iframe source when closing | |
| } | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment