Skip to content

Instantly share code, notes, and snippets.

@ternbusty
Last active March 29, 2023 05:43
Show Gist options
  • Save ternbusty/373d3d88ddfd690ae454715f377c283b to your computer and use it in GitHub Desktop.
Save ternbusty/373d3d88ddfd690ae454715f377c283b to your computer and use it in GitHub Desktop.
// ==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