Created
February 7, 2023 06:00
-
-
Save RyanTheTechMan/daa93b628ae548043c9da98d94b163de to your computer and use it in GitHub Desktop.
This script allows you to get all of the links of episodes of a show on: Discovery, Travel Channel, Investigation Discovery, TLC, HGTV, Food Network, Science Channel, Animal Planet, Destination America, Cooking Channel, OWN, Discovery Life, AHC, Motor Trend, Magnolia Network.
This file contains 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
/* | |
* This script allows you to get all of the links of episodes of a show on: | |
* Discovery, Travel Channel, Investigation Discovery, TLC, HGTV, Food Network, Science Channel, | |
* Animal Planet, Destination America, Cooking Channel, OWN, Discovery Life, AHC, Motor Trend, Magnolia Network | |
* | |
* For a list of all of the shows that are supported, go to: | |
* https://watch.travelchannel.com/shows?network=all or https://www.sciencechannel.com/shows?network=all | |
* The list will be the same, just choose a different subnetwork by clicking the icon of the network you want to see. | |
* | |
* Note: | |
* You MUST be logged into a provider or an account that has access to the show. Otherwise, an error will be displayed. | |
* This script will NOT download the videos, it will only get the links to the videos in a format that can meet your needs. | |
* | |
* Usage: | |
* Start the script on the lowest season (usually season 1) and it will go through all of the seasons that are available. | |
* It will then output all of the links to the console in an easy to copy format or download as a text file. | |
*/ | |
/* --- SETTINGS --- */ | |
let enableButtonInjection = true; // This will inject some buttons into the page that will allow you to activate the script. | |
let timePerSeasonLoadTest = 10; // This is the number of seconds that the script will wait for a season to load before it stops the script. | |
let outputToBlob = true; // If false, the script will only output the links to the console. | |
let outputFormat = 'yt-dlp --cookies ./cookies.txt -o "{Series Title}/Season {season}/{Series Title} - S{season}E{episode} - {Episode Title}.%%(ext)s" {link}'; // This is the format that the script will output the links in. ("{link}" is replaced with the link) | |
let selectFirstSeasonOnReverse = true; // This will select the first season when the seasons are reversed | |
// --- END OF SETTINGS --- // | |
/* --- SCRIPT --- */ | |
let seasonIndex = 0; // This is the part of the season list that the script will go to next. | |
// NOTE: Not the same as the season number | |
let outputFile = ""; // This is the string that will be outputted to the file (if enabled) | |
function getEpisodeCollection() { | |
// Get episode collection element with data-sonic-type="collection" | |
let episodeCollection = document.querySelector("[data-sonic-type='collection']"); | |
// If no responce, return an empty list | |
if (episodeCollection == null) { | |
return []; | |
} | |
// Get the list of episodes | |
let episodeList = episodeCollection.children[0]; | |
return episodeList; | |
} | |
function getFormattedEpisodeStrings(episodeCollection) { | |
// Get the links to the episodes | |
let episodeLinks = []; | |
for (let i = 0; i < episodeCollection.children.length; i++) { | |
let episode = episodeCollection.children[i]; | |
let link = episode.children[0].children[0].href; | |
// test if the link contains a login link | |
if (link.includes("://auth.") || link.includes("login-affiliates")) { | |
console.log("ERROR: You are not logged in or you do not have access to this show."); | |
return []; | |
} | |
if (outputFormat == "") { | |
episodeLinks.push(link); | |
} | |
else { | |
// get class by name that contains a part of a class name | |
let epData = episode.innerText.replace('"','\\"').split("\n"); | |
let season = epData[0].split(" ")[0].replace("S", "").trim(); | |
let episodeNumber = epData[0].split(" ")[1].replace("E", "").trim(); | |
let episodeTitle = epData[2]; | |
let seriesTitle = document.querySelector("h1").innerText; | |
let output = outputFormat.replace(/{link}/g, link); | |
output = output.replace(/{season}/g, season); | |
output = output.replace(/{episode}/g, episodeNumber); | |
output = output.replace(/{Episode Title}/g, episodeTitle); | |
output = output.replace(/{Series Title}/g, seriesTitle); | |
episodeLinks.push(output); | |
} | |
} | |
return episodeLinks; | |
} | |
function flipSeasonCollection() { | |
// Get element with id "selector-button" | |
let seasonSelector = document.getElementById("selector-button"); | |
// Get the list element that contains the seasons. Located in sibling of seasonSelector | |
let seasonList = seasonSelector.nextElementSibling; | |
// Reverse the list so that the seasons are in order (not required but it's easier to read through when completed) | |
for(let i = seasonList.children.length - 1; i >= 0; i--) { | |
seasonList.appendChild(seasonList.children[i]); | |
} | |
if (selectFirstSeasonOnReverse) { | |
seasonList.children[0].click(); | |
} | |
} | |
// Returns a number of the season number unless there was an error or there is no new season, then it returns false | |
async function goToNextSeason() { | |
// Get element with id "selector-button" | |
let seasonSelector = document.getElementById("selector-button"); | |
// Get the list element that contains the seasons. Located in sibling of seasonSelector | |
let seasonList = seasonSelector.nextElementSibling; | |
// Determine if there is a next season, if not, return false | |
if (seasonIndex >= seasonList.children.length) { | |
// If the seasonIndex is greater than the number of seasons, stop the script | |
return false; | |
} | |
// Get seasonIndex from the list AND increment it for the next season | |
let season = seasonList.children[seasonIndex++]; | |
// Select the season | |
season.click(); | |
// Keep waiting 1 second and check to see if the season has loaded using a while loop | |
let seasonLoaded = false; | |
let tries = timePerSeasonLoadTest; | |
while (!seasonLoaded) { | |
// Wait 1 second | |
await new Promise(r => setTimeout(r, 1000)); | |
// Check to see if the season has loaded | |
if (getEpisodeCollection()?.children?.length > 0) { | |
// The season has loaded, so stop the while loop | |
seasonLoaded = true; | |
console.log("Season " + season.innerText + " loaded successfully."); | |
} | |
// If the season has not loaded after 10 tries, stop the script and return false. | |
// Also, de-increment the seasonIndex so that the script will try again on the same season. | |
if (--tries <= 0) { | |
console.log("Season " + season.innerText + " did not load after " + timePerSeasonLoadTest + " seconds."); | |
return false; | |
} | |
} | |
// Get the season number | |
let seasonNumber = document.getElementById("selector-button").innerText.split(" ")[1]; | |
return seasonNumber; | |
} | |
async function getShowLinks() { | |
let season = await goToNextSeason(); | |
while (season != false) { | |
// Get the list of episodes | |
let episodeList = getEpisodeCollection(); | |
// If the list is empty, then something went wrong, so stop the script | |
if (episodeList.children.length == 0) { | |
console.log("Error: No episodes found."); | |
return false; | |
} | |
let episodesOut = getFormattedEpisodeStrings(episodeList); | |
// Output the episodesOut to the console | |
console.log("Season " + season + " episodesOut:"); | |
for (let i = 0; i < episodesOut.length; i++) { | |
console.log(episodesOut[i]); | |
outputFile += episodesOut[i] + "\n"; | |
} | |
season = await goToNextSeason(); | |
} | |
// Output to a file | |
if (outputToBlob) { | |
let file = new File([outputFile], "output", {type: "text/plain;charset=utf-8"}); | |
window.open(URL.createObjectURL(file)); | |
} | |
return true; | |
} | |
function injectButtons() { | |
// Get the element that contains the season selector | |
let seasonSelector = document.getElementById("season-wrapper-id"); | |
// Create Reverse Seasons button | |
let button = document.createElement("button"); | |
button.innerText = "↕ Reverse"; | |
button.style = ` | |
margin: 5px; | |
background-color: blue; | |
color: white; | |
border: none; | |
border-radius: 5px; | |
padding: 5px; | |
font-size: 16px; | |
`; | |
button.onclick = flipSeasonCollection; | |
seasonSelector.append(button); | |
// Create Get Links button | |
button = document.createElement("button"); | |
button.innerText = "➜ Get Links"; | |
button.style = ` | |
margin: 5px; | |
background-color: blue; | |
color: white; | |
border: none; | |
border-radius: 5px; | |
padding: 5px; | |
font-size: 16px; | |
`; | |
button.onclick = function() { | |
button.disabled = true; | |
button.style.backgroundColor = "darkblue"; | |
let orignalText = button.innerText; | |
button.innerText = "Grabbing links..."; | |
getShowLinks().then(() => { | |
button.style.backgroundColor = "blue"; | |
button.innerText = orignalText; | |
button.disabled = false; | |
}); | |
}; | |
seasonSelector.append(button); | |
} | |
// wait for page to load | |
async function waitForPageToLoad() { | |
while (document.readyState !== "complete"); | |
await new Promise(r => setTimeout(r, 1500)); | |
if (enableButtonInjection) { | |
injectButtons(); | |
} | |
} | |
waitForPageToLoad(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment