仿抖音用户作品数据解密:douyin 项目中 user_video_list/*.md 的数据结构、加载链路与生成脚本
2026/9/15 16:15:12 网站建设 项目流程

仿抖音用户作品数据解密:douyin 项目中 user_video_list/*.md 的数据结构、加载链路与生成脚本

【免费下载链接】douyinVue3 + Pinia 仿抖音,Vue 在移动端的最佳实践 . Imitate TikTok ,Vue Best practices on Mobile项目地址: https://gitcode.com/GitHub_Trending/do/douyin

导读

在 douyin(Vue3 + Pinia 仿抖音移动端项目)的public/data/user_video_list/目录下,存放着一批以.md结尾、实际却是7-Zip 压缩 JSON的"伪 Markdown"数据文件。它们承载着项目"我的页面 / 用户主页作品列表"的全部数据,从接口层被加载后直接以 JSON 解析并渲染。本文将以user-8357999.md为样本,完整拆解这份数据的内部字段结构、前端加载链路(src/mock/index.tssrc/api/user.tsUserPanel.vue/Me.vue)、与 users.json 的关联方式,以及仓库中生成这些文件的 Node 脚本逻辑。读完后,你将能独立定位、解析并复现这套"压缩 JSON 伪装 Markdown"的数据管线。


一、文件本质:.md后缀下的 7z 压缩 JSON

1.1 为什么无法直接按文本读取

public/data/user_video_list/user-8357999.md直接以 UTF-8 文本读取会立即失败。用十六进制查看文件头可看到特征签名:

7z bc af 27 1c 00 04 ...

这正是7-Zip 归档格式的魔数(magic number)。也就是说,该文件并非 Markdown 文本,而是一个 7z 压缩包,包内只有一个同名的user-8357999.json文件。

说明:同一目录下的其他文件(如user-04074747.mduser-LL991221.mduser-12345xiaolaohu.md等)以及 public/data/videos.md 都具有相同的 7z 文件头,说明这是项目统一的数据打包策略,而非个别文件损坏。

1.2 解压验证

使用 Python 的py7zr可以无损还原包内 JSON:

import py7zr with py7zr.SevenZipFile('public/data/user_video_list/user-8357999.md') as z: print(z.getnames()) # ['user-8357999.json'] z.extractall(path='/tmp/user8357999')

解压出的user-8357999.json约 326 KB,是一个JSON 数组,包含 57 条抖音视频(aweme)记录。相比原始 JSON(约 326 KB),7z 压缩后的体积显著减小,这正是项目采用"压缩 + 伪装扩展名"来静态托管数据的原因——既能模拟接口返回,又能控制仓库体积。


二、数据结构全景:一条视频记录的 22 个字段

解压后的每一条记录都是一个平铺的抖音 aweme 对象,样本user-8357999.json首条记录包含以下顶层键:

字段类型含义
aweme_idstring视频唯一 ID
descstring视频文案/描述
create_timenumber发布时间戳(秒)
musicobject背景音乐信息
videoobject播放地址、封面、分辨率
share_urlstring分享链接
statisticsobject点赞/评论/收藏/分享等计数
statusobject审核、删除、分享权限等状态
text_extraarray文案中的话题/@提及
is_topnumber是否置顶(1 表示置顶)
share_infoobject分享文案与链接
durationnumber视频时长(毫秒)
image_infosarray图集信息
risk_infosobject风险/投票信息
positionobject位置信息
author_user_idnumber作者用户 ID
prevent_downloadboolean是否禁止下载
long_videoobject长视频标识
aweme_controlobject转发/分享/评论权限控制
imagesarray图片列表
suggest_wordsobject推荐词

2.1 视频播放信息video

video对象是前端播放与封面渲染的核心来源:

{ "play_addr": { "uri": "v0d00fg10000cj1lq4jc77u0ng6s1gt0", "url_list": [ "https://www.douyin.com/aweme/v1/play/?video_id=v0d00fg10000cj1lq4jc77u0ng6s1gt0&line=0&file_id=bed51c00899b458cbc5d8280147c22a1&sign=7749aec7bd62a3760065f60e40fc1867&is_play_url=1&source=PackSourceEnum_PUBLISH" ], "width": 1080, "height": 1920, "url_key": "v0d00fg10000cj1lq4jc77u0ng6s1gt0_h264_1080p_2053262", "data_size": 3480280, "file_hash": "7749aec7bd62a3760065f60e40fc1867" }, "cover": { "uri": "tos-cn-i-0813/oYVDeaFZyENAAAAKXCYfxD6hI4zADNAURgtySl", "url_list": ["jwWCPZVTIA4IKM-8WipLF.png"], "width": 720, "height": 720 }, "height": 3840, "width": 2160, "ratio": "1080p", "duration": 13560 }

要点:

  • play_addr.url_list提供可直接播放的 URL;url_key记录了分辨率与码率(如h264_1080p);
  • cover.url_list是封面图地址,注意这里被改写成了仓库内图片文件名(如jwWCPZVTIA4IKM-8WipLF.png),对应 public/images 下的静态资源;
  • width/height/ratio用于前端做比例适配与清晰度选择。

2.2 互动数据statistics

{ "admire_count": 0, "comment_count": 21582, "digg_count": 1246636, "collect_count": 64460, "play_count": 0, "share_count": 172803 }
  • digg_count:点赞数(该样本单条视频超过 124 万);
  • comment_count/collect_count/share_count:评论、收藏、分享数;
  • admire_count:赞赏数;
  • play_count:播放数(部分来源为 0,前端会做兜底处理)。

这些字段直接驱动作品列表、视频卡片上的数字展示。

2.3 状态与权限statusaweme_control

{ "status": { "listen_video_status": 0, "is_delete": false, "allow_share": true, "is_prohibited": false, "in_reviewing": false, "part_see": 0, "private_status": 0, "review_result": { "review_status": 0 } }, "aweme_control": { "can_forward": true, "can_share": true, "can_comment": true, "can_show_comment": true } }

前端通过status.is_deletein_reviewing等决定是否渲染该条视频,通过aweme_control控制转发、分享、评论按钮的可用性。

2.4 背景音乐music

{ "id": 7123453673090321000, "title": "禁盗用", "author": "LoveW_", "cover_medium": { "uri": "720x720/aweme-avatar/...", "url_list": ["https://p3-pc.douyinpic.com/..."], "width": 720, "height": 720 }, "cover_thumb": { "uri": "100x100/aweme-avatar/...", "url_list": ["..."], "width": 100, "height": 100 }, "play_url": "...", "duration": 0, "user_count": 0, "owner_id": 0, "owner_nickname": "" }

music提供 BGM 的标题、作者、封面缩略图与播放地址,用于视频播放器的音乐组件展示。


三、前端加载链路:从静态文件到组件渲染

3.1 接口定义 src/api/user.ts

项目在 API 层声明了用户作品列表接口:

export function userVideoList(params?: any, data?: any) { // 内部通过 axios/http 请求 '/user/video_list' }

3.2 Mock 层拦截 src/mock/index.ts

由于项目没有真实后端,Mock 层用 axios-mock-adapter 拦截/user/video_list请求,并从静态数据文件读取:

mock.onGet(/user\/video_list/).reply(async (config) => { const id = config.params.id const r2 = await _fetch(`${FILE_URL}/user_video_list/user-${id}.md`) const v = await r2.json() if (v) { return [200, { data: v, code: 200 }] } return [200, { code: 500 }] })

这段代码直接印证了文件名约定:请求user_video_list({ id })时,会加载user_video_list/user-${id}.md文件,并对 7z 内的 JSON 执行r2.json()解析。因此这里的.md扩展名对浏览器而言是"无妨的容器",真正起作用的是压缩包内的 JSON 内容。

另外,在"我的"页面接口video/my中,默认加载的是 user-12345xiaolaohu.md:

mock.onGet(/video\/my/).reply(async (config) => { const page = getPage2(config.params) if (!userVideos.length) { const r = await _fetch(BASE_URL + '/data/user_video_list/user-12345xiaolaohu.md') const list = await r.json() // ... 将 userList 中的作者信息合并进每条视频 userVideos = list.map((w) => { const item = userList.find((a) => String(a.uid) === String(w.author_user_id)) if (item) w.author = item return w }) } return [200, { data: { pageNo, total: userVideos.length, list: userVideos.slice(page.offset, page.limit) }, code: 200 }] })

注意:user-12345xiaolaohu.mduser-8357999.md同构数据(均为 7z 压缩的 JSON 数组),且共用 public/data/users.json 中的用户信息做作者回填,说明这套数据格式在整个用户作品场景中是一致的。

3.3 组件消费 UserPanel.vue

用户主页面板通过userVideoList({ id })拉取该用户的全部作品:

import { userVideoList } from '@/api/user' // ... let r: any = await userVideoList({ id })

随后把返回的list交给视频列表组件进行滑动播放;Me.vue 的"我的作品"Tab(name="videoList")同样消费这份数据。


四、数据如何生成:仓库中的 Node 处理脚本

public/data/user_video_list/*.md并非手写,而是由仓库node/目录下的脚本处理生成的。相关脚本包括:

  • node/process-post-list.js:读取node/post/data/下各用户的作品 JSON,按用户交叉排列后写出posts.json/posts6.json
  • node/post/process-post.js 与 node/post/process-post-img.js:处理图文/视频帖子与图片;
  • node/user/process-user.js、node/user/process-user-img.js:处理用户数据及头像图片;
  • node/comment/process.js:处理评论数据;
  • node/remove-dist-json.js:构建后清理 dist 中的 JSON;
  • node/rename.js:批量重命名文件。

以 node/process-post-list.js 为例,其核心逻辑为:遍历post/data/目录下所有用户作品 JSON,记录每个文件的最大长度,然后按下标交叉取出各用户视频组成新列表,前 6 条写入posts6.json,其余写入posts.json,并通过users.find(a => a.uid == v.author_user_id)回填作者对象:

let data = newList.slice(0, 6) data.map((v) => { let u = users.find((a) => a.uid == v.author_user_id) if (u) v.author = u }) fs.writeFileSync('./posts6.json', JSON.stringify(data, null, 2)) fs.writeFileSync('./posts.json', JSON.stringify(newList.slice(6), null, 2))

从脚本结构与输出文件命名可以推断:user_video_list/*.json为脚本的原始输入(抓取自抖音接口),随后被压缩为.md容器放入public/data/,供 Mock 层静态加载。


五、与公共数据文件的联动

user_video_list中的记录通过author_user_id与以下公共数据联动:

  • public/data/users.json(及 users.md):用户基础信息,Mock 中通过baseStore.users查找作者对象回填w.author
  • public/data/posts.json:总帖子数据(当前为 62 条,字段如idmodel_typenote_card),是推荐流的数据源之一;
  • public/data/comments:评论数据,与视频aweme_id关联;
  • public/data/videos.md:同样以 7z 容器存放的视频列表数据。

这种"数据集中托管在public/data/、Mock 层按接口模式读取"的做法,使项目在无后端的情况下完整复现了抖音 App 的信息流、用户主页、评论与搜索体验。


六、实操:如何在自己的分支中复用这套数据

  1. 解压任意用户数据:用py7zr或 7-Zip 打开public/data/user_video_list/user-<id>.md,得到同名 JSON 数组;
  2. 接入新用户:将新的 JSON 数组压缩为 7z 并命名为user-<uid>.md放入public/data/user_video_list/,同时在 public/data/users.json 中补充对应用户信息(uidauthor_user_id对应);
  3. 前端联调:调用userVideoList({ id }),Mock 层会自动匹配user-${id}.md并返回数据;
  4. 重新生成聚合数据:修改node/post/data/下的输入 JSON 后,运行 node/process-post-list.js 可重新生成posts.json系列文件(示例脚本使用 ES Module 与 fs 直接读写,运行环境为 Node 18+,详见 node/package.json)。

前提说明:以上数据管线描述基于当前仓库实际文件与脚本推断而来,脚本中fs.writeFileSync的输出路径为脚本所在目录的相对路径,具体落地位置以仓库实际结构为准。


小结

user_video_list/user-8357999.md是 douyin 项目中一个颇具代表性的数据文件:以 7z 压缩 JSON 伪装 Markdown 扩展名,承载 57 条抖音作品的完整字段(播放、统计、状态、音乐、控制权限),经 src/mock/index.ts 的/user/video_list拦截加载,最终驱动用户主页作品列表渲染,并由node/下的脚本与users.json共同维护其生成与作者关联。理解这条管线,也就理解了整个仿抖音项目"静态数据模拟真实接口"的核心设计思路。

【免费下载链接】douyinVue3 + Pinia 仿抖音,Vue 在移动端的最佳实践 . Imitate TikTok ,Vue Best practices on Mobile项目地址: https://gitcode.com/GitHub_Trending/do/douyin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询