Skip to content

Instantly share code, notes, and snippets.

@eyemono-moe
Created August 16, 2024 03:04
Show Gist options
  • Save eyemono-moe/3b73f491986dbdfa23824f0f6c50076b to your computer and use it in GitHub Desktop.
Save eyemono-moe/3b73f491986dbdfa23824f0f6c50076b to your computer and use it in GitHub Desktop.
import { google } from "googleapis";
import { JSDOM } from "jsdom";
const getLiveUrls = async () => {
const url =
"https://wikiwiki.jp/nijisanji/Grand%20Theft%20Autoまとめ/GTA5/にじさんじGTA/配信一覧";
const dom = await JSDOM.fromURL(url);
const document = dom.window.document;
const links = Array.from(document.getElementsByTagName("li"))
.filter((li) => {
// 配信日が24日までのものだけを取得
const match = /^\[06\/(\d{2})]/.exec(li.textContent ?? "");
if (match) {
const date = parseInt(match[1], 10);
return date <= 24;
}
return false;
})
.map((l) => l.getElementsByTagName("a")[0].href)
// youtubeのリンクだけを取得
// (叶さんのtwitch配信の時間数えるの面倒だったのでyoutubeにアップされた切り抜きの時間を採用しています)
.filter((l) => l.includes("youtube") || l.includes("youtu.be"));
return links;
};
const getVideoId = (url: string) => {
const re =
/(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
const match = re.exec(url);
if (match) {
return match[1];
}
return null;
};
const sliceBy = <T>(arr: T[], n: number) => {
const result = [];
for (let i = 0; i < arr.length; i += n) {
result.push(arr.slice(i, i + n));
}
return result;
};
const getVideoDurations = async (ids: string[]) => {
const youtube = google.youtube({
version: "v3",
auth: process.env["GOOGLE_API_KEY"] ?? "",
});
const results = await Promise.all(
sliceBy(ids, 50).map((ids) =>
youtube.videos.list({
part: ["contentDetails"],
id: ids,
maxResults: 50,
})
)
);
const durations = results.map((res) =>
res.data.items?.map((item) => item.contentDetails?.duration)
);
return durations.flat();
};
// parse ISO8601 duration
const parseDuration = (duration: string) => {
const re = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/;
const match = re.exec(duration);
if (match) {
const hours = parseInt(match[1] ?? "0", 10);
const minutes = parseInt(match[2] ?? "0", 10);
const seconds = parseInt(match[3] ?? "0", 10);
return hours * 3600 + minutes * 60 + seconds;
}
return 0;
};
const main = async () => {
const urls = await getLiveUrls();
const ids = urls.map(getVideoId).filter((id) => id !== null);
const durations = await getVideoDurations(ids);
const seconds = durations
.map((d) => parseDuration(d ?? ""))
.reduce((a, b) => a + b, 0);
console.log(seconds);
};
await main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment