利用短代码在hugo博客中加入bilibili播放器插件
王叨叨在 开源Bilibili播放器插件 一文中,分享了一个Typecho插件,用于将Bilibili默认播放器替换为HTML5移动端播放器,并提供更多自定义选项。
基于他的源代码,考虑将其移植到Hugo中,并通过短代码来实现bilibili视频的调用。
短代码是Hugo的一项强大功能,允许我们在Markdown中嵌入HTML片段,实现复杂的布局和功能。
王叨叨的Typecho插件主要通过以下方式工作:
- 通过正则表达式匹配和替换Bilibili播放器iframe
- 提供丰富的配置选项(宽度、高度、自动播放、弹幕开关等)
- 替换默认播放器为HTML5移动端播放器,提升用户体验
在上述的基础上,借助AI实现了一定程度的高度自适应和自定义封面功能,为什么说是一定程度上的高度自适应,其实是新加入了ratio参数,即明确了视频宽高比,再根据视频宽高比,使用 padding-bottom 技巧实现真正的宽高比自适应。
示例
具体实现
创建短代码文件
在Hugo项目的 layouts/shortcodes/ 目录下创建 bilibili.html 文件:
{{- $id := .Get "id" -}}
{{- $page := .Get "page" | default "1" -}}
{{- $width := .Get "width" | default "100%" -}}
{{- $height := .Get "height" | default "auto" -}}
{{- $ratio := .Get "ratio" | default "16x9" -}}
{{- $cover := .Get "cover" | default "" -}}
{{- $coverPosition := .Get "coverPosition" | default "center" -}}
{{- $autoplay := .Get "autoplay" | default "0" -}}
{{- $danmaku := .Get "danmaku" | default "1" -}}
{{- $muted := .Get "muted" | default "0" -}}
{{- $hasMuteButton := .Get "hasMuteButton" | default "0" -}}
{{- $hideCoverInfo := .Get "hideCoverInfo" | default "0" -}}
{{- $hideDanmakuButton := .Get "hideDanmakuButton" | default "0" -}}
{{- $noFullScreenButton := .Get "noFullScreenButton" | default "0" -}}
{{- $fjw := .Get "fjw" | default "1" -}}
{{- $queryParams := dict "page" $page -}}
{{- if ne $autoplay "0" }}{{ $queryParams = merge $queryParams (dict "autoplay" $autoplay) }}{{ end -}}
{{- if ne $danmaku "0" }}{{ $queryParams = merge $queryParams (dict "danmaku" $danmaku) }}{{ end -}}
{{- if ne $muted "0" }}{{ $queryParams = merge $queryParams (dict "muted" $muted) }}{{ end -}}
{{- if ne $hasMuteButton "0" }}{{ $queryParams = merge $queryParams (dict "hasMuteButton" $hasMuteButton) }}{{ end -}}
{{- if ne $hideCoverInfo "0" }}{{ $queryParams = merge $queryParams (dict "hideCoverInfo" $hideCoverInfo) }}{{ end -}}
{{- if ne $hideDanmakuButton "0" }}{{ $queryParams = merge $queryParams (dict "hideDanmakuButton" $hideDanmakuButton) }}{{ end -}}
{{- if ne $noFullScreenButton "0" }}{{ $queryParams = merge $queryParams (dict "noFullScreenButton" $noFullScreenButton) }}{{ end -}}
{{- if ne $fjw "0" }}{{ $queryParams = merge $queryParams (dict "fjw" $fjw) }}{{ end -}}
{{- $aspectRatio := "56.25%" -}}
{{- if eq $ratio "4x3" }}{{ $aspectRatio = "75%" }}{{ end -}}
{{- if eq $ratio "1x1" }}{{ $aspectRatio = "100%" }}{{ end -}}
{{- if eq $ratio "21x9" }}{{ $aspectRatio = "42.86%" }}{{ end -}}
{{- $playerID := printf "bilibili-player-%s" (md5 (printf "%s-%s" $id $page)) -}}
<style>
.bilibili-player-wrapper {
position: relative;
width: {{ $width }};
{{ if eq $height "auto" }}
height: 0;
padding-bottom: {{ $aspectRatio }};
{{ else }}
height: {{ $height }};
{{ end }}
margin: 1rem 0;
border-radius: 8px;
overflow: hidden;
background-color: #f5f5f5;
}
.bilibili-player-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
border-radius: 8px;
}
.bilibili-custom-cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: {{ $coverPosition }};
background-repeat: no-repeat;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
z-index: 10;
transition: opacity 0.3s ease;
}
.bilibili-custom-cover:hover .bilibili-play-button {
transform: scale(1.1);
background-color: #00a1d6;
}
.bilibili-play-button {
width: 70px;
height: 70px;
background-color: rgba(0, 161, 214, 0.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.bilibili-play-button::after {
content: "▶";
margin-left: 4px;
}
.bilibili-player-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.7);
color: white;
font-size: 16px;
z-index: 5;
border-radius: 8px;
}
.bilibili-player-loading.hidden {
display: none;
}
@media (max-width: 768px) {
.bilibili-play-button {
width: 60px;
height: 60px;
font-size: 20px;
}
}
</style>
{{- if $id -}}
<div class="bilibili-player-wrapper" id="{{ $playerID }}">
{{- if $cover -}}
<div class="bilibili-custom-cover" style="background-image: url('{{ $cover }}');" onclick="loadBilibiliPlayer('{{ $playerID }}')">
<div class="bilibili-play-button"></div>
</div>
{{- end -}}
<div class="bilibili-player-loading hidden">视频加载中...</div>
{{- if hasPrefix $id "BV" -}}
<iframe
{{ if $cover }}data-src{{ else }}src{{ end }}="//www.bilibili.com/blackboard/html5mobileplayer.html?bvid={{ $id }}{{ with $queryParams }}{{ if gt (len .) 0 }}&{{ . | querify | safeURL }}{{ end }}{{ end }}"
scrolling="no"
frameborder="no"
framespacing="0"
allowfullscreen="true"
loading="lazy"
{{ if $cover }}style="display:none;"{{ end }}>
</iframe>
{{- else if hasPrefix $id "av" -}}
<iframe
{{ if $cover }}data-src{{ else }}src{{ end }}="//www.bilibili.com/blackboard/html5mobileplayer.html?aid={{ strings.TrimPrefix "av" $id }}{{ with $queryParams }}{{ if gt (len .) 0 }}&{{ . | querify | safeURL }}{{ end }}{{ end }}"
scrolling="no"
frameborder="no"
framespacing="0"
allowfullscreen="true"
loading="lazy"
{{ if $cover }}style="display:none;"{{ end }}>
</iframe>
{{- else -}}
{{- errorf "Invalid Bilibili video ID: %s. Must start with 'BV' or 'av'" $id -}}
{{- end -}}
</div>
<script>
function loadBilibiliPlayer(playerId) {
const playerWrapper = document.getElementById(playerId);
const cover = playerWrapper.querySelector('.bilibili-custom-cover');
const iframe = playerWrapper.querySelector('iframe');
const loading = playerWrapper.querySelector('.bilibili-player-loading');
if (iframe && iframe.hasAttribute('data-src')) {
// 显示加载提示
loading.classList.remove('hidden');
// 设置iframe的src属性
iframe.src = iframe.getAttribute('data-src');
// 显示iframe
iframe.style.display = 'block';
// 隐藏封面
cover.style.opacity = '0';
// 等待iframe加载完成后隐藏加载提示
iframe.onload = function() {
loading.classList.add('hidden');
setTimeout(function() {
cover.style.display = 'none';
}, 300);
};
}
}
// 如果没有自定义封面,直接显示播放器
document.addEventListener('DOMContentLoaded', function() {
const players = document.querySelectorAll('.bilibili-player-wrapper');
players.forEach(function(player) {
const cover = player.querySelector('.bilibili-custom-cover');
const iframe = player.querySelector('iframe');
if (!cover && iframe && iframe.hasAttribute('data-src')) {
iframe.src = iframe.getAttribute('data-src');
iframe.style.display = 'block';
}
});
});
</script>
{{- else -}}
{{- errorf "Missing required parameter 'id' for bilibili shortcode" -}}
{{- end -}}
使用方法
基本用法
在Hugo文章的Markdown内容中,你可以通过以下方式使用短代码:
// 基本用法(BV号)
{{< bilibili id="BV1CG1LBbENh" >}}
// 基本用法(av号)
{{< bilibili id="av810872" >}}
// 基本用法(带自定义封面)
{{< bilibili id="BV1CG1LBbENh" cover="images/cover.jpg" >}}
// 带分P的视频
{{< bilibili id="BV1CG1LBbENh" page="2" >}}
// 自定义宽高比
{{< bilibili id="BV1CG1LBbENh" ratio="4x3" >}}
// 固定宽度(仍然自适应高度)
{{< bilibili id="BV1CG1LBbENh" width="800px" >}}
// 强制固定高度(不推荐)
{{< bilibili id="BV1CG1LBbENh" height="400px" >}}
// 开启自动播放和静音
{{< bilibili id="BV1CG1LBbENh" autoplay="1" muted="1" >}}
// 关闭弹幕
{{< bilibili id="BV1CG1LBbENh" danmaku="0" >}}
// 自定义封面和位置
{{< bilibili
id="BV1CG1LBbENh"
cover="images/cover.jpg"
coverPosition="top"
>}}
// 完整参数示例
{{< bilibili
id="BV1GJ411x7h7"
page="1"
width="100%"
ratio="16x9"
cover="images/cover.jpg"
coverPosition="center"
autoplay="0"
danmaku="1"
muted="0"
hasMuteButton="0"
hideCoverInfo="0"
hideDanmakuButton="0"
noFullScreenButton="0"
fjw="1"
>}}
参数说明
| 参数 | 说明 | 默认值 | 使用方法 |
|---|---|---|---|
id | B站视频BV号(或AV号) | 必填 | 例如:BV1CG1LBbENh |
page | 视频分P | 1 | |
width | 播放器宽度 | 100% | 例如:100%, 800px |
ratio | 视频宽高比 | 16x9 | 16x9, 4x3, 1x1, 21x9 |
cover | 自定义封面图片URL | 空 | 任何有效的图片URL |
coverPosition | 封面图片定位 | center | center, top, bottom, left, right |
height | 播放器高度 | auto | 该参数一般无需明确,也可强制定义高度值 |
autoplay | 是否自动播放 | 0 | 1: 开启, 0: 关闭 |
danmaku | 默认弹幕开关 | 1 | 1: 开启, 0: 关闭 |
muted | 是否静音播放 | 0 | 1: 开启, 0: 关闭 |
hasMuteButton | 一键静音按钮是否显示 | 0 | 1: 开启, 0: 关闭 |
hideCoverInfo | 视频封面下方信息显示 | 0 | 1: 隐藏, 0: 显示 |
hideDanmakuButton | 是否隐藏弹幕按钮 | 0 | 1: 隐藏, 0: 显示 |
noFullScreenButton | 是否隐藏全屏按钮 | 0 | 1: 隐藏, 0: 显示 |
fjw | 是否开始记忆播放 | 1 | 1: 开启, 0: 关闭 |
扩展功能
如果你需要更多自定义功能,可以参考B站官方提供的参数进行扩展:
<!-- 在短代码中添加更多B站播放器参数 -->
{{ $highQuality := .Get "highQuality" | default "1" }}
{{ $asWide := .Get "asWide" | default "1" }}
<!-- 然后在iframe的src中添加这些参数 -->
&high_quality={{ $highQuality }}&as_wide={{ $asWide }}
© 转载需附带本文链接,依据 CC BY-NC-SA 4.0 发布。
猜你喜欢
评论