我正在写a small Python program来播放终端中8tracks的播放列表。
它由三部分组成:client.py
使用stdlib的cmd模块,api.py
模块使用python请求访问API,player.py
模块使用在从模式下创建一个mplayer子进程并向其发送命令。
到目前为止,问题在于除了轮询子进程'stdout之外,我没有别的办法可以判断一首歌是否已经在mplayer中播放完了。这意味着我必须观看这个过程,以便在歌曲播放完毕后我可以请求并开始播放列表中的下一首歌曲。
问题是等待子进程会阻塞cmd
模块的主循环。我不能简单地在单独的线程或进程中运行它,因为我必须共享对子进程'stdout的引用,并且这些引用不能在进程之间共享。
我想到了不同的解决方案。我可以将player.py
放在一个单独的进程中并通过队列发送文本命令,但这会使事情过于复杂。我可以创建一个Twisted应用程序,但Twisted非常大,我不知道从哪里开始。另外,我不希望在我的项目中有这样的依赖。
第三种解决方案是使用Gevent。问题是我如何使用cmd
模块。据我所知,Gevent,我必须在每个我“等待”某事的地方屈服。在这种情况下,这将是在HTTP请求期间,在cmd.cmdloop()
期间等待以及在子进程轮询之间的暂停期间。但是如何让cmd
模块产生?某种子类或猴子修补?
答案 0 :(得分:1)
从代码的外观来看,你可以在与mplayer通信时使用pexpect。 Pexpect(或者对此事的期待)非常适合来回传播。
答案 1 :(得分:-2)
似乎我明白了。我以前尝试使用线程的所有尝试都使用multiprocessing.dummy
模块,该模块包装了threading
模块,但在传递参数时行为略有不同 - 您无法传递引用。
直接使用threading
,它似乎有效。我是通过每次加载新曲目时启动后台线程来实现的。当曲目播放完毕后,我向客户端发送SIGUSR1
信号,通过加载和播放新歌来处理它。
player.py
import os
import signal
import threading
from pipes import quote
class MPlayer(object):
def __init__(self):
self.process = Process(['mplayer',
'-slave', '-idle',
'-really-quiet', '-msglevel', 'global=6:cplayer=4', '-msgmodule',
'-input', 'nodefault-bindings',
'-cache', '1024',
], bufsize=1)
self.write_lock = threading.Lock()
# (...)
def load(self, path):
with self.write_lock:
self.p.write('loadfile {}\n'.format(quote(path)))
def wait_for_finish(process):
# HERE: poll process for track ending with process.read()
os.kill(os.getpid(), signal.SIGUSR1)
t = threading.Thread(target=wait_for_finish, args=(self.process,))
t.daemon = True
t.start()
client.py
import cmd
import signal
from player import MPlayer
class PlayCommand(cmd.Cmd, object):
def __init__(self, *args, **kwargs):
# (...)
self.p = MPlayer()
signal.signal(signal.SIGUSR1, self._song_end_handler)
def _song_end_handler(self, signum, frame):
print('SIGUSR1!!!!!!!!!!111!1')
# HERE: Fetch new track URL
self.p.load()
但是,如果有人认为他/她已找到使用协同程序或事件的更好解决方案,请随时回答您的解决方案。