// ==UserScript==
// @name bilibili视频下载
// @namespace https://github.com/injahow
// @version 1.4.0
// @description 支持Web、RPC、Blob、Aria等下载方式;支持flv、dash、mp4视频格式;支持下载港区番剧;支持会员下载;支持换源播放,自动切换为高清视频源
// @author injahow
// @homepage https://github.com/injahow/bilibili-parse
// @copyright 2021, injahow (https://github.com/injahow)
// @match *://www.bilibili.com/video/av*
// @match *://www.bilibili.com/video/BV*
// @match *://www.bilibili.com/bangumi/play/ep*
// @match *://www.bilibili.com/bangumi/play/ss*
// @match https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png*
// @require https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/flv.js/dist/flv.min.js
// @require https://cdn.jsdelivr.net/npm/dplayer/dist/DPlayer.min.js
// @compatible chrome
// @compatible firefox
// @license MIT
// @grant none
// @downloadURL none
// ==/UserScript==
/* globals $, DPlayer waitForKeyElements */
(function () {
'use strict';
if (window.bp_fun_locked) return;
window.bp_fun_locked = true;
let auth;
(function () {
// https://greasyfork.org/zh-CN/scripts/25718-%E8%A7%A3%E9%99%A4b%E7%AB%99%E5%8C%BA%E5%9F%9F%E9%99%90%E5%88%B6/code
if (location.href.match(/^https:\/\/www\.mcbbs\.net\/template\/mcbbs\/image\/special_photo_bg\.png/) != null) {
if (location.href.match('access_key') != null && window.opener != null) {
window.stop();
document.children[0].innerHTML = '
');
$('#bilibiliPlayer').hide();
} else if (!!$('#bilibili-player')[0]) {
bili_player_id = '#bilibili-player';
$('#bilibili-player').before('
');
$('#bilibili-player').hide();
}
$('#player_mask_module').hide();
window.my_dplayer = new DPlayer({
container: $('#my_dplayer')[0],
mutex: false,
video: {
url: url,
type: 'auto'
},
danmaku: true,
apiBackend: {
read: function (options) {
request_danmaku(options, video_status.base().cid());
},
send: function (options) { // ?
options.error('此脚本无法将弹幕同步到云端');
}
},
contextmenu: [
{
text: '脚本信息',
link: 'https://github.com/injahow/bilibili-parse'
},
{
text: '脚本作者',
link: 'https://injahow.com'
}
]
});
if (config.format === 'dash' && url_2 && url_2 !== '#') {
$('body').append('
');
window.my_dplayer_2 = new DPlayer({
container: $('#my_dplayer_2')[0],
mutex: false,
video: {
url: url_2,
type: 'auto'
}
});
const my_dplayer = window.my_dplayer;
const my_dplayer_2 = window.my_dplayer_2;
my_dplayer.on('play', function () {
!my_dplayer.paused && my_dplayer_2.play();
});
my_dplayer.on('playing', function () {
!my_dplayer.paused && my_dplayer_2.play();
});
my_dplayer.on('timeupdate', function () {
if (Math.abs(my_dplayer.video.currentTime - my_dplayer_2.video.currentTime) > 1) {
my_dplayer_2.pause();
my_dplayer_2.seek(my_dplayer.video.currentTime);
}
!my_dplayer.paused && my_dplayer_2.play();
});
my_dplayer.on('seeking', function () {
my_dplayer_2.pause();
my_dplayer_2.seek(my_dplayer.video.currentTime);
});
my_dplayer.on('waiting', function () {
my_dplayer_2.pause();
});
my_dplayer.on('pause', function () {
my_dplayer_2.pause();
});
my_dplayer.on('suspend', function () {
my_dplayer_2.speed(my_dplayer.video.playbackRate);
});
my_dplayer.on('volumechange', function () {
my_dplayer_2.volume(my_dplayer.video.volume);
my_dplayer_2.video.muted = my_dplayer.video.muted;
});
}
}
function bili_video_tag() {
if (!!$('video[crossorigin="anonymous"]')[0]) {
return 'video[crossorigin="anonymous"]';
} else if (!!$('bwp-video')[0]) {
return 'bwp-video';
}
return '';
}
function bili_video_stop() { // listener
let bili_video;
if (bili_video = $(bili_video_tag())[0]) {
bili_video.pause();
bili_video.currentTime = 0;
}
}
function recover_player() {
if (window.my_dplayer) {
utils.Message.info('恢复播放器');
const bili_video = $(bili_video_tag())[0];
!!bili_video && bili_video.removeEventListener('play', bili_video_stop, false);
window.my_dplayer.destroy();
window.my_dplayer = null;
$('#my_dplayer').remove();
if (window.my_dplayer_2) {
window.my_dplayer_2.destroy();
window.my_dplayer_2 = null;
$('#my_dplayer_2').remove();
}
$(bili_player_id).show();
/*$('#player_mask_module').show();*/
}
}
// Message & MessageBox
utils.Message = {
success: (html) => {
message(html, 'success');
},
warning: (html) => {
message(html, 'warning');
},
danger: (html) => {
message(html, 'danger');
},
info: (html) => {
message(html, 'info');
}
};
utils.MessageBox = {
alert: (html, affirm) => {
messageBox({
html, callback: { affirm }
}, 'alert');
},
confirm: (html, affirm, cancel) => {
messageBox({
html, callback: {
affirm, cancel
}
}, 'confirm');
}
};
const components_css =
'';
const components_html =
'
' +
'
' +
'
' +
'
提示:' +
'
...
' +
'
' +
'' +
'
' +
'
';
function messageBox(ctx, type) {
if (type === 'confirm') {
$('div.message_box_btn button[name="cancel"]').show();
} else if (type === 'alert') {
$('div.message_box_btn button[name="cancel"]').hide();
}
if (ctx.html) {
$('div#message_box_context').html(`
${ctx.html}
`);
} else {
$('div#message_box_context').html('
╰( ̄▽ ̄)╮
');
}
$('#message_box').show();
$('div#message_box').animate({
'opacity': '1'
}, 300);
$('div.message_box_btn button[name="affirm"]')[0].onclick = () => {
$('div#message_box').hide();
if (ctx.callback && ctx.callback.affirm) {
ctx.callback.affirm();
}
};
$('div.message_box_btn button[name="cancel"]')[0].onclick = () => {
$('div#message_box').hide();
if (ctx.callback && ctx.callback.cancel) {
ctx.callback.cancel();
}
};
}
let id = 0;
function message(html, type) {
id += 1;
messageEnQueue(`
`, id);
messageDeQueue(id);
}
function messageEnQueue(message, id) {
$('div.message-bg').append(message);
$(`div#message-${id}`).animate({
'margin-top': '+=70px',
'opacity': '1',
}, 300);
}
function messageDeQueue(id, time = 3000) {
setTimeout(() => {
const e = `div#message-${id}`;
$(e).animate({
'margin-top': '-=70px',
'opacity': '0',
}, 300, () => {
$(e).remove();
});
}, time);
}
$('body').append(components_html + components_css);
})();
// video
let video_status;
(function () {
video_status = {
type, base, get_quality
};
function type() {
if (!!window.__INITIAL_STATE__.epInfo) {
return 'bangumi';
} else if (!!window.__INITIAL_STATE__.videoData) {
return 'video';
}
}
function base() {
const _type = type();
if (_type === 'video') {
return {
title: () => {
return (window.__INITIAL_STATE__.videoData && window.__INITIAL_STATE__.videoData.title || 'unknown').replace(/[\/\\:*?"<>|]+/g, '');
},
aid: () => {
return window.__INITIAL_STATE__.videoData.aid;
},
p: () => {
return window.__INITIAL_STATE__.p || 1;
},
cid: () => {
const aid = window.__INITIAL_STATE__.videoData.aid;
const p = window.__INITIAL_STATE__.p || 1;
return window.__INITIAL_STATE__.cidMap[aid].cids[p];
},
epid: () => {
return '';
},
need_vip: () => {
return false;
},
vip_need_pay: () => {
return false;
}
};
} else if (_type === 'bangumi') {
return {
title: () => {
return (window.__INITIAL_STATE__.h1Title || 'unknown').replace(/[\/\\:*?"<>|]+/g, '');
},
aid: () => {
return window.__INITIAL_STATE__.epInfo.aid;
},
p: () => {
return window.__INITIAL_STATE__.epInfo.i || 1;
},
cid: () => {
return window.__INITIAL_STATE__.epInfo.cid;
},
epid: () => {
return window.__INITIAL_STATE__.epInfo.id;
},
need_vip: () => {
return window.__INITIAL_STATE__.epInfo.badge === '会员';
},
vip_need_pay: () => {
return window.__INITIAL_STATE__.epPayMent.vipNeedPay;
}
};
}
}
function get_quality() {
let _q = 0, _q_max = 0;
if (!!$('li.bui-select-item')[0] && !!(_q_max = parseInt($('li.bui-select-item')[0].dataset.value))) {
_q = parseInt($('li.bui-select-item.bui-select-item-active').attr('data-value')) || (_q_max > 80 ? 80 : _q_max);
} else if (!!$('li.squirtle-select-item')[0] && !!(_q_max = parseInt($('li.squirtle-select-item')[0].dataset.value))) {
_q = parseInt($('li.squirtle-select-item.active').attr('data-value')) || (_q_max > 80 ? 80 : _q_max);
} else {
_q = _q_max = 80;
}
if (!user_status.is_login()) {
_q = _q_max > 80 ? 80 : _q_max;
}
return { q: _q, q_max: _q_max };
}
})();
// user
let user_status;
(function () {
user_status = {
is_login, vip_status, mid,
need_replace,
lazy_init
};
let _is_login = false, _vip_status = 0, _mid = '';
let is_init = false;
function lazy_init(last_init = false) {
if (!is_init) {
if (window.__BILI_USER_INFO__) {
_is_login = window.__BILI_USER_INFO__.isLogin;
_vip_status = window.__BILI_USER_INFO__.vipStatus;
_mid = window.__BILI_USER_INFO__.mid || '';
} else if (window.__BiliUser__) {
_is_login = window.__BiliUser__.isLogin;
if (window.__BiliUser__.cache) {
_vip_status = window.__BiliUser__.cache.data.vipStatus;
_mid = window.__BiliUser__.cache.data.mid || '';
} else {
_vip_status = 0;
_mid = '';
}
} else {
_is_login = false;
_vip_status = 0;
_mid = '';
}
is_init = last_init;
}
}
function is_login() {
return _is_login;
}
function vip_status() {
return _vip_status;
}
function mid() {
return _mid;
}
function need_replace() {
return (!_is_login || (_is_login && !_vip_status && video_status.base().need_vip()));
}
})();
// check
const check = {
aid: '', cid: '', q: ''
};
(function () {
function refresh() {
//utils.Message.info('refresh...');
console.log('refresh...');
$('#video_download').hide();
$('#video_download_2').hide();
utils.Player.recover();
// 更新check
const video_base = video_status.base();
[check.aid, check.cid] = [video_base.aid(), video_base.cid()];
check.q = video_status.get_quality().q;
}
// 监听p
$('body').on('click', 'a.router-link-active', function () {
if (this !== $('li[class="on"]').find('a')[0]) {
refresh();
}
});
$('body').on('click', 'li.ep-item', function () {
refresh();
});
$('body').on('click', 'button.bilibili-player-iconfont-next', function () {
refresh();
});
!!$('video[crossorigin="anonymous"]')[0] && ($('video[crossorigin="anonymous"]')[0].onended = function () {
refresh();
});
// 监听q
$('body').on('click', 'li.bui-select-item', function () {
refresh();
});
setInterval(function () {
if (check.q !== video_status.get_quality().q) {
refresh();
}
}, 1000);
// 监听aid
$('body').on('click', '.rec-list', function () {
refresh();
});
$('body').on('click', '.bilibili-player-ending-panel-box-videos', function () {
refresh();
});
// 定时检查 aid 和 cid
setInterval(function () {
const video_base = video_status.base();
if (check.aid !== video_base.aid() || check.cid !== video_base.cid()) {
refresh();
}
}, 3000);
})();
// main
(function () {
$('body').append('
');
$('body').append('
');
// 暂且延迟处理...
setTimeout(function () {
let my_toolbar;
if (!!$('#arc_toolbar_report')[0]) {
my_toolbar =
'
';
$('#arc_toolbar_report').after(my_toolbar);
} else if (!!$('#toolbar_module')[0]) {
my_toolbar =
'
';
$('#toolbar_module').after(my_toolbar);
}
user_status.lazy_init();
auth.check_login_status();
}, 3000);
$('body').on('click', '#setting_btn', function () {
// set form by config
for (const key in config) {
if (Object.hasOwnProperty.call(config, key)) {
$(`#${key}`).val(config[key]);
}
}
$('#my_config').show();
});
$('body').on('click', '#video_download', function () {
const type = config.download_type;
if (type === 'web') {
$('#video_url')[0].click();
} else if (type === 'a') {
const [video_url, video_url_2] = [
$('#video_url').attr('href'),
$('#video_url_2').attr('href')
];
const msg = '建议使用IDM、FDM等软件安装其浏览器插件后,鼠标右键点击链接下载~
' +
`
视频地址` +
(config.format === 'dash' ? `
音频地址` : '');
utils.MessageBox.alert(msg);
} else if (type === 'aria') {
const [video_url, video_url_2] = [
$('#video_url').attr('href'),
$('#video_url_2').attr('href')
];
const video_title = video_status.base().title();
let file_name, file_name_2;
if (video_url.match('.flv')) {
file_name = video_title + '.flv';
} else if (video_url.match('.m4s')) {
file_name = video_title + '_video.mp4';
} else if (video_url.match('.mp4')) {
file_name = video_title + '.mp4';
}
file_name_2 = video_title + '_audio.mp4';
const aria2_header = `--header "User-Agent: ${window.navigator.userAgent}" --header "Referer: ${window.location.href}"`;
const [code, code_2] = [
`aria2c "${video_url}" --out "${file_name}" ${aria2_header}`,
`aria2c "${video_url_2}" --out "${file_name_2}" ${aria2_header}`
]
const msg = '点击文本框即可复制下载命令!
' +
`视频:
` +
(config.format === 'dash' ? `音频:
` +
`全部:
` : '');
!window.bp_clip_btn && (window.bp_clip_btn = (id) => {
$(`#${id}`).select();
if (document.execCommand('copy')) {
utils.Message.success('复制成功');
} else {
utils.Message.warning('复制失败');
}
});
utils.MessageBox.alert(msg);
} else {
const url = $('#video_url').attr('href');
let file_name = video_status.base().title();
if (url.match('.flv')) {
file_name += '.flv';
} else if (url.match('.m4s')) {
file_name += '_video.mp4';
} else if (url.match('.mp4')) {
file_name += '.mp4';
} else {
return;
}
utils.Video.download(url, file_name, type);
}
});
$('body').on('click', '#video_download_2', function () {
const type = config.download_type;
if (type === 'web') {
$('#video_url_2')[0].click();
} else if (type === 'a') {
$('#video_download')[0].click();
} else if (type === 'aria') {
$('#video_download')[0].click();
} else {
const url = $('#video_url_2').attr('href');
let file_name = video_status.base().title();
if (url.match('.m4s')) {
file_name += '_audio.mp4';
} else {
return;
}
utils.Video.download(url, file_name, type);
}
});
let api_url, api_url_temp;
$('body').on('click', '#bilibili_parse', function () {
user_status.lazy_init(true); // init
const video_base = video_status.base();
const [aid, p, cid, epid] = [
video_base.aid(), video_base.p(), video_base.cid(), video_base.epid()
];
const [type, q] = [
video_status.type(), video_status.get_quality().q
];
api_url = `${config.base_api}?av=${aid}&p=${p}&cid=${cid}&ep=${epid}&q=${q}&type=${type}&format=${config.format}&otype=json`;
const [auth_id, auth_sec] = [
localStorage.getItem('bp_auth_id') || '',
localStorage.getItem('bp_auth_sec') || ''
];
if (config.auth === '1' && auth_id && auth_sec) {
api_url += `&auth_id=${auth_id}&auth_sec=${auth_sec}`;
}
if (api_url === api_url_temp) {
utils.Message.info('(^・ω・^)~喵喵喵~');
const url = $('#video_url').attr('href');
const url_2 = $('#video_url_2').attr('href');
if (url && url !== '#') {
$('#video_download').show();
config.format === 'dash' && $('#video_download_2').show();
if (user_status.need_replace() || config.replace_force === '1') {
!$('#my_dplayer')[0] && utils.Player.replace(url, url_2);
}
}
return;
}
$('#video_url').attr('href', '#');
$('#video_url_2').attr('href', '#');
api_url_temp = api_url;
utils.Message.info('开始请求');
$.ajax(api_url, {
dataType: 'json',
success: (result) => {
if (result && result.code === 0) {
utils.Message.success('请求成功');
const url = config.format === 'dash' ? result.video.replace(/^https?\:\/\//i, 'https://') : result.url.replace(/^https?\:\/\//i, 'https://');
const url_2 = config.format === 'dash' ? result.audio.replace(/^https?\:\/\//i, 'https://') : '#';
$('#video_url').attr('href', url);
$('#video_download').show();
if (config.format === 'dash') {
$('#video_url_2').attr('href', url_2);
$('#video_download_2').show();
}
if (user_status.need_replace() || config.replace_force === '1') {
utils.Player.replace(url, url_2);
}
} else {
utils.Message.warning('请求失败:' + result.message);
}
},
error: (e) => {
utils.Message.danger('请求异常');
console.log('error', e);
}
});
});
})();
})();