您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Media Source Extensions API 数据 Dump 工具
- // ==UserScript==
- // @name MSE Dump Tools
- // @name:zh-CN MSE Dump Tools
- // @name:zh-TW MSE Dump Tools
- // @name:ja MSE Dump Tools
- // @namespace CloudMoeMediaSourceExtensionsAPIDataDumper
- // @version 1.6.6
- // @description Media Source Extensions API Data Dump Tool
- // @description:zh-CN Media Source Extensions API 数据 Dump 工具
- // @description:zh-TW Media Source Extensions API 資料 Dump 工具
- // @description:ja Media Source Extensions API データ ダンプ ツール
- // @author TGSAN
- // @match *://*/*
- // @run-at document-start
- // @noframes
- // @grant GM_unregisterMenuCommand
- // @grant GM_registerMenuCommand
- // @grant unsafeWindow
- // ==/UserScript==
- (function () {
- 'use strict';
- const DefualtI18N = "zh";
- const I18NDict = {
- "en": {
- "视频": "Video",
- "音频": "Audio",
- "视频 - 最快播放速度": "Video - Fastest playback speed",
- "视频 - 恢复播放速度": "Video - Resume playback speed",
- "音频 - 最快播放速度": "Audio - Fastest playback speed",
- "音频 - 恢复播放速度": "Audio - Resume playback speed",
- "尝试直接下载页面中的视频": "Attempt to directly download the video on the page",
- "尝试直接下载页面中的音频": "Attempt to directly download the audio on the page",
- "结束 Dump": "End Dump",
- "没有找到可以下载的项目。": "No downloadable items found.",
- "没有找到可以直接下载的项目,但是找到了": "No directly downloadable items found, but",
- "个使用 MSE 的项目,需要在想要下载时点击 “结束 Dump” 按钮来停止存储。": "items using MSE were found. Click the \"End Dump\" button when you want to download to stop storing data.",
- "轨道:": "Track:",
- "已结束保存。": "has finished saving.",
- "下载数据:": "Download data:",
- },
- "ja": {
- "视频": "動画",
- "音频": "音声",
- "视频 - 最快播放速度": "動画 - 最速再生速度",
- "视频 - 恢复播放速度": "動画 - 再生速度を元に戻す",
- "音频 - 最快播放速度": "音声 - 最速再生速度",
- "音频 - 恢复播放速度": "音声 - 再生速度を元に戻す",
- "尝试直接下载页面中的视频": "ページ内の動画を直接ダウンロードを試みる",
- "尝试直接下载页面中的音频": "ページ内の音声を直接ダウンロードを試みる",
- "结束 Dump": "ダンプを終了",
- "没有找到可以下载的项目。": "ダウンロード可能な項目が見つかりませんでした。",
- "没有找到可以直接下载的项目,但是找到了": "直接ダウンロード可能な項目は見つかりませんでしたが、",
- "个使用 MSE 的项目,需要在想要下载时点击 “结束 Dump” 按钮来停止存储。": "個のMSEを使用している項目が見つかりました。ダウンロードしたい時に「ダンプ終了」ボタンをクリックして、保存を停止する必要があります。",
- "轨道:": "トラック:",
- "已结束保存。": "保存が完了しました。",
- "下载数据:": "ダウンロードデータ:",
- }
- };
- function GetI18NString(key) {
- let lang = navigator.language || navigator.userLanguage;
- lang = lang.substr(0, 2);
- if (lang !== DefualtI18N) {
- if (I18NDict[lang] === undefined) {
- lang = "en";
- }
- if (I18NDict[lang] && I18NDict[lang][key]) {
- return I18NDict[lang][key];
- }
- }
- return key;
- }
- GM_registerMenuCommand(GetI18NString("视频 - 最快播放速度"), function () { document.getElementsByTagName("video")[0].playbackRate = 16 });
- GM_registerMenuCommand(GetI18NString("视频 - 恢复播放速度"), function () { document.getElementsByTagName("video")[0].playbackRate = 1 });
- GM_registerMenuCommand(GetI18NString("音频 - 最快播放速度"), function () { document.getElementsByTagName("audio")[0].playbackRate = 16 });
- GM_registerMenuCommand(GetI18NString("音频 - 恢复播放速度"), function () { document.getElementsByTagName("audio")[0].playbackRate = 1 });
- GM_registerMenuCommand(GetI18NString("尝试直接下载页面中的视频"), function () { DirectDownloadPlayingVideo("video") });
- GM_registerMenuCommand(GetI18NString("尝试直接下载页面中的音频"), function () { DirectDownloadPlayingVideo("audio") });
- GM_registerMenuCommand(GetI18NString("结束 Dump"), EndAllDumpTasks);
- var dumpEndTasks = [];
- function dateFormat(dataObj, fmt) {
- var o = {
- "M+": dataObj.getMonth() + 1, // 月份
- "d+": dataObj.getDate(), // 日
- "h+": dataObj.getHours(), // 小时
- "m+": dataObj.getMinutes(), // 分
- "s+": dataObj.getSeconds(), // 秒
- "q+": Math.floor((dataObj.getMonth() + 3) / 3), // 季度
- "S": dataObj.getMilliseconds() // 毫秒
- };
- if (/(y+)/.test(fmt)) {
- fmt = fmt.replace(RegExp.$1, (dataObj.getFullYear() + "").substr(4 - RegExp.$1.length));
- }
- for (var k in o) {
- if (new RegExp("(" + k + ")").test(fmt)) {
- fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
- }
- }
- return fmt;
- }
- async function DirectDownloadPlayingVideo(tag) {
- let elements = document.getElementsByTagName(tag);
- let downloadCount = 0;
- let mseCount = 0;
- for (let i = 0; i < elements.length; i++) {
- let videoLink = document.getElementsByTagName("video")[i].currentSrc;
- if (videoLink == "") {
- continue;
- }
- if (videoLink.startsWith("blob:")) {
- mseCount++;
- continue;
- }
- let a = document.createElement('a');
- a.download = "direct_" + tag;
- var res = await fetch(videoLink);
- var videoBlob = await res.blob();
- var url = window.URL.createObjectURL(videoBlob);
- a.href = url;
- a.click();
- a.remove();
- window.URL.revokeObjectURL(url);
- downloadCount++;
- }
- if (downloadCount == 0) {
- if (mseCount == 0) {
- alert(GetI18NString("没有找到可以下载的项目。"));
- } else {
- alert(GetI18NString("没有找到可以直接下载的项目,但是找到了") + " " + mseCount + " " + GetI18NString("个使用 MSE 的项目,需要在想要下载时点击 “结束 Dump” 按钮来停止存储。"));
- }
- }
- }
- function EndAllDumpTasks() {
- while (dumpEndTasks.length > 0) {
- let endTask = dumpEndTasks.shift();
- endTask();
- }
- }
- unsafeWindow.SavedDataList = [];
- unsafeWindow.DownloadData = function (dataKey, fileName) {
- const link = document.createElement('a');
- link.href = URL.createObjectURL(new Blob([unsafeWindow.SavedDataList[dataKey]]));
- link.download = fileName;
- link.click();
- window.URL.revokeObjectURL(link.href);
- }
- function DownloadDataCmd(key, ext, type, date) {
- if (ext == "mp4" && type == "audio") {
- ext = "m4a";
- }
- DownloadData(key, "dumped_" + type + "_" + dateFormat(date, "yyyyMMddhhmmss") + "." + ext);
- }
- function Uint8ArrayConcat(a, b) {
- var c = new Uint8Array(a.length + b.length);
- c.set(a);
- c.set(b, a.length);
- return c;
- }
- function BytesToSize(bytes) {
- if (bytes === 0) return '0 B';
- var k = 1024;
- var sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- var i = Math.floor(Math.log(bytes) / Math.log(k));
- return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
- }
- var _addSourceBuffer = unsafeWindow.MediaSource.prototype.addSourceBuffer;
- unsafeWindow.MediaSource.prototype.addSourceBuffer = function (mime) {
- console.log("MediaSource addSourceBuffer Type: ", mime);
- mime = mime.trim();
- const regex = /^.+\/(.+);\s*codecs=\"{0,1}(.+?)\"{0,1}$/g;
- let mimeMatches = regex.exec(mime);
- let format = "bin";
- let codecs = "";
- let basicCodecs = "";
- if (mimeMatches != null && mimeMatches.length == 3) {
- format = mimeMatches[1];
- codecs = mimeMatches[2];
- codecs = codecs.replace("\"", "");
- let basicCodecsArray = codecs.split(",");
- for (let i = 0; i < basicCodecsArray.length; i++) {
- let basicCodec = basicCodecsArray[i];
- let indexOfBasicCodec = basicCodec.indexOf(".");
- if (indexOfBasicCodec > 0) {
- basicCodec = basicCodec.substring(0, indexOfBasicCodec);
- }
- if (i == 0) {
- basicCodecs = basicCodec;
- } else {
- basicCodecs = basicCodecs + "," + basicCodec;
- }
- }
- }
- var sourceBuffer = _addSourceBuffer.call(this, mime);
- var _append = sourceBuffer.appendBuffer;
- var endToSave = false;
- var sourceBufferData = new Uint8Array();
- var isVideo = (mime.startsWith("audio") ? false : true);
- var type = (isVideo ? "video" : "audio");
- var key = type + "_" + window.performance.now().toString();
- var startDate = new Date();
- dumpEndTasks.push(() => {
- endToSave = true;
- console.warn(GetI18NString("轨道:") + " " + mime + " " + GetI18NString("已结束保存。"));
- unsafeWindow.SavedDataList[key] = sourceBufferData;
- let downloadCaption = `${GetI18NString("下载数据:")} ${(isVideo ? GetI18NString("视频") : GetI18NString("音频"))} ${basicCodecs != "" ? (basicCodecs) : ""} (${BytesToSize(sourceBufferData.length)}, at ${dateFormat(startDate, "hh:mm:ss")})`;
- GM_registerMenuCommand(downloadCaption, () => { DownloadDataCmd(key, format, type, startDate); });
- });
- sourceBuffer.appendBuffer = function (buffer) {
- if (!endToSave) {
- sourceBufferData = Uint8ArrayConcat(sourceBufferData, new Uint8Array(buffer));
- }
- _append.call(this, buffer);
- }
- return sourceBuffer;
- }
- })();