Skip to content

Instantly share code, notes, and snippets.

@elazar
Last active October 20, 2021 23:44
Show Gist options
  • Save elazar/c0e1c7d18aa0c23848c41ac632e5bd6e to your computer and use it in GitHub Desktop.
Save elazar/c0e1c7d18aa0c23848c41ac632e5bd6e to your computer and use it in GitHub Desktop.
Userscript to add sort options for video length and channel to YouTube
const getVideoList = () => document.getElementById("contents");
const getVideoCount = () => {
return Number(
document
.getElementById("stats")
.getElementsByClassName("yt-formatted-string")[0]
.textContent
);
}
const getVideos = () => {
const list = getVideoList();
const videos = Array.from(
list.getElementsByTagName("ytd-playlist-video-renderer")
);
return videos;
}
const scroll = async () => {
const videoCount = getVideoCount();
const videoList = getVideoList();
do {
window.scrollTo(0, videoList.scrollHeight);
await new Promise(resolve => setTimeout(resolve, 500));
} while (getVideos().length < videoCount);
window.scrollTo(0, 0);
};
const getOption = (text, callback) => {
const html = `
<a class="yt-simple-endpoint style-scope yt-dropdown-menu" tabindex="-1" aria-selected="false">
<tp-yt-paper-item class="style-scope yt-dropdown-menu" role="option" tabindex="0" aria-disabled="false">
<!--css-build:shady-->
<tp-yt-paper-item-body class="style-scope yt-dropdown-menu">
<!--css-build:shady-->
<div class="item style-scope yt-dropdown-menu">${text}</div>
<div secondary="" id="subtitle" class="style-scope yt-dropdown-menu" hidden="">
</div>
</tp-yt-paper-item-body>
<yt-reload-continuation class="style-scope yt-dropdown-menu">
</yt-reload-continuation>
</tp-yt-paper-item>
</a>
`;
const document = new DOMParser().parseFromString(html, "text/html");
const element = document.activeElement.firstChild;
element.onclick = callback;
return element;
};
const sortVideos = (callback) => {
const list = getVideoList();
const videos = getVideos();
videos.forEach((video) => video.parentNode.removeChild(video));
videos.sort(callback);
videos.forEach((video) => list.appendChild(video));
};
const getTime = (video) => {
const text = video.getElementsByTagName("span")[0].textContent.trim();
const numbers = text.split(":").map(Number);
numbers.reverse();
numbers[1] *= 60;
if (numbers[2]) {
numbers[2] *= 3600;
}
const time = numbers.reduce((previous, current) => previous + current, 0);
return time;
};
const getChannelName = (video) => {
const name = video.getElementsByTagName("ytd-channel-name")[0];
const a = name.getElementsByTagName("a")[0];
const text = a.textContent.trim();
return text;
};
scroll().then(() => {
const menu = Array.from(
document.getElementsByTagName("tp-yt-paper-listbox")
)
.find(el => el.id === "menu");
menu.prepend(
getOption("Length (shortest)", () => {
sortVideos((a, b) => getTime(a) - getTime(b));
})
);
menu.prepend(
getOption("Length (longest)", () => {
sortVideos((a, b) => getTime(b) - getTime(a))
})
);
menu.prepend(
getOption("Channel (A-Z)", () => {
sortVideos((a, b) => getChannelName(a) < getChannelName(b) ? -1 : 1);
})
);
menu.prepend(
getOption("Channel (Z-A)", () => {
sortVideos((a, b) => getChannelName(b) < getChannelName(a) ? -1 : 1);
})
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment