Last active
March 29, 2023 05:43
-
-
Save ternbusty/373d3d88ddfd690ae454715f377c283b to your computer and use it in GitHub Desktop.
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
// ==UserScript== | |
// @name YouTube Playlist Player | |
// @namespace https://ternbusty.github.io | |
// @version 1.0 | |
// @description Shuffle or Reverse Your YouTube Playlist | |
// @author ternbusty | |
// @match https://ternbusty.github.io/*youtube.html* | |
// @grant GM.xmlHttpRequest | |
// @connect www.youtube.com | |
// ==/UserScript== | |
let table = document.getElementById("que"); | |
function addRow(table, text0, text1, text2) { | |
var tr = table.insertRow(-1); | |
var th = document.createElement("th"); | |
th.innerHTML = text0; | |
tr.appendChild(th); | |
var cell1 = tr.insertCell(1); | |
var cell2 = tr.insertCell(2); | |
cell1.innerHTML = text1; | |
cell2.innerHTML = text2; | |
} | |
const shuffle = ([...array]) => { | |
for (let i = array.length - 1; i >= 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[array[i], array[j]] = [array[j], array[i]]; | |
} | |
return array; | |
}; | |
function createShuffledArray(num) { | |
let arr = [...Array(num)].map((_, i) => i); | |
let shuffled_arr = shuffle(arr); | |
return shuffled_arr; | |
} | |
function makeGetRequest(url) { | |
return new Promise((resolve, reject) => { | |
GM.xmlHttpRequest({ | |
method: "GET", | |
url: url, | |
onload: function (response) { | |
resolve(response.responseText); | |
}, | |
}); | |
}); | |
} | |
async function getPlaylistLength(playlist_id) { | |
const url = "https://www.youtube.com/playlist?list=" + playlist_id; | |
let res = await makeGetRequest(url); | |
let title = /,"title":{"simpleText":"(.*?)"}/.exec(res); | |
let owner = /"ownerText":{"runs":\[{"text":"(.*?)"/.exec(res); | |
let first_video_id = /{"contents":\[{"playlistVideoRenderer":{"videoId":"(.*?)"/.exec(res); | |
let playlist_title = ""; | |
if (title !== null) { | |
playlist_title += title[1]; | |
if (owner !== null) playlist_title += " - " + owner[1]; | |
} else { | |
if (owner !== null) playlist_title += owner[1]; | |
else playlist_title = "YouTube Playlist Shuffler"; | |
} | |
if (first_video_id !== null) document.first_video_id = first_video_id[1]; | |
else document.first_video_id = ""; | |
console.log(title); | |
console.log(owner); | |
document.getElementsByTagName("h1")[0].innerText = playlist_title; | |
document.title = playlist_title; | |
try { | |
let result = /"stats":\[{"runs":\[{"text":"(\d{1,3}(,\d{3})*)"}/.exec(res); | |
return result[1].replace(",", ""); | |
} catch (error) { | |
document.failflag = 1; | |
return undefined; | |
} | |
} | |
async function getVideoID(playlist_id, index) { | |
const url = | |
"https://www.youtube.com/watch?v=+&list=" + playlist_id + "&index=" + index; | |
let res = await makeGetRequest(url); | |
let video_id = /"watchEndpoint":{"videoId":"(.*?)",/.exec(res); | |
return video_id[1]; | |
} | |
async function getVideoTitle(video_id) { | |
const url = "https://www.youtube.com/watch?v=" + video_id; | |
let res = await makeGetRequest(url); | |
let title = /<meta name="title" content="(.*?)">/.exec(res); | |
return title[1]; | |
} | |
function extractParams(url) { | |
var parser = new URL(url); | |
if ( | |
!parser.searchParams.has("playlist_id") || | |
!parser.searchParams.has("type") | |
) { | |
return false; | |
} | |
let type = parser.searchParams.get("type"); | |
if (type !== "random" && type !== "reverse" && type !== "normal") { | |
return false; | |
} | |
return [ | |
parser.searchParams.get("type"), | |
parser.searchParams.get("playlist_id"), | |
]; | |
} | |
async function main() { | |
const url = window.location.href; | |
let params = extractParams(url); | |
if (!params) return; | |
let type = params[0]; | |
let playlist_id = params[1]; | |
let num = await getPlaylistLength(playlist_id); | |
if (document.failflag) { | |
console.log("failed to get information of the playlist"); | |
return; | |
} | |
num = Number(num); | |
if (type === "random") { | |
document.idx_arr = createShuffledArray(num); | |
} else if (type === "reverse") { | |
document.idx_arr = [...Array(num)].map((_, i) => i).reverse(); | |
} else { | |
document.idx_arr = [...Array(num)].map((_, i) => i); | |
} | |
document.id_arr = Array(num); | |
let id_set = new Set(); | |
document.title_arr = Array(num); | |
for (let i = 0; i < num; i++) { | |
let video_id; | |
if (document.idx_arr[i] === 0) { | |
video_id = document.first_video_id; | |
} else { | |
video_id = await getVideoID(playlist_id, document.idx_arr[i]); | |
if (video_id === document.first_video_id && document.idx_arr[i] !== 0) video_id = ""; | |
} | |
let video_title; | |
if (id_set.has(video_id)) { | |
video_id = ""; | |
video_title = "非公開動画"; | |
} else { | |
try { | |
video_title = await getVideoTitle(video_id); | |
} catch { | |
video_id = ""; | |
video_title = "非公開動画"; | |
} | |
id_set.add(video_id); | |
} | |
document.id_arr[i] = video_id; | |
document.title_arr[i] = video_title; | |
let title_link = | |
video_id === "" | |
? "非公開動画" | |
: `<a onclick="document.player.yts.changeVideoByClick(document.player, ${i})">${video_title}</a>`; | |
addRow(table, i + 1, "", title_link); | |
await new Promise((s) => setTimeout(s, 1000)); | |
} | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment