// ==UserScript==
// @name MSE Dump Tools
// @namespace CloudMoeMediaSourceExtensionsAPIDataDumper
// @version 1.4.0
// @description Media Source Extensions API 数据 Dump 工具
// @author TGSAN
// @include /.*/
// @run-at document-start
// @grant GM_unregisterMenuCommand
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// ==/UserScript==
(function () {
'use strict';
GM_registerMenuCommand(`视频 - 最快播放速度`, function () { document.getElementsByTagName("video")[0].playbackRate = 16 });
GM_registerMenuCommand(`视频 - 恢复播放速度`, function () { document.getElementsByTagName("video")[0].playbackRate = 1 });
GM_registerMenuCommand(`音频 - 最快播放速度`, function () { document.getElementsByTagName("audio")[0].playbackRate = 16 });
GM_registerMenuCommand(`音频 - 恢复播放速度`, function () { document.getElementsByTagName("audio")[0].playbackRate = 1 });
GM_registerMenuCommand(`结束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;
}
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, type, date) {
var ext = "bin";
if (type == "audio") {
ext = "m4a";
} else if (type == "video") {
ext = "mp4";
}
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);
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(`轨道: ${mime} 已结束保存。`);
unsafeWindow.SavedDataList[key] = sourceBufferData;
let downloadCaption = `下载${(isVideo ? "视频" : "音频")}数据 (${BytesToSize(sourceBufferData.length)}, at ${dateFormat(startDate, "yyyy/MM/dd hh:mm:ss")})`;
GM_registerMenuCommand(downloadCaption, () => { DownloadDataCmd(key, type, startDate); });
});
sourceBuffer.appendBuffer = function (buffer) {
if (!endToSave) {
sourceBufferData = Uint8ArrayConcat(sourceBufferData, new Uint8Array(buffer));
}
_append.call(this, buffer);
}
return sourceBuffer;
}
})();