302 lines
10 KiB
Python
302 lines
10 KiB
Python
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())
|