群晖NAS自动更换登录页背景

群晖默认的登陆页实在是太单调了,之前看过很多抓取必应图片自动更换的例子,今天也想亲自实战一把应用在群晖登陆页上。于是 Google 到了这样一篇文章巧用计划任务修改群晖登录壁纸,这是一篇发布时间较久的文章,其中的一些信息可能已经失效,但其他地方的很多文章都是拷贝这里的,所以还是决定按图索骥试一试。

这个文章是用 shell 写的,但我觉得里边的功能用 PHP 也能实现(这怎么会难得住世界上最好的语言呢😜),通过必应首页控制台分析可知,从一个地址https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&nc=1572500909531&pid=hp&FORM=BEHPTB&video=1获取图片 URI,返回完整结果如下。取的图片 URI 后再以 GET 方式获取图片内容。进一步分析可得,URL中的 idx 为 0 时表示返回当天图片,1 为前一天,以此类推,最大有效值为 8。

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
{
"images": [
{
"startdate": "20191031",
"fullstartdate": "201910311600",
"enddate": "20191101",
"url": "/th?id=OHR.AlbertaOwl_ZH-CN1184867720_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp",
"urlbase": "/th?id=OHR.AlbertaOwl_ZH-CN1184867720",
"copyright": "一只坐在白杨树上的大雕鸮,阿尔伯塔 (© Ambre Haller/Getty Images)",
"copyrightlink": "/search?q=%e5%a4%a7%e9%9b%95%e9%b8%ae&form=HPCAPT&mkt=zh-cn",
"title": "",
"quiz": "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20191031_AlbertaOwl%22&FORM=HPQUIZ",
"wp": true,
"hsh": "a76d074e286a1dd8aa778815fe611e2e",
"drk": 1,
"top": 1,
"bot": 1,
"hs": []
}
],
"tooltips": {
"loading": "正在加载...",
"previous": "上一个图像",
"next": "下一个图像",
"walle": "此图片不能下载用作壁纸。",
"walls": "下载今日美图。仅限用作桌面壁纸。",
"play": "播放视频",
"pause": "暂停视频"
}
}

接下来,我们要分析群晖是从何处读取的图片,从查到的文章可知,图片位于/usr/syno/etc/login_background.jpg/usr/syno/etc/login_background_hd.jpg。所以我们要到终端一探究竟。首先,我们需要在控制面板的终端里开启 ssh 服务

然后我们以命令ssh username@hostname [-p port]登陆 NAS,由于图片存储位置需要 root 权限,我们还需要用命令sudo -i切换到 root 用户。(如果需要在公网ssh登陆,只需要在路由器上将 NAS 的 ssh 端口转发即可)

小插曲:当我配置了.ssh/config 想要 ssh 免密登陆时,发现 NAS 仍然每次要求密码,使得我一度以为是配置有误,直到我重新给桌角吃灰的树莓派通电,才发现确实是 NAS 系统的问题,最后找到了这样一篇文章设置群晖 6.1 以及 6.2 使用证书免密登录,在按照文章重启 sshd 服务尝试修复时,发现 sshd 服务起不来了😅,最后终于在控制面板 ==> 信息中心 ==> 服务 中重新启动服务。嗯,我觉得我还能忍,也就每次登陆的时候多花两秒钟嘛,强迫症要适时低头

当我进入系统后,发现/usr/syno/etc/下并没有login_background*.jpg的文件,然后在控制面板的主题中修改登录页图片,并用ll -t命令观察该目录下的变化,发现出现了这两个文件,时间也是刚刚操作的时间,因此可以判定确实读取该位置的文件。

接下来,我们通过代码直接获取图片内容及介绍信息,填充登陆页。

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
<?php
// 获取图片信息
$img_info = file_get_contents('https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&pid=hp&FORM=BEHPTB&video=1');

// 请求失败时,使用「Server 酱」将错误信息推送至绑定的微信
if (false === $img_info) {
file_get_contents('https://sc.ftqq.com/SCg552xxx.send?text=' . urlencode('Get bing wallpaper error!'));
}

$img_obj = json_decode($img_info)->images[0];

// 获取图片地址
$img_url = 'https://cn.bing.com' . $img_obj->url;

// 获取图片名
$url_params = parse_url($img_url)['query'];
parse_str($url_params, $result);
$filename = $result['id'];

// 下载图片并保存
// 注意文件路径写为绝对路径,避免出错
$img_rsc = file_get_contents($img_url);
file_put_contents('/var/services/homes/palemoky/work/bing_wallpaper/' . $filename, $img_rsc);

// 将图片复制到目标位置
copy('/var/services/homes/palemoky/work/bing_wallpaper/' . $filename, '/usr/syno/etc/login_background.jpg');
copy('/var/services/homes/palemoky/work/bing_wallpaper/' . $filename, '/usr/syno/etc/login_background_hd.jpg');

// 获取图片描述信息
list($msg, $title) = explode('(', rtrim($img_obj->copyright, ')'));

// 修改欢迎信息
shell_exec('sed -i "/login_welcome_title/c login_welcome_title=\"' . $title . '\"" /etc/synoinfo.conf');
shell_exec('sed -i "/login_welcome_msg/c login_welcome_msg=\"' . $msg . '\"" /etc/synoinfo.conf');

小插曲:这里我们需要修改系统配置文件/etc/synoinfo.conf,我查询到的 php 修改文件方式都是把整个文件读取出来,然后再把修改后的文件赋值给变量,最后再覆写原来的文件,这种方式不太优雅,我修改一个字符都要读取全部的文件并回写,因此选择使用了 sed 来操作。sed 相同的命令在 Mac 下执行就会报 sed: 1: "synoinfo.conf": unterminated substitute pattern 的错误,但 Linux 下就完全正常,Unix 和 Linux 系统间还是要注意这些微小的差别,害得我调了半天。

把以上代码保存为文件,由于背景图文件是 root 用户,因此需要使用 root 用户执行php .../bing_wallpaper.php,如果发现在目标位置产生了最新的背景图文件,并在主题设置页生效,则说明代码工作正常。

接下来就是创建计划任务了,离成功只有一步了

至此,大功告成,来看下效果图。emmmm, not bad.

最后,如果你觉得左上角的信息有些无趣,也可以用chinese-poetry每天自动更换古诗词,或者抓取 One·一个的每日一句替换,还可以抓包分析豆瓣的 iOS 每日电影 widget,获取电影台词……更多玩法等待解锁。

— 2019.11.10 更 —

Chrome 首页插件 Momentum 的图片会以 GET 请求https://api.momentumdash.com/feed/bulk?syncTypes=backgrounds&localDate=2019-11-10header中必须携带authorization参数,其它参数均为可选。URL 中的localDate为必填,但没有实际意义,日期结果由x-momentum-clientdate控制,未设定x-momentum-clientdate则表示为当日日期。

获取authorization可在页面网络中查看,以下是 Insomnia 的请求及返回

感谢@我的精神家园 提供的 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#/usr/bin/python3

import time
import random
import requests
from PIL import Image
from urllib.request import urlretrieve

client_id = '' # 替换成自己的
directory = '/friday'
filename = directory + '/bg.jpg'
bg_blur = directory + '/bg_blur.jpg'
today = time.strftime("%Y-%m-%d")

headers = {
'authorization': 'Bearer eyJ0eXA...', # 替换
'Host': 'api.momentumdash.com',
'Accept': '*/*',
'X-Momentum-ClientId': client_id,
'x-momentum-clientdate': '2018-03-16',
'X-Momentum-Version': '0.100.1',
'x-momentum-settings-etag': '0400bf20-0000-0000-0000-5aaf255a0000',
'X-Momentum-ClientDate': today,
'X-Requested-With': 'XMLHttpRequest',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, li ke Gecko) Chrome/57.0.2987.133 Safari/537.36',
'Content-Type': 'application/json',
'cookie': '' # 替换
}

r = requests.get('https://api.momentumdash.com/feed/bulk?syncTypes=backgrounds&localDate=' + today, headers=headers)
# print(r.content)
# print(r.text()print(r.json())
image_list = r.json()['backgrounds']
image_url = ''
print(image_list)
for image in image_list:
if image['forDate'] == today:
image_url = image['filename']
print(image_url)
if image_url:
urlretrieve(image_url, filename)
img = Image.open(filename)
new_img = img.resize((1536, 1023), Image.ANTIALIAS)
new_img.save(filename, quility=100)
因为热爱,所以执着。