]> code.delx.au - bluplayer/blobdiff - bluplayer.py
Run mplayer in a background thread so it doesn't block UI
[bluplayer] / bluplayer.py
index ab10d992ba0e707af7e8626cd2d0aea45b1634b8..4b6f2b8e592467aa5e55e17b290522030898662d 100755 (executable)
@@ -67,17 +67,6 @@ class Title(object):
        def __str__(self):
                return "Title%s: %s %s" % (self.id, self.duration, self.video_url)
 
-       def play(self):
-               cmd = [
-                       MPLAYER_PATH,
-                       "-fs",
-                       "-lavdopts", "threads=%s" % subprocess.check_output("nproc").strip(),
-                       "-volume", "100",
-                       self.video_url,
-               ]
-               logging.info("Running mplayer: %s", self.video_url)
-               subprocess.check_call(cmd)
-
 
 class MakeMkvCon(object):
        def __init__(self, params):
@@ -124,8 +113,30 @@ class MakeMkvCon(object):
                        self.buf += data
 
 
+class MPlayer(QObject):
+       play_finished = pyqtSignal()
+       fatal_error = pyqtSignal(str)
+
+       def play(self, video_url):
+               logging.info("Running mplayer: %s", video_url)
+               try:
+                       cmd = [
+                               MPLAYER_PATH,
+                               "-fs",
+                               "-lavdopts", "threads=%s" % subprocess.check_output("nproc").strip(),
+                               "-volume", "100",
+                               video_url,
+                       ]
+                       subprocess.check_call(cmd)
+               except Exception, e:
+                       self.fatal_error.emit(format_exception("MPlayer failed to play the video", e))
+               finally:
+                       self.play_finished.emit()
+
+
 class MakeMkv(QObject):
        title_loaded = pyqtSignal(Title)
+       title_load_complete = pyqtSignal()
        status = pyqtSignal(str)
        fatal_error = pyqtSignal(str)
 
@@ -152,7 +163,9 @@ class MakeMkv(QObject):
                makemkvcon = MakeMkvCon(["stream", "disc:%s" % disc_number])
                for key, line in makemkvcon:
                        if key == "MSG" and line[0] == "4500":
-                               url = "http://localhost:%s/" % line[6]
+                               # Sometimes the port in field 6 is wrong
+                               port = line[5].split(":")[1]
+                               url = "http://localhost:%s/" % port
                                self.load_titles(url)
                        elif key == "MSG":
                                self.status.emit(line[3])
@@ -168,6 +181,7 @@ class MakeMkv(QObject):
                        title_page = grab_dict(title_list_page["title%d" % i])
                        title = Title(title_page)
                        self.title_loaded.emit(title)
+               self.title_load_complete.emit()
 
        def run(self):
                logging.info("MakeMKV thread started")
@@ -199,13 +213,17 @@ class MakeMkv(QObject):
 
 
 class PlayerWindow(QWidget):
+       video_selected = pyqtSignal(str)
+
        def __init__(self):
                QWidget.__init__(self)
+
                self.title_map = {}
+               self.is_playing = False
 
                self.list_widget = QListWidget(self)
                self.list_widget.itemActivated.connect(self.handle_activated)
-               self.list_widget.setFocus()
+               self.list_widget.setEnabled(False)
 
                self.log = QTextEdit(self)
                self.log.setReadOnly(True)
@@ -220,13 +238,24 @@ class PlayerWindow(QWidget):
                self.setWindowTitle("BluPlayer")
 
        def add_title(self, title):
-               name = "Title %s (%s)" % (title.id, title.duration)
+               name = "Title %s (%s)" % (int(title.id)+1, title.duration)
                self.list_widget.addItem(name)
-               if not self.title_map:
-                       # select the first item
-                       self.list_widget.setCurrentItem(self.list_widget.item(0))
                self.title_map[name] = title
 
+       def select_longest_title(self):
+               longest_title = None
+               longest_item = None
+               for i in xrange(self.list_widget.count()):
+                       item = self.list_widget.item(i)
+                       name = str(item.text())
+                       title = self.title_map[name]
+                       if longest_title is None or title.duration > longest_title.duration:
+                               longest_title = title
+                               longest_item = item
+               self.list_widget.setCurrentItem(longest_item)
+               self.list_widget.setEnabled(True)
+               self.list_widget.setFocus()
+
        def add_log_entry(self, text):
                self.log.append(text)
 
@@ -235,12 +264,18 @@ class PlayerWindow(QWidget):
                qApp.quit()
 
        def handle_activated(self, item):
+               if self.is_playing:
+                       return
                name = str(item.text())
                title = self.title_map[name]
-               try:
-                       title.play()
-               except Exception, e:
-                       popup_error_exit("MPlayer failed to play the video", e)
+               self.is_playing = True
+               self.list_widget.setEnabled(False)
+               self.video_selected.emit(title.video_url)
+
+       def set_play_finished(self):
+               self.is_playing = False
+               self.list_widget.setEnabled(True)
+               self.list_widget.setFocus()
 
        def keyPressEvent(self, e):
                if e.key() in (Qt.Key_Escape, Qt.Key_Backspace):
@@ -256,6 +291,7 @@ def killall_makemkvcon():
 def main():
        logging.basicConfig(format="%(levelname)s: %(message)s")
        logging.getLogger().setLevel(logging.DEBUG)
+       logging.info("Configuring application")
 
        app = QApplication(sys.argv)
        app.setQuitOnLastWindowClosed(True)
@@ -269,23 +305,35 @@ def main():
        player_window.show()
 
        makemkv = MakeMkv()
-       worker_thread = QThread()
-       makemkv.moveToThread(worker_thread)
+       makemkv_thread = QThread()
+       makemkv.moveToThread(makemkv_thread)
 
-       worker_thread.started.connect(makemkv.run)
+       mplayer = MPlayer()
+       mplayer_thread = QThread()
+       mplayer.moveToThread(mplayer_thread)
+
+       makemkv_thread.started.connect(makemkv.run)
        makemkv.title_loaded.connect(player_window.add_title)
+       makemkv.title_load_complete.connect(player_window.select_longest_title)
        makemkv.status.connect(player_window.add_log_entry)
        makemkv.fatal_error.connect(player_window.popup_fatal_error)
 
-       logging.info("Starting worker thread")
-       worker_thread.start()
+       player_window.video_selected.connect(mplayer.play)
+       mplayer.play_finished.connect(player_window.set_play_finished)
+
+       logging.info("Starting application")
+       makemkv_thread.start()
+       mplayer_thread.start()
        result = app.exec_()
 
        logging.info("Shutting down")
-       worker_thread.quit()
+       makemkv_thread.quit()
+       mplayer_thread.quit()
        killall_makemkvcon()
-       logging.info("Waiting for worker thread")
-       worker_thread.wait(2000)
+       logging.info("Waiting for makemkv thread")
+       makemkv_thread.wait(2000)
+       logging.info("Waiting for mplayer thread")
+       mplayer_thread.wait(2000)
        logging.info("Exiting...")
        sys.exit(result)