feat(bgvideo): 修复bilibili视频播放,并增加流播放模式(需要接口配合暂不开放)

This commit is contained in:
lxy 2020-10-26 15:07:57 +08:00
parent e9a7f8a761
commit 28a799d938
9 changed files with 3283 additions and 102 deletions

10
.babelrc Normal file
View File

@ -0,0 +1,10 @@
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}

View File

@ -253,6 +253,7 @@
</script>
</#if>
<script type='text/javascript' src='${cdn_base_url!}/js/src/qrcode.min.js' defer></script>
<script type='text/javascript' src='${theme_base!}/source/js/flv.min.js' defer></script>
<script type='text/javascript' src='${theme_base!}/js/app.min.js?ver=1.2.0'></script>
<#nested />
<#if settings.live2d_switch!true>

451
js/app.js
View File

@ -39,6 +39,9 @@ var LIlGGAttachContext = {
$bg_video_stu = $(".video-stu"),
$bg_video_add = $("#video-add"),
dom = $bg_video[0],
diskTime = 20 * 1000,
keyframesIndex,
flvPlayer,
mediaBlob;
var bindBgVideoEvent = function () {
@ -65,14 +68,12 @@ var LIlGGAttachContext = {
};
dom.onended = function () {
$bg_video.attr("src", "");
$bg_video_add.hide();
$bg_video_btn.addClass("loadvideo").removeClass("video-pause");
$bg_video_btn.removeClass("videolive");
$bg_video_btn.removeClass("haslive");
$(".focusinfo").css({
top: "49.3%",
});
defaultStyle();
flvPlayer.pause()
flvPlayer.unload()
flvPlayer.detachMediaElement()
flvPlayer.destroy()
flvPlayer = null
};
$bg_video_add.on("click", function () {
@ -80,6 +81,16 @@ var LIlGGAttachContext = {
});
};
var defaultStyle = function () {
$bg_video_add.hide();
$bg_video_btn.addClass("loadvideo").removeClass("video-pause");
$bg_video_btn.removeClass("videolive");
$bg_video_btn.removeClass("haslive");
$(".focusinfo").css({
top: "49.3%",
});
}
var bgPlay = function () {
$bg_video_btn.addClass("video-pause").removeClass("video-play").show();
$bg_video_stu.css({
@ -90,7 +101,7 @@ var LIlGGAttachContext = {
});
$("#banner_wave_1").addClass("banner_wave_hide");
$("#banner_wave_2").addClass("banner_wave_hide");
dom.play();
flvPlayer.play();
};
var bgPause = function () {
@ -111,63 +122,116 @@ var LIlGGAttachContext = {
}
};
var loadSource = function () {
function playVideo(result) {
$bg_video_stu.html("正在载入视频 ...").css({
bottom: "0px",
});
var req = new XMLHttpRequest();
req.open("GET", result.url, true);
req.responseType = "blob";
req.onload = function () {
if (this.status === 200) {
var videoBlob = this.response;
mediaBlob = URL.createObjectURL(videoBlob);
$bg_video.attr("src", mediaBlob);
$bg_video.attr("video-name", result.title);
}
};
req.onerror = function () {
console.log("视频加载失败!");
};
req.send();
}
try {
var loadSource = function () {
function handleResult(result) {
var config;
$bg_video_stu.html("正在载入视频 ...").css({
bottom: "0px",
});
// 这里开始自定义的选项,根据不同的服务接口,处理不同的逻辑(具有较强的独特性)
switch (result.server) {
case "bilibili":
switch (result.type) {
case "mp4":
result["url"] = result["playUrl"][0]["url"];
break;
case "m4s":
throw new RuntimeException("目前暂不支持m4s格式");
default:
if (result.type.indexOf("flv") != -1) {
result.type = "flv";
}
result["segments"] = result["playUrl"];
for (var i = 0; i < result["segments"].length; i++) {
result["segments"][i]["duration"] = result["segments"][i]["length"];
}
if (mediaBlob) {
$bg_video.attr("src", mediaBlob);
return;
}
var b = "https://api.lixingyong.com/api/:server?id=:id&r=:r";
"undefined" != typeof bg_video_api && (b = bg_video_api);
var dom = $bg_video[0];
var url = dom.dataset.url;
var id = dom.dataset.id;
if (url) {
var source = {
title: dom.dataset.name || dom.dataset.title || "Video name",
url: dom.dataset.url,
};
playVideo(source);
} else if (id) {
var api = dom.dataset.api || b;
(api = api.replace(":server", dom.dataset.server)),
(api = api.replace(":id", id));
api = api.replace(":r", Math.random());
var http = new XMLHttpRequest();
(http.onreadystatechange = function () {
if (
4 === http.readyState &&
((200 <= http.status && 300 > http.status) || 304 === http.status)
) {
var source = JSON.parse(http.responseText);
playVideo(source);
config = {
rangeLoadZeroStart: true,
lazyLoadMaxDuration: 10,
lazyLoadRecoverDuration: 10,
enableStashBuffer: false
}
break;
}
break;
case "local":
// 获取后缀
var urladdr = result.url.split('?')[0].split('/');
var type = urladdr[urladdr.length - 1].split('.')[1];
if (type != "mp4" && type != "flv") {
throw new RuntimeException("无法解析的播放格式");
}
result["type"] = type;
break;
}
}),
http.open("get", api, true),
http.send();
}
};
playVideo(result, config);
}
function playVideo(result, config = {}) {
var mediaDataSource = {
type: result.type,
url: result.url || ''
};
if (result.segments) {
// 对视频进行物理分片
for (var i = 0; i < result.segments.length; i++) {
var bitRate = parseInt(Math.round((result.segments[i].size / result.segments[i].length) * 1000) / 1000);
result.segments[i].url = result.segments[i].url + "&bitRate=" + bitRate * diskTime + "&size=" + result.segments[i].size
}
mediaDataSource.segments = result.segments;
}
flvPlayer = flvjs.createPlayer(mediaDataSource, config);
flvPlayer.attachMediaElement(dom);
flvPlayer.load();
}
if (mediaBlob) {
$bg_video.attr("src", mediaBlob);
return;
}
var b = "https://api.lixingyong.com/api/:server?type=urllist&id=:id&cid=:cid&qn=:qn&vtype=:vtype&r=:r";
"undefined" != typeof bg_video_api && (b = bg_video_api);
var dom = $bg_video[0];
var url = dom.dataset.url;
var id = dom.dataset.id;
if (url) {
var source = {
title: dom.dataset.name || dom.dataset.title || "Video name",
url: dom.dataset.url,
server: "local"
};
handleResult(source);
} else if (id) {
var api = dom.dataset.api || b;
api = api
.replace(":server", dom.dataset.server || 'bilibili')
.replace(":id", id)
.replace(":cid", dom.dataset.cid || '')
.replace(":qn", dom.dataset.qn || '')
.replace(":vtype", dom.dataset.vtype || '')
.replace(":r", Math.random());
var http = new XMLHttpRequest();
(http.onreadystatechange = function () {
if (
4 === http.readyState &&
((200 <= http.status && 300 > http.status) || 304 === http.status)
) {
var source = JSON.parse(http.responseText);
source["server"] = dom.dataset.server || 'bilibili';
handleResult(source);
}
}),
http.open("get", api, true),
http.send();
}
};
} catch (e) {
Log.e("video", e.msg)
defaultStyle();
}
if (
dom != undefined &&
@ -449,7 +513,7 @@ var LIlGGAttachContext = {
$("html").css("background", "#31363b");
$(".site-content").css("background-color", "#fff");
$("body").addClass("dark");
for(var i = 0; i < comments.length; i++) {
for (var i = 0; i < comments.length; i++) {
var shadowDom = comments[i].shadowRoot.getElementById("halo-comment");
$(shadowDom).addClass("dark")
}
@ -457,7 +521,7 @@ var LIlGGAttachContext = {
$("html").css("background", "unset");
$("body").removeClass("dark");
$(".site-content").css("background-color", "rgba(255, 255, 255, .8)");
for(var i = 0; i < comments.length; i++) {
for (var i = 0; i < comments.length; i++) {
var shadowDom = comments[i].shadowRoot.getElementById("halo-comment");
$(shadowDom).removeClass("dark")
}
@ -605,35 +669,35 @@ var LIlGGAttachContext = {
},
itemSelector: ".gallery-item",
};
$masonrys.find("img.lazyload").on('load', function() {
$(this).parents(".gallery-item").css("background", "#222")
$masonrys.isotope(option);
})
// 过滤
$("#gallery-filter li a").on("click", function () {
$("#gallery-filter li a").removeClass("active");
$(this).addClass("active");
var dataFilter = $(this).data("filter");
$masonrys.isotope({
filter: dataFilter,
});
return false;
$masonrys.find("img.lazyload").on('load', function () {
$(this).parents(".gallery-item").css("background", "#222")
$masonrys.isotope(option);
})
// 过滤
$("#gallery-filter li a").on("click", function () {
$("#gallery-filter li a").removeClass("active");
$(this).addClass("active");
var dataFilter = $(this).data("filter");
$masonrys.isotope({
filter: dataFilter,
});
return false;
});
if (Poi.photosStyle == "masonry") {
// 切换风格
$("#grid-changer a").on("click", function () {
$("#grid-changer a").removeClass("active");
$(this).toggleClass("active");
for (var i = 2; i < 9; i++) {
$masonrys.find(".gallery-item").removeClass("col-" + i);
}
$masonrys.find(".gallery-item").toggleClass($(this).closest("li").attr("class"));
$masonrys.isotope(option);
});
}
if (Poi.photosStyle == "masonry") {
// 切换风格
$("#grid-changer a").on("click", function () {
$("#grid-changer a").removeClass("active");
$(this).toggleClass("active");
for (var i = 2; i < 9; i++) {
$masonrys.find(".gallery-item").removeClass("col-" + i);
}
$masonrys.find(".gallery-item").toggleClass($(this).closest("li").attr("class"));
$masonrys.isotope(option);
});
}
};
if ($masonrys.length > 0) {
@ -699,12 +763,12 @@ var LIlGGAttachContext = {
};
},
// 评论组件
CMN: function() {
CMN: function () {
// 复制一个css副本
var commentStyle = $("#comment-style").clone();
commentStyle.attr("media", "all");
var comments = $("halo-comment");
for(var i = 0; i < comments.length; i++) {
for (var i = 0; i < comments.length; i++) {
// 注入外部css
comments[i].shadowRoot.appendChild(commentStyle[0]);
}
@ -1249,9 +1313,9 @@ $(function () {
function headertop_down() {
var coverOffset = $('#content').offset().top;
$('html,body').animate({
scrollTop: coverOffset
scrollTop: coverOffset
},
600);
600);
}
var supplement = function () {
@ -1357,13 +1421,58 @@ var utils = {
document.cookie =
name + mashiro_option.cookie_version + "=; Max-Age=-99999999;";
},
/**
* 视频流关键帧搜索
* @param {*} keyframesIndex 关键帧索引
* @param {*} milliseconds
*/
getNearestKeyframe: function (keyframesIndex, milliseconds) {
var keyframeIdx = this._search(keyframesIndex.times, milliseconds);
return {
index: keyframeIdx,
milliseconds: table.times[keyframeIdx],
fileposition: table.filepositions[keyframeIdx]
};
},
/**
* 搜索方式
* @param {*} list
* @param {*} value
*/
_search: function (list, value) {
var idx = 0;
var last = list.length - 1;
var mid = 0;
var lbound = 0;
var ubound = last;
if (value < list[0]) {
idx = 0;
lbound = ubound + 1;
}
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (value >= list[mid] && value < list[mid + 1])) {
idx = mid;
break;
} else if (list[mid] < value) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
return idx;
}
};
/**
* 封装的toast组件使用纯js可以单独拿出去使用
* @author LIlGG
*/
var Toast = function Toast() {
_classCallCheck(this, Toast);
@ -1455,8 +1564,156 @@ var Toast = function Toast() {
o1[attr] = o2[attr];
}
}
};
/**
* 自定义日志
*/
var Log = function () {
return {
e: function (tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (!Log.ENABLE_ERROR) {
return;
}
if (console.error) {
console.error(str);
} else if (console.warn) {
console.warn(str);
} else {
console.log(str);
}
},
i: function (tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (!Log.ENABLE_INFO) {
return;
}
if (console.info) {
console.info(str);
} else {
console.log(str);
}
},
w: function (tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (!Log.ENABLE_WARN) {
return;
}
if (console.warn) {
console.warn(str);
} else {
console.log(str);
}
},
d: function (tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (!Log.ENABLE_DEBUG) {
return;
}
if (console.debug) {
console.debug(str);
} else {
console.log(str);
}
},
v: function (tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (!Log.ENABLE_VERBOSE) {
return;
}
console.log(str);
}
}
}();
Log.GLOBAL_TAG = 'Sakura';
Log.FORCE_GLOBAL_TAG = false;
Log.ENABLE_ERROR = true;
Log.ENABLE_INFO = true;
Log.ENABLE_WARN = true;
Log.ENABLE_DEBUG = true;
Log.ENABLE_VERBOSE = true;
/**
* 自定义异常
* @param {*} message
*/
var RuntimeException = function (message) {
this._message = message;
RuntimeException.prototype = {
get name() {
return 'RuntimeException';
},
get message() {
return this._message;
},
toString() {
return this.name + ': ' + this.message;
}
}
}
var IllegalStateException = function (message) {
IllegalStateException.prototype = new RuntimeException();
IllegalStateException.prototype = {
get name() {
return 'IllegalStateException';
}
}
}
var InvalidArgumentException = function (message) {
InvalidArgumentException.prototype = new RuntimeException();
InvalidArgumentException.prototype = {
get name() {
return 'InvalidArgumentException';
}
}
}
var NotImplementedException = function (message) {
NotImplementedException.prototype = new RuntimeException();
NotImplementedException.prototype = {
get name() {
return 'NotImplementedException';
}
}
}
function _toConsumableArray(arr) {
return (
_arrayWithoutHoles(arr) ||

2
js/app.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
<div id="video-container">
<video id="bgvideo" class="video" data-id="${settings.bgvideo_id!''}" data-server="${settings.bgvideo_server!'bilibili'}" data-url="${settings.bgvideo_url!''}" width="auto" preload="auto"></video>
<video id="bgvideo" class="video" data-id="${settings.bgvideo_id!''}" data-server="${settings.bgvideo_server!'bilibili'}" data-url="${settings.bgvideo_url!''}" width="auto" preload="auto" data-cid="${settings.bilibili_video_cid!''}" data-qn="${settings.bilibili_video_qn!''}" data-vtype="${settings.bilibili_video_vtype!''}"></video>
<div id="video-btn" class="loadvideo videolive"></div>
<div id="video-add"></div>
<div class="video-stu"></div>

2847
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,23 @@
"build-autoprefix": "postcss --use autoprefixer --map false --output styles/style.css styles/style.css",
"build-cleancss": "cleancss -o styles/style.min.css styles/style.css",
"build-clean": "rimraf css",
"build-app-js": "uglifyjs js/app.js -o js/app.min.js",
"build-app-js": "babel js/app.js -o js/app.min.js && uglifyjs js/app.min.js -o js/app.min.js",
"build-js": "npm run build-app-js",
"deploy": "npm run build"
},
"browserify": {
"transform": [
[
"babelify",
{
"presets": [
"es2015"
],
"sourceMap": true
}
]
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/LIlGG/halo-theme-sakura.git"
@ -23,10 +36,15 @@
},
"homepage": "https://github.com/LIlGG/halo-theme-sakura#readme",
"devDependencies": {
"@babel/cli": "^7.12.1",
"@babel/core": "^7.12.3",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.12.1",
"autoprefixer": "^9.7.4",
"clean-css-cli": "^4.3.0",
"postcss-cli": "^7.1.0",
"rimraf": "^3.0.2",
"uglify-js": "^3.8.0"
}
},
"dependencies": {}
}

View File

@ -215,7 +215,7 @@ mainScreen:
label: 关闭
bgvideo_url:
name: bgvideo_url
label: 视频链接(优先选择
label: 视频链接(优先播放视频链接
type: attachment
bgvideo_server:
name: bgvideo_server
@ -230,6 +230,47 @@ mainScreen:
label: 视频id
type: text
description: 'B站视频可选择AV/BV号目前只支持B站视频'
bilibili_video_cid:
name: bilibili_video_cid
label: B站视频分P id
type: text
description: '分P ID不设置则默认为 1P'
bilibili_video_qn:
name: bilibili_video_qn
label: B站视频清晰度
type: select
data-type: long
default: 6
options:
- value: 6
label: 极速
- value: 16
label: 流畅
- value: 32
label: 清晰
- value: 64
label: 720P
- value: 74
label: 720P60
- value: 80
label: 高清
- value: 112
label: 1080P+
- value: 116
label: 1080P60
description: '这里展示的是除了4K之外的所有清晰度并不代表目标视频的清晰度请确定好目标视频所支持的清晰度否则将报错'
bilibili_video_vtype:
name: bilibili_video_vtype
label: B站视频格式
type: select
data-type: long
default: 0
options:
- value: 0
label: FLV
- value: 1
label: MP4
description: 'MP4只有流畅或清晰'
sns:
label: 社交网络
items:

7
source/js/flv.min.js vendored Normal file

File diff suppressed because one or more lines are too long