Widget:BilibiliVideo:修订间差异
跳到导航
跳到搜索
(fix) |
(BilibiliVideo出问题了,推倒旧H萌娘的更改,直接使用萌百模板看看行不行) 标签:已被回退 |
||
| 第48行: | 第48行: | ||
padding-left: 1em; | padding-left: 1em; | ||
background-image: url(/skins/Vector/images/search-ltr.png?39f97); | background-image: url(/skins/Vector/images/search-ltr.png?39f97); | ||
background-image: linear-gradient(transparent,transparent), url(data:image/svg+xml, | background-image: linear-gradient(transparent,transparent),url(data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2212%22%20height=%2213%22%3E%20%3Cg%20fill=%22none%22%20stroke=%22%2354595d%22%20stroke-width=%222%22%3E%20%3Cpath%20d=%22M11.29%2011.71l-4-4%22/%3E%20%3Ccircle%20cx=%225%22%20cy=%225%22%20r=%224%22/%3E%20%3C/g%3E%20%3C/svg%3E); | ||
background-position: left center; | background-position: left center; | ||
background-repeat: no-repeat; | background-repeat: no-repeat; | ||
| 第104行: | 第102行: | ||
display: none; | display: none; | ||
background: white; | background: white; | ||
padding: | padding: 0 0.5em; | ||
} | |||
html > body > .navpopup .bilibili-video-container > * { | |||
display: none !important; | |||
} | |||
html > body > .navpopup .bilibili-video-container::before { | |||
content: "请不要在注释里使用 BilibiliVideo 模板"; | |||
font-style: italic; | |||
} | } | ||
</style><script> | </style><script> | ||
"use strict"; | "use strict"; | ||
window. | window.RLQ.push(async () => { | ||
const errMsg = { | const errMsg = { | ||
id: '此处填写的id有误,请参考<a href=" | id: '此处填写的id有误,请参考<a href="/Template:BilibiliVideo#firstHeading" target="_blank">模板文档</a>修正……', | ||
error: "执行出现问题,请复制以下内容并在提问求助区处粘贴寻求帮助:$$$", | error: "执行出现问题,请复制以下内容并在提问求助区处粘贴寻求帮助:$$$", | ||
attr: '下方填写的参数 $$$ 有误,请参考<a href=" | attr: '下方填写的参数 $$$ 有误,请参考<a href="/Template:BilibiliVideo#firstHeading" target="_blank">模板文档</a>修正……', | ||
}; | }; | ||
await $.ready; | |||
const ifNamespaceAllow = [0, 4, 10, 12].includes(mw.config.get("wgNamespaceNumber")); | |||
const sanNode = $(`<${"span/"}>`); | const sanNode = $(`<${"span/"}>`); | ||
const genErr = (type, msg = "") => type | const genErr = (type, msg = "") => Reflect.has(errMsg, type) ? `<${"div"} style="font-style: italic; border: 1px dashed red;">BilibiliVideo模板:${errMsg[type].replace("$$$", sanNode.text(msg).html())}<${"/div"}>` : ""; | ||
const injectErrMsgBefore = ($ele, type, msg = "") => $ele.before(genErr(type, msg)); | const injectErrMsgBefore = ($ele, type, msg = "") => $ele.before(genErr(type, msg)); | ||
const getErrorType = (code) => { | |||
switch (code) { | |||
case 62003: { | |||
return false; | |||
} | |||
case -403: { | |||
return "forbidden"; | |||
} | |||
default: { | |||
return "failed"; | |||
} | |||
} | |||
}; | |||
/** | |||
* @param { string } id | |||
*/ | |||
const idCorrector = (id) => { | |||
let type = false; | |||
// 1. 有明确的 av 前缀且符合 aid 规则 | |||
if (/^av[1-9]\d*$/i.test(id)) { | |||
type = "av"; | |||
} | |||
// 2. 有明确的 bv 前缀且符合 bvid 规则 | |||
else if (/^[bB][vV]1[FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf]{9}$/.test(id)) { | |||
type = "bv"; | |||
} | |||
// 3. 没有明确的前缀,但符合 aid 规则 | |||
else if (/^[1-9]\d*$/.test(id)) { | |||
type = "av"; | |||
} | |||
// 4. 没有明确的前缀,但符合 bvid 规则且不符合 aid 规则 | |||
else if (/^1?[FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf]{9}$/.test(id)) { | |||
type = "bv"; | |||
} | |||
// 5. 无法判断 | |||
if (type === "av") { | |||
return { | |||
id: id.replace(/^av/i, ""), | |||
prefix: { | |||
href: "av", | |||
iframe: "aid", | |||
}, | |||
}; | |||
} | |||
if (type === "bv") { | |||
return { | |||
id: id.replace(/^.*([FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf]{9})$/i, "1$1"), | |||
prefix: { | |||
href: "BV", | |||
iframe: "bvid", | |||
}, | |||
}; | |||
} | |||
return false; | |||
}; | |||
try { | try { | ||
const isNaN = Number.isNaN || window.isNaN; | const isNaN = Number.isNaN || window.isNaN; | ||
| 第144行: | 第207行: | ||
const fixedNumber = (number) => `${+number < 10 ? "0" : ""}${number}`; | const fixedNumber = (number) => `${+number < 10 ? "0" : ""}${number}`; | ||
const secondsParser = (seconds) => `${Math.floor(+seconds / 60)}:${fixedNumber(+seconds % 60)}`; | const secondsParser = (seconds) => `${Math.floor(+seconds / 60)}:${fixedNumber(+seconds % 60)}`; | ||
const | const submit = (ids) => { | ||
if ( | if (!ifNamespaceAllow) { | ||
return; | |||
} | |||
if (--ids.pending > 0) { | |||
return; | |||
} | |||
let changed = false; | |||
if (ids.failed.aid.size + ids.failed.bvid.size > 0 && !mw.config.get("wgCategories").includes("带有失效视频的条目")) { | |||
} | changed = true; | ||
} | |||
if (ids.forbidden.aid.size + ids.forbidden.bvid.size > 0 && !mw.config.get("wgCategories").includes(" 带有受限视频的条目")) { | |||
changed = true; | |||
} | |||
if (ids.failed.aid.size + ids.failed.bvid.size === 0 && mw.config.get("wgCategories").includes(" 带有失效视频的条目")) { | |||
changed = true; | |||
} | |||
if (ids.forbidden.aid.size + ids.forbidden.bvid.size === 0 && mw.config.get("wgCategories").includes("带有受限视频的条目")) { | |||
changed = true; | |||
} | |||
if (changed) { | |||
const url = new URL("https://moegirlpedia.annangela.cn/bilibiliCollector/videoCheck"); | |||
url.searchParams.set("pageid", mw.config.get("wgArticleId")); | |||
$.get(`${url}`); | |||
} | |||
}; | |||
const global_element = $("#mw-content-text"); | |||
const placeholderToggle = (iframe) => { | |||
if (iframe.data("displayFlag")) { | |||
iframe.data("displayFlag", false); | |||
iframe.data("placeholder").fadeOut(370); | |||
} | } | ||
}; | }; | ||
if ( | let lazyLoadObserver; | ||
$(".bilibili- | if (Reflect.has(window, "IntersectionObserver") && | ||
const | Reflect.has(window, "IntersectionObserverEntry") && | ||
Reflect.has(window.IntersectionObserverEntry.prototype, "intersectionRatio") && | |||
Reflect.has(window.IntersectionObserverEntry.prototype, "isIntersecting")) { | |||
const _id = dataset.id | lazyLoadObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | |||
if (entry.isIntersecting) { | |||
entry.target.src = entry.target.dataset.src; | |||
setTimeout(() => { | |||
placeholderToggle($(entry.target)); | |||
let page = parseInt(dataset.page); | }, 13070); | ||
lazyLoadObserver.unobserve(entry.target); | |||
} | |||
}); | |||
}); | |||
} else { | |||
lazyLoadObserver = { | |||
observe: (target) => { | |||
target.src = target.dataset.src; | |||
setTimeout(() => { | |||
placeholderToggle($(target)); | |||
}, 13070); | |||
}, | |||
}; | |||
} | |||
const iframe_href_base = "https://www.bilibili.com/blackboard/newplayer.html?playlist=true&playlist_order=sequential&musth5=1&noEndPanel=1&crossDomain=1&autoplay=0&"; | |||
const EPSILON = 2.220446049250313e-16, | |||
rememberWH = (ele) => { | |||
ele.data({ width: ele.width(), height: ele.height() }); | |||
}, | |||
setTureHeight = (ele) => { | |||
const barHeight = ele.data("height") - ele.data("width") * 9 / 16; //计算标题和播放器控制栏高度 | |||
ele.height(ele.width() * 9 / 16 + barHeight); | |||
}, | |||
setWH = (ele) => { | |||
ele.css({ width: "100%", height: "100%" }); | |||
}, | |||
recallWH = (ele) => { | |||
ele.width(ele.data("width")).height(ele.data("height")); | |||
}, | |||
setMaxHeight = (container, target) => { | |||
const h = container.outerHeight(true); | |||
let t = 0; | |||
container.children().each((_, ele) => { | |||
t += $(ele).outerHeight(true); | |||
}); | |||
target.css("max-height", `calc(100% - ${parseInt(t - h + 2 - (Number.EPSILON || EPSILON))}px)`); | |||
}; | |||
const run = () => { | |||
const ids = { | |||
failed: { | |||
aid: new Set(), | |||
bvid: new Set(), | |||
}, | |||
forbidden: { | |||
aid: new Set(), | |||
bvid: new Set(), | |||
}, | |||
pending: 0, | |||
}; | |||
const targets = $(".bilibili-video-container:not(.exec)"); | |||
ids.pending = targets.length; | |||
targets.addClass("exec").each((_, ele) => { | |||
const { dataset } = ele; | |||
const _id = dataset.id; | |||
const selfbox = $(ele); | |||
const toggleButton = selfbox.find(".bilibili-toggle"); | |||
const widescreenButton = selfbox.find(".bilibili-widescreen"); | |||
const validation = idCorrector(_id); | |||
if (!validation) { | |||
ele.outerHTML = genErr("id"); | |||
return; | |||
} | |||
const { id, prefix } = validation; | |||
let page = parseInt(+(dataset.page || 1)); | |||
if (isNaN(page) || page < 1) { | if (isNaN(page) || page < 1) { | ||
page = 1; | page = 1; | ||
if (typeof dataset.page === "string" && dataset.page !== "") { | if (typeof dataset.page === "string" && dataset.page !== "") { | ||
injectErrMsgBefore( | injectErrMsgBefore(selfbox, "attr", "page"); | ||
} | } | ||
} | } | ||
const { pagename, title } = dataset; | |||
injectErrMsgBefore( | const height = cssLengthUnitValidator(dataset.height, "441px", (isValidated) => isValidated || selfbox.removeAttr("data-height"), "height", selfbox); | ||
const width = cssLengthUnitValidator(dataset.width, "665px", (isValidated) => isValidated || selfbox.removeAttr("data-width"), "width", selfbox); | |||
const maxHeight = cssLengthUnitValidator(dataset.maxHeight, "100vh", (isValidated) => isValidated || selfbox.removeAttr("data-max-height"), "maxHeight", selfbox); | |||
const maxWidth = cssLengthUnitValidator(dataset.maxWidth, "100%", (isValidated) => isValidated || selfbox.removeAttr("data-max-width"), "maxWidth", selfbox); | |||
const subtitle = !!(dataset.subtitle === "true"); | |||
const t = parseInt(dataset.t); | |||
const tIsInvalid = isNaN(t) || t <= 0; | |||
const iframeContainer = selfbox.find(".bilibili-iframe-container"); | |||
const title_text = $("<a/>").attr("rel", "nofollow noreferrer noopener").addClass("external text").attr({ | |||
href: `https://www.bilibili.com/video/${prefix.href}${id}?p=${page}${tIsInvalid ? "" : `&t=${t}`}`, | |||
target: "_blank", | |||
}).prependTo(selfbox.find(".bilibili-title")); | |||
const iframe = $("<iframe/>").attr({ | |||
allow: "fullscreen", | |||
allowfullscreen: true, | |||
frameborder: 0, | |||
scrolling: "no", | |||
src: "", | |||
"class": "bilibili-iframe", | |||
}).css({ | |||
width: width, | |||
height: height, | |||
"max-width": maxWidth, | |||
"max-height": maxHeight, | |||
}); | |||
if (!tIsInvalid) { | |||
selfbox.removeAttr("data-auto-expand"); | |||
} else if (typeof dataset.t === "string" && dataset.t !== "") { | |||
injectErrMsgBefore(selfbox, "attr", "t"); | |||
} | } | ||
const | const time = secondsParser(t); | ||
title_text.text(`${(title || prefix.href + id) + ([0, 1].includes(page) ? "" : ` (P${page})`) + (tIsInvalid ? "" : `[视频从${time}开始播放]`)}【视频信息加载中……】`); | |||
iframeContainer.css({ | |||
width: width, | |||
height: height, | |||
"max-width": maxWidth, | |||
"max-height": maxHeight, | |||
}); | |||
iframe.appendTo(iframeContainer); | |||
} | const div = $("<div/>"); | ||
div.css({ | |||
position: "absolute", | |||
top: "0", | |||
left: "0", | |||
bottom: "0", | |||
right: "0", | |||
"z-index": "99", | |||
display: "flex", | |||
"align-items": "center", | |||
background: "rgba(255, 255, 255, .37)", | |||
}); | |||
const text = $("<div/>"); | |||
text.css({ | |||
"text-align": "center", | |||
width: "100%", | |||
}).text("正在加载中,若长时间空白则说明是网络问题……"); | |||
div.append(text).appendTo(iframeContainer); | |||
iframe.data({ | |||
placeholder: div, | |||
displayFlag: true, | |||
}); | |||
iframe[0].addEventListener("load", () => { | |||
placeholderToggle(iframe); | |||
}); | |||
$.ajax({ | $.ajax({ | ||
url: `https://api.bilibili.com/x/web-interface/view?${prefix.iframe}=${id}&jsonp=jsonp`, | url: `https://api.bilibili.com/x/web-interface/view?${prefix.iframe}=${id}&jsonp=jsonp`, | ||
| 第202行: | 第392行: | ||
dataType: "jsonp", | dataType: "jsonp", | ||
timeout: 10000, | timeout: 10000, | ||
success: | success: ({ code, message, data }) => { | ||
if (code !== 0) { | if (code !== 0) { | ||
title_text.text((title || prefix.href + id) + ([0, 1].includes(page) ? "" : ` (P${page})`) + (tIsInvalid ? "" : `[视频从${time}开始播放]`)); | |||
iframe.attr("data-src", `${iframe_href_base + prefix.iframe}=${id}&page=${page}${tIsInvalid ? "" : `&t=${t}`}`); | |||
lazyLoadObserver.observe(iframe[0]); | |||
console.info("Widget:BilibiliVideo", `${prefix.href}${id}`, code, message); | console.info("Widget:BilibiliVideo", `${prefix.href}${id}`, code, message); | ||
const errorType = getErrorType(code); | |||
if (errorType) { | |||
ids[errorType][prefix.iframe].add(id); | |||
} | |||
return; | return; | ||
} | } | ||
const list = data.pages; | const list = data.pages; | ||
let _page = 1; | let _page = 1; | ||
const name = title || (data.title | const name = title || (data.title || prefix.href + id); | ||
let index; | let index; | ||
let length; | let length; | ||
| 第221行: | 第417行: | ||
} else { _page = page; } | } else { _page = page; } | ||
index = _page - 1; | index = _page - 1; | ||
const | const href = title_text.attr("href"); | ||
if (list[index] !== undefined && list[index].cid !== undefined) { | |||
iframe.attr("data-src", `${iframe_href_base}${prefix.iframe}=${id}&cid=${list[index].cid}&page=${_page}${tIsInvalid ? "" : `&t=${t}`}`); | |||
title_text.attr("href", href.replace(new RegExp(`\\?p=${page}`, "g"), `?p=${_page}`)); | |||
title_text.text(`${name} [${_page}/${list.length}]${tIsInvalid ? "" : `[ 视频从${time} 开始播放]`}`); | |||
if (subtitle) { title_text.append(`<br>(${_page}、${list[index].part})`); } | |||
} else { | |||
title_text.text(name + (tIsInvalid ? "" : `[视频从${time}开始播放]`)); | |||
iframe.attr("data-src", `${iframe_href_base + prefix.iframe}=${id}&page=${_page}${tIsInvalid ? "" : `&t=${t}`}`); | |||
} | } | ||
lazyLoadObserver.observe(iframe[0]); | |||
}, | }, | ||
error: | error: () => { | ||
title_text.text((title || prefix.href + id) + ([0, 1].includes(page) ? "" : ` (P${page})`) + (tIsInvalid ? "" : `[视频从${time}开始播放]`)); | |||
iframe.attr("data-src", `${iframe_href_base + prefix.iframe}=${id}&page=${page}${tIsInvalid ? "" : `&t=${t}`}`); | |||
lazyLoadObserver.observe(iframe[0]); | |||
}, | }, | ||
complete: () => { | |||
if (selfbox.is('[data-auto-expand="true"]')) { | |||
selfbox.addClass("onshow"); | |||
iframeContainer.show(); | |||
toggleButton.text(" 隐藏视频"); | |||
selfbox.removeAttr("style"); | |||
} | } | ||
} | submit(ids); | ||
}, | |||
}); | }); | ||
//toggle | |||
toggleButton.on("click", () => { | |||
selfbox.width(iframeContainer.outerWidth(true)); | |||
selfbox.toggleClass("onshow"); | |||
iframeContainer.toggle(); | |||
if (toggleButton.text() === " 显示视频") { | |||
toggleButton.text(" 隐藏视频"); | |||
$(window).resize(); | |||
} else { | } else { | ||
toggleButton.text(" 显示视频"); | |||
selfbox.removeAttr("style"); | |||
} | } | ||
}); | |||
if ( | widescreenButton.on("click", () => { | ||
if (selfbox.is(":not(.onshow)")) { return; } | |||
if (selfbox.is(".widescreen")) { | |||
selfbox.removeClass("widescreen"); | |||
widescreenButton.text("显示宽屏"); | |||
recallWH(iframeContainer); | |||
recallWH(iframe); | |||
recallWH(selfbox); | |||
} else { | |||
selfbox.addClass("widescreen"); | |||
widescreenButton.text(" 退出宽屏"); | |||
rememberWH(selfbox); | |||
selfbox.css("width", selfbox.parent().width() > Math.min(911, global_element.width()) ? "73%" : "100%"); //可以看见按钮的最小宽度 665 的 1/0.73 倍 | |||
setTureHeight(selfbox); | |||
rememberWH(iframe); | |||
rememberWH(iframeContainer); | |||
setWH(iframe); | |||
setWH(iframeContainer); | |||
iframeContainer.height(selfbox.height() - title_text.parent().height()); | |||
setMaxHeight(selfbox, iframeContainer); | |||
} | } | ||
}); | }); | ||
}); | }); | ||
} | }; | ||
$(run); | |||
mw.hook("wikipage.content").add(run); | |||
$(window).on("load", run); | |||
$(window).on("resize", () => { | |||
$(".bilibili-video-container.onshow.widescreen").each((_, ele) => { | |||
const selfbox = $(ele); | |||
selfbox.css("width", selfbox.parent().width() > Math.min(911, global_element.width()) ? "73%" : "100%"); | |||
setTureHeight(selfbox); | |||
setMaxHeight(selfbox, selfbox.find(".bilibili-iframe-container")); | |||
}); | |||
}); | |||
} catch (e) { | } catch (e) { | ||
/* eslint-disable */ | /* eslint-disable */ | ||
2026年6月20日 (六) 17:23的版本
| 名称: | Bilibili视频插件 |
| 作者: | 加大号的猫 |
| 修订: | Boxsnake |
| 重修订: | AnnAngela |
| H5版再修订: | |
| 新H5版又修订: | |
| 移动版支持: | XYZ指示物 |
| 版权协定: | MIT |
| 发布日期: | 2012年6月29日第一版发布; |
| 发布地址: | https://zh.moegirl.org.cn/Widget:BilibiliVideo && https://zh.moegirl.org.cn/Template:BilibiliVideo |
| 注意事项: | 如有问题,请联系作者。 |
本Widget不能单独使用,请使用{{BilibiliVideo}}!