import argparse import fstools from gui import MainWindow, SelectionDialog, QueueWorker import media import os from PySide6.QtCore import QObject, Signal, Slot from PySide6.QtWidgets import QApplication, QMessageBox import pyudev from pyudev.pyside6 import MonitorObserver from queue import Queue import report import sys logger = report.app_logging_config() # TODO: Stop / Kill Threads before close: QThread: Destroyed while thread '' is still running MEDIA_GUI_DICT = { MainWindow.KEY_ALBUM_ARTIST: media.KEY_ARTIST, MainWindow.KEY_ALBUM_ALBUM: media.KEY_ALBUM, MainWindow.KEY_ALBUM_YEAR: media.KEY_YEAR, MainWindow.KEY_ALBUM_GENRE: media.KEY_GENRE } class ThisQueueWorker(QueueWorker): def __init__(self, basepath): super().__init__() self.__basepath__ = basepath class RipQueueWorker(ThisQueueWorker): def progress_adaption(self, value, track_num): self.progress_updated.emit(50 * value, track_num - 1) def action(self, job_index): logger.info("action: Start ripping track %02d...", job_index + 1) # Rip track job_index track_info = self.__job_data__.get(job_index) if track_info is None: msg = "No track information found for track %02d" % job_index logger.error("action: " + msg) self.set_error(msg) else: wavfile = media.track_to_targetpath(self.__basepath__, track_info, 'wav') try: fstools.mkdir(os.path.dirname(wavfile)) except PermissionError: msg = f"Insufficient permissions to create ripping target path: {os.path.dirname(wavfile)}" logger.error("action: " + msg) self.set_error(msg) else: if media.disc_track_rip(job_index + 1, wavfile, self.progress_adaption) != 0: msg = f"Unable to rip: {wavfile}" logger.error("action: " + msg) self.set_error(msg) class EncodeQueueWorker(ThisQueueWorker): def progress_adaption(self, value, track_num): self.progress_updated.emit(50 + 50 * value, track_num - 1) def action(self, job_index): logger.info("action: Start encoding track %02d...", job_index + 1) # Rip track job_index track_info = self.__job_data__.get(job_index) if track_info is None: msg = "No track information found for track %02d" % job_index logger.error("action: " + msg) self.set_error(msg) else: wavfile = media.track_to_targetpath(self.__basepath__, track_info, 'wav') rv = media.wav_to_mp3(wavfile, self.__basepath__, track_info, self.progress_adaption) if rv != 0: msg = f"Unable to encode: {wavfile}" logger.exception("action: " + msg) self.set_error(msg) try: os.remove(wavfile) except Exception as e: logger.error("action: Unable to delete wavfile %s", wavfile) class RipMainWindow(MainWindow): def read_it(self): self.set_status("Reading data from disc...") disc_data = media.get_media_data(media.get_disc_device(), self.cddb_choose_dialog) if disc_data is None: msg = "Could not read disc data!" logger.error(msg) self.set_status(msg) QMessageBox.critical(self, "Error!", msg) else: self.clear() # Set album info to gui for key in MEDIA_GUI_DICT: self.set_album_field(key, str(disc_data[MEDIA_GUI_DICT[key]])) # Set tracks to gui for track in disc_data[media.KEY_TRACKLIST]: self.append_title(track[media.KEY_TITLE]) # Enable rip button self.set_status("Disc data received") self.button_rip.setEnabled(True) def cddb_choose_dialog(self, what: int, info: dict): if what == media.CALLBACK_CDDB_CHOICE: values = tuple(info.values()) keys = tuple(info.keys()) dlg = SelectionDialog(self, "Multiple CDDB entries found. Choose one.", values) if dlg.exec(): return keys[dlg.get_selected_index()] def get_job_data(self, list_index): list_item = self.list_widget.item(list_index) row_widget = self.list_widget.itemWidget(list_item) track_info = {} track_info[media.KEY_TRACK] = list_index + 1 for key in MEDIA_GUI_DICT: track_info[MEDIA_GUI_DICT[key]] = self.get_album_field(key) track_info[media.KEY_TITLE] = row_widget.text_edit.toPlainText() return track_info def get_rip_worker(self): return RipQueueWorker(self.__basepath__) def get_encode_worker(self): return EncodeQueueWorker(self.__basepath__) if __name__ == "__main__": # # Command line arguments # default_baspath = os.path.join(os.getenv("HOME"), "pyrip") parser = argparse.ArgumentParser(description='Description') parser.add_argument('-b', '--basepath', help=f'The rip and encode basepath (default is {default_baspath})', default=default_baspath) args = parser.parse_args() # # Initialise GUI # app = QApplication(sys.argv) window = RipMainWindow(args.basepath) # # Start media observer # context = pyudev.Context() monitor = pyudev.Monitor.from_netlink(context) observer = MonitorObserver(monitor) observer.deviceEvent.connect(window.device_callback) observer.daemon = True monitor.start() # # Start GUI # window.show() sys.exit(app.exec())