Automatically download HLS stream segments when a video is played
当前为
// ==UserScript==
// @name Download HLS Stream Segments with Observer
// @namespace http://tampermonkey.net/
// @license GPL-3.0
// @version 2024-10-16
// @description Automatically download HLS stream segments when a video is played
// @author You
// @match https://privacy.com.br/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=privacy.com.br
// @grant none
// ==/UserScript==
(function () {
"use strict";
function filterHLS(urlBegin, text) {
let rtn = [];
for (let line of text.split("\n")) {
if (line.startsWith("#")) continue;
rtn.push(urlBegin + line);
}
let fhd = rtn.filter((e) => e.includes("1080p"));
if (fhd) return fhd;
let hd = rtn.filter((e) => e.includes("720p"));
if (hd) return hd;
return rtn[0];
return rtn;
}
async function downloadFiles(button, urls) {
for (let url of urls) {
let start = url.lastIndexOf("/");
let filename = url.substring(start);
let split = url.indexOf("hls/") + 4;
let urlBegin = url.substring(0, split);
await fetch(url)
.then((response) => response.text())
.then(async (text) => {
const fileContent = text;
let video = filterHLS(urlBegin, text);
await helper(button, urlBegin, video);
})
.catch((err) => console.error("Error downloading file:", err));
}
}
async function helper(button, beginUrl, url) {
fetch(url)
.then((response) => response.text())
.then(async (text) => {
console.log(text);
let tsFiles = filterHLS(beginUrl, text);
await downloadVideos(button, tsFiles);
})
.catch((err) => console.error("Error downloading file:", err));
}
async function downloadVideos(button, tsFiles) {
const combinedBuffers = [];
for (const tsFile of tsFiles) {
const response = await fetch(tsFile);
if (!response.ok) {
throw new Error(`Failed to fetch ${tsFile}: ${response.statusText}`);
}
const arrayBuffer = await response.arrayBuffer();
combinedBuffers.push(arrayBuffer);
let percent = (combinedBuffers.length / tsFiles.length) * 100
button.innerText = "Downloading (" + percent.toPrecision(2) + "%)"
}
button.innerText = "Download Videos"
console.log(combinedBuffers.length);
const videoBlob = new Blob(combinedBuffers, { type: "video/mp2t" });
const url = URL.createObjectURL(videoBlob);
const downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = "combined_video.ts";
downloadLink.textContent = "Download Combined Video";
document.body.appendChild(downloadLink);
downloadLink.click();
URL.revokeObjectURL(url);
}
let allVideos = new Set();
function find_docs() {
let elements = document.querySelectorAll("privacy-web-mediahub-carousel");
for (let element of elements) {
let mediasStr = element.getAttribute("medias");
let medias = collectObjects(mediasStr);
let videos = medias
.filter((e) => e.url && e.url.endsWith(".m3u8") && !allVideos.has(e.url))
.map((e) => e.url);
videos.forEach((e) => allVideos.add(e));
if (videos.length < 1) continue;
let start = videos[0].indexOf("hls/") + 4
let end = videos[0].indexOf("--")
let buttonId = videos[0].substring(start, end)
console.log(buttonId)
let button = document.createElement("button");
button.innerText = "Download Videos";
button.setAttribute("id", buttonId)
button.addEventListener("click", function () {
downloadFiles(button, videos);
});
element.shadowRoot.appendChild(button);
}
}
function collectObjects(mediasStr) {
let start = 0;
let offset = 0;
let objects = [];
for (let i = 0; i < mediasStr.length; i++) {
let char = mediasStr[i];
if (char == "{") {
start = i;
} else if (char == "}") {
let objStr = mediasStr.substring(start, start + offset + 1);
objects.push(JSON.parse(objStr));
offset = 0;
} else {
offset += 1;
}
}
return objects;
}
setInterval(() => find_docs(), 1000);
})();