def connectDatabase(envt): """Connect base MySQL/Sqlite.""" FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_envt = Json_params.getMember(envt) MODE_SQLI = group_envt['typb'] BASE_RAC = r'' + group_envt['raci'] RACI_DOU = group_envt['cate'] boolcon = False if MODE_SQLI == 'sqlite': db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName(BASE_SQLI.format(envt=envt)) if not db.isValid(): qDebug(envt+' problem no valid database') else: BASE_SEV = group_envt['serv'] BASE_USR = group_envt['user'] BASE_PAS = group_envt['pass'] BASE_NAM = group_envt['base'] BASE_PRT = group_envt['port'] if MODE_SQLI == 'mysql': db = QSqlDatabase.addDatabase("QMYSQL") db.setHostName(BASE_SEV) db.setDatabaseName(BASE_NAM) db.setUserName(BASE_USR) db.setPassword(BASE_PAS) db.setPort(BASE_PRT) elif MODE_SQLI == 'mssql': db = QSqlDatabase.addDatabase("QODBC3") driver = "DRIVER={SQL Server Native Client 11.0};Server=" + BASE_SEV + ";Database=" + BASE_NAM driver += ";Uid=" + BASE_USR + ";Port=" + str(BASE_PRT) + ";Pwd=" + BASE_PAS + ";Trusted_connection=yes" #print(driver) db.setDatabaseName(driver); list_category = [] if RACI_DOU is not None: list_category += Json_params.buildCategories(envt) if db.isValid(): boolcon = db.open() else: qDebug(envt+' problem for open database : '+db.lastError().text()) return boolcon, db, MODE_SQLI, BASE_RAC, list_category
#!/usr/bin/env python # -*- coding: utf-8 -*- from os import path from PyQt5.QtCore import Qt, QVariant, QModelIndex, pyqtSignal, QSortFilterProxyModel, QAbstractTableModel from PyQt5.QtSql import QSqlQuery from PyQt5.QtGui import QColor from DBDatabase import DBFuncBase, getrequest from DBFunction import convertUNC from DBReadJson import JsonParams FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') TEXT_NCO = group_dbalbums['text_nocov'] THUN_NOD = group_dbalbums['thnail_nod'] FONT_MAI = group_dbalbums['font00_ttx'] # MODEL ABSTRACT generique class ModelDBAbstract(QAbstractTableModel): """Abstract model for qtableview.""" signalthubuild = pyqtSignal(float, str) # build base def __init__(self, parent, colsname, req): """Init Model Abstract.""" super(ModelDBAbstract, self).__init__(parent) self.parent = parent self.columns = colsname self.request = req self.arraydata = []
class InventGui(QWidget, Ui_UpdateWindows): signalend = pyqtSignal() PATH_PROG = path.dirname(path.abspath(__file__)) LOGS_PROG = path.join(PATH_PROG, 'LOG') # Read File DBAlbums.ini qDebug('read json params file') FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') VERS_PROG = group_dbalbums['prog_build'] TITL_PROG = "DBAlbums v{v} (2017)".format(v=VERS_PROG) TITL_PROG = TITL_PROG + " : Update Database" WIDT_MAIN = group_dbalbums['wgui_width'] HEIG_MAIN = group_dbalbums['wgui_heigh'] WIDT_PICM = group_dbalbums['thun_csize'] WINS_ICO = path.join(PATH_PROG, 'IMG', group_dbalbums['wins_icone']) THEM_COL = group_dbalbums['name_theme'] TEXT_NCO = group_dbalbums['text_nocov'] FONT_CON = group_dbalbums['font01_ttx'] def __init__(self, list_albums, list_columns, list_category, typeupdate, modsql, envt, themecolor, parent=None): """Init Gui, start invent""" super(InventGui, self).__init__(parent) self.setupUi(self) self.resize(self.WIDT_MAIN, self.HEIG_MAIN - 300) self.setWindowIcon(QIcon(self.WINS_ICO)) self.setWindowTitle(self.TITL_PROG + " Environment : " + envt + " mode : " + typeupdate) centerWidget(self) self.parent = parent self.list_albums = list_albums self.list_category = list_category self.list_columns = list_columns self.modsql = modsql self.envits = envt self.typeupdate = typeupdate self.curthe = themecolor self.logname = QDateTime.currentDateTime().toString( 'yyMMddhhmmss') + "_UPDATE_DATABASE_" + self.envits + ".log" self.logname = path.join(self.LOGS_PROG, self.logname) self.total_p = None self.albumnew = 0 self.alupdate = 0 self.aldelete = 0 self.apresent = 0 self.actionerror = 0 self.selecttrowg = 0 self.list_actions = [] font = QFont() font.setFamily("Courrier New") font.setFixedPitch(True) font.setPointSize(10) fontconsol = QFont() fontconsol.setFamily(self.FONT_CON) fontconsol.setFixedPitch(True) fontconsol.setPointSize(8) self.levelcolors = [Qt.white, Qt.green, Qt.magenta, Qt.red] self.menua = QMenu() self.action_OPF = self.menua.addAction( self.style().standardIcon(QStyle.SP_DialogOpenButton), "Open Folder...", self.getFolder) self.lab_result.setFont(font) self.lab_release.setFont(font) self.lab_advance.setFont(font) self.lab_releaseadvance.setFont(font) self.textEditrelease.setStyleSheet( "background-color: black;color:white;") self.textEditrelease.setLineWrapMode(QTextEdit.NoWrap) self.textEditrelease.setReadOnly(True) self.textEditrelease.setFont(fontconsol) self.vScrollBar = QScrollBar(self.textEditrelease.verticalScrollBar()) self.vScrollBar.sliderPressed.connect(self.onScrollPressed) self.btn_action.clicked.connect(self.realiseActions) self.btn_action.setEnabled(False) self.btn_quit.clicked.connect(self.closeImport) self.lcdTime.setSegmentStyle(QLCDNumber.Flat) self.seconds = 0 self.myThreadtime = myThreadTimer(self) self.myThreadtime.timeElapsed.connect(self.showlcd) self.myThreadtime.start() self.tbl_update.setContextMenuPolicy(Qt.CustomContextMenu) self.tbl_update.customContextMenuRequested.connect( self.popUpTreeUpdate) self.tableMdlUpd = ModelTableUpdatesABS(self, [[] * 5]) self.tbl_update.setModel(self.tableMdlUpd) for i in range(len(self.tableMdlUpd.U_C_WIDTH)): self.tbl_update.setColumnWidth(i, self.tableMdlUpd.U_C_WIDTH[i]) self.tbl_update.verticalHeader().setDefaultSectionSize( self.tableMdlUpd.C_HEIGHT) self.tbl_update.horizontalHeader().setStretchLastSection(True) self.applyTheme() self.show() def startAnalyse(self): qDebug('Start BuildInvent') self.setCursor(Qt.WaitCursor) self.prepareInvent = BuildInvent(self.list_albums, self.list_columns, self.list_category, self.typeupdate, self.modsql, self.envits) self.prepareInvent.signalchgt.connect(self.onBuild) self.prepareInvent.signaltext.connect(self.updateInfos) self.prepareInvent.inventDatabase() self.setCursor(Qt.ArrowCursor) self.lab_result.setText('Completed Analyse in ' + self.total_p) qDebug('End BuildInvent') self.btn_quit.setText('Close') if len(self.prepareInvent.list_action ) > 0 and not self.checkBoxStart.isChecked(): self.btn_action.setEnabled(True) self.realiseActions() def onBuild(self, percent, message): """Display advance browsing folders.""" self.lab_result.setText(message) self.progressBar.setValue(percent) mesresu = 'PRESENT : ' + format( self.prepareInvent.apresent + self.prepareInvent.alupdate - self.prepareInvent.aldelete, '05d') mesresu += '\nADD : ' + format(self.prepareInvent.albumnew, '05d') mesresu += '\nUPDATE : ' + format(self.prepareInvent.alupdate, '05d') mesresu += '\nDELETE : ' + format(self.prepareInvent.aldelete, '05d') self.lab_advance.setText(mesresu) self.lab_releaseadvance.setText(mesresu) self.tableMdlUpd.update(self.prepareInvent.list_action) self.tbl_update.scrollToBottom() QApplication.processEvents() def realiseActions(self, list_actions=None): """Execute Actions Update.""" if list_actions is None: self.list_actions = self.prepareInvent.list_action self.apresent = self.prepareInvent.apresent self.albumnew = self.prepareInvent.albumnew self.alupdate = self.prepareInvent.alupdate self.aldelete = self.prepareInvent.aldelete else: # no analyse self.btn_quit.setText('Close') self.list_actions = list_actions self.lab_result.setText('No Analyse') self.lab_advance.setText('') self.progressBar.setValue(100) self.tableMdlUpd.update(self.list_actions) self.apresent = len(self.list_albums) for action in self.list_actions: if action[2] == 'DELETE': self.aldelete += 1 elif action[2] == 'UPDATE': self.alupdate += 1 elif action[2] == 'ADD': self.albumnew += 1 run = ReleaseInvent(self.list_actions, self.modsql) run.signalrun.connect(self.updateRun) run.signalend.connect(self.updateEnd) run.signaltxt.connect(self.updateInfos) run.executeActions() def updateRun(self, percent, text): """Display run operations update database.""" index = self.tableMdlUpd.index(self.selecttrowg, 0) self.tbl_update.selectRow(index.row()) index = self.tbl_update.currentIndex() self.tbl_update.scrollTo(index) self.selecttrowg += 1 self.progressBarrelease.setValue(percent) if text == 'DELETE': self.aldelete -= 1 self.apresent -= 1 elif text == 'UPDATE': self.alupdate -= 1 elif text == 'ADD': self.albumnew -= 1 self.apresent += 1 elif text == 'ERROR': self.actionerror += 1 mesresu = 'ERROR : ' + format(self.actionerror, '05d') mesresu += '\nADD : ' + format(self.albumnew, '05d') mesresu += '\nUPDATE : ' + format(self.alupdate, '05d') mesresu += '\nDELETE : ' + format(self.aldelete, '05d') self.lab_releaseadvance.setText(mesresu) QApplication.processEvents() @pyqtSlot() def updateEnd(self): """Operations finished.""" QApplication.processEvents() self.lab_release.setText('Completed Operations in ' + self.total_p) if len(self.list_actions) > 0: # create log file self.textEditrelease.append('\n- Completed Operations in ' + self.total_p) self.textEditrelease.append('\n- Create log file : ' + self.logname) text_file = open(self.logname, "w", 'utf-8') text_file.write(self.textEditrelease.toPlainText()) text_file.close() self.textEditrelease.moveCursor(QTextCursor.Start) self.textEditrelease.ensureCursorVisible() # refresh self.signalend.emit() QMessageBox.information(self, 'Update Database', 'Completed Operations in ' + self.total_p) def updateInfos(self, line, level=None): """Write Reception signal run update.""" if not level: if line.startswith('-'): level = 1 elif line.startswith('WARNING'): level = 2 elif line.startswith('ERROR'): level = 3 else: level = 0 # set color self.textEditrelease.setTextColor(QColor(self.levelcolors[level])) if self.focusWidget() != self.vScrollBar: # display cursor = self.textEditrelease.textCursor() cursor.movePosition(QTextCursor.End) #cursor.insertText(line) self.textEditrelease.append(line.rstrip()) self.textEditrelease.setTextCursor(cursor) if self.focusWidget() != self.vScrollBar: self.textEditrelease.ensureCursorVisible() self.textEditrelease.horizontalScrollBar().setValue(0) def golbalInsertCovers(self): request = "SELECT ALBUMS.ID_CD, ALBUMS.Cover FROM ALBUMS " \ "LEFT JOIN COVERS ON ALBUMS.ID_CD = COVERS.ID_CD " \ "WHERE COVERS.ID_CD IS NULL AND ALBUMS.Cover<>'{textnopic}'" request = request.format(textnopic=self.TEXT_NCO) query = QSqlQuery(request) query.exec_() while query.next(): DBFuncBase().imageToSql(query.value(1), query.value(0), self.WIDT_PICM) def getFolder(self): """Open album folder.""" indexes = self.tbl_update.selectedIndexes() self.currow = indexes[0].row() albumpath = self.tableMdlUpd._array[self.currow][5] openFolder(albumpath) def popUpTreeUpdate(self, position): self.menua.exec_(self.tbl_update.viewport().mapToGlobal(position)) @pyqtSlot(int) def showlcd(self, seconds): hours, seconds = seconds // 3600, seconds % 3600 minutes, seconds = seconds // 60, seconds % 60 self.total_p = "%02d:%02d:%02d" % (hours, minutes, seconds) self.lcdTime.display(self.total_p) QApplication.processEvents() def applyTheme(self): """Apply color Theme to main Gui.""" # main mainstyle = 'QWidget{{background-color: {col1};}}' \ 'QLCDNumber{{border: none;color: black; background-color: {col1};}}' \ 'QScrollBar:vertical{{width: 14px;}}' \ 'QScrollBar:horizontal{{height: 14px;}}' \ 'QTableView::item:selected{{ background-color:{col5}; color:white;}}' mainstyle = mainstyle.format(col1=self.curthe.listcolors[0], col2=self.curthe.listcolors[1], col5=self.curthe.listcolors[4]) self.setStyleSheet(mainstyle) gridstyle = 'alternate-background-color: {col3};background-color: {col4};' gridstyle = gridstyle.format(col3=self.curthe.listcolors[2], col4=self.curthe.listcolors[3]) self.tbl_update.setStyleSheet(gridstyle) def closeImport(self): """Close Windows.""" self.myThreadtime.quit() self.myThreadtime.stop() self.destroy() def onScrollPressed(self): pass
class CardAlbum(QObject): signaltxt = pyqtSignal(str, int) # message / level display mask_artwork = ('.jpg', '.jpeg', '.png', '.bmp', '.tif', '.bmp', '.tiff') mask_acovers = ('cover.jpg', 'Cover.jpg', 'cover.jpeg', 'cover.png', 'front.jpg', 'folder.jpg', 'folder.jpeg') mask_amedias = ('.flac', '.ape', '.wma', '.mp3', '.wv', '.aac', '.mpc') DCardAlbum = { 'ID_CD': None, # Base 'CATEGORY': None, # Params 'FAMILY': None, # Params 'NAME': None, # Path 'ARTIST': None, # Tag 'PATHNAME': None, # Path 'POSITION': None, # Path 'SUBPOSITION': None, # Path 'POSITIONHDD': None, # Path 'AUDIOTRACKS': 0, # Files 'TRACKS': 0, # Files 'CUE': 0, # Files 'COVER': None, # Files / Tag 'PIC': 0, # Files 'SIZE': 0, # Files 'CD': 0, # Tag / Path / Files 'YEAR': None, # Path / Tag 'ISRC': None, # Path 'LABEL': None, # Path 'TAGISRC': None, # Tag 'TAGLABEL': None, # Tag 'STYLE': None, # Tag 'COUNTRY': None, # Tag 'LENGTHSECONDS': 0, # Tag 'LENGTHDISPLAY': None, # Tag / Calcul 'TYPEMEDIA': None, # Tag 'SCORE': 0, # default value 'TAGMETHOD': None, # method tag album 'ADD': None, # Calcul 'MODIFIED': None # Calcul } PATH_PROG = path.dirname(path.abspath(__file__)) # Read File DBAlbums.ini FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') TEXT_NCO = group_dbalbums['text_nocov'] def __init__(self, parent=None): """Init.""" super(CardAlbum, self).__init__(parent) self.parent = parent def defineAlbum(self, pathalbum, category, family): """Define Card Album.""" cardalbum = deepcopy(self.DCardAlbum) listcardtrack = [] cardalbum['NAME'] = path.basename(pathalbum).replace('_', ' ').replace( '-WEB', '') list_tracksaudio = list(getListFiles(pathalbum, self.mask_amedias)) cardalbum['AUDIOTRACKS'] = len(list_tracksaudio) if cardalbum['AUDIOTRACKS'] > 0: cardalbum['CATEGORY'] = category cardalbum['FAMILY'] = family cardalbum['PATHNAME'] = pathalbum cardalbum['PIC'] = len( list(getListFiles(pathalbum, self.mask_artwork))) cardalbum['SIZE'] = int( round(getFolderSize(pathalbum) / 1024 / 1024, 0)) cardalbum['POSITION'] = pathalbum.split("\\")[-3] cardalbum['SUBPOSITION'] = pathalbum.split("\\")[-2] cardalbum['POSITIONHDD'] = pathalbum.split( "\\")[-3] + '\\' + pathalbum.split("\\")[-2] # cover path coversfile = list( getListFilesNoSubFolders(pathalbum, self.mask_acovers, 'Exactly')) coversfile.sort() if len(coversfile) > 0: # get first cardalbum['COVER'] = coversfile[0] else: coversfile = list( getListFiles(pathalbum, self.mask_acovers, 'Exactly')) coversfile.sort() if len(coversfile) > 0: cardalbum['COVER'] = coversfile[0] else: # tag no picture cardalbum['COVER'] = self.TEXT_NCO # year path yearfind = match(r'.*([(][1-2][0-9]{3}[)])', cardalbum['NAME']) if yearfind: cardalbum['YEAR'] = yearfind.group(1)[1:-1] cardalbum['NAME'] = cardalbum['NAME'][:-6] # label path if 'LABEL' in family.upper(): cardalbum['LABEL'] = cardalbum['SUBPOSITION'].upper() # isrc path if cardalbum['NAME'].startswith('['): cardalbum['ISRC'] = cardalbum['NAME'].split(']')[0].split( '[')[1] cardalbum['NAME'] = cardalbum['NAME'][cardalbum['NAME']. find(']') + 2:] # artist path if 'ARTIST' in family.upper(): cardalbum['ARTIST'] = cardalbum['SUBPOSITION'].upper() # list valid CUEsheet file listfilescue = [] filescue = list(getListFiles(pathalbum, ('.cue', ))) for filecue in filescue: try: parser = CueParser(filecue) lastfile = parser.lastfiletrack pathmedia = path.join(path.dirname(filecue), lastfile) if pathmedia.upper().endswith('.WAV'): pathmedia = pathmedia[:-3] + list_tracksaudio[0].split( '.')[-1] if path.exists(pathmedia): listfilescue.append(filecue) except: pass cardalbum['CUE'] = len(listfilescue) listfilescue.sort(reverse=False) # TAG file media method ? CUE or TAG if len(listfilescue) < cardalbum['AUDIOTRACKS']: cardalbum['TAGMETHOD'] = 'TAG' # album folder numbers of CD ? audioroot = list( getListFilesNoSubFolders(pathalbum, self.mask_amedias)) if len(audioroot) > 0: cardalbum['CD'] += 1 albumcardtracks = CardTracks().defineListTracksFiles( pathalbum) for cardtrack in albumcardtracks: if cardtrack['TRACKNUMBER'] is not None: cardtrack['TRACKORDER'] = str( cardtrack['TRACKNUMBER'].split('/')[0]).zfill( 2) listcardtrack += albumcardtracks elif len(audioroot) < cardalbum['AUDIOTRACKS']: albumfolders = getListFolders(pathalbum) albumfolders.sort(reverse=False) for albumfolder in albumfolders: fullalbumfolder = path.join(pathalbum, albumfolder) if len( list( getListFilesNoSubFolders( fullalbumfolder, self.mask_amedias))) > 0: cardalbum['CD'] += 1 albumcardtracks = CardTracks( ).defineListTracksFiles(fullalbumfolder) counter = 1 for cardtrack in albumcardtracks: cardtrack['DISC'] = cardalbum['CD'] if cardtrack['TRACKNUMBER'] is None: cardtrack['TRACKORDER'] = str( cardalbum['CD']).zfill(2) + str( counter).zfill(2) else: cardtrack['TRACKORDER'] = str( cardalbum['CD']).zfill(2) + str( cardtrack['TRACKNUMBER'].split( '/')[0]).zfill(2) counter += 1 listcardtrack += albumcardtracks else: cardalbum['TAGMETHOD'] = 'CUE' for filecue in listfilescue: cardalbum['CD'] = cardalbum['CD'] + 1 albumcardtracks = CardTracks().defineListTracksCUE( filecue, list_tracksaudio[0].split('.')[-1]) for cardtrack in albumcardtracks: cardtrack['DISC'] = cardalbum['CD'] if len(listfilescue) > 1: cardtrack['TRACKORDER'] = str( cardalbum['CD']).zfill(2) + str( cardtrack['TRACKNUMBER']).zfill(2) else: cardtrack['TRACKORDER'] = str( cardtrack['TRACKNUMBER']).zfill(2) if cardtrack['FILENAME'].upper().endswith('.WAV'): cardtrack['FILENAME'] = cardtrack[ 'FILENAME'][:-3] + cardtrack[ 'TYPEMEDIA'].split('/')[1] listcardtrack += albumcardtracks # numbers cds path if cardalbum['CD'] is None: numbercdfind = match(r'.*([1-9][C][D])', cardalbum['NAME']) if numbercdfind: cardalbum['CD'] = numbercdfind.group(1)[:-2] # tracks tag to album card if listcardtrack: cardtrack = listcardtrack[0] cardalbum['COUNTRY'] = cardtrack['COUNTRY'] cardalbum['TYPEMEDIA'] = cardtrack['TYPEMEDIA'] if cardtrack['ISRC'] is not None: cardalbum['TAGISRC'] = cardtrack['ISRC'].upper() if cardtrack['ORGANIZATION'] is not None: cardalbum['TAGLABEL'] = cardtrack['ORGANIZATION'].upper() if cardalbum['TAGISRC'] is None: pass #cardalbum['TAGISRC'] = cardalbum['ISRC'] # albumartist or artist tag if ' VA ' in cardalbum['NAME'] or cardalbum['NAME'].startswith( 'VA '): cardalbum['ARTIST'] = 'Various' else: cardalbum['ARTIST'] = cardtrack['ALBUMARTIST'] if cardalbum['ARTIST'] is not None: cardalbum['ARTIST'] = cardalbum['ARTIST'].replace( 'VA', 'Various') cardalbum['ARTIST'] = cardalbum['ARTIST'].replace( 'Various Artists', 'Various') if cardalbum['ARTIST'] is None: cardalbum['ARTIST'] = cardtrack['ARTIST'] # year tag if cardalbum['YEAR'] is None: cardalbum['YEAR'] = cardtrack['DATE'] else: if cardtrack['DATE'] is not None: if cardalbum['YEAR'] != cardtrack['DATE'][:4]: self.signaltxt.emit( 'WARNING : year tag {tag} <> year path {pat}'. format(tag=cardtrack['DATE'], pat=cardalbum['YEAR'][:4]), 2) else: self.signaltxt.emit('ERROR : no tracks ' + pathalbum, 3) # no year tag and no year path format if cardalbum['YEAR'] is None: yearfind = match(r'.*([1-2][0-9]{3})', cardalbum['NAME']) if yearfind: cardalbum['YEAR'] = yearfind.group(1) # cover if one image artwork if cardalbum['COVER'] == self.TEXT_NCO and cardalbum['PIC'] == 1: cardalbum['COVER'] = list( getListFiles(pathalbum, self.mask_artwork))[0] # cover tag if cardalbum['COVER'] == self.TEXT_NCO: cover = path.join(cardalbum['PATHNAME'], ) if DBMediasTags().getImageFromTag(cover): cardalbum['COVER'] = cover cardalbum['PIC'] = 1 # length tag & genres liststyles = [] seconds = 0 for cardtrack in listcardtrack: seconds += cardtrack['LENGTHSECONDS'] if cardtrack['GENRE'] is not None: genres = cardtrack['GENRE'] genres = genres.replace(';', '/').replace( ',', '/').replace('"', '').replace('-', ' ').replace(' ', ' ') for genre in genres.split('/'): genre = genre.strip().title() if genre not in liststyles: liststyles.append(genre) cardalbum['STYLE'] = '/'.join(liststyles) minutes = seconds // 60 cardalbum['LENGTHSECONDS'] = seconds cardalbum['LENGTHDISPLAY'] = "%02d:%02d" % (minutes, seconds % 60) # number of tracks albums cardalbum['TRACKS'] = len(listcardtrack) # date cardalbum['ADD'] = QDateTime.currentDateTime().toString( 'yyyy-MM-dd HH:mm:ss') cardalbum['MODIFIED'] = QDateTime.currentDateTime().toString( 'yyyy-MM-dd HH:mm:ss') # clean album name cleanfind = match(r'.*([-][1-2][0-9]{3})', cardalbum['NAME']) if cleanfind: self.signaltxt.emit('WARNING : clean : ' + cardalbum['NAME'], 2) cardalbum['NAME'] = self.cleanfolderalbum(cardalbum['NAME']) if cardalbum['NAME'].endswith('-'): cardalbum['NAME'] = cardalbum['NAME'][:-1] # cover from multiple pics if cardalbum['COVER'] == self.TEXT_NCO and cardalbum['PIC'] > 1: listcovers = list(getListFiles(pathalbum, self.mask_artwork)) for listcover in listcovers: if 'front' in listcover.lower(): cardalbum['COVER'] = listcover return cardalbum, listcardtrack def displayCardAlbum(self, cardalbum): """Display CardAlbum.""" print('-', '______----ALBUM----______') for key, value in cardalbum.items(): print(f'{key:<15} = {value}') def cleanfolderalbum(self, albumname): """Clean folder album name WEB.""" albumtempo = albumname.replace("-psy-music.ru", "").replace( "_", " ").replace(".", " ").replace("--", "-") yearfind = match(r'.*([-][1-2][0-9]{3})', albumtempo) albumtempo = yearfind.group(0)[:-5] if albumtempo.find(' - ') > 0: arrayalbum = albumtempo.split(' - ') else: arrayalbum = albumtempo.split('-') numbertirets = len(arrayalbum) if numbertirets == 1: albumtempo = arrayalbum[0].strip() else: albumtempo = arrayalbum[0].strip() + ' - ' + arrayalbum[1].strip() #if numbertirets == 2: # albumtempo = arrayalbum[0].strip() + '-' + arrayalbum[1].strip() + "[" + arrayalbum[2].strip() + "]" return albumtempo
class ReleaseInvent(QObject): signalrun = pyqtSignal(int, str) # percent / message signaltxt = pyqtSignal(str, int) # message / level display signalend = pyqtSignal() signalmacroend = pyqtSignal() PATH_PROG = path.dirname(path.abspath(__file__)) FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') WIDT_PICM = group_dbalbums['thun_csize'] TEXT_NCO = group_dbalbums['text_nocov'] def __init__(self, list_actions, modsql): """Init invent, build list albums exists in database.""" super(ReleaseInvent, self).__init__() self.list_action = list_actions self.modsql = modsql def executeActions(self): """Action for update database. [CATEGORY, FAMILY, 'DELETE/UPDATE/ADD', ID_CD, 'NAME', 'PATHNAME']""" numbersactions = len(self.list_action) counter = 1 for listalbum in self.list_action: self.signalrun.emit((counter / numbersactions) * 100, listalbum[2]) message = "\n- {act} ({num}/{tot}) : {nam}".format( act=listalbum[2], num=counter, tot=numbersactions, nam=listalbum[5]) self.signaltxt.emit(message, 1) if listalbum[2] == 'DELETE': self.deleteAlbum(listalbum[3]) elif listalbum[2] == 'UPDATE': #print('UPDATE', listalbum[0], listalbum[1], listalbum[5], listalbum[3]) self.updateAlbum(listalbum[0], listalbum[1], listalbum[5], listalbum[3]) elif listalbum[2] == 'ADD': #print('ADD', listalbum[0], listalbum[1], listalbum[5], listalbum[5]) self.addAlbum(listalbum[0], listalbum[1], listalbum[5]) else: self.signaltxt.emit( 'ERROR : Operation "' + listalbum[2] + '" error', 3) counter += 1 #QApplication.processEvents() self.signalrun.emit(100, 'Completed') self.signalend.emit() def emitDisplayCardAlbum(self, cardalbum, cardtracks): self.signaltxt.emit( displayArrayDict([cardalbum], ('ID_CD', 'CATEGORY', 'FAMILY', 'TAGMETHOD', 'POSITIONHDD', 'NAME')), 0) self.signaltxt.emit( displayArrayDict([cardalbum], ('AUDIOTRACKS', 'TRACKS', 'LENGTHDISPLAY', 'CUE', 'PIC', 'SIZE', 'CD', 'YEAR', 'ISRC', 'LABEL', 'TAGISRC', 'TAGLABEL', 'COUNTRY')), 0) self.signaltxt.emit( displayArrayDict( cardtracks, ('TRACKORDER', 'LENGTHDISPLAY', 'ARTIST', 'TITLE', 'TYPEMEDIA', 'DATE', 'GENRE', 'DISC', 'FILENAME')), 0) def updateAlbum(self, category, family, folder, idcd): """Build album card.""" # capture datas old oldcardalbum = DBFuncBase().sqlToArrayDict('ALBUMS', 'ID_CD', idcd) oldcardalbum = oldcardalbum[0] oldcardtracks = DBFuncBase().sqlToArrayDict('TRACKS', 'ID_CD', idcd) # delete old DBFuncBase().deleteTable("TRACKS", "ID_CD", idcd) DBFuncBase().deleteTable("COVERS", "ID_CD", idcd) # new analysealbum = CardAlbum() analysealbum.signaltxt.connect(self.infoAnalysealbum) cardalbum, cardtracks = analysealbum.defineAlbum( folder, category, family) # apply ID album for cardtrack in cardtracks: cardtrack['ID_CD'] = idcd # copy old datas albums cardalbum['ID_CD'] = oldcardalbum['ID_CD'] cardalbum['SCORE'] = oldcardalbum['SCORE'] cardalbum['ADD'] = oldcardalbum['ADD'] # copy score track for oldcardtrack in oldcardtracks: if oldcardtrack['SCORE'] > 0: print("score") # copy score for cardtrack in cardtracks: if cardtrack['FILENAME'] == oldcardtrack[ 'FILENAME'] and cardtrack[ 'TRACKORDER'] == oldcardtrack['TRACKORDER']: cardtrack['SCORE'] = oldcardtrack['SCORE'] # write album update DBFuncBase().arrayCardsToSql('UPDATE', cardalbum, 'ALBUMS', 'ID_CD') DBFuncBase().arrayCardsToSql('INSERT', cardtracks, 'TRACKS', 'ID_TRACK') if cardalbum['COVER'] != self.TEXT_NCO: DBFuncBase().imageToSql(cardalbum['COVER'], idcd, self.WIDT_PICM) # display consol self.emitDisplayCardAlbum(cardalbum, cardtracks) self.signalmacroend.emit() def addAlbum(self, category, family, folder): """Add New Album in database.""" analysealbum = CardAlbum() analysealbum.signaltxt.connect(self.infoAnalysealbum) cardalbum, cardtracks = analysealbum.defineAlbum( folder, category, family) DBFuncBase().arrayCardsToSql('INSERT', cardalbum, 'ALBUMS', 'ID_CD') # last id for cardtracks #request = "SELECT LAST_INSERT_ID() as lastid;" request = getrequest('lastid', self.modsql) print(request) idcd = DBFuncBase().sqlToArray(request)[0] for cardtrack in cardtracks: cardtrack['ID_CD'] = idcd DBFuncBase().arrayCardsToSql('INSERT', cardtracks, 'TRACKS', 'ID_TRACK') if cardalbum['COVER'] != self.TEXT_NCO: DBFuncBase().imageToSql(cardalbum['COVER'], idcd, self.WIDT_PICM) # display consol self.emitDisplayCardAlbum(cardalbum, cardtracks) self.signalmacroend.emit() def deleteAlbum(self, idcd): """Delete Album in database.""" DBFuncBase().deleteTable("ALBUMS", "ID_CD", idcd) DBFuncBase().deleteTable("TRACKS", "ID_CD", idcd) DBFuncBase().deleteTable("COVERS", "ID_CD", idcd) self.signalmacroend.emit() def infoAnalysealbum(self, text, level): self.signaltxt.emit(text, level)
class DBloadingGui(QWidget, Ui_LoadingWindow): PATH_PROG = path.dirname(path.abspath(__file__)) RESS_LOGO = path.join(PATH_PROG, 'IMG') chdir(PATH_PROG) FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') LOGO = group_dbalbums['progr_logo'] FONT_MAI = group_dbalbums['font00_ttx'] def __init__(self, modsql, title, parent): super(DBloadingGui, self).__init__(parent) self.setupUi(self) self.parent = parent self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowFlags(Qt.SplashScreen) centerWidget(self) # font font = QFont() font.setFamily(self.FONT_MAI) font.setFixedPitch(True) font.setPointSize(14) self.lab_text.setFont(font) self.tabWidget.currentChanged.connect(self.chgtLogo) # logo gif self.numlogo = 1 self.movielogo = QMovie(path.join(self.RESS_LOGO, "logo1.gif")) self.lab_logo.setMovie(self.movielogo) self.movielogo.start() # tab1 req = DBFuncBase().buildReqTCD("CATEGORY", "FAMILY", "ALBUMS", "ALBUM", "1", True, modsql) self.buildTab(req, self.tableWid1) # tab2 req = DBFuncBase().buildReqTCD("CATEGORY", "FAMILY", "ALBUMS", "SIZE (GO)", "ROUND( `Size` /1024,1)", True, modsql) self.buildTab(req, self.tableWid2) # tab3 req = DBFuncBase().buildReqTCD("YEAR", "CATEGORY", "ALBUMS", "YEAR", "1", True, modsql) self.buildTab(req, self.tableWid3) # message basedate = DBFuncBase().sqlToArray(getrequest('datedatabase', modsql)) if len(basedate) == 0: txt_message = modsql + " Base \nlast modified :\nnever" elif isinstance(basedate[0], QDateTime): txt_message = modsql + " Base \nlast modified :\n" + basedate[ 0].toString('dd/MM/yyyy hh:mm:ss') else: txt_message = modsql + " Base \nlast modified :\n" + basedate[ 0].replace('T', ' ') self.lab_text.setText(title + "\nConnected " + txt_message) # quit self.btn_quit.clicked.connect(lambda: self.hide()) # theme self.applyTheme() @pyqtSlot() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape or event.key() == Qt.Key_F1: self.hide() def buildTab(self, req, tab): model = QSqlQueryModel(self) model.setQuery(req) tab.setModel(model) tab.resizeColumnsToContents() tab.verticalHeader().setVisible(False) tab.horizontalHeader().setStretchLastSection(True) tab.verticalHeader().setDefaultSectionSize(self.parent.C_HEIGHT) def chgtLogo(self): self.movielogo.stop() self.numlogo += 1 self.numlogo = self.numlogo % 3 logo = path.join(self.RESS_LOGO, "logo" + str(self.numlogo) + ".gif") self.movielogo = QMovie(logo) self.lab_logo.setMovie(self.movielogo) self.movielogo.start() def applyTheme(self): """Apply color Theme to main Gui.""" # main mainstyle = 'QWidget{{background-color: {col2};}}' \ 'QScrollBar:vertical{{width: 14px;}}' \ 'QScrollBar:horizontal{{height: 14px;}}' mainstyle = mainstyle.format(col2=self.parent.curthe.listcolors[1]) self.setStyleSheet(mainstyle) gridstyle = 'alternate-background-color: {col3};background-color: {col4};' gridstyle = gridstyle.format(col3=self.parent.curthe.listcolors[2], col4=self.parent.curthe.listcolors[3]) self.tableWid1.setStyleSheet(gridstyle) self.tableWid2.setStyleSheet(gridstyle) self.tableWid3.setStyleSheet(gridstyle)
class DBAlbumsQT5Mini(QMainWindow): """Init mini Gui constants.""" PATH_PROG = path.dirname(path.abspath(__file__)) RESS_ICOS = path.join(PATH_PROG, 'IMG', 'ICO') chdir(PATH_PROG) VERS_PROG = '1.01' TITL_PROG = "♫ DBAlbums mini v{v} (2017)".format(v=VERS_PROG) FILE__INI = 'DBAlbums.json' Json_params = JsonParams(FILE__INI) group_dbalbums = Json_params.getMember('dbalbums') WINS_ICO = path.join(PATH_PROG, 'IMG', group_dbalbums['wins_icone']) PICM_NCO = path.join(PATH_PROG, 'IMG', group_dbalbums['pict_blank']) THEM_COL = group_dbalbums['name_theme'] ENVT_DEF = group_dbalbums['envt_deflt'] def __init__(self, parent=None): super(DBAlbumsQT5Mini, self).__init__(parent) self.setWindowIcon(QIcon(self.WINS_ICO)) self.setWindowTitle(self.TITL_PROG + ' : [' + self.ENVT_DEF + ']') self.h_main = 400 self.resize(1248, self.h_main) centerWidget(self) self.menua = QMenu() self.action_OPF = self.menua.addAction( self.style().standardIcon(QStyle.SP_DialogOpenButton), "Open Folder...", self.getFolder) self.textsearch = QLineEdit() self.textsearch.setFixedSize(170, 22) self.statusBar().addPermanentWidget(self.textsearch) self.textsearch.textChanged.connect(self.onFiltersChanged) self.btn_style = QPushButton() self.btn_style.setIcon(self.style().standardIcon( QStyle.SP_DialogResetButton)) self.btn_style.setStyleSheet("border: none;") self.btn_style.clicked.connect( lambda: [self.curthe.nextTheme(), self.applyTheme()]) self.statusBar().addPermanentWidget(self.btn_style) boolconnect, self.dbbase, self.modsql, self.rootDk, self.lstcat = connectDatabase( self.ENVT_DEF) autoList = DBFuncBase().sqlToArray( getrequest('autocompletion', self.modsql)) self.com_autcom = QCompleter(autoList, self.textsearch) self.com_autcom.setCaseSensitivity(Qt.CaseInsensitive) self.textsearch.setCompleter(self.com_autcom) self.mytable = QTableView(self) self.mytable.setAlternatingRowColors(True) self.mytable.setSortingEnabled(True) self.mytable.setSelectionBehavior(QTableView.SelectRows) self.mytable.setSelectionMode(QAbstractItemView.SingleSelection) self.mytable.doubleClicked.connect(self.onSelect) self.mytable.setContextMenuPolicy(Qt.CustomContextMenu) self.mytable.customContextMenuRequested.connect(self.popUpTreeAlbums) self.curthe = ThemeColors(self.THEM_COL) self.applyTheme() req = getrequest('albumslist', self.modsql) self.model = ModelTableAlbumsABS(self, req) self.model.SortFilterProxy.layoutChanged.connect(self.listChanged) self.model.SortFilterProxy.sort(-1) self.mytable.setModel(self.model.SortFilterProxy) # width columns for ind in range(len(self.model.A_C_WIDTH)): self.mytable.setColumnWidth(ind, self.model.A_C_WIDTH[ind]) # height rows self.mytable.verticalHeader().setDefaultSectionSize( self.model.C_HEIGHT) self.displayTitle() self.setCentralWidget(self.mytable) def onFiltersChanged(self): self.model.SortFilterProxy.updateFilters(self.textsearch.text()) self.displayTitle() def onSelect(self): indexes = self.mytable.selectedIndexes() indexes = self.model.SortFilterProxy.mapToSource(indexes[0]) self.currow = indexes.row() albumname = self.model.getData(self.currow, 'NAME') idcd = self.model.getData(self.currow, 'ID_CD') coveral = DBFuncBase().sqlToPixmap(idcd, self.PICM_NCO) CoverViewGui(coveral, albumname, self.h_main, self.h_main) def popUpTreeAlbums(self, position): self.menua.exec_(self.mytable.viewport().mapToGlobal(position)) def getFolder(self): """Open album folder.""" indexes = self.mytable.selectedIndexes() indexes = self.model.SortFilterProxy.mapToSource(indexes[0]) self.currow = indexes.row() albumpath = self.model.getData(self.currow, 'PATHNAME') openFolder(albumpath) def listChanged(self): pass def displayTitle(self): if int(((self.model.SortFilterProxy.cpt_len / 60 / 60) / 24) * 10) / 10 < 1: # seoncd -> Hours txt_len = str( int(((self.model.SortFilterProxy.cpt_len / 60 / 60)) * 10) / 10) + ' Hours' else: # seoncd -> Days txt_len = str( int(((self.model.SortFilterProxy.cpt_len / 60 / 60) / 24) * 10) / 10) + ' Days' if int(self.model.SortFilterProxy.cpt_siz / 1024) == 0: txt_siz = str(self.model.SortFilterProxy.cpt_siz) + ' Mo' else: txt_siz = str(int( self.model.SortFilterProxy.cpt_siz / 1024)) + ' Go' txt_sch = (self.textsearch.text() if len(self.textsearch.text()) > 0 else 'all') message = "Search Result \"{sch}\" : {alb} | {trk} | {cds} | {siz} | {dur}" message = message.format( alb=displayCounters(self.model.SortFilterProxy.rowCount(), 'Album'), cds=displayCounters(self.model.SortFilterProxy.cpt_cds, 'CD'), trk=displayCounters(self.model.SortFilterProxy.cpt_trk, 'Track'), siz=txt_siz, dur=txt_len, sch=txt_sch) self.statusBar().showMessage(message) # setsetWindowTitle(message) def applyTheme(self): """Apply color Theme to main Gui.""" # main mainstyle = 'QMainWindow{{background-color: {col1};border: 1px solid black;}}' \ 'QLineEdit{{background-color: {col2};}}' \ 'QStatusBar{{background-color: {col1};border: 1px solid black;}}' \ 'QScrollBar:vertical{{width: 14px;}}' \ 'QScrollBar:horizontal{{height: 14px;}}' \ 'QTableView{{alternate-background-color: {col3};background-color: {col4};}}' \ 'QTableView::item:selected{{ background-color:{col5}; color:white;}}' mainstyle = mainstyle.format(col1=self.curthe.listcolors[0], col2=self.curthe.listcolors[1], col3=self.curthe.listcolors[2], col4=self.curthe.listcolors[3], col5=self.curthe.listcolors[4]) self.setStyleSheet(mainstyle) # treeview gridstyle = 'QHeaderView::section{{background-color: {col2};border-radius:1px;margin: 1px;padding: 2px;}}' gridstyle = gridstyle.format(col2=self.curthe.listcolors[1]) self.mytable.setStyleSheet(gridstyle)