2

現在の状況

端末で 8 トラックのプレイリストを再生する小さな Python プログラムを作成しています。

これはclient.py、stdlib のcmdモジュールapi.pyを使用するモジュール、python-requests を使用して API にアクセスするplayer.pyモジュール、スレーブ モードで mplayer サブプロセスを作成してコマンドを送信するモジュールの 3 つの部分で構成されます。

問題

これはこれまでのところ機能していますが、問題は、サブプロセスの stdout をポーリングする以外に、mplayer で曲の再生が終了したかどうかを確認する方法が他にないことです。これは、曲の再生が終了した後にプレイリストの次の曲をリクエストして開始できるように、プロセスを監視する必要があることを意味します。

問題は、サブプロセスを待機するとcmdモジュールのメイン ループがブロックされることです。ただし、サブプロセスの標準出力への参照を共有する必要があり、これらのような参照はプロセス間で共有できないため、別のスレッドまたはプロセスで単純に実行することはできません。

可能なアプローチ

これについては、さまざまな解決策を考えました。別のプロセスを作成し、キューを介してテキスト コマンドを送信することもできplayer.pyますが、それでは複雑すぎます。Twisted アプリを作成できましたが、Twisted は非常に大きく、どこから始めればよいかわかりません。また、プロジェクトにそのような依存関係を持たないことをお勧めします。

3 番目の解決策は、Gevent を使用することです。問題は、これをcmdモジュールで動作させる方法です。私が理解している限り、私は何かを「待っている」あらゆる場所で屈服しなければなりません。この場合、これは HTTP 要求中、待機中、cmd.cmdloop()およびサブプロセス ポーリング間の一時停止中です。しかし、どうすればcmdモジュールを生成できますか? ある種のサブクラスまたはモンキーパッチ?

4

2 に答える 2

-1

思いついたようです。スレッドを使用する以前の試みはすべてモジュールを使用していました。これは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()

それでも、誰かがコルーチンやイベントを使用してより良い解決策を見つけたと思う場合は、遠慮なく解決策を答えてください。

于 2013-04-09T19:43:58.683 に答える