You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
27 KiB
Python

import sys
import os
import pathlib
import shutil
import errno
from collections import OrderedDict
from functools import partial
from pytube import Playlist, YouTube, exceptions
from DXMB_ui import Ui_dxmb
from YTVidItem import QYTVidItem
from PyQt5 import QtCore as qtc
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
import resources
##ToolKlasse ButtonManager (Für SongbookTab)
class BTN_MANAGER():
def copyAtoB(self,sourcePath_list,targetPath_list):#done
# Pro SourcePath immer jeweils ein targetPath auch wenn das gleicher ort ist -> sonst error(oft permission error)
#print("BTN Man: CopyAtoB")
if len(sourcePath_list) != len(targetPath_list):
print("Error: Path Count mismatch")
return
try:
for index in range(0,len(sourcePath_list)) :
sourcePath = sourcePath_list[index]
targetPath = targetPath_list[index]
try:
shutil.copytree(sourcePath,targetPath,dirs_exist_ok=True)
except OSError as e:
# If the error was caused because the source wasn't a directory
if e.errno == errno.ENOTDIR:
shutil.copy(sourcePath,targetPath)
else:
print('Directory not copied. Error: %s' % e)
except PermissionError:
print("PermissionError. Try function in admin-role!")
except Exception as e:
try:
print("Error: {}".format(e.what()))
except:
print("Unknown Error.")
def moveAtoB(self,sourcePath_list,targetPath_list):#done
#print("BTN Man: MoveAtoB")
if (len(sourcePath_list) != len(targetPath_list)):
print("Error: Path Count mismatch")
print("Source: {} | Target:{} ".format(len(sourcePath_list),len(targetPath_list)))
print("Source: {} ".format(sourcePath_list))
print("Target: {} ".format(targetPath_list))
return
try:
for index in range(0,len(sourcePath_list)) :
sourcePath = sourcePath_list[index]
targetPath = targetPath_list[index]
shutil.move(sourcePath,targetPath)
except PermissionError:
print("PermissionError. Try function in admin-role!")
except Exception as e:
try:
print("Error: ()".format(e.what()))
except:
print("Unknown Error.")
def delete(self,targetPath):#done
if os.path.isdir(targetPath):
shutil.rmtree(targetPath,ignore_errors=True)
else:
if os.path.isfile(targetPath):
os.remove(targetPath)
else:
#dürfte nie auftreten
print("Error Delete: File doesn't exist anymore")
##DXM Bragi - Hauptprogramm
class DXM_Bragi(qtw.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
self.main_window = Ui_dxmb()
self.main_window.setupUi(self)
self.btn_man = BTN_MANAGER()
self.homePath = os.getenv('USERPROFILE')
self.curr_filePath = os.path.dirname(os.path.abspath(__file__))
self.prepare_songbook_tab()
self.prepare_learnTaste_tab()
self.prepare_searchSongs_tab()
self.ytVidODict = OrderedDict()
####################################################################################
## SongBook Tab (Filesystem für Song-Verwaltung Tab)
def prepare_songbook_tab(self):#semi done (nur noch connecten zu Analyse)
#Vorbereiten aller Felder und Funktionen für Songbook
#Source TreeView (Links)
#Default Source Path "Eigene Musik" von User (Wenn nicht da dann Home Order selbst)
self.sourcePath = self.homePath
if (self.homePath !=None):
musicPath = os.path.join(self.homePath,'Music')
if (os.path.isdir(musicPath)):
self.sourcePath = musicPath
self.model_source = qtw.QFileSystemModel()
self.model_source.setRootPath(self.sourcePath)
self.setTreeView("Source",self.main_window.treeView_source,self.model_source,self.sourcePath)
#Target TreeView(Rechts)
#Default Target Path in aktuellem File Ordner (Defualt in Audio-Library)
#Wenn Ordner noch nicht exisitert -> Erstellen
targetPath = os.path.join(self.curr_filePath, "Audio Library")
if (not os.path.isdir(targetPath)):
os.makedirs(targetPath)
self.model_target = qtw.QFileSystemModel()
self.model_target.setRootPath(targetPath)
self.setTreeView("Target",self.main_window.treeView_target,self.model_target,targetPath)
#Funktionsbuttons connecten
#SourceTree(links)
self.main_window.btn_copyToTarget.clicked.connect(self.copy_file_sourceToTarget)
self.main_window.btn_copyAllToTarget.clicked.connect(self.copy_fileAll_sourceToTarget)
self.main_window.btn_moveToTarget.clicked.connect(self.move_file_sourceToTarget)
self.main_window.btn_moveAllToTarget.clicked.connect(self.move_fileAll_sourceToTarget)
self.main_window.btn_deleteSourceItem.clicked.connect(self.delete_file_source)
self.main_window.btn_makeDirSource.clicked.connect(self.make_dir_source)
self.main_window.btn_analyseSource.clicked.connect(self.analyse_file_source)
self.main_window.toolButton_source.clicked.connect(self.setNewRootPath_source)
#TargetTree(rechts)
self.main_window.btn_copyToSource.clicked.connect(self.copy_file_targetToSource)
self.main_window.btn_copyAllToSource.clicked.connect(self.copy_fileAll_targetToSource)
self.main_window.btn_moveToSource.clicked.connect(self.move_file_targetToSource)
self.main_window.btn_moveAllToSource.clicked.connect(self.move_fileAll_targetToSource)
self.main_window.btn_deleteTargetItem.clicked.connect(self.delete_file_target)
self.main_window.btn_makeDirTarget.clicked.connect(self.make_dir_target)
self.main_window.btn_analyseTarget.clicked.connect(self.analyse_file_target)
self.main_window.toolButton_target.clicked.connect(self.setNewRootPath_target)
####
## Context Menü Funktionen
####
def context_Menu_source(self):#semi-done(Add new menus)
menu = qtw.QMenu()
open = menu.addAction("Open")
open.triggered.connect(self.open_file_source)
cursor = qtg.QCursor()
menu.exec_(cursor.pos())
def context_Menu_target(self):#semi-done(Add new menus)
menu = qtw.QMenu()
open = menu.addAction("Open")
open.triggered.connect(self.open_file_target)
cursor = qtg.QCursor()
menu.exec_(cursor.pos())
def open_file_source(self):#done
index = self.tree_source.currentIndex()
tree_file_path = self.model_source.filePath(index)
os.startfile(tree_file_path)
def open_file_target(self):#done
index = self.tree_target.currentIndex()
tree_file_path = self.model_target.filePath(index)
os.startfile(tree_file_path)
####
## Source TreeView Funktionen
####
def copy_file_sourceToTarget(self):#done
self.copy_file_AToB(self.tree_source,self.model_source,self.tree_target,self.model_target,self.main_window.lineEdit_target)
def copy_fileAll_sourceToTarget(self):#done
index_source = self.tree_source.currentIndex()
if (os.path.isdir(self.model_source.filePath(index_source))):
self.copy_file_AToB(self.tree_source,self.model_source,self.tree_target,self.model_target,self.main_window.lineEdit_target)
else:
self.copy_file_AToB(self.tree_source,self.model_source,self.tree_target,self.model_target,self.main_window.lineEdit_target,True)
def move_file_sourceToTarget(self):#dome
self.move_file_AToB(self.tree_source,self.model_source,self.tree_target,self.model_target,self.main_window.lineEdit_target)
def move_fileAll_sourceToTarget(self):#done
self.move_file_AToB(self.tree_source,self.model_source,self.tree_target,self.model_target,self.main_window.lineEdit_target,True)
def delete_file_source(self):#done
self.delete_file_inTree(self.tree_source,self.model_source)
def make_dir_source(self):#done
self.readDirName(self.tree_source,self.model_source,self.main_window.lineEdit_source)
def setNewRootPath_source(self):#done
self.setNewRootPath("Source",self.main_window.lineEdit_source.text())
def analyse_file_source(self):
pass
####
## Target TreeView Funktionen
####
def copy_file_targetToSource(self):#done
self.copy_file_AToB(self.tree_target,self.model_target,self.tree_source,self.model_source,self.main_window.lineEdit_source)
def copy_fileAll_targetToSource(self):#done
index_target = self.tree_target.currentIndex()
if (os.path.isdir(self.model_target.filePath(index_target))):
self.copy_file_AToB(self.tree_target,self.model_target,self.tree_source,self.model_source,self.main_window.lineEdit_source)
else:
self.copy_file_AToB(self.tree_target,self.model_target,self.tree_source,self.model_source,self.main_window.lineEdit_source,True)
def move_file_targetToSource(self):#done
self.move_file_AToB(self.tree_target,self.model_target,self.tree_source,self.model_source,self.main_window.lineEdit_source)
def move_fileAll_targetToSource(self):#done
self.move_file_AToB(self.tree_target,self.model_target,self.tree_source,self.model_source,self.main_window.lineEdit_source,True)
def delete_file_target(self):#done
self.delete_file_inTree(self.tree_target,self.model_target)
def make_dir_target(self):#done
self.readDirName(self.tree_target,self.model_target,self.main_window.lineEdit_target)
def setNewRootPath_target(self):#done
self.setNewRootPath("Target",self.main_window.lineEdit_target.text())
def analyse_file_target(self):
pass
####
## Allgemeine ToolFunktionen
####
def setTreeView(self,profile ,treeView,model,newPath):
#TODO iwann herausfinden wie columns über HeaderData in FileSystemModel geändert werden können
#Vermutlich irgendwie über subclasse bilden
if( profile == "Source"):
self.tree_source = treeView
self.tree_source.setModel(model)
self.tree_source.setAlternatingRowColors(True)
model.directoryLoaded.connect(self.tree_source.expandAll)
self.tree_source.setRootIndex(model.index(newPath))
self.tree_source.setSortingEnabled(True)
self.tree_source.setContextMenuPolicy(qtc.Qt.CustomContextMenu)
self.tree_source.customContextMenuRequested.connect(self.context_Menu_source)
self.tree_source.setColumnWidth(0,175)
self.tree_source.setColumnWidth(1,50)
self.main_window.lineEdit_source.setText(newPath)
elif (profile == "Target"):
self.tree_target = treeView
self.tree_target.setModel(model)
self.tree_target.setAlternatingRowColors(True)
model.directoryLoaded.connect(self.tree_target.expandAll)
self.tree_target.setRootIndex(model.index(newPath))
self.tree_target.setSortingEnabled(True)
self.tree_target.setContextMenuPolicy(qtc.Qt.CustomContextMenu)
self.tree_target.customContextMenuRequested.connect(self.context_Menu_target)
self.tree_target.setColumnWidth(0,175)
self.tree_target.setColumnWidth(1,50)
self.main_window.lineEdit_target.setText(newPath)
else:
print("Error: Unknown TreeView")
def copy_file_AToB(self,treeSource,modelSource,treeTarget,modelTarget,lineEditTarget,copyFromParentFolder=False):#done
#Note Regeln: Wenn kein source index gewählt-> kein einzel Copy
index_source = treeSource.currentIndex()
if (modelSource.filePath(index_source) ==''):
#Fehler :Ohne selektion kein einzel copy
print("Error: Kein einzel copy ohne Selektion")
return
treeSource_filePath = list()
if (copyFromParentFolder):
treeSource_filePath.append(os.path.dirname(modelSource.filePath(index_source)))
else:
treeSource_filePath.append(modelSource.filePath(index_source))
index_target = treeTarget.currentIndex()
treeTarget_filePath = list()
if (modelTarget.filePath(index_target) ==''):
treeTarget_filePath.append(lineEditTarget.text())
if (os.path.isdir(modelTarget.filePath(index_target))):
treeTarget_dirName = modelTarget.filePath(index_target)
else:
treeTarget_dirName = os.path.dirname(modelTarget.filePath(index_target))
if (copyFromParentFolder):
target_dirName = os.path.dirname(modelTarget.filePath(index_target))
treeTarget_filePath.append(target_dirName)
else:
treeSource_fileName = modelSource.fileName(index_source)
treeTarget_filePath.append(os.path.join(treeTarget_dirName,treeSource_fileName))
#Auführen des Copy-Befehls von BTN-Man
self.btn_man.copyAtoB(treeSource_filePath,treeTarget_filePath)
def move_file_AToB(self,treeSource,modelSource,treeTarget,modelTarget,lineEditTarget,moveAllInFolder=False):#done
#Note Regeln: Wenn kein Index gewählt-> Nichts bewegen(Keine Aktion)
#Wenn Ordner als Index (Außer Root Ordner) -> Kompletten order + Inhalt zu Ziel Bewegen
#Wenn MoveAll auf File als Index -> alle Elemente des Parent ordners OHNE Ordner selbst bewegen
#Wenn MoveAll auf Ordner als Index -> Kompletten ordner + Inhalt zu Ziel bewegen
index_source = treeSource.currentIndex()
if (modelSource.filePath(index_source) ==''):
#Fehler :Ohne selektion kein einzel copy
print("Error: Kein einzel move ohne Selektion")
return
treeSource_filePath = list()
treeSource_fileNames = list()
sourceIsDir = False
if (os.path.isdir(modelSource.filePath(index_source))):#Move/MoveAll auf Folder
sourceIsDir = True
#Wenn Index ein Folder ist dann ist moveAll Funktion = move Funktion
moveAllInFolder = False
treeSource_filePath.append(modelSource.filePath(index_source))
else:
if (moveAllInFolder):#MoveAll auf File
for file in os.listdir(os.path.dirname(modelSource.filePath(index_source))):
treeSource_filePath.append(os.path.join(os.path.dirname(modelSource.filePath(index_source)),file))
treeSource_fileNames.append(file)
else:#Move auf einzelnes File
treeSource_filePath.append(modelSource.filePath(index_source))
treeSource_fileName = modelSource.fileName(index_source)
index_target = treeTarget.currentIndex()
treeTarget_filePath = list()
treeTarget_dirName = str()
targetIsDir = False
if (modelTarget.filePath(index_target) ==''):
treeTarget_dirName = lineEditTarget.text()
#RootOrdner in LineEdit ist IMMER Folder
targetIsDir = True
else:
if (os.path.isdir(modelTarget.filePath(index_target))):
treeTarget_dirName = modelTarget.filePath(index_target)
targetIsDir = True
else:
treeTarget_dirName = os.path.dirname(modelTarget.filePath(index_target))
if(moveAllInFolder):#MoveAll auf File in Source -> Einzel-Move-Paths vorbereiten
for file in treeSource_fileNames:
treeTarget_filePath.append(os.path.join(treeTarget_dirName,file))
else:
if (sourceIsDir == True and targetIsDir == True):
treeTarget_filePath.append(modelTarget.filePath(index_target))
elif(sourceIsDir == True and targetIsDir == False):
treeTarget_filePath.append(treeTarget_dirName)
else:
treeTarget_filePath.append(os.path.join(treeTarget_dirName,treeSource_fileName))
#Ausführen BTN-Man Funktion
self.btn_man.moveAtoB(treeSource_filePath,treeTarget_filePath)
def delete_file_inTree(self,targetTree,targetModel):#done
#Note Regeln: Wenn kein Index gewählt -> Kein File Löschen (Keine Aktion; vllt Warn-Print)
#Wenn Ordner als Index (Außer Root Ordner) -> Kompletten order + Inhalt löschen
index_source = targetTree.currentIndex()
if (targetModel.filePath(index_source) ==''):
#Fehler :Ohne selektion kein einzel delete
print("Error: Kein einzel copy ohne Selektion")
return
# Ausführen BTN-Man Command
self.btn_man.delete(targetModel.filePath(index_source))
def readDirName(self,tree,model,lineEdit):#done
name, result = qtw.QInputDialog.getText(self, "Folder Name","Enter new folder name:")
if(result):
index_source = tree.currentIndex()
if (model.filePath(index_source) ==''):
#Kein Index gewählt-> LineEdit als Quell-Pfad
filePath = os.path.join(lineEdit.text(),name)
else:
if (os.path.isdir(model.filePath(index_source))):
filePath = os.path.join(model.filePath(index_source),name)
else:
dirName = os.path.dirname(model.filePath(index_source))
filePath = os.path.join(dirName,name)
os.makedirs(filePath)
else:
print("Error: Eingabe Abbruch")
def setNewRootPath(self,profile,currentPath):#done
dialog = qtw.QFileDialog(self)
dialog.setWindowTitle('Set Root Path for Tree')
dialog.setDirectory(currentPath)
dialog.setFileMode(qtw.QFileDialog.DirectoryOnly)
if dialog.exec_() == qtw.QDialog.Accepted:
file_full_path = str(dialog.selectedFiles()[0])
print(file_full_path)
if (profile == "Source"):
self.model_source = qtw.QFileSystemModel()
self.model_source.setRootPath(file_full_path)
self.setTreeView("Source",self.main_window.treeView_source,self.model_source,file_full_path)
elif(profile == "Target"):
self.model_target = qtw.QFileSystemModel()
self.model_target.setRootPath(file_full_path)
self.setTreeView("Target",self.main_window.treeView_target,self.model_target,file_full_path)
else:
print("Error: Unknown TreeView")
else:
print("Error: Abbruch SetNewRoot")
def analyse_file(self,filePah):
# Nutze ausgewähltes File für "Learn Taste Tab" und starte analyse
pass
#########################################################################################
## Learn Taste tab (MachineLearning Tab)
def prepare_learnTaste_tab(self):
pass
#########################################################################################
## Search Songs tab (Youtube-Downloader & ML-Prediction Tab)
def prepare_searchSongs_tab(self):
self.targetPath = os.path.join(self.curr_filePath, "Downloads")
if (not os.path.isdir(self.targetPath)):
os.makedirs(self.targetPath)
self.main_window.lineEdit_targetPath.setText(self.targetPath)
#Funktionsbuttons connecten
self.main_window.btn_insertUrl.clicked.connect(self.insertUrl)
self.main_window.toolBtn_targetPath.clicked.connect(self.setTargetPath)
self.main_window.toolBtn_addProfile.clicked.connect(self.addProfile)
self.main_window.toolBtn_removeProfile.clicked.connect(self.removeProfile)
def insertUrl(self):
self.main_window.insert_url_msg.setText("")
if (self.main_window.lineEdit_url.text() == ''):
return
else:
newUrl = self.main_window.lineEdit_url.text()
##Checken ob Link bereits als Objekt vorliegt (2mal laden der gleichen URL führt zu absturz)
if newUrl.strip() in self.ytVidODict:
self.main_window.insert_url_msg.setText("Error: URL allready loaded")
return
#Test URL: Rick Roll https://www.youtube.com/watch?v=o-YBDTqX_ZU
#Test URL2: BudS&TerH - Lala Remix: https://www.youtube.com/watch?v=lL7jwhWAU_k
#Test URL3: Nee Junge: https://www.youtube.com/watch?v=1uNHA3pcDmQ
item = QYTVidItem(self.main_window.scrollA)
returnCode = item.setData(newUrl)
#print("returnCode: ",returnCode )
if (returnCode == "OK"):
row = self.main_window.url_scroller_grid.count()
col = 0
self.main_window.url_scroller_grid.setRowStretch(row,0)
self.main_window.url_scroller_grid.addWidget(item,row,col,0,0)
#Setzen der externen Buttons
item.btn_remove.clicked.connect(partial(self.removeYTVid,newUrl))
item.btn_download.clicked.connect(partial(self.downloadYTVid,newUrl))
#Speichern in der OrderedDict:
self.ytVidODict[newUrl.strip()] = item
else:
self.main_window.insert_url_msg.setText(returnCode)
self.main_window.lineEdit_url.setText("")
def setTargetPath(self):
pass
def addProfile(self):
pass
def removeProfile(self):
pass
def downloadYTVid(self,url):
targetPath = self.main_window.lineEdit_targetPath.text()
print("Download nach {}".format(targetPath))
for vidItem in self.ytVidODict:
if vidItem == url.strip():
self.ytVidODict[vidItem].downloadItem(targetPath)
return
print("Error: ungültiges Item")
def removeYTVid(self,url):
#Rauswerfen aus der Scrollarea (nachrücken aller folgenden Items im Grid)
for vidItem in self.ytVidODict:
if vidItem == url.strip():
self.ytVidODict[vidItem].deleteLater()
self.ytVidODict.pop(url.strip())
break
#danach gridlayout neu aufbauen
for i in reversed(range(self.main_window.url_scroller_grid.count())):
widgetToRemove = self.main_window.url_scroller_grid.itemAt( i ).widget()
# remove it from the layout list
self.main_window.url_scroller_grid.removeWidget( widgetToRemove )
# gridlayout neu aufbauen
for vidItem in self.ytVidODict:
row = self.main_window.url_scroller_grid.count()
col = 0
self.main_window.url_scroller_grid.addWidget(self.ytVidODict[vidItem],row,col,1,1)
####################################################################################
## ChangeSongsDetails Tab (ID3-Tag Editor für Songs Tab)
def changeSongDetails_tab(self):#to be implemented
#Auslesen und Manipulieren aller ID3 Tags eines Songs
pass
#############################################################################################
if __name__ == "__main__":
app = qtw.QApplication([])
##Set "DarkMode"
app.setStyle("Fusion")
# Now use a palette to switch to dark colors:
palette = qtg.QPalette()
palette.setColor(qtg.QPalette.Window, qtg.QColor(53, 53, 53))
palette.setColor(qtg.QPalette.WindowText, qtc.Qt.white)
palette.setColor(qtg.QPalette.Base, qtg.QColor(25, 25, 25))
palette.setColor(qtg.QPalette.AlternateBase, qtg.QColor(53, 53, 53))
palette.setColor(qtg.QPalette.ToolTipBase, qtc.Qt.black)
palette.setColor(qtg.QPalette.ToolTipText, qtc.Qt.white)
palette.setColor(qtg.QPalette.Text, qtc.Qt.white)
palette.setColor(qtg.QPalette.Button, qtg.QColor(53, 53, 53))
palette.setColor(qtg.QPalette.ButtonText, qtc.Qt.white)
palette.setColor(qtg.QPalette.BrightText, qtc.Qt.red)
palette.setColor(qtg.QPalette.Link, qtg.QColor(42, 130, 218))
palette.setColor(qtg.QPalette.Highlight, qtg.QColor(42, 130, 218))
palette.setColor(qtg.QPalette.HighlightedText, qtc.Qt.black)
app.setPalette(palette)
mwindow = DXM_Bragi()
mwindow.show()
##youtube test: https://www.youtube.com/watch?v=Lrj2Hq7xqQ8
#link = 'https://www.youtube.com/watch?v=Lrj2Hq7xqQ8'
#yt_vid = YouTube(link)
#filePath = os.path.join(os.path.dirname(os.path.abspath(__file__)),'TestYT')
##zeigt vorhandene stream items an (Verschiedene Versionen des links)
#for video in yt_video.streams:
# print(video)
##anzeigen Video daten
#print(yt_vid.title)
#print(yt_vid.thumbnail_url)
#print(yt_vid.streams.filter(only_audio=True))
##caption infos (Audio-Abschnitte[können auto generiert sein])
#print(yt_vid.captions)
#caption = yt_vid.captions.get_by_language_code('a.en')
#print(caption.xml_captions)
#print(caption.generate_srt_captions())
# herunter laden von audio in bester qualy
#yt_audioStream = yt_vid.streams.get_audio_only()
#print(yt_audioStream.title)
#yt_audioStream.download(filePath)
app.exec_()