我是真的没有想到之前做的《把网易云歌名输出到OBS【威力增强版】》竟然真的有人在用,而且终于,把这个小玩意儿的最大的限制再次摆在了我的面前。
0x00. PTE的最大限制
Process Title Extractor(又叫PTE),也就是我这个小玩意儿,基于常见音乐软件都有在标题栏放当前播放音乐名称的习惯。读取这个信息是非侵入式(non-intrusive)的做法,不会因为目标软件版本变化而崩掉。我当然是因为图省事用了进程标题作为歌曲名的来源,因为通常这些软件进程只有一个窗口,比如Foobar2000和QQ音乐。而读取进程标题是.Net自带的一个基本方法,一行代码就能搞定,不需要调用Win32API,而且反正是能用。
其实PTE到了网易云音乐就有点崩了,因为网易云有好几个cloudmusic.exe,但是恰好只有一个EXE的标题是有内容的,所以还是能简单判断一下标题非空来解决。
但是最终还是折在了PotPlayer上面,因为PotPlayer有个奇怪的特性:它的界面有多个窗口,进程标题用的是你当前点击的那个窗口的标题。所以一旦你点击了播放列表,PTE的歌名获取就崩掉了。你可以点这里看到热心网友的反馈(竟然还有录屏!),说实话我是相当感动了,因为PTE竟然真的有人用……
0x01. 新的思路
其实思路早就是清晰的,调用Win32API来枚举窗口:EnumWindows,然后根据前一步获取的窗口句柄来获取窗口标题:GetWindowTextW,原理上依然很简单,但是做一个比较合适的界面很烦人,毕竟系统里的窗口是如此之多,.Net的Winform看着就有点寒酸了。
当然还是因为懒,所以直接上Python。人生苦短……
0x02. 实现原理
0x03. 使用说明
安装
因为这次换成了Python开发,所以左思右想还是提供两种使用方式,一种是自行配置Python环境,直接双击打开,适合有Python经验的各位(没经验也不是很难);一种是Nuitka直接打包整个环境,解压双击EXE的开袋即食方式,适合像我一样比较懒的各位,自行挑选咯。
使用
这次的使用也不难,先看图文教程吧。
首先,你得把APP启动了,不管是app.py还是app.exe,应该长这个样:
默认情况下,程序运行在 http://127.0.0.1:14514 这么一个莫名其妙的端口上,当然是为了防止端口冲突别多想,如果确实有冲突,程序支持-p或者–port参数指定一个端口,例如-p 80就可以开在80端口上。
你要做的,就是用浏览器打开这个网址。
正常情况下,你会看到你当前电脑里运行的所有窗口和标题,用左上的文本框筛选一下,应该很容易就能找到你需要的那个窗口:
找到之后,点击对应的窗口句柄,就进入了你应该很熟悉的设置界面:
这个页面会展示出当前选择的窗口句柄和对应标题,而且会实时展示你写的捕获格式到底捕获到了啥。
你可以慢慢调整到OK,然后复制粘贴到输出格式里去。
Protip:这个框其实除了Foobar的%title%格式,理论上还支持直接用Python正则表达式(当然没怎么经过测试,不好用的可能性更大一些),总之还是先用这个以前的格式吧。
这次实际上主推的方式是网页版而不是文本,但你依然可以勾选文本模式:
开启文本输出勾选之后,在程序所在的文件夹会出现和以前一样的music_title.txt,这次就不支持修改路径了(反正意义也不大)。
点击提交之后,进入成功页面,你就可以点开网页版预览一下了:
OBS端设置
对于文本方式,在OBS添加“文本(GDI+)”源,然后勾选“从文件读取”,选择music_title.txt就行,和以前一样。
这次主推的方式,在OBS添加“浏览器”源,URL输入“http://127.0.0.1:14514/title”,宽高建议设置成你的屏幕宽高,建议勾选“不可见时关闭源”。正常情况下,白色的歌名内容就出现在屏幕上了,拖动到合适的位置即可。
0x04. 高级自定义
为什么说主推浏览器源,因为浏览器源实际上是一个网页(废话),这意味着能调整的东西比文本源多太多了。
浏览器源的原始页面在程序的templates\title.htm(有个title-sample.htm是默认值,改崩了可以改回来),你只需要知道一个参数就是“{{ op }}”这个标签指的是歌名就行。({{ id }}是歌名对应的随机数字id,2023.01.20更新后加入)
这是个网页,所以你可以动用几乎一切可以用的前端手段来调整它!
比如,加个阴影:
<h1 id="{{ id }}" class="fancy">{{ op }}</h1> <style> .fancy { color: white; padding-left: 1em; text-shadow: -2px 2px 0px #00e6e6, -4px 4px 0px #01cccc, -6px 6px 0px #00bdbd; } </style>
修改完成之后,记得重启程序才能生效。得到的结果是这样:
就开始有点离谱了对不对……这在上个版本是几乎无法做到的(你愿意折腾倒也是可以)。
当然,由于这个网页每秒自动刷新一次(不然歌名怎么变),所以不建议用CSS动画,因为每秒都会卡顿一下。但是没人阻止你用啦……
而且,这个网页的基础框架在templates\showtitle.htm,如果你想要做出更多修改,可以从这里入手。
甚至,由于title.htm对应的API在“http://127.0.0.1:14514/titletext”,你甚至可以自己再做一个反代,然后……我不是搞前端的我就不多说了。
# 2023.01.20更新:引入了htmx的插件idiomorph,此插件可以实现拉取内容的按需更新,打个比方,如果两次拉取的内容id和内容都一样,这个插件会阻止htmx刷新当前内容,也就是说,每次拉取如果歌名不变,则内容也不做任何刷新,这样就可以保留CSS动画的播放状态。
举例:
修改templates\showtitle.htm,加入所需的CSS动画样式:
{% extends 'framework.htm' %} {% block headcss %} {% endblock %} {% block body %} <div hx-ext="morph"> <div hx-trigger="load, every 1s" hx-get="/titletext" hx-swap="morph:innerHTML" class="content"> </div> </div> <style> .neon{ animation: neon 1s ease infinite; margin: 1em; } @keyframes neon { 0%, 100% { text-shadow: 0 0 10px #ef00e3a8, 0 0 20px #ef00e3a8, 0 0 20px #ef00e3a8, 0 0 20px #ef00e3a8, 0 0 2px #fed128, 2px 2px 2px #806914; color: #f5efcb; } 50% { text-shadow: 0 0 2px #800e0b, 0 0 5px #800e0b, 0 0 5px #800e0b, 0 0 5px #800e0b, 0 0 2px #800e0b, 4px 4px 2px #40340a; color: #eda0d3; } } </style> {% endblock %}
然后在templates\title.htm放上所需的class和对应的id(插件需要一个id属性来工作):
<h1 id="{{ id }}" class="neon">{{ op }}</h1>
结果就像下面这样:
更离谱了!
0x05. 结语
Python还是真香啊……
2023年8月前来挖坑,非常感谢,这对我有非常非常大的帮助,爱来自湖的北边❤
确实很增强,至少不会傻傻去捕捉同进程不同句柄的桌面歌词了。。。。之前自己也搓了一个,不过用pywin32获取窗口名字还要管理员,比您的旧旧版还烂(笑)
大佬大佬,啥时候做个获取歌词的插件!