前言
作为一名钓鱼爱好者,我在抖音分享了许多钓鱼视频。为了让博客访问者能够直观地浏览我的抖音作品,决定在Hexo博客中集成抖音视频相册功能。
本文将详细介绍整个实现过程,从数据配置到页面展示,再到进阶优化,帮助你快速在博客中实现类似功能。
一、需求分析与方案设计
1.1 功能需求
在博客中展示抖音视频作品,需要实现以下核心功能:
| 功能 |
描述 |
| 视频列表展示 |
以网格形式展示视频封面卡片 |
| 标题与日期 |
在封面上显示视频标题和发布日期 |
| 视频播放 |
点击卡片弹出播放器,支持上一首/下一首切换 |
| 数据管理 |
通过JSON文件管理视频数据,支持增删改查 |
| 年份分类 |
按视频发布年份进行分组展示 |
1.2 技术选型
- 静态博客框架:Hexo v6+
- 主题:Next v8+
- 数据格式:JSON(轻量、易维护)
- 页面渲染:Nunjucks(Swig模板引擎)
- 样式方案:Stylus(主题内置支持)
1.3 整体架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ┌─────────────────────────────────────────────────────┐ │ 数据层 │ │ source/douyin/videos.json │ │ (标题、日期、视频ID、描述、封面图URL) │ └─────────────────────┬───────────────────────────────┘ ↓ Hexo 生成前 ┌─────────────────────────────────────────────────────┐ │ 处理层 │ │ themes/next/scripts/filters/douyin-videos.js │ │ (读取JSON → 按年份分组 → 注入主题配置) │ └─────────────────────┬───────────────────────────────┘ ↓ 模板渲染 ┌─────────────────────────────────────────────────────┐ │ 展示层 │ │ themes/next/layout/douyin.swig │ │ source/_data/styles.styl │ │ (视频卡片网格、弹窗播放器、封面遮罩层) │ └─────────────────────────────────────────────────────┘
|
数据流向:
videos.json 存储原始视频数据
douyin-videos.js 在Hexo生成前读取数据,按年份分组
- 注入
theme.douyinVideos 全局配置
douyin.swig 模板循环读取配置,渲染页面
- 浏览器端JavaScript处理弹窗播放逻辑
二、核心实现步骤
2.1 创建视频数据文件
在 source/douyin/ 目录下创建 videos.json 文件,这是整个功能的数据源。
文件结构:
1 2 3 4 5 6 7 8 9 10 11
| { "videos": [ { "title": "天气转凉", "date": "2025-11-02", "short_id": "RkEfPk9Pzj4", "video_id": "7568078861650152723", "desc": "天气转凉,钓鱼爱好者们,你们都空军了吗?" } ] }
|
字段说明:
| 字段 |
类型 |
必填 |
说明 |
| title |
字符串 |
是 |
视频标题,用于展示和提取 |
| date |
字符串 |
是 |
发布日期,格式:YYYY-MM-DD,用于排序和年份分组 |
| short_id |
字符串 |
否 |
抖音短链ID(如 v.douyin.com/xxx 中的 xxx) |
| video_id |
字符串 |
是 |
视频完整ID,19位数字,用于播放和封面获取 |
| desc |
字符串 |
否 |
视频描述文字 |
| cover |
字符串 |
否 |
封面图URL,不填则使用随机图 |
2.2 创建数据处理脚本
在 themes/next/scripts/filters/ 目录下创建 douyin-videos.js。这是一个Hexo过滤器,在生成页面之前执行。
核心逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| hexo.extend.filter.register('before_generate', function() { const config = JSON.parse(fs.readFileSync(configFile, 'utf8')); const grouped = {}; videos.forEach(video => { const year = video.date.split('-')[0]; if (!grouped[year]) grouped[year] = []; grouped[year].push({ }); }); hexo.theme.config.douyinVideos = { years: Object.keys(grouped).sort((a, b) => b - a), grouped, allVideos: videos.flatMap(y => grouped[y]), videoCount: videos.length }; });
|
输出数据:
| 变量 |
类型 |
说明 |
theme.douyinVideos.years |
数组 |
所有年份,如 ['2025', '2024', '2023'] |
theme.douyinVideos.grouped |
对象 |
按年份分组,{ '2025': [...], '2024': [...] } |
theme.douyinVideos.allVideos |
数组 |
所有视频扁平数组,按年份降序 |
theme.douyinVideos.videoCount |
数字 |
视频总数 |
2.3 创建页面模板
在 themes/next/layout/ 目录下创建 douyin.swig。
关键实现点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| {% for video in theme.douyinVideos.allVideos %} <div class="videos-item video-card" data-id="{{ video.video_id }}"> <!-- 封面图:有cover用cover,没有用随机图 --> <div class="video-cover" style="background-image: url('{{ video.cover || ('https://picsum.photos/180/220?random=' + video.video_id) }}');"> <!-- 播放图标 --> <div class="video-play-icon"><i class="fa fa-play"></i></div> <!-- 标题和日期遮罩层 --> <div class="video-overlay"> <!-- truncate(10, true) 限制标题为10字符 --> <div class="video-title">{{ video.title | truncate(10, true) }}</div> <div class="video-date">{{ video.date }}</div> </div> </div> <!-- 描述文字 --> <div class="video-desc">{{ video.desc }}</div> </div> {% endfor %}
|
模板亮点:
truncate(10, true) Nunjucks过滤器限制标题为10个字符
- 封面图使用视频ID作为随机种子,保证每次访问图片一致
- 支持三元运算符处理可选封面
2.4 配置样式
在 source/_data/styles.styl 中添加样式。
核心样式说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| .videos-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; }
.video-cover { width: 180px; height: 220px; border: 2px solid #e74c3c; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.3s ease; &:hover { transform: scale(1.03); border-color: #c0392b; } }
.video-overlay { position: absolute; bottom: 0; left: 0; right: 0; padding: 8px 10px 6px; background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, transparent 100%); .video-title { color: #fff; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .video-date { color: rgba(255,255,255,0.85); font-size: 11px; margin-top: 2px; } }
|
2.5 创建页面入口
在 source/douyin/index.md 创建页面文件:
1 2 3 4 5 6
| --- title: 抖音作品 date: 2026-04-01 12:00:00 type: "douyin" layout: "douyin" ---
|
只需配置 layout: "douyin",Hexo会自动使用 douyin.swig 模板渲染。
2.6 配置菜单
在 themes/next/languages/zh-CN.yml 中添加:
三、视频播放功能实现
3.1 弹窗HTML结构
在 douyin.swig 模板末尾添加弹窗代码:
1 2 3 4 5 6 7 8
| <div id="douyin-modal" class="modal"> <div class="modal-content"> <span class="close-btn">×</span> <span class="nav-btn prev-btn">❮</span> <span class="nav-btn next-btn">❯</span> <iframe id="douyin-player" frameborder="0" allowfullscreen></iframe> </div> </div>
|
3.2 弹窗样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.9); z-index: 9999; &.active { display: block; } }
.modal-content { width: 80%; max-width: 900px; margin: 5% auto; background: #000; border-radius: 8px; iframe { width: 100%; height: 500px; } }
.close-btn { position: absolute; top: -40px; right: 0; color: #fff; font-size: 32px; cursor: pointer; }
|
3.3 播放逻辑(JavaScript)
视频播放使用抖音开放平台的嵌入播放器:
1
| https://open.douyin.com/player/video?vid={video_id}
|
JavaScript实现的功能:
| 功能 |
实现方式 |
| 点击打开弹窗 |
给 .video-card 绑定点击事件 |
| 加载视频 |
设置iframe.src为播放地址 |
| 关闭弹窗 |
点击关闭按钮或背景区域 |
| 上一首/下一首 |
数组索引加减,支持循环 |
| 禁止背景滚动 |
弹窗打开时设置 body.overflow: hidden |
四、辅助工具详解
为了更方便地管理视频数据,我开发了两个Python工具。
4.1 抖音视频链接转换工具
工具位置: tools/抖音视频链接转换工具/
功能:
- 从抖音分享文本中提取视频信息
- 自动获取视频完整ID(通过短链请求)
- 生成可直接使用的JSON格式
4.1.1 输入文件格式
在 douyin_input.txt 中按以下格式放置抖音分享文本(每行一条):
1 2 3 4 5 6
| # 格式1:从抖音APP复制的完整文本(推荐) 6.46 l@C.uf ZZm:/ 05/18 # 视频标题 # 标签 https://v.douyin.com/u5QvD6hzoZU/ 复制此链接,打开Dou音搜索,直接观看视频!
# 格式2:仅包含链接 https://v.douyin.com/xkqUZFxICQM/
|
文本特征说明:
| 位置 |
内容示例 |
说明 |
| 开头 |
6.46 l@C.uf ZZm:/ |
抖音APP复制的标识符(可忽略) |
| 日期 |
05/18 |
月/日格式,会自动补充年份 |
| 标题区 |
视频标题文字 |
提取第一个分隔符前的内容 |
| 链接 |
https://v.douyin.com/xxx/ |
短链ID提取 |
4.1.2 运行步骤
1 2 3 4 5 6 7 8 9 10 11
| cd tools/抖音视频链接转换工具
python 抖音视频链接转换工具.py
|
工具输出示例:
1 2 3 4 5 6 7 8 9 10 11
| ================================================== 抖音视频链接转换工具 ==================================================
正在获取 video_id: u5QvD6hzoZU ...成功 -> 7286633511249825079 ✓ 提取: # 我要上热门 # 神奇动物在抖音 (ID: 7286633511249825079)
处理完成! 共转换 1 个视频 输出文件: douyin_output.json
将 douyin_output.json 内容复制到 source/douyin/videos.json 即可
|
4.1.3 核心处理逻辑
工具内部处理流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def process(text):
|
4.2 抖音视频ID提取工具
工具位置: tools/抖音视频ID提取工具/
功能:
- 为已有视频数据补充缺失的video_id
- 自动获取视频封面图URL
- 验证video_id格式是否正确(19位数字)
使用场景:
| 场景 |
说明 |
| 补充ID |
分享链接已过期,只能通过已有短链重新获取ID |
| 获取封面 |
需要为视频添加封面图时自动抓取 |
| 数据校验 |
验证video_id是否为19位有效数字 |
运行命令:
1 2
| cd tools/抖音视频ID提取工具 python douyin-id-extractor.py
|
输出示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ================================================== 抖音视频ID提取工具 ==================================================
共找到 38 个视频
[1/38] 寒冬冰水中的连竿喜悦... -> 已有完整ID: 7314629156933864742 -> 正在获取封面图... -> 封面图: https://p3-sign.douyinpic.com/...
完成! 更新了 15 个视频封面图 配置文件已保存: source/douyin/videos.json
|
4.3 完整使用流程
1 2 3 4 5 6 7 8 9 10 11
| 步骤1:获取抖音分享链接 ↓ 步骤2:复制到douyin_input.txt(每行一条) ↓ 步骤3:运行"抖音视频链接转换工具" ↓ 步骤4:复制douyin_output.json内容到videos.json ↓ 步骤5:运行hexo g验证效果 ↓ (如需封面)运行"抖音视频ID提取工具"
|
五、进阶功能与优化
5.1 视频排序算法
默认按日期降序排列(最新的在前)。如需调整排序,可使用以下Python脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import json
with open('source/douyin/videos.json', 'r', encoding='utf-8') as f: data = json.load(f)
videos = data.get('videos', [])
videos.sort(key=lambda x: x.get('date', ''), reverse=True)
row_size = 4 cols = [[] for _ in range(row_size)] for i, video in enumerate(videos): cols[i % row_size].append(video)
new_videos = [] for i in range(len(videos)): new_videos.append(cols[i % row_size][i // row_size])
data['videos'] = new_videos
with open('source/douyin/videos.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2)
|
排序效果:
| 位置 |
排序前 |
排序后 |
| 第1行第1列 |
最早 |
最新 |
| 第1行最后 |
最晚 |
较新 |
| 最后1行第1列 |
最新 |
最旧 |
5.2 响应式布局
样式中已包含响应式断点:
| 屏幕宽度 |
列宽 |
间距 |
| > 768px |
180px |
20px |
| 480-768px |
160px |
16px |
| < 480px |
140px |
12px |
六、常见问题
6.1 日期提取错误
问题: 从分享文本中自动提取的日期可能是错误的,特别是未来日期。
原因: 日期格式 MM/DD 缺少年份,工具通过当前年份推断,可能出错。
解决方案:
- 手动核对
videos.json 中的日期字段
- 修正错误日期后重新生成:
hexo clean && hexo g
6.2 视频播放地址失效
问题: 弹窗无法播放视频。
原因: 抖音播放器地址可能发生变化。
解决方案:
- 使用
https://www.douyin.com/video/{video_id} 直接访问
- 使用抖音开放平台的 embed 接口
- 考虑直接跳转抖音链接打开
6.3 修改后页面未更新
问题: 修改了配置但页面没有变化。
解决方案:
七、文件结构汇总
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| BlogCode/ ├── source/ │ ├── douyin/ │ │ ├── videos.json # 视频数据文件 │ │ └── index.md # 页面入口 │ └── _posts/ # 博客文章 │ └── Hexo博客集成抖音视频相册实战指南.md │ ├── themes/next/ │ ├── layout/ │ │ └── douyin.swig # 页面模板 │ ├── scripts/filters/ │ │ └── douyin-videos.js # 数据处理脚本 │ └── languages/ │ └── zh-CN.yml # 菜单配置 │ ├── source/_data/ │ └── styles.styl # 样式文件 │ └── tools/ ├── 抖音视频链接转换工具/ # 工具1 └── 抖音视频ID提取工具/ # 工具2
|
八、总结
通过本文的详细讲解,你应该能够:
- ✅ 理解三层架构设计(数据层→处理层→展示层)
- ✅ 创建和管理视频数据(videos.json)
- ✅ 实现视频列表的网格展示
- ✅ 添加标题和日期的封面遮罩展示
- ✅ 实现视频弹窗播放功能(支持上一首/下一首)
- ✅ 使用辅助工具自动化获取视频信息
- ✅ 处理常见问题(日期错误、播放失效)
这个抖音视频相册功能为博客增添了一个生动的展示窗口,让访问者能够更直观地了解我的钓鱼生活。
如果你在实现过程中遇到任何问题,欢迎在评论区留言交流!
查看效果
想查看抖音视频相册的实际效果吗?点击下方链接:
👉 抖音作品 - 我的抖音视频列表
本文对应的代码版本:v1.0