import traceback from PyQt6.QtWidgets import ( QApplication, QMainWindow, QLabel, QPushButton, QLineEdit, QWidget, QFrame, QSplitter, QStackedWidget, QListWidget, QListWidgetItem, QGraphicsScene, QGraphicsView, QGraphicsPixmapItem, QLayout, QScrollArea, QVBoxLayout, QHBoxLayout, QDialog, QFileDialog ) from PyQt6.QtGui import QPixmap from PyQt6.QtCore import Qt, QAbstractListModel from dialogs import ErrorPopup, ListDialog import os, sys, json, time from extractor import ExtractMetadata, ExtractMediaData from action_runner import RunningDialog def calculateAspectRatio(pixmap, width, height, aspectRatioMode): return pixmap.scaled(width, height, aspectRatioMode) # E:\Downloads\4kvideodownloader class FolderScanner: def __init__(self, path): self.path = path def scan(self): data = [] if not os.path.exists(self.path): print("Path does not exist: ", self.path) return data for root, dirs, files in os.walk(self.path): for file in files: if file.endswith(".mp3"): p = os.path.join(root, file) metadata = ExtractMetadata(p) # debug.json # with open(f"{file}.debug.json", "w") as f: # json.dump(metadata, f, indent=4) data.append(metadata) metadata.update({ "file:": p }) if "data" not in data: metadata.update({ "thumbnail": self.ExtractImageData(p, f"thumbnails\\{file}.png"), }) return data def ExtractImageData(self, file, output): try: return ExtractMediaData(file, output) except Exception as e: print("### ExtractImageData Error ###") traceback.print_exc() pass class MusicModel(QAbstractListModel): def __init__(self, data): super().__init__() self._data = data def rowCount(self, parent): return len(self._data) def data(self, index, role): if role == Qt.ItemDataRole.DisplayRole: return self._data[index.row()] class MusicModelWidget(QFrame): def __init__(self, parent = None, name = None, path = None): super(MusicModelWidget, self).__init__(parent) self.setObjectName(name) layout = QHBoxLayout() self.image = image( "default.png" ) self.image.setFixedSize(64, 64) iteminfo_base = QWidget() iteminfo = QVBoxLayout(iteminfo_base) iteminfo.setSpacing(0) self.itemname = QLabel(name) self.itemname.setObjectName("itemname") self.artist = QLabel("Artist") iteminfo.addWidget(self.itemname) iteminfo.addWidget(self.artist) layout.addWidget(self.image) layout.addWidget(iteminfo_base) self.setStyleSheet("#itemname { font-size: 16px; }") self.setLayout(layout) def openFile(self): os.startfile(self.data["format"]["filename"]) class image(QFrame): def __init__(self, path = None): super().__init__() if path is None: path = "default.png" self.setObjectName("Logo") self.setFrameShadow(QFrame.Shadow.Raised) self.setFrameShape(QFrame.Shape.StyledPanel) self.sceene = QGraphicsScene(self) self.view = QGraphicsView(self.sceene, self) self.view.setFixedSize(self.width(), self.height()) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.view.move(0, 0) # remove border self.view.setLineWidth(0) self.view.setMidLineWidth(0) self.view.setFrameShadow(QFrame.Shadow.Plain) self.view.setFrameShape(QFrame.Shape.NoFrame) self.view.setStyleSheet("background-color: transparent;") self.pixmap = None self.pixmap = QGraphicsPixmapItem() self.sceene.addItem(self.pixmap) try: self.pixmap.setPixmap(QPixmap(path).scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio)) except Exception as e: print("### Init Error ###") traceback.print_exc() def loadimage(self, path): try: print("Setting image: ", path) self.pixmap.setPixmap(QPixmap(path).scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio)) except Exception as e: print("### Load Image Error ###") traceback.print_exc() def resizeEvent(self, event): self.view.setFixedSize(self.width(), self.height()) calculateAspectRatio(self.pixmap.pixmap(), self.width(), self.height(), Qt.AspectRatioMode.KeepAspectRatio) class MusicItem: def __init__(self): self.data = None self.image = None self.title = None self.artist = None self.imagepath = None def setupData(self, ffprobeJsonData): self.data = ffprobeJsonData data_format = self.data["format"] # get title from the tags if "tags" in data_format: if "title" in data_format["tags"]: self.title = data_format["tags"]["title"] if "artist" in data_format["tags"]: self.artist = data_format["tags"]["artist"] if "thumbnail" in self.data: self.image = self.data["thumbnail"] def getImage(self): return self.image def openFile(self): try: os.startfile(self.data['file:']) except Exception as e: print("Unable to open file: ", e) traceback.print_exc() class MusicLibraryTab(QFrame): def __init__(self): super().__init__() self.list = QListWidget() self.scanpathInput = QLineEdit() self.scanpathInput.setPlaceholderText("Path to scan") self.scanbutton = QPushButton("Scan") self.scanbutton.clicked.connect(self.scan) self.list.setObjectName("list") self.list.setLineWidth(2) self.list.setMidLineWidth(2) self.list.setFrameShadow(QFrame.Shadow.Raised) self.list.setFrameShape(QFrame.Shape.StyledPanel) self.list.doubleClicked.connect(lambda: self.list.currentItem().data(0).openFile()) scan_base = QWidget() scan_layout = QHBoxLayout(scan_base) scan_layout.setSpacing(0) scan_layout.setContentsMargins(0, 0, 0, 0) scan_layout.addWidget(self.scanbutton) scan_layout.addWidget(self.scanpathInput) layout = QVBoxLayout() layout.setSpacing(0) layout.addWidget(scan_base) layout.addWidget(self.list) self.setLayout(layout) # check if scan.json exists if os.path.exists("scan.json"): self.scan("scan.json") def scan(self, file = None): path = self.scanpathInput.text() musicscanner = FolderScanner(path) runningDialog = RunningDialog(None) try: # clear list self.list.clear() runningDialog.show() runningDialog.setWindowTitle("Scanning: " + path) scandate = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) if not file: # formated date time scandate = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) scan = musicscanner.scan() else: loadeddata = json.load(open(file))["scan"] scan = loadeddata unknownData = False unknownFiles = [] # debug.scan.json with open("scan.json", "w") as f: settings={ "path": self.scanpathInput.text(), "scanned": scandate } finaldata ={ "settings": settings, "scan": scan } json.dump(finaldata, f, indent=4) # check if any of the data is unknown for data in scan: # does data key exist if "data" in data: # is data key unknown if data["data"] == "Unknown": print("Unknown data: ", data["file:"]) unknownData = True unknownFiles.append(data["file:"]) # music = MusicItem() try: music.setupData(data) except Exception as e: print("Skipping setup data: ", e) item = QListWidgetItem(self.list) item.setData(0, music) item_frame = MusicModelWidget(self.list, music.title) if music.image: runningDialog.setWindowTitle("Loading Image: " + music.image) item_frame.image.loadimage(music.image) item.setSizeHint(item_frame.sizeHint()) if music.artist: item_frame.artist.setText(music.artist) else: item_frame.artist.setText("Unknown Artist") # double click # item_frame.itemname.mouseDoubleClickEvent = os.startfile(data["format"]["filename"]) self.list.addItem(item) self.list.setItemWidget(item, item_frame) if unknownData: ListDialog(None, "Unknown Files", "Some files metadata could not be extracted", unknownFiles) runningDialog.close() except Exception as e: runningDialog.close() traceback.print_exc() try: ErrorPopup(self, str(e), str(traceback.format_exc( limit=1, chain=True ))).exec() except Exception as e: print("Unable to show error popup: ", e) pass def resizeEvent(self, event): self.list.resize(self.width(), self.height())