前言
豆瓣刚出小组件的时候,我用过一段时间豆瓣电影日历,当时的体验还不错。后来在慕尼黑Hugendubel书店,有一个区域是专门卖各种日历的,有风景画,哈利·波特主题,和二次元等等。我是为了看推理小说才去的那家书店,所以萌生了做一款推理主题日历的想法。
我想先从最简单的功能开始,因为是在手机上观看,日历卡片设计成了竖屏,日历的条目包括推理主题的书籍和电影,最上面是一张作品的海报,下方是作品名称,类型等信息。
我先用Canvas设计了一个海报,然后每天手动更新图片和信息。结果第二天我就出了差错,只更新了日期,但没更新星期几。虽然Canvas的设计我觉得还挺好看的,但手动更新时间毕竟不是长久之计,我开始考虑用Python生成海报。
开发过程
因为最初的想法只是根据手动输入的网址生成一张海报,然后用脚本自动标记上时间,防止时间出现错误,所以我先用Pillow这个Python包写了一下海报生成的代码。我主要是设计了一下海报和文字的位置和颜色,其中颜色是根据条目的主题而变化的,每天都不一样。豆瓣评分的图标用的是GitHub上常见的设计。
我随便选了一个可以商用的字体,叫作「站酷文艺体」。可有一次我发现《和騎士度過的那一夜》这本推理短篇集的名字无法正确显示,才意识到字体需要包含简体和繁体,所以更换成了「霞鹜文楷」。
我试用了一下,因为不满足于手动输入图片网址的繁琐,又找了一下网上的豆瓣爬虫,自动抓取豆瓣条目的图片。不过由于豆瓣条目的网址还是需要手动输入,整个流程很费时间。我想到了使用一个json文件来存储这些网址,脚本只需要在json文件里读取最新输入的条目。等以后积累的条目多了,就可以随机选取一条来当作背景图片。
每天输入一条的话,不知道要等到猴年马月才能积累足够的条目。偶然间发现了一个GitHub Action,可以从豆瓣上爬取用户标记过的作品。一次性把全部条目下载下来,每个条目包含了名称,类型,上映/出版日期等信息,十分方便。
{
"comment": "《巧克力公寓谋杀案》",
"rating": {
"count": 1,
"max": 5,
"star_count": 4,
"value": 4
},
"sharing_text": "我的评分:★★★★ 《巧克力公寓谋杀案》 https://book.douban.com/subject/36480672/ 来自@豆瓣App",
"sharing_url": "https://www.douban.com/doubanapp/dispatch?uri=/subject/36480672/interest/4036595172",
"tags": [],
"charts": [],
"platforms": [],
"vote_count": 1,
"create_time": "2024-03-15 20:05:51",
"status": "done",
"id": 4036595172,
"is_private": false,
"subject": {
"rating": {
"count": 2253,
"max": 10,
"star_count": 3.5,
"value": 6.8
},
"controversy_reason": "",
"pubdate": [
"2024-1"
],
"pic": {
"large": "https://img2.doubanio.com/view/subject/l/public/s34735111.jpg",
"normal": "https://qnmob3.doubanio.com/view/subject/m/public/s34735111.jpg?imageView2/2/q/80/w/200/h/300/format/jpg/sharpen/1"
},
"honor_infos": [],
"other_versions_count": 0,
"is_show": false,
"vendor_icons": [
"https://img1.doubanio.com/f/frodo/161387ce451872cbf51fcb288cd5ffd1dcd89705/pics/vendors/logo_dedao@2x.png",
"https://img1.doubanio.com/f/frodo/f6f620132e6d8a02d171f03114bbe2339aa8af97/pics/vendors/logo_doubanread@2x.png"
],
"card_subtitle": "紫金陈 / 2024 / 湖南文艺出版社",
"book_subtitle": "",
"id": "36480672",
"author": [
"紫金陈"
],
"is_released": true,
"vendor_original_price": "",
"color_scheme": {
"is_dark": true,
"primary_color_light": "a55634",
"_base_color": [
0.05000000000000001,
0.6837606837606838,
0.9176470588235294
],
"secondary_color": "f9f6f4",
"_avg_color": [
0.045662100456621,
0.6666666666666666,
0.8588235294117647
],
"primary_color_dark": "7f4228"
},
"type": "book",
"cover_url": "https://dou.img.lithub.cc/book/36480672.jpg",
"min_sale_price": null,
"press": [
"湖南文艺出版社"
],
"pages": [
"424"
],
"sharing_url": "https://book.douban.com/subject/36480672/",
"url": "https://book.douban.com/subject/36480672/",
"title": "长夜难明 : 双星",
"uri": "douban://douban.com/book/36480672",
"subtype": "book",
"intro": "闹市街头,女子坠楼身亡,腹部有刀伤。\n两天后,一段赵泽宇手持带血匕首逃离案发现场的视频,被人在网上公开。赵泽宇是江北市原市长赵忠悯的独生子,江北知名企业家,案件顿时引发全社会关注。赵泽宇被警方刑拘,可他坚称自己没有杀人,是被人陷害的,事发后他因害怕仓促逃离现场。死者实为跳楼自杀。\n警方当然不信这番狡辩,可通过一系列的技术手段还原案发经过,结果证实死者竟真的是自杀。\n现在警方面临的问题更加棘手了。从证据角度讲,死者是自杀的,赵泽宇无罪,警方应该放人;可视频在前,全网皆知,加上赵泽宇的家庭背景,如果警方对外通报死者是自杀的,谁会相信?人民群众会质疑警方徇私枉法。\n事到如今,各方都希望赵泽宇“有罪”,最好还是重罪,这样才能平息这场风波。",
"buy_more_uri": "",
"null_rating_reason": "",
"article_intros": [
{
"useful_count": 1,
"uri": "douban://douban.com/review/15811580",
"comment_count": 1,
"intro": "没有多么长夜难明,不如改名《巧克力公寓谋杀案》",
"total": 1,
"type": "review"
}
],
"vendor_sale_price": "",
"has_ebook": true
}
},
因为是「推理日历」,我在脚本中加了筛选条件,随机选取数据库中的条目,直到选到悬疑或者推理类的作品为止。然而并不是所有的条目都有类型标注,所以我筛选的方式是判断简介里面是否出现了「悬疑」,「推理」,「侦探」等关键词。
我能想到的发布方式有好几种。最简单省事儿的办法就是发布在社媒上,比如豆瓣相册,小红书,公众号之类的地方。头两天的海报我也确实是这么做的。之前用过Flask,可以在pythonanywhere上发布,优点是站点访问速度较快,但每三个月就要点一下更新,不然站点会过期。
更正式的发布方式,是做成类似「ONE·一个」那样的APP,但我没搞过移动端的开发,对网上看到其他人用的flutter之类的不熟。而且不付费注册的话,只能把APP发布在Test Flight里,不能上架应用商店。其实除了APP,ios还可以使用小组件发布一些轻量级的应用,我看到了两款日语单词学习组件,调用了词典网站或者APP的API,可以随机展示日语单词和翻译,用的就是Scriptable。
我决定使用的办法,就是用GitHub Pages发布日历并且设置成每天自动更新,然后移动端使用小组件来展示网页。日历的网址是https://code-cp.github.io/mystery-calendar/,小组件叫作Web Widget。这款小组件可以根据输入的网址显示网页快照。
不过我适用了一段时间后,发现Web Widget有时候无法更新网页。我发现了另一款相似的小组件Glimpse 2,网页的更新更加及时,试用了几天感觉很不错。
上文提到了GitHub Action,我用它来运行代码库中生成日历的脚本。在代码库中新建一个.github/workflows文件夹,然后创建yml格式的配置文件。GitHub会根据配置文件里设定的运行时间(cron: '0 1 * * *'
)来执行里面的步骤,比如可以每隔一个小时,每天某个时间运行等等。具体如何设置时间,可以参考这个网站。
不过GitHub Action并不能完全按照yml文件里的时间来执行,这取决于GitHub的runner的承载能力。我看网上的讨论说,最好把分钟数(也就是第一位的0)改成非0的值,因为大多数的Action都是在0分的时候执行。
name: run-main
on:
workflow_dispatch:
schedule:
- cron: '0 1 * * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repo content
uses: actions/checkout@v2 # checkout the repository content to github runner
- name: setup python
uses: actions/setup-python@v4
with:
python-version: '3.10' # install the python version needed
- name: install python packages
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: execute py script # run main.py
run: |
cd src/
python main.py
- name: commit files
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git diff-index --quiet HEAD || (git commit -a -m "updated logs" --allow-empty)
- name: push changes
uses: ad-m/github-push-action@v0.6.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: main
但在调试的时候没时间等待它自动运行,往往需要手动执行workflow,可以在配置文件里加上workflow_dispatch:
,就可以在Github网站上点击Run workflow来运行了。
这个脚本先执行checkout repo content
,因为我要运行的是Python脚本,还要加上setup python
,这些都是GitHub提供的。接下来会安装代码库的requirements.txt里的包,然后运行脚本。main.py会生成一个新的日历,保存在images文件夹内,所以需要commit files和push changes,把图片保存在代码库中,GitHub Pages就会在deploy的时候使用新的图片。
结语
制作日历之前,我以为这只是一个很简单的工程,没想到有这么多细节需要处理,比如怎么处理不同格式的时间和字体。除了豆瓣评分,我还想要加上IMDb和烂番茄的评分,但暂时没时间去研究那些网站的API了。
目前这个日历仅仅是一个demo,以后有机会,还是想把它做成「ONE·一个」那样的风格。其实我不仅仅想做一个日历,我还没找到任何一款专门做推理内容的APP,所以更想做一款包含推理资讯,论坛等内容丰富的推理APP。