本WIKI由spt赛小息6于2020年05月27日担任站长并维护更新,编辑权限开放,如遇Chrome浏览器登陆后无法编辑点这里    BWIKI反馈留言板
目前WIKI是开放编辑权限,任何人都可以在遵守《BWIKI社区规则》和《赛尔号星球大战WIKI社区规则》的前提下进行内容制作,违规者将会被封禁。

全站通知:

B站视频Widget

阅读

    

2024-04-17更新

    

最新编辑:spt赛小息6

阅读:

  

更新日期:2024-04-17

  

最新编辑:spt赛小息6

来自赛尔号星球大战WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

此模板为一键安装导入,不建议手动修改。

简单说明

版本:1.2

说明:本工具可以将B站视频快速插入至页面

安装方法

  • 自动安装:拥有站点管理员权限的同学可以在本页面使用一键安装功能
  • 手动安装:复制以下依赖页面中的内容到相同页面名称的页面中

使用说明

参数

  • w:播放器的宽度,如:16px20%
  • h:播放器的高度,如:16px20%
  • bv:BV号,如:BV1sv4y1f7Q2
  • P:视频分P编号,如:1
  • Pname:视频分P的名称(非必填,moshi值为2时是必填项),如:【非常好玩具】
  • moshi:为调用模式(非必填),该值可不添加,值默认为1。
    • moshi的值为“1”:调用普通的播放器
    • moshi的值为“2”:需填入Pname的名称,即可成功调取对应分P的视频

示例

{{#Widget:Biliplayer|bv=BV1sv4y1f7Q2|P=1|w=763px|h=419px}} 

{{#Widget:Biliplayer|moshi=2|bv=BV1bV411u7kS|Pname=【非常好玩具】|w=763px|h=419px}} 

作者

星空指引の方向


依赖页面

<script> window.activity = { id: 11610, spmId: '888.211' }; window.reportMsgObj = {}; window.reportConfig = { sample: 1, extMsgs: { appBuvid: (navigator.userAgent.match(/Buvid\/([a-zA-Z0-9]+)/) || [])[1] || '' }, scrollTracker: true, msgObjects: 'reportMsgObj', pvMsg: {}, errorTracker: true }; window.SEARCH_PARAM = { autoplay: false, poster: true, muted: false }; try { document.domain = 'bilibili.com'; } catch (e) {} </script> <script src="https://s1.hdslb.com/bfs/seed/jinkela/short/player/player-util.js"></script> <script src="https://s1.hdslb.com/bfs/static/player/main/core.9ba60fbf.js"></script> <div class="BiliPlayer"></div> <script> /* ===== 设备判断 ===== */ const ua = navigator.userAgent.toLowerCase(); let shebei = ''; if (/android|adr/gi.test(ua)) { shebei = 'android'; } else if (/\(i[^;]+;( U;)? CPU.+Mac OS X/gi.test(ua) || /iPad/gi.test(ua)) { shebei = 'ios'; } else if (/windows nt/.test(ua)) { shebei = 'pc'; } /* ===== 结束卡数据注入(只改DOM,不改播放器UI)===== */ const _fetch = url => fetch(url).then(r => r.json()); /** 获取UP主信息 */ function _getUpInfo(mid) { return _fetch(`https://api.bilibili.com/x/web-interface/card?mid=${mid}&photo=true`) .then(res => { if (res.code !== 0 || !res.data || !res.data.card) return null; const c = res.data.card; return { mid: c.mid, name: c.uname, face: c.face, fans: c.fans }; }).catch(() => null); } /** 监听播放器容器,ending card出现时注入UP主头像和链接 */ function _watchEndingCard(container, vd, upInfo) { if (!container || !vd) return; const faceUrl = upInfo?.face || vd.owner?.face || ''; const spaceUrl = upInfo  ? `//space.bilibili.com/${upInfo.mid}/`  : `//space.bilibili.com/${vd.owner?.mid || 0}/`; const upName = upInfo?.name || vd.owner?.name || ''; const upMid = upInfo?.mid || vd.owner?.mid || 0; const inject = () => { const avatarWrap = container.querySelector('.bpx-player-ending-functions-avatar[data-action="upinfo"]'); if (!avatarWrap) return; const img = avatarWrap.querySelector('img'); if (img && faceUrl) { img.src = faceUrl; img.alt = upName; } const link = avatarWrap.querySelector('a'); if (link) { link.href = spaceUrl; link.setAttribute('data-mid', String(upMid)); } }; inject(); new MutationObserver(inject).observe(container, { childList: true, subtree: true }); } /* ===== BiliPlayer ===== */ class BiliPlayer { /* 类级计数器:第 0、1、2 … 次调用 */ static _callCount = 0; /* 私有字段 */ #playerInst = null; // nano 实例(moshi=1/2 PC端) #container = null; // 播放器/根容器 #scrollWrap = null; // moshi=1/2 旧用法的滚动包装容器 #prefix = null; // multi 模式的 CSS 前缀(如 b3/b4/b5) /** * 支持四种调用方式: * 1) 旧用法(moshi=1/2):new BiliPlayer('BV1xx', page, opts) * 2) 指定容器(moshi=1/2):new BiliPlayer(containerDOM, 'BV1xx', page, opts) * 3) multi 模式(moshi=3/4/5):new BiliPlayer(containerDOM, opts) * opts.multiMode = 'allP' — 全分P列表(moshi=3) * opts.multiMode = 'customP' — 自定义分P列表(moshi=4) * opts.multiMode = 'multiBV' — 多BV列表(moshi=5) */ constructor(arg1, arg2, arg3) { /* ---------- 参数分派 ---------- */ const isEl = v => v instanceof HTMLElement; const isStr = v => typeof v === 'string'; const isObj = v => v !== null && typeof v === 'object' && !isEl(v); /* multi 模式:(container, opts) */ if ((isEl(arg1) || isStr(arg1)) && isObj(arg2) && arg2.multiMode) { const container = isEl(arg1) ? arg1 : document.querySelector(arg1); this.#container = container; this.#initMulti(arg2); return; } /* 旧用法 / 指定容器(moshi=1/2) */ let container, bvid, page, opts; if (isStr(arg1) && /^BV/.test(arg1)) { /* 旧用法:(bvid, page, opts) */ [bvid, page, opts] = [arg1, arg2 || 1, arg3 || {}]; container = null; } else { /* 指定容器:(container, bvid, page, opts) */ container = isEl(arg1) ? arg1 : document.querySelector(arg1); [bvid, page, opts] = [arg2, arg3 || 1, arguments[3] || {}]; } /* 1. 创建或复用容器 */ if (!container) { this.#scrollWrap = document.createElement('div'); this.#scrollWrap.className = 'b3-scroll-wrap'; if (opts.scrollContent) { this.#scrollWrap.innerHTML = opts.scrollContent; } container = document.createElement('div'); container.className = 'biliplayerW-dom'; container.id = `d${BiliPlayer._callCount}`; this.#scrollWrap.appendChild(container); const anchors = document.getElementsByClassName('player-anchor'); const targetAnchor = anchors[BiliPlayer._callCount]; (targetAnchor || document.body).appendChild(this.#scrollWrap); BiliPlayer._callCount++; } this.#container = container; /* 2. 提取配置 */ const { width = '100%', height = '400px', autoplay = false, muted = false, ...otherOpts } = opts; /* 3. 移动端 / PC 端区分渲染 */ if (shebei === 'android' || shebei === 'ios') { this.#container.style.cssText = `width:${width};height:${height};max-width:100%;`; this.#container.innerHTML = `<iframe name="video-frame" src="https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=${encodeURIComponent(bvid)}&p=${encodeURIComponent(page)}${autoplay ? '&autoplay' : ''}" frameborder="no" scrolling="no" allowfullscreen style="width:100%;height:100%;"></iframe>`; this.ready = Promise.resolve(this); this.#playerInst = null; } else { if (!window.nano) { throw new Error('未检测到 nano 播放器 SDK'); } this.#container.style.cssText = `width:${width};height:${height};max-width:100%;`; this.#playerInst = window.nano.createPlayer({ element: this.#container, bvid, p: parseInt(page, 10), autoplay, muted, poster: true, stats: { bSource: 'widget_demo' }, ...otherOpts }); /* 先拉视频信息获取owner数据,再监听ending card注入 */ _fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) .then(async res => { if (res.code !== 0 || !res.data) return; const vd = res.data; const upInfo = await _getUpInfo(vd.owner.mid); /* 播放器connect后注入UP主数据到ending card */ this.#playerInst.connect().then(() => { console.log(`播放器 ${this.#container.id} 加载成功`); _watchEndingCard(this.#container, vd, upInfo); }).catch(err => console.error(`播放器 ${this.#container.id} 连接失败`, err)); }).catch(err => console.error(`播放器 ${this.#container.id} 获取视频信息失败`, err)); this.ready = this.#playerInst.connect().then(() => this).catch(err => { console.error(`播放器 ${this.#container.id} 加载失败`, err); throw err; }); } } /* ------------------------------------------------------------------ */ /* multi 模式初始化(moshi=3/4/5 公共入口) */ /* ------------------------------------------------------------------ */ #initMulti(opts) { const { multiMode, // 'allP' | 'customP' | 'multiBV' bvid = '', // moshi=3/4 用 pages = [], // moshi=4:指定的分P编号数组 bvArr = [], // moshi=5:多BV数组 width = '100%', height = '400px', } = opts; /* 生成唯一 uid 避免多实例样式冲突 */ const uid = 'bp' + Date.now() + Math.random().toString(36).substr(2, 5); this.#prefix = uid; const wrap = this.#container; wrap.id = uid; /* 注入样式 */ const style = document.createElement('style'); style.textContent = ` #${uid} .bpm-player{ width:${width};height:${height};max-width:100%;margin-bottom:10px; } #${uid} .bpm-scroll-wrap{ overflow-x:auto;white-space:nowrap;-webkit-overflow-scrolling:touch; } #${uid} .bpm-pagelist{ display:inline-flex;gap:8px;padding:0 2px; } #${uid} .bpm-part{ flex:0 0 auto;padding:6px 12px;border:1px solid #e5e5e5; border-radius:4px;cursor:pointer;background:#fff;color:#222; transition:all .2s;user-select:none; } #${uid} .bpm-part:hover{ border-color:#999 !important; } #${uid} .bpm-part.active{ background:#00aeec !important;color:#fff !important;border-color:#00aeec !important; } `; wrap.appendChild(style); /* 注入结构 */ const playerDiv = document.createElement('div'); playerDiv.className = 'bpm-player'; const scrollWrap = document.createElement('div'); scrollWrap.className = 'bpm-scroll-wrap'; const listDiv = document.createElement('div'); listDiv.className = 'bpm-pagelist'; scrollWrap.appendChild(listDiv); wrap.appendChild(playerDiv); wrap.appendChild(scrollWrap); /* 当前 multi 区域内活跃的 nano 实例(PC端切换时需要先销毁) */ let activeNano = null; /* 点击切换监听 */ listDiv.addEventListener('click', e => { const btn = e.target.closest('.bpm-part'); if (!btn) return; listDiv.querySelectorAll('.bpm-part').forEach(b => b.classList.remove('active')); btn.classList.add('active'); if (multiMode === 'multiBV') { activeNano = this.#loadVideo(playerDiv, btn.dataset.bv, 1, width, height, activeNano); } else { activeNano = this.#loadVideo(playerDiv, bvid, parseInt(btn.dataset.p, 10), width, height, activeNano); } }); /* 根据模式执行不同的数据获取,并将 activeNano 回写 */ const onFirstLoad = (inst) => { activeNano = inst; }; if (multiMode === 'allP') { this.#fetchAndRenderAllP(bvid, playerDiv, listDiv, width, height, onFirstLoad); } else if (multiMode === 'customP') { this.#fetchAndRenderCustomP(bvid, pages, playerDiv, listDiv, width, height, onFirstLoad); } else if (multiMode === 'multiBV') { this.#fetchAndRenderMultiBV(bvArr, playerDiv, listDiv, width, height, onFirstLoad); } this.ready = Promise.resolve(this); } /** * 加载播放器(PC 端用 nano,移动端用 iframe) * @param {HTMLElement} playerEl 播放器挂载容器 * @param {string} bvid * @param {number} page * @param {string} width * @param {string} height * @param {object|null} prevNano 上一个 nano 实例,切换前销毁 * @returns {object|null} 新的 nano 实例(移动端返回 null) */ #loadVideo(playerEl, bvid, page, width, height, prevNano) { /* 先销毁旧的 nano 实例 */ if (prevNano && prevNano.destroy) { prevNano.destroy(); } /* 清空容器 */ playerEl.innerHTML = ''; playerEl.style.cssText = `width:${width};height:${height};max-width:100%;`; if (shebei === 'android' || shebei === 'ios') { /* 移动端:iframe */ const url = `https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=${encodeURIComponent(bvid)}&p=${encodeURIComponent(page)}&autoplay`; playerEl.innerHTML = `<iframe name="video-frame" src="${url}" frameborder="no" scrolling="no" allowfullscreen style="width:100%;height:100%;"></iframe>`; return null; } else { /* PC 端:nano */ if (!window.nano) { playerEl.innerHTML = '<div style="display:flex;height:100%;align-items:center;justify-content:center;color:#f66;">未检测到 nano 播放器 SDK</div>'; return null; } const nanoInst = window.nano.createPlayer({ element : playerEl, bvid, p  : parseInt(page, 10), autoplay: false, muted  : false, poster  : true, stats  : { bSource: 'widget_demo' } }); /* 异步拉取视频信息获取owner数据,用于ending card注入 */ _fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) .then(async res => { if (res.code !== 0 || !res.data) return; const vd = res.data; const upInfo = await _getUpInfo(vd.owner.mid); nanoInst.connect() .then(() => { console.log(`[BiliPlayer multi] ${bvid} P${page} 加载成功`); _watchEndingCard(playerEl, vd, upInfo); }) .catch(err => console.error(`[BiliPlayer multi] ${bvid} P${page} 加载失败`, err)); }) .catch(() => {}); return nanoInst; } } /* moshi=3:全分P列表 */ #fetchAndRenderAllP(bvid, playerEl, listEl, width, height, onFirstLoad) { fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) .then(r => r.json()) .then(res => { if (res.code !== 0 || !res.data || !res.data.pages) { playerEl.innerHTML = '<div style="display:flex;height:100%;align-items:center;justify-content:center;color:#f66;">获取视频信息失败</div>'; return; } res.data.pages.forEach(p => { const btn = document.createElement('div'); btn.className = 'bpm-part'; btn.textContent = `P${p.page} · ${p.part}`; btn.dataset.p = p.page; listEl.appendChild(btn); }); /* 默认播放 P1 */ const inst = this.#loadVideo(playerEl, bvid, 1, width, height, null); onFirstLoad(inst); listEl.querySelector('.bpm-part')?.classList.add('active'); }) .catch(() => { playerEl.innerHTML = '<div style="display:flex;height:100%;align-items:center;justify-content:center;color:#f66;">网络异常,无法获取分P信息</div>'; }); } /* moshi=4:自定义分P列表 */ #fetchAndRenderCustomP(bvid, pageArr, playerEl, listEl, width, height, onFirstLoad) { fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) .then(r => r.json()) .then(res => { if (res.code !== 0 || !res.data || !res.data.pages) { playerEl.innerHTML = '<div style="display:flex;height:100%;align-items:center;justify-content:center;color:#f66;">获取视频信息失败</div>'; return; } const allPages = res.data.pages; pageArr.forEach(pNum => { const pageObj = allPages.find(o => o.page === pNum); if (!pageObj) return; const btn = document.createElement('div'); btn.className = 'bpm-part'; btn.textContent = `P${pageObj.page} · ${pageObj.part}`; btn.dataset.p = pageObj.page; listEl.appendChild(btn); }); /* 默认播放第一个指定的分P */ const defaultP = pageArr[0] || 1; const inst = this.#loadVideo(playerEl, bvid, defaultP, width, height, null); onFirstLoad(inst); listEl.querySelector(`.bpm-part[data-p="${defaultP}"]`)?.classList.add('active'); }) .catch(() => { playerEl.innerHTML = '<div style="display:flex;height:100%;align-items:center;justify-content:center;color:#f66;">网络异常,无法获取分P信息</div>'; }); } /* moshi=5:多BV列表 */ #fetchAndRenderMultiBV(bvArr, playerEl, listEl, width, height, onFirstLoad) { Promise.all( bvArr.map(bvid => fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) .then(r => r.json()) .then(res => ({ bvid, title: (res.code === 0 && res.data) ? res.data.title : '(获取失败)' })) .catch(() => ({ bvid, title: '(网络错误)' })) ) ).then(list => { list.forEach((item, idx) => { const btn = document.createElement('div'); btn.className = 'bpm-part'; btn.textContent = `L${idx + 1}·${item.title}`; btn.dataset.bv = item.bvid; listEl.appendChild(btn); }); /* 默认播放第一个 */ if (list.length > 0) { const inst = this.#loadVideo(playerEl, list[0].bvid, 1, width, height, null); onFirstLoad(inst); listEl.querySelector('.bpm-part')?.classList.add('active'); } }); } /* 对外暴露 nano 实例(移动端 / multi 模式始终为 null) */ get player() { return this.#playerInst; } /* 设置滚动容器内容(兼容旧接口) */ setScrollContent(content) { if (this.#scrollWrap) { this.#scrollWrap.innerHTML = content; } } /* 销毁:停止播放并移除 DOM */ destroy() { if (this.#playerInst && this.#playerInst.destroy) { this.#playerInst.destroy(); } if (this.#scrollWrap) { this.#scrollWrap.remove(); } else { this.#container?.remove(); } this.#playerInst = null; this.#container = null; this.#scrollWrap = null; } } </script> <script> (window.RLQ = window.RLQ || []).push(['jquery', () => { $(document).ready(function () { var val1 = ``; var val2 = ``; var mode = ``; if (val2 === `` || val2 === undefined) { mode = val1; } else { mode = val2; } if (window.biliplayerWc == undefined) { window.biliplayerWc = 0; } if (mode == ``) { mode = `1`; } /* 当前实例对应的 .BiliPlayer 根元素 */ const rootEl = document.querySelectorAll(".BiliPlayer")[window.biliplayerWc]; switch (mode) { /* -------- moshi=1:单P,直接指定分P编号 -------- */ case `1`: { rootEl.innerHTML = `<div class="player-anchor"></div>`; const bv = ``; const p = `` || '1'; const w = `` || '100%'; const h = `` || '400px'; new BiliPlayer(bv, p, { width: w, height: h, autoplay: false }); break; } /* -------- moshi=2:按分P名称查找 -------- */ case `2`: { rootEl.innerHTML = `<div class="player-anchor"></div>`; const Bvid = ``; const Pname = ``; const w = `` || '100%'; const h = `` || '400px'; fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${Bvid}`) .then(r => r.json()) .then(res => { const page = res.data.pages.find(p => p.part === Pname)?.page || 1; new BiliPlayer(Bvid, page, { width: w, height: h, autoplay: false }); }) .catch(err => { console.error('case2 捕获异常:', err); rootEl.innerHTML = '<div style="height:100%;display:flex;align-items:center;justify-content:center;color:#f66;">获取分P失败</div>'; }); break; } /* -------- moshi=3:全分P列表(BiliPlayer multi=allP) -------- */ case `3`: { const Bvid = ``; const w = `` || '100%'; const h = `` || '400px'; /* 将根元素本身作为容器交给 BiliPlayer */ new BiliPlayer(rootEl, { multiMode : 'allP', bvid  : Bvid, width  : w, height  : h }); break; } /* -------- moshi=4:自定义分P列表(BiliPlayer multi=customP) -------- */ case `4`: { const Bvid = ``; const rawP = ``; const w = `` || '100%'; const h = `` || '400px'; const pageArr = rawP .replace(/,|、/g, ',') .split(',') .map(s => parseInt(s.trim(), 10)) .filter(n => !isNaN(n) && n > 0); new BiliPlayer(rootEl, { multiMode : 'customP', bvid  : Bvid, pages  : pageArr.length ? pageArr : [1], width  : w, height  : h }); break; } /* -------- moshi=5:多BV列表(BiliPlayer multi=multiBV) -------- */ case `5`: { const rawBV = ``; const w = `` || '100%'; const h = `` || '400px'; const bvArr = rawBV .replace(/,|、/g, ',') .split(',') .map(s => s.trim()) .filter(v => v); new BiliPlayer(rootEl, { multiMode : 'multiBV', bvArr  : bvArr.length ? bvArr : ['BV1xx411c7mD'], width  : w, height  : h }); break; } } window.biliplayerWc = window.biliplayerWc + 1; }); }]); </script>

更新日志

1.2 新增模式参数,以及用分P名称来定位分P
1.1 修复了更改分P选项无法显示对应分P的内容
1.0 本项目创建