Python监听Spotify切歌事件的信号重复、延迟及弃用警告问题
实现目标
本人仅有约两天编程经验,目标是开发一款程序,实现Spotify每次切换歌曲时自动触发对应快捷键。参考相关论坛内容拼凑出初始代码如下:
#!/usr/bin/env python3 # encoding: utf-8 BUS_NAME='org.mpris.MediaPlayer2.spotify' import subprocess import dbus, urllib.request from dbus.mainloop.glib import DBusGMainLoop from gi.repository import GObject def dbus_signal_receiver(interface, changes, sig): if dbus.String('Metadata') in changes: subprocess.run(["/home/user/Scripts/shortcut.sh"]) if __name__ == '__main__': DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus() bus.add_signal_receiver(dbus_signal_receiver, path='/org/mpris/MediaPlayer2', bus_name=BUS_NAME) loop = GObject.MainLoop() loop.run()
现存问题
- 运行程序时抛出弃用警告,已知警告含义但不会修复,当前程序可正常运行,警告内容如下:
/home/lorenz/Scripts/LatestApproach.py:23: PyGIDeprecationWarning: GObject.MainLoop is deprecated; use GLib.MainLoop instead loop = GObject.MainLoop()
- 切歌时DBus会重复输出6次变更信号,导致快捷键在1秒内被触发6次。之前尝试通过
time.sleep添加2秒延迟,最终变成每2秒固定触发一次快捷键,不符合预期。
修复方案
修复弃用警告
直接替换弃用的GObject主循环为GLib主循环即可:
- 将导入语句
from gi.repository import GObject替换为from gi.repository import GLib - 将主循环初始化语句
loop = GObject.MainLoop()替换为loop = GLib.MainLoop()
修复信号重复触发问题
不要用sleep做延迟,这种方式会阻塞事件循环,排队的信号会等sleep结束后依次执行,还是会重复触发。正确的做法是通过歌曲唯一ID做去重:
- 定义变量记录上一次成功触发脚本时对应的歌曲Track ID
- 每次收到Metadata变更信号时,先提取当前播放歌曲的唯一Track ID
- 只有当当前Track ID和上一次记录的ID不一致时,才执行快捷键脚本,同时更新记录的ID;ID一致直接跳过处理即可
修复后的完整代码:
#!/usr/bin/env python3 # encoding: utf-8 BUS_NAME='org.mpris.MediaPlayer2.spotify' MPRIS_PATH = '/org/mpris/MediaPlayer2' import subprocess import dbus from dbus.mainloop.glib import DBusGMainLoop from gi.repository import GLib # 记录上一次处理的歌曲ID,初始为空 last_track_id = "" def dbus_signal_receiver(interface, changes, sig): global last_track_id if 'Metadata' in changes: metadata = changes['Metadata'] # 提取当前歌曲的唯一Track ID current_track_id = metadata.get('mpris:trackid', '') # ID和上次不同才触发脚本 if current_track_id and current_track_id != last_track_id: last_track_id = current_track_id subprocess.run(["/home/user/Scripts/shortcut.sh"]) if __name__ == '__main__': DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus() bus.add_signal_receiver( dbus_signal_receiver, path=MPRIS_PATH, bus_name=BUS_NAME, signal_name='PropertiesChanged' ) loop = GLib.MainLoop() loop.run()
注:代码里删掉了未使用的urllib.request导入,同时明确指定监听
PropertiesChanged信号,避免接收无关信号增加不必要的判断。
内容的提问来源于stack exchange,提问作者Atomfried Fallout
相关产品推荐
相关产品推荐

