Loading...

文章背景图

从一段 SQL,到一整套「播放周榜系统」的诞生记

2025-12-20
18
-
- 分钟
|

这篇文章,记录的是一个很完整的过程:
从最初的 weekly_rank.py,到一张“能用”的周榜海报,再到 Apple 风格的视觉系统,最后一步一步被部署到云服务器上,变成可以每周自动生成的「播放周榜(Weekly Playback Statistics)」系统。

它不是一个一蹴而就的脚本,而是一条慢慢长出来的产品线。


一、最初的 weekly_rank:先把“东西算出来”

故事从一个很朴素的需求开始:

想知道这周在 Jellyfin 上到底看了些什么?
能不能自动算一下 Top 3,然后推到手机上?

于是诞生了最初版本的 weekly_rank.py——它的职责非常直接:

  • 从 Jellyfin 的 playback_reporting.db 里读数据

  • 统计电影 / 剧集这周的播放次数与时长

  • 做一个 文字版的 Top 榜单

  • 最后通过 Server 酱推送到手机

这个阶段的关注点几乎全部在“数据正确”上:

  • 怎么算这周?(一开始通常会用“今天往前推 7 天”,后来改成自然周)

  • SQL 写对了吗?有没有把 Episode / Movie 混一起?

  • 排序逻辑是按时长还是按次数?

那时的脚本输出大概长这样(简化):

【NERV-BASE Jellyfin 播放周榜】

统计周期: 2025-12-15 ~ 2025-12-21

📽️ 电影 Top 3:
1. xxx
2. yyy
3. zzz

📺 电视剧 Top 3:
……

那时候的目标非常简单:
先把“讲得清楚”这一步做好。


二、第一次给数据“穿衣服”:简单版海报出场

数据有了之后,很自然的下一个问题就是:

能不能把它们画成一张图?
方便转发、存档、发朋友圈。

于是,Pillow 登场了。脚本多了一个重要角色:

  • draw_poster:负责根据统计结果画一张海报

在这个阶段,版式是典型的“功能优先”风格:

  • 左边大标题

  • 右边是三列榜单

  • 背景一个比较“热情”的红 / 粉 / 紫渐变

  • Top1 封面更大,配合圆形排名标

这种设计的优点是:一眼就有“榜单感”,缺点也很明显:

  • 视觉上“喊得有点大声”

  • 内容层级和留白不够克制

  • 比起 Apple,更像某些游戏活动页

但它完成了一个非常重要的阶段目标:
输出不再是纯文本,而是一张可以公开展示的海报。


三、遇到的第一个瓶颈:重叠、拥挤与“主榜思维”

继续使用过程中,问题自然浮现出来:

  1. 海报元素重叠

    • 斜向排布的封面在某些分辨率 / 排列下会互相盖住

    • 需要调 diagonal_offset_x / diagonal_offset_y 来避免重叠

  2. 版式的“主榜 / 副榜结构”不再适合

    • 一开始的想法是:播放最多的分类上“主榜轨道”,其他略缩小当“副榜”

    • 但慢慢会觉得:

      • 电影、电视剧、番剧,其实在生活里 地位是等同的

      • 不该在呈现上暗示“某个类型更高级”

      于是我们做了第一次比较大的方向调整:

  • 从「主榜 + 辅榜 + 信息列」

  • 转向 「三分类等权」 的设计思路

这时候很重要的一点是:

不是马上改生产脚本,而是先写测试脚本来试版式。
因为版式这种东西,只有生成很多张“假海报”看一遍,才知道哪里舒服、哪里不顺眼。


四、Apple 风格的版式实验:test_poster 与 test_layout_eq

为了让版式更接近 Apple 的视觉语言,我们做了几件事情:

1. 从“装饰”回到“结构”

  • 背景不再是高饱和的炫彩,而是低饱和、柔和的渐变

  • 取消花哨的圆形徽章,改成:

    • 排名数字嵌在卡片内部

    • 小号、低对比度,放在左上角内边距位置

  • 通过 留白和对齐 来建立秩序感,而不是靠重装饰

2. 三列等权的测试脚本:test_layout_eq.py

我们专门写了一个脚本,只干一件事:
用占位色块和方块来验证三列等宽栅格在各种数据组合下是否稳定。

关键参数大致如下:

  • 画布宽度:1080

  • 左右边距:50

  • 列间距:30

  • 三列列宽

  • 每列最多 3 张卡片

  • 无数据时:

    • 不消失

    • 保留列标题

    • 显示灰色提示文字:“本周暂无播放记录”

    我们针对 7 种场景分别生成了测试图:

  1. 仅番剧有数据

  2. 仅电影有数据

  3. 仅电视剧有数据

  4. 电影 + 番剧

  5. 电视剧 + 番剧

  6. 电影 + 电视剧

  7. 三个分类都有数据

通过这些图,我们验证了一件事:

无论哪一列有数据,哪一列空,画面都不会显得单薄或失衡。

这意味着这个版式有潜力做成“长期模板”。


五、从版式实验到视觉系统:visual_system.py

在版式测试稳定之后,我们进一步把它抽象成一个「视觉系统测试脚本」visual_system.py

它和真实生产的差异在于:

  • 不连数据库

  • 不去 Jellyfin 拉封面

  • 用统一尺寸的纯色块 + 模拟剧集名代替内容

但它严格遵守这几条核心规范:

  • 三列等宽栅格

  • 每列固定 3 张卡片位置

  • 有数据:彩色卡片 + 排名 + 剧集名

  • 无数据:灰色卡片 + 提示文案

  • 顶部 Header + 底部 Footer 结构一致

这一步的意义在于

把“这张海报”升级为“一个可以反复复用的视觉系统”。
任何后续的内容,只要遵守这个系统的栅格和层级,都能无痛接入。


六、把视觉系统真正接上数据:weekly_rank_v2

有了视觉系统之后,下一步就是:

把生产脚本从旧版 draw_poster 升级到新的三列等权版式。

于是诞生了 weekly_rank_v2.py

1. 数据侧:依旧稳定

  • 统计逻辑:

    • 电影:ItemType = 'Movie'

    • 剧集:ItemType = 'Episode',再基于 API 信息分为电视剧 / 番剧

  • 周期使用自然周,文本里会明确打印:

    • 统计周期: 2025-12-15 ~ 2025-12-21

  • 依旧会计算“本周片王”(观看时长最高用户),仅用于文字部分,不再硬塞进海报版式里

2. 视觉侧:完全换脑子

draw_poster_v2 里做了以下重要变更:

  • 背景换成低饱和的暖灰 / 冷灰渐变

  • 正中央结构是三列等宽的 Column:

    • 电影 / 电视剧 / 番剧

    • 中英文双标题

  • 每列:

    • 最多 3 条数据

    • 真正有数据就去拿 Jellyfin 封面

    • 拿不到封面时:

      • 用分类颜色画卡片

      • 卡片中间显示剧集名

  • 所有卡片下方都有剧集名一行,最多显示 30 个字符,超出加 ...

  • 排名数字:

    • 小型数字落在卡片左上

    • 白色、半透明、低对比度

    最终效果是
    上映的内容不再被“激动的 UI”抢走镜头,而是安静地站在画布中间。


七、让它自动跑起来:从 Windows 计划任务到云服务器 cron

脚本本地跑得很稳定之后,会自然想到:

既然这是“每周榜”,那它最好每周自动来一次。

1. Windows 计划任务

在本机上,我们配置了一个简单的计划任务:

  • 触发:每周一 10:00

  • 操作:执行 python weekly_rank_v2.py

  • 工作目录:c:\Users\zhanw\Desktop\weekly_rank

这解决了“电脑开着的时候每周自动跑一次”的问题。

2. 问题:那如果电脑关机呢?

答案很现实:
——那就这周没榜了。

为了不受本地电脑开关机影响,我们决定把整套脚本搬到云服务器上运行。

3. 部署脚本:deploy.py

为了避免每次登录服务器手动拷文件、改路径,我们写了一个 deploy.py,它自动做:

  1. SSH 连接到服务器(Ubuntu)

  2. 创建远程目录 /home/ubuntu/weekly_rank

  3. 上传:

    • weekly_rank_v2.py

    • 字体文件(从 Windows 拷贝过去)

  4. sed 把脚本里的字体路径从

    • C:/Windows/Fonts/...

    • 改成 /home/ubuntu/weekly_rank/fonts/...

  5. 安装依赖:

    • Pillow

    • requests

    • paramiko(因为脚本本身还要从 NAS 拿数据库)

  6. 现场跑一次 python3 weekly_rank_v2.py 验证一切正常

  7. 写入 crontab:

   0 10 * * 1 cd /home/ubuntu/weekly_rank && /usr/bin/python3 weekly_rank_v2.py >> /home/ubuntu/weekly_rank/cron.log 2>&1

从此之后:

  • 每周一 10:00

  • 云服务器会自动:

    • 从 NAS 拉最新数据库

    • 统计这周播放数据

    • 生成海报

    • 上传到图床

    • 通过 Server 酱推送一条消息

  • 所有过程输出写入 cron.log,方便之后排错


八、回头看:脚本之外的收获

如果只看结果,现在你拥有的是:

  • 一套 稳定的统计逻辑

  • 一套 克制、Apple 风格的视觉系统

  • 一条 从 NAS → 服务器 → 图床 → 推送 的自动化链路

  • 一份 可以每周准点出现的“生活数据报告”

但从过程来看,更有意思的是这些“隐形的收获”:

  • 从“一张海报”转变为“一个视觉系统”的思维变化

  • 从“手动跑脚本”转变为“自动调度,异常可追踪”的工程习惯

  • 从“先堆功能”到“先搭版式骨架,再慢慢填内容”的设计节奏

如果你有自己的媒体服务器、阅读记录、睡眠日志、运动数据……
完全可以照着这条路子,再做一套「属于自己的周报 / 月报系统」。

下一步你完全可以做的扩展:

  • 月度榜 / 年度回顾海报(在同一栅格上换内容维度)

  • 针对某一个分类(比如番剧)做「特别回顾」

  • 把多年数据做一个「年度观影报告」(那种“你今年看了 123 部影片”的总结)

而你现在已经有了最难也最关键的两个东西:

  • 一个可靠的数据 pipeline

  • 一套足够克制、可以长久不腻的视觉语言

后面就是慢慢往里填故事了。

评论交流

文章目录