bilibili-API-collect/assets/webmask.html-BwxLSheT.js
github-actions 1424f73d2e generated
2026-01-22 17:37:01 +00:00

28 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import{_ as l,c as t,b as n,a as o,d as a,e as p,w as d,r,o as i}from"./app-Dgsdh8A6.js";const c={};function D(y,s){const e=r("RouteLink");return i(),t("div",null,[s[3]||(s[3]=n("h1",{id:"智能防挡弹幕",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#智能防挡弹幕"},[n("span",null,"智能防挡弹幕")])],-1)),s[4]||(s[4]=n("p",null,"B 站部分视频提供“智能防挡弹幕”功能,其原理是提供了一个 webmask 二进制文件,其中保存了视频中各个位置的 svg 格式蒙版。",-1)),s[5]||(s[5]=n("p",null,"首先需要获取 webmask 资源的地址。",-1)),s[6]||(s[6]=n("h2",{id:"获取-webmask-资源地址",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#获取-webmask-资源地址"},[n("span",null,"获取 webmask 资源地址")])],-1)),n("p",null,[s[1]||(s[1]=a("通过 ")),p(e,{to:"/docs/video/player.html"},{default:d(()=>s[0]||(s[0]=[a("web 播放器资源接口")])),_:1,__:[0]}),s[2]||(s[2]=a(" 获取 webmask 二进制文件的地址。"))]),s[7]||(s[7]=o(`<h2 id="webmask-资源" tabindex="-1"><a class="header-anchor" href="#webmask-资源"><span>webmask 资源</span></a></h2><p>获取的 url 没有权鉴,不需要后面的参数也可以获取。</p><p>示例:</p><div class="language-shell line-numbers-mode" data-highlighter="shiki" data-ext="shell" style="background-color:#1E1E1E;color:#D4D4D4;"><pre class="shiki dark-plus vp-code"><code class="language-shell"><span class="line"><span style="color:#DCDCAA;">curl</span><span style="color:#569CD6;"> -G</span><span style="color:#CE9178;"> https://upos-sz-staticcos-cmask.bilivideo.com/cmaskboss/825851971_30_0.webmask</span><span style="color:#D4D4D4;"> &gt;&gt; </span><span style="color:#CE9178;">825851971_30_0.webmask</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>下载后是二进制文件。</p><h2 id="webmask-二进制读取" tabindex="-1"><a class="header-anchor" href="#webmask-二进制读取"><span>webmask 二进制读取</span></a></h2><p>参考:<a href="https://github.com/andrewvy/webmask-renderer" target="_blank" rel="noopener noreferrer">andrewvy/webmask-renderer</a></p><table><thead><tr><th>name</th><th>offset</th><th>length</th><th>type</th><th>desc</th></tr></thead><tbody><tr><td>mask</td><td>0</td><td>4</td><td>char</td><td>&#39;MASK&#39; 文件头</td></tr><tr><td>version</td><td>4</td><td>4</td><td>int</td><td>是1</td></tr><tr><td>vU</td><td>8</td><td>4</td><td>?</td><td>不知道是干什么的</td></tr><tr><td>Ly</td><td>12</td><td>4</td><td>int</td><td>后续数据的段数</td></tr><tr><td>time_1</td><td>16</td><td>8</td><td>long</td><td>第一段对应视频开始时间</td></tr><tr><td>offset_1</td><td>24</td><td>8</td><td>long</td><td>第一段蒙版信息开始处对应二进制偏移</td></tr><tr><td>...</td><td>...</td><td>...</td><td>...</td><td>...</td></tr><tr><td>time_{Ly}</td><td>16+(Ly-1)*16</td><td>8</td><td>long</td><td>第 <code>Ly</code> 段对应视频开始时间</td></tr><tr><td>offset_{Ly}</td><td>24+(Ly-1)*16</td><td>8</td><td>long</td><td>第 <code>Ly</code> 段蒙版信息开始处对应二进制偏移</td></tr><tr><td>segments_1</td><td>由前面offset_1提供</td><td>由 <code>offset_2-offset_1</code> 计算得到</td><td>binary</td><td>蒙版信息块,使用 gzip 压缩</td></tr><tr><td>...</td><td>...</td><td>...</td><td>...</td><td>...</td></tr></tbody></table><p>蒙版信息块是经过 gzip 压缩文本得到的二进制。解压缩后得到一个字节串。</p><p>前 16 字节是两个 long记为 <code>left</code> 和 <code>right</code>,暂时不清楚其作用。<code>left</code> 似乎和平均每张蒙版的时间有关。<code>right</code> 总是 <code>i*10000</code><code>i</code> 是从 0 开始数的数据段次序。</p><p>后面是各个 svg 文本直接拼起来,可以直接通过 svg 格式头 <code>data:image/svg+xml;base64,</code> 分开。</p><p>Python 示例:</p><div class="language-python line-numbers-mode" data-highlighter="shiki" data-ext="python" style="background-color:#1E1E1E;color:#D4D4D4;"><pre class="shiki dark-plus vp-code"><code class="language-python"><span class="line"><span style="color:#C586C0;">from</span><span style="color:#D4D4D4;"> struct </span><span style="color:#C586C0;">import</span><span style="color:#D4D4D4;"> unpack</span></span>
<span class="line"><span style="color:#C586C0;">import</span><span style="color:#D4D4D4;"> gzip</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D4D4D4;">f = </span><span style="color:#DCDCAA;">open</span><span style="color:#D4D4D4;">(</span><span style="color:#CE9178;">&#39;你的 webmask&#39;</span><span style="color:#D4D4D4;">, </span><span style="color:#CE9178;">&#39;rb&#39;</span><span style="color:#D4D4D4;">)</span></span>
<span class="line"><span style="color:#D4D4D4;">buf = f.read()</span></span>
<span class="line"><span style="color:#D4D4D4;">_Ly = buf[</span><span style="color:#B5CEA8;">12</span><span style="color:#D4D4D4;">:</span><span style="color:#B5CEA8;">16</span><span style="color:#D4D4D4;">]</span></span>
<span class="line"><span style="color:#D4D4D4;">Ly = unpack(</span><span style="color:#CE9178;">&#39;&gt;i&#39;</span><span style="color:#D4D4D4;">, _Ly)[</span><span style="color:#B5CEA8;">0</span><span style="color:#D4D4D4;">] </span><span style="color:#6A9955;"># 大端序 int</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D4D4D4;">times = []</span></span>
<span class="line"><span style="color:#D4D4D4;">offsets = []</span></span>
<span class="line"><span style="color:#C586C0;">for</span><span style="color:#D4D4D4;"> idx </span><span style="color:#C586C0;">in</span><span style="color:#DCDCAA;"> range</span><span style="color:#D4D4D4;">(Ly):</span></span>
<span class="line"><span style="color:#D4D4D4;"> op = </span><span style="color:#B5CEA8;">16</span><span style="color:#D4D4D4;"> + idx * </span><span style="color:#B5CEA8;">16</span><span style="color:#6A9955;"> # 个人习惯,我算偏移时喜欢用 \`op\`\`ed\` 作为开始和结束的名字。</span></span>
<span class="line"><span style="color:#D4D4D4;"> time = unpack(</span><span style="color:#CE9178;">&#39;&gt;q&#39;</span><span style="color:#D4D4D4;">, buf[op: op+</span><span style="color:#B5CEA8;">8</span><span style="color:#D4D4D4;">])[</span><span style="color:#B5CEA8;">0</span><span style="color:#D4D4D4;">]</span></span>
<span class="line"><span style="color:#D4D4D4;"> offset = unpack(</span><span style="color:#CE9178;">&#39;&gt;q&#39;</span><span style="color:#D4D4D4;">, buf[op+</span><span style="color:#B5CEA8;">8</span><span style="color:#D4D4D4;">: op+</span><span style="color:#B5CEA8;">16</span><span style="color:#D4D4D4;">])[</span><span style="color:#B5CEA8;">0</span><span style="color:#D4D4D4;">]</span></span>
<span class="line"><span style="color:#D4D4D4;"> times.append(time)</span></span>
<span class="line"><span style="color:#D4D4D4;"> offsets.append(offset)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D4D4D4;">frames = []</span></span>
<span class="line"><span style="color:#C586C0;">for</span><span style="color:#D4D4D4;"> idx </span><span style="color:#C586C0;">in</span><span style="color:#DCDCAA;"> range</span><span style="color:#D4D4D4;">(Ly):</span></span>
<span class="line"><span style="color:#D4D4D4;"> op = offsets[idx]</span></span>
<span class="line"><span style="color:#C586C0;"> if</span><span style="color:#D4D4D4;"> idx == Ly - </span><span style="color:#B5CEA8;">1</span><span style="color:#D4D4D4;">: ed = </span><span style="color:#DCDCAA;">len</span><span style="color:#D4D4D4;">(buf)</span></span>
<span class="line"><span style="color:#C586C0;"> else</span><span style="color:#D4D4D4;">: ed = offsets[idx+</span><span style="color:#B5CEA8;">1</span><span style="color:#D4D4D4;">]</span></span>
<span class="line"><span style="color:#D4D4D4;"> ba = buf[op: ed]</span></span>
<span class="line"><span style="color:#D4D4D4;"> bad = gzip.decompress(ba)</span></span>
<span class="line"><span style="color:#D4D4D4;"> badl = bad.split(</span><span style="color:#569CD6;">b</span><span style="color:#CE9178;">&#39;data:image/svg+xml;base64,&#39;</span><span style="color:#D4D4D4;">)</span></span>
<span class="line"><span style="color:#6A9955;"> # badl[0]是16字节\`left\`\`right\`</span></span>
<span class="line"><span style="color:#D4D4D4;"> frames.append(badl[</span><span style="color:#B5CEA8;">1</span><span style="color:#D4D4D4;">: ])</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div>`,13))])}const b=l(c,[["render",D]]),u=JSON.parse('{"path":"/docs/danmaku/webmask.html","title":"智能防挡弹幕","lang":"zh-CN","frontmatter":{},"git":{"updatedTime":1714889183000,"contributors":[{"name":"holazzer","username":"holazzer","email":"45134013+holazzer@users.noreply.github.com","commits":1,"url":"https://github.com/holazzer"},{"name":"z0z0r4","username":"z0z0r4","email":"z0z0r4@outlook.com","commits":1,"url":"https://github.com/z0z0r4"}],"changelog":[{"hash":"1d557be039a887ef25bc25da0c396eb5e03eb42e","time":1714889183000,"email":"45134013+holazzer@users.noreply.github.com","author":"Han Zhang","message":"智能防挡弹幕 (#659)","coAuthors":[{"name":"z0z0r4","email":"z0z0r4@outlook.com"}]}]},"filePathRelative":"docs/danmaku/webmask.md"}');export{b as comp,u as data};