把网易云歌名输出到OBS【威力再度增强版】

我是真的没有想到之前做的《把网易云歌名输出到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. 实现原理

你可能并不想看,想看再点开
实现原理自然是用Web来做这个界面!都2023年了,各大厂商都开始习惯于用浏览器做界面了,比如libCEF啊Electron啊,Tauri什么的听说过没用过。但总之,用Web搓一个界面是再简单不过的了。

Python做个Web端就是用Flask而已,我们的小玩意儿不需要考虑百万并发,Flask绰绰有余。CSS用Bulma,逻辑部分上htmx,用Flask自带的模板渲染就足够了。

Win32API的调用有Python自带的ctypes,也不是非常复杂,从StackOverflow抄一个下来就妥了。

0x03. 使用说明

安装

因为这次换成了Python开发,所以左思右想还是提供两种使用方式,一种是自行配置Python环境,直接双击打开,适合有Python经验的各位(没经验也不是很难);一种是Nuitka直接打包整个环境,解压双击EXE的开袋即食方式,适合像我一样比较懒的各位,自行挑选咯。

自行配置Python环境法点这里
自行配置Python环境稍微复杂一些,当然建议你配置一个然后顺便学一下Python,很香的。

第一步,从Python的官网下载安装包:点击此处。下载,双击,一路Next,记得碰到那个“Add to PATH”的选项一定要勾上就行,百度一下到处都是教程,这里不多展开了。

第二步,直接下载源码:PTEII-main(没错只有72K而已),或者去GitHub下载最新版,如果我忘了在这里更新的话(应该一定会忘):点击此处打不开GitHub不是我的问题:)

第三步,正常情况下,解压源码,双击app.py,程序就启动了。不正常情况下,建议重装Python,或者换下面的安装方法……

开袋即食法点这里
最不折腾的方法,大家都喜欢:)

第一步,下载完整打包的PTEII:PTEII-230120-executable(15.8MB……),当然如果有条件的话,去GitHub下载一个Release包更好,因为我的网站水管太小:点击此处

第二步,找个好地方,解压,双击app.exe,程序就启动了。Nice.gif!

使用

这次的使用也不难,先看图文教程吧。

首先,你得把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还是真香啊……

项目地址:https://github.com/Raka-loah/PTEII

分享到: