// ==UserScript==
// @name 百度网盘视频播放增强
// @namespace https://github.com/Huang131/userscripts
// @version 1.0.0
// @description 百度网盘视频播放增强:倍速任意调整、分辨率任意切换、自动加载播放列表、自动加载字幕、可加载本地字幕、字幕样式精细设置、画面比例调整、色彩调整、播放设置自动记忆
// @author Huang131
// @license MIT
// @homepageURL https://github.com/Huang131/userscripts
// @supportURL https://github.com/Huang131/userscripts/issues
// @match http*://yun.baidu.com/s/*
// @match https://pan.baidu.com/s/*
// @match https://pan.baidu.com/wap/home*
// @match https://pan.baidu.com/play/video*
// @match https://pan.baidu.com/pfile/video*
// @match https://pan.baidu.com/pfile/mboxvideo*
// @match https://pan.baidu.com/mbox/streampage*
// @require https://unpkg.com/hls.js@1.6.0/dist/hls.min.js
// @require https://unpkg.com/artplayer@5.2.2/dist/artplayer.js
// @run-at document-start
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @compatible chrome
// @compatible firefox
// @compatible edge
// ==/UserScript==
(function () {
'use strict';
var obj = {
video_page: {
flag: "",
file: {},
filelist: [],
quality: [],
adToken: "",
getUrl: null
}
};
obj.currentList = function () {
try {
var currentList = unsafeWindow.require('system-core:context/context.js').instanceForSystem.list.getCurrentList();
if (currentList.length) {
sessionStorage.setItem("currentList", JSON.stringify(currentList));
} else {
setTimeout(obj.currentList, 500);
}
} catch (e) {}
window.onhashchange = function () {
setTimeout(obj.currentList, 500);
};
var fufHyA = document.querySelector(".fufHyA");
if (fufHyA) {
[].forEach.call(document.querySelectorAll(".fufHyA"), function (element) {
element.onclick = function () {
setTimeout(obj.currentList, 500);
};
});
}
};
obj.forcePreview = function () {
unsafeWindow.jQuery(document).on("click", "#shareqr dd", function () {
try {
var selectedFile = unsafeWindow.require('system-core:context/context.js').instanceForSystem.list.getSelected();
var file = selectedFile[0];
if (file.category == 1) {
var ext = file.server_filename.split(".").pop().toLowerCase();
if (["ts", "3gp2", "3g2", "3gpp", "amv", "divx", "dpg", "f4v", "m2t", "m2ts", "m2v", "mpe", "mpeg", "mts", "vob", "webm", "wxp", "wxv"].indexOf(ext) >= 0) {
window.open("https://pan.baidu.com" + location.pathname + "?fid=" + file.fs_id, "_blank");
}
}
} catch (error) {}
});
};
obj.sharevideo = function () {
if (unsafeWindow.require) {
unsafeWindow.locals.get("file_list", "share_uk", "shareid", "sign", "timestamp", function (file_list, share_uk, shareid, sign, timestamp) {
if (file_list.length == 1 && file_list[0].category == 1) {
obj.startObj().then(function () {
obj.video_page.flag = "sharevideo";
var fs_id = (obj.video_page.file = file_list[0]).fs_id;
var vip = obj.getVip();
obj.video_page.getUrl = function (type) {
return "/share/streaming?channel=chunlei&uk=" + share_uk + "&fid=" + fs_id + "&sign=" + sign + "×tamp=" + timestamp + "&shareid=" + shareid + "&type=" + type + "&vip=" + vip + "&jsToken=" + unsafeWindow.jsToken;
};
obj.getAdToken().then(function () {
obj.addQuality();
obj.addFilelist();
obj.initVideoPlayer();
});
});
} else {
obj.currentList();
obj.forcePreview();
}
});
}
};
obj.playvideo = function () {
unsafeWindow.jQuery(document).ajaxComplete(function (event, xhr, options) {
var response, requestUrl = options.url;
if (requestUrl.indexOf("/api/categorylist") >= 0) {
response = xhr.responseJSON;
obj.video_page.filelist = response.info || [];
} else if (requestUrl.indexOf("/api/filemetas") >= 0) {
response = xhr.responseJSON;
if (response && response.info) {
obj.startObj().then(function () {
obj.video_page.flag = "playvideo";
var path = (obj.video_page.file = response.info[0]).path;
var vip = obj.getVip();
obj.video_page.getUrl = function (type) {
if (type.includes(1080) && vip <= 1) type = type.replace(1080, 720);
return "/api/streaming?path=" + encodeURIComponent(path) + "&app_id=250528&clienttype=0&type=" + type + "&vip=" + vip + "&jsToken=" + unsafeWindow.jsToken;
};
obj.getAdToken().then(function () {
obj.addQuality();
obj.addFilelist();
obj.initVideoPlayer();
});
});
}
}
});
};
obj.video = function () {
var appEl = document.querySelector("#app");
var globalProps = appEl && appEl.__vue_app__ ? appEl.__vue_app__.config.globalProperties : {};
var $pinia = globalProps.$pinia;
var $router = globalProps.$router;
if ($pinia && $router && Object.keys(($pinia.state._rawValue.videoinfo || {}).videoinfo || {}).length) {
obj.startObj().then(function () {
obj.video_page.flag = "video";
var state = $pinia.state._rawValue;
var videoinfo = state.videoinfo.videoinfo;
var recommendListInfo = state.videoinfo.recommendListInfo || {};
var selectionVideoList = recommendListInfo.selectionVideoList;
if (Array.isArray(selectionVideoList) && selectionVideoList.length) {
obj.video_page.filelist = selectionVideoList;
} else {
Object.defineProperty(recommendListInfo, "selectionVideoList", {
enumerable: true,
set: function (val) {
obj.video_page.filelist = val;
}
});
}
var path = (obj.video_page.file = videoinfo).path;
var vip = obj.getVip();
obj.video_page.getUrl = function (type) {
if (type.includes(1080) && vip <= 1) type = type.replace(1080, 720);
return "/api/streaming?path=" + encodeURIComponent(path) + "&app_id=250528&clienttype=0&type=" + type + "&vip=" + vip + "&jsToken=" + unsafeWindow.jsToken;
};
obj.getAdToken().then(function () {
obj.addQuality();
obj.addFilelist();
obj.initVideoPlayer();
});
});
$router.isReady().then(function () {
$router.afterEach(function (to, from) {
if (from.fullPath !== "/" && from.fullPath !== to.fullPath) location.reload();
});
});
} else {
obj.delay().then(obj.video);
}
};
obj.mboxvideo = function () {
var appEl = document.querySelector("#app");
var globalProps = appEl && appEl.__vue_app__ ? appEl.__vue_app__.config.globalProperties : {};
var $pinia = globalProps.$pinia;
var $router = globalProps.$router;
if ($pinia && $router && Object.keys(($pinia.state._rawValue.videoinfo || {}).videoinfo || {}).length) {
obj.startObj().then(function () {
obj.video_page.flag = "mboxvideo";
var videoinfo = $pinia.state._rawValue.videoinfo.videoinfo;
obj.video_page.file = videoinfo;
var to = videoinfo.to, from_uk = videoinfo.from_uk, msg_id = videoinfo.msg_id;
var fs_id = videoinfo.fs_id, type = videoinfo.type, trans = videoinfo.trans, ltime = videoinfo.ltime;
obj.video_page.getUrl = function (stream_type) {
return "/mbox/msg/streaming?to=" + to + "&from_uk=" + from_uk + "&msg_id=" + msg_id + "&fs_id=" + fs_id + "&type=" + type + "&stream_type=" + stream_type + "&trans=" + (trans || "") + "<ime=" + ltime;
};
obj.video_page.adToken = videoinfo.adToken || "";
obj.getAdToken().then(function () {
obj.addQuality();
obj.addFilelist();
obj.initVideoPlayer();
});
});
$router.isReady().then(function () {
$router.afterEach(function (to, from) {
if (from.fullPath !== "/" && from.fullPath !== to.fullPath) location.reload();
});
});
} else {
obj.delay().then(obj.mboxvideo);
}
};
obj.videoView = function () {
var previewEl = document.querySelector(".preview-video");
var videoFile = previewEl && previewEl.__vue__ ? previewEl.__vue__.videoFile : null;
if (videoFile) {
obj.startObj().then(function () {
obj.video_page.flag = "videoView";
obj.video_page.file = videoFile;
var path = videoFile.path;
obj.video_page.getUrl = function (type) {
if (type.includes(1080) && +unsafeWindow.locals && +unsafeWindow.locals.isVip <= 1) type = type.replace(1080, 720);
return "/rest/2.0/xpan/file?method=streaming&path=" + encodeURIComponent(path) + "&type=" + type;
};
obj.getAdToken().then(function () {
obj.addQuality();
obj.addFilelist();
obj.initVideoPlayer();
});
});
} else {
obj.delay().then(obj.videoView);
}
};
obj.initVideoPlayer = function () {
obj.replaceVideoPlayer().then(function () {
var vp = obj.video_page;
var defaultQ = vp.quality.find(function (item) { return item.default; }) || vp.quality[0];
var options = {
adToken: vp.adToken,
file: vp.file,
filelist: vp.filelist,
quality: vp.quality,
getUrl: vp.getUrl,
url: defaultQ.url,
type: defaultQ.type,
id: "" + vp.file.fs_id,
poster: (Object.values(vp.file.thumbs || []).slice(-1)[0] || "").replace(/size=c\d+_u\d+/, "size=c850_u580")
};
obj.artPlugins().init(options).then(function () {
obj.showTip("视频播放器已就绪 ...", "success");
obj.destroyPlayer();
});
});
};
obj.replaceVideoPlayer = function () {
var flag = obj.video_page.flag;
var videoNode = document.querySelector("#video-wrap, .vp-video__player, #app .video-content");
if (videoNode) {
while (videoNode.nextSibling) {
videoNode.parentNode.removeChild(videoNode.nextSibling);
}
var container = document.getElementById("artplayer");
if (!container) {
container = document.createElement("div");
container.setAttribute("id", "artplayer");
if (flag === "videoView") {
container.setAttribute("style", "width: 100%; height: 3.75rem;");
} else {
container.setAttribute("style", "width: 100%; height: 100%;");
}
obj.videoNode = videoNode.parentNode.replaceChild(container, videoNode);
container.parentNode.style.cssText += "z-index: auto;";
return Promise.resolve();
}
} else {
return obj.delay().then(function () {
return obj.replaceVideoPlayer();
});
}
};
obj.artPlugins = function () {
var plugins = [
obj.pluginQuality,
obj.pluginPlaylist,
obj.pluginPlaybackRate,
obj.pluginSubtitle,
obj.pluginImageFilter,
obj.pluginPlaySetting,
obj.pluginHotkey
];
return {
version: "1.0.0",
init: function (opts) {
return Promise.all([
obj.readyHls(),
obj.readyArtplayer()
]).then(function () {
return obj.initArtplayer(opts, plugins);
});
}
};
};
obj.readyHls = function () {
if (window.Hls || unsafeWindow.Hls) return Promise.resolve();
return obj.loadJs("https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.6.0/hls.min.js");
};
obj.readyArtplayer = function () {
if (window.Artplayer || unsafeWindow.Artplayer) return Promise.resolve();
return obj.loadJs("https://cdnjs.cloudflare.com/ajax/libs/artplayer/5.2.2/artplayer.min.js");
};
obj.loadJs = function (url) {
window.instances = window.instances || {};
if (window.instances[url]) return window.instances[url];
window.instances[url] = new Promise(function (resolve, reject) {
var n = document.createElement("script");
n.src = url;
n.type = "text/javascript";
n.onload = resolve;
n.onerror = reject;
Node.prototype.appendChild.call(document.head, n);
});
return window.instances[url];
};
obj.initArtplayer = function (opts, plugins) {
var Artplayer = window.Artplayer || unsafeWindow.Artplayer;
var Hls = window.Hls || unsafeWindow.Hls;
var isMobile = Artplayer.utils.isMobile;
Artplayer.ASPECT_RATIO = ["default", "自动", "4:3", "16:9"];
Artplayer.AUTO_PLAYBACK_TIMEOUT = 10000;
Artplayer.NOTICE_TIME = 5000;
var artOption = {
container: "#artplayer",
url: opts.url,
quality: opts.quality,
type: "hls",
autoplay: true,
autoPlayback: true,
aspectRatio: true,
contextmenu: [],
customType: {
hls: function (video, url, art) {
if (!Hls || !Hls.isSupported()) {
if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = url;
} else {
art.notice.show = "不支持此播放格式: m3u8";
}
return;
}
if (art.hls) art.hls.destroy();
var hls = new Hls({
maxBufferLength: 10 * Hls.DefaultConfig.maxBufferLength,
xhrSetup: function (xhr, url) {
var host = (url.match(/^https?:\/\/(.*?)\//) || [])[1];
if (host && host !== location.host) {
if (/backhost=/.test(url)) {
var backhostMatch = (decodeURIComponent(url || "").match(/backhost=(\[.*\])/) || [])[1];
if (backhostMatch) {
try {
var backhosts = JSON.parse(backhostMatch);
if (backhosts && backhosts.length) {
backhosts = [].concat(backhosts, [host]);
var idx = backhosts.findIndex(function (h) { return h === art.realHost; });
art.realHost = backhosts[idx + 1 >= backhosts.length ? 0 : idx + 1];
}
} catch (e) {}
}
}
if (art.realHost) {
url = url.replace(host, art.realHost);
xhr.open("GET", url, true);
}
}
}
});
hls.loadSource(url);
hls.attachMedia(video);
hls.on(Hls.Events.ERROR, function (event, data) {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
if (data.details === Hls.ErrorDetails.MANIFEST_LOAD_ERROR) {
setTimeout(function () { hls.loadSource(hls.url); }, 1000);
} else if (data.details === Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT || data.details === Hls.ErrorDetails.MANIFEST_PARSING_ERROR) {
hls.loadSource(hls.url);
} else if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) {
hls.fragLoadError = (hls.fragLoadError || 0) + 1;
if (hls.fragLoadError < 5) {
hls.loadSource(hls.url);
hls.media.currentTime = art.currentTime;
hls.media.play();
} else {
hls.destroy();
art.notice.show = "视频播放错误次数过多,请刷新重试";
}
} else {
hls.startLoad();
}
break;
case Hls.ErrorTypes.MEDIA_ERROR:
hls.recoverMediaError();
break;
default:
hls.destroy();
art.notice.show = "视频播放异常,请刷新重试";
}
}
});
art.hls = hls;
art.on("destroy", function () { hls.destroy(); });
}
},
flip: false,
icons: {
loading: '
',
state: '',
indicator: '
'
},
id: opts.id || "",
pip: !isMobile,
poster: opts.poster || "",
playbackRate: false,
screenshot: true,
setting: true,
subtitle: {
url: "",
type: "auto",
style: {
color: "#fe9200",
bottom: "5%",
fontSize: "25px",
fontWeight: 400,
fontFamily: "",
textShadow: ""
},
encoding: "utf-8",
escape: false
},
subtitleOffset: false,
hotkey: true,
fullscreen: true,
fullscreenWeb: !isMobile
};
Object.assign(artOption, {
getUrl: opts.getUrl,
adToken: opts.adToken,
file: opts.file,
filelist: opts.filelist,
sublist: opts.sublist || []
});
var art = new Artplayer(artOption, function (art) {
plugins.forEach(function (pluginFn) {
var plugin = pluginFn();
art.plugins.add(plugin);
});
});
return art;
};
obj.pluginQuality = function () {
return function (art) {
var i18n = art.i18n;
var option = art.option;
var notice = art.notice;
var storage = art.storage;
var controls = art.controls;
var isMobile = art.constructor.utils.isMobile;
function shortText(text) {
return isMobile ? text.split(/\s/).shift() : text;
}
function updateQuality() {
var quality = option.quality;
var current = quality.find(function (q) { return q.default; }) || quality[0];
controls.update({
name: "quality",
html: current ? shortText(current.html) : "",
selector: quality.map(function (q) { return Object.assign({}, q); }),
onSelect: function (item) {
art.switchQuality(item.url);
notice.show = i18n.get("Switch Video") + ": " + item.html;
return shortText(item.html);
}
});
}
function init() {
updateQuality();
option.qualityid = option.id;
art.on("restart", function () {
if (option.qualityid === option.id) {
var autoPlayback = art.layers["auto-playback"];
if (autoPlayback) art.constructor.utils.setStyle(autoPlayback, "display", "none");
} else {
option.qualityid = option.id;
updateQuality();
}
});
}
i18n.update({ "zh-cn": { "Switch Video": "切换画质" } });
if (art.isReady) init(); else art.once("ready", init);
return { name: "quality" };
};
};
obj.pluginPlaylist = function () {
return function (art) {
var i18n = art.i18n;
var option = art.option;
var controls = art.controls;
var isMobile = art.constructor.utils.isMobile;
var showtext = !isMobile;
var icon = '';
function init() {
art.once("ready", function () {
var filelist = option.filelist || [];
if (filelist.length <= 1) {
if (controls.hasOwnProperty("playlist")) controls.remove("playlist");
return;
}
controls.update({
html: showtext ? i18n.get("PlayList") : icon,
name: "playlist",
position: "right",
style: { paddingLeft: "10px", paddingRight: "10px" },
selector: filelist.map(function (f) { return Object.assign({}, f, { html: f.name, style: { textAlign: "left" } }); }),
onSelect: function (item) {
option.file = item;
if (typeof item.open === "function") item.open();
return showtext ? i18n.get("PlayList") : icon;
}
});
});
}
i18n.update({ "zh-cn": { PlayList: "播放列表" } });
init();
return { name: "playlist" };
};
};
obj.pluginPlaybackRate = function () {
return function (art) {
var i18n = art.i18n;
var icons = art.icons;
var storage = art.storage;
var setting = art.setting;
var contextmenu = art.contextmenu;
var layers = art.layers;
var notice = art.notice;
var PLAYBACK_RATE = art.constructor.PLAYBACK_RATE;
var SETTING_ITEM_WIDTH = art.constructor.SETTING_ITEM_WIDTH;
var utils = art.constructor.utils;
var query = utils.query;
var append = utils.append;
var setStyle = utils.setStyle;
var inverseClass = utils.inverseClass;
var isMobile = utils.isMobile;
function getAutoPlaybackRateLayer() {
return layers["auto-playbackrate"] || layers.update({
name: "auto-playbackrate",
html: '