1. add simple-gui

This commit is contained in:
Yaronzz
2022-06-23 10:03:17 +08:00
parent b1ebd6f0b2
commit d643de43e9
6 changed files with 286 additions and 96 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
from tidal_dl.printf import VERSION
setup(
name='tidal-dl',
name='tidal-dl-test',
version=VERSION,
license="Apache2",
description="Tidal Music Downloader.",
+12 -2
View File
@@ -13,18 +13,20 @@ import getopt
from tidal_dl.events import *
from tidal_dl.settings import *
from tidal_dl.gui import *
def mainCommand():
try:
opts, args = getopt.getopt(sys.argv[1:],
"hvl:o:q:r:",
["help", "version", "link=", "output=", "quality", "resolution"])
"hvgl:o:q:r:",
["help", "version", "gui", "link=", "output=", "quality", "resolution"])
except getopt.GetoptError as errmsg:
Printf.err(vars(errmsg)['msg'] + ". Use 'tidal-dl -h' for useage.")
return
link = None
showGui = False
for opt, val in opts:
if opt in ('-h', '--help'):
Printf.usage()
@@ -32,6 +34,9 @@ def mainCommand():
if opt in ('-v', '--version'):
Printf.logo()
return
if opt in ('-g', '--gui'):
showGui = True
return
if opt in ('-l', '--link'):
link = val
continue
@@ -51,6 +56,10 @@ def mainCommand():
if not aigpy.path.mkdirs(SETTINGS.downloadPath):
Printf.err(LANG.MSG_PATH_ERR + SETTINGS.downloadPath)
return
if showGui:
startGui()
return
if link is not None:
if not loginByConfig():
@@ -123,6 +132,7 @@ def test():
Printf.settings()
# test example
# https://tidal.com/browse/track/70973230
# track 70973230 77798028 212657
start('70973230')
# album 58138532 77803199 21993753 79151897 56288918
+2 -1
View File
@@ -14,6 +14,7 @@ import logging
from tidal_dl.paths import *
from tidal_dl.printf import *
from tidal_dl.decryption import *
from tidal_dl.tidal import *
def __isSkip__(finalpath, url):
@@ -91,7 +92,7 @@ def downloadAlbumInfo(album, tracks):
infos = ""
infos += "[ID] %s\n" % (str(album.id))
infos += "[Title] %s\n" % (str(album.title))
infos += "[Artists] %s\n" % (str(album.artist.name))
infos += "[Artists] %s\n" % (TIDAL_API.getArtistsName(album.artists))
infos += "[ReleaseDate] %s\n" % (str(album.releaseDate))
infos += "[SongNum] %s\n" % (str(album.numberOfTracks))
infos += "[Duration] %s\n" % (str(album.duration))
+72 -85
View File
@@ -25,84 +25,66 @@ START DOWNLOAD
'''
def __album__(obj: Album):
try:
Printf.album(obj)
tracks, videos = TIDAL_API.getItems(obj.id, Type.Album)
if SETTINGS.saveAlbumInfo:
downloadAlbumInfo(obj, tracks)
if SETTINGS.saveCovers:
downloadCover(obj)
for item in tracks:
downloadTrack(item, obj)
for item in videos:
downloadVideo(item, obj)
except Exception as e:
Printf.err(str(e))
def start_album(obj: Album):
Printf.album(obj)
tracks, videos = TIDAL_API.getItems(obj.id, Type.Album)
if SETTINGS.saveAlbumInfo:
downloadAlbumInfo(obj, tracks)
if SETTINGS.saveCovers:
downloadCover(obj)
for item in tracks:
downloadTrack(item, obj)
for item in videos:
downloadVideo(item, obj)
def __track__(obj: Track):
try:
album = TIDAL_API.getAlbum(obj.album.id)
if SETTINGS.saveCovers:
def start_track(obj: Track):
album = TIDAL_API.getAlbum(obj.album.id)
if SETTINGS.saveCovers:
downloadCover(album)
downloadTrack(obj, album)
def start_video(obj: Video):
# Printf.video(obj)
downloadVideo(obj, obj.album)
def start_artist(obj: Artist):
albums = TIDAL_API.getArtistAlbums(obj.id, SETTINGS.includeEP)
Printf.artist(obj, len(albums))
for item in albums:
start_album(item)
def start_playlist(obj: Playlist):
Printf.playlist(obj)
tracks, videos = TIDAL_API.getItems(obj.uuid, Type.Playlist)
for index, item in enumerate(tracks):
album = TIDAL_API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
downloadTrack(item, album, obj)
if SETTINGS.saveCovers and not SETTINGS.usePlaylistFolder:
downloadCover(album)
downloadTrack(obj, album)
except Exception as e:
Printf.err(str(e))
for item in videos:
downloadVideo(item, None)
def __video__(obj: Video):
try:
# Printf.video(obj)
downloadVideo(obj, obj.album)
except Exception as e:
Printf.err(str(e))
def start_mix(obj: Mix):
Printf.mix(obj)
for index, item in enumerate(obj.tracks):
album = TIDAL_API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
downloadTrack(item, album)
if SETTINGS.saveCovers and not SETTINGS.usePlaylistFolder:
downloadCover(album)
for item in obj.videos:
downloadVideo(item, None)
def __artist__(obj: Artist):
try:
albums = TIDAL_API.getArtistAlbums(obj.id, SETTINGS.includeEP)
Printf.artist(obj, len(albums))
for item in albums:
__album__(item)
except Exception as e:
Printf.err(str(e))
def __playlist__(obj: Playlist):
try:
Printf.playlist(obj)
tracks, videos = TIDAL_API.getItems(obj.uuid, Type.Playlist)
for index, item in enumerate(tracks):
album = TIDAL_API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
downloadTrack(item, album, obj)
if SETTINGS.saveCovers and not SETTINGS.usePlaylistFolder:
downloadCover(album)
for item in videos:
downloadVideo(item, None)
except Exception as e:
Printf.err(str(e))
def __mix__(obj: Mix):
try:
Printf.mix(obj)
for index, item in enumerate(obj.tracks):
album = TIDAL_API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
downloadTrack(item, album)
if SETTINGS.saveCovers and not SETTINGS.usePlaylistFolder:
downloadCover(album)
for item in obj.videos:
downloadVideo(item, None)
except Exception as e:
Printf.err(str(e))
def __dealFile__(string):
def start_file(string):
txt = aigpy.file.getContent(string)
if aigpy.string.isNull(txt):
Printf.err("Nothing can read!")
@@ -118,6 +100,20 @@ def __dealFile__(string):
start(item)
def start_type(etype: Type, obj):
if etype == Type.Album:
start_album(obj)
elif etype == Type.Track:
start_track(obj)
elif etype == Type.Video:
start_video(obj)
elif etype == Type.Artist:
start_artist(obj)
elif etype == Type.Playlist:
start_playlist(obj)
elif etype == Type.Mix:
start_mix(obj)
def start(string):
if aigpy.string.isNull(string):
Printf.err('Please enter something.')
@@ -128,7 +124,7 @@ def start(string):
if aigpy.string.isNull(item):
continue
if os.path.exists(item):
__dealFile__(item)
start_file(item)
return
try:
@@ -137,19 +133,10 @@ def start(string):
Printf.err(str(e) + " [" + item + "]")
return
if etype == Type.Album:
__album__(obj)
elif etype == Type.Track:
__track__(obj)
elif etype == Type.Video:
__video__(obj)
elif etype == Type.Artist:
__artist__(obj)
elif etype == Type.Playlist:
__playlist__(obj)
elif etype == Type.Mix:
__mix__(obj)
try:
start_type(etype, obj)
except Exception as e:
Printf.err(str(e))
'''
=================================
+180
View File
@@ -0,0 +1,180 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File : test.py
@Date : 2022/03/28
@Author : Yaronzz
@Version : 1.0
@Contact : yaronhuang@foxmail.com
@Desc :
"""
import os
import sys
import _thread
from tidal_dl.events import *
from tidal_dl.settings import *
from PyQt5.QtCore import Qt
from PyQt5.QtCore import pyqtSignal
from PyQt5 import QtWidgets
from qt_material import apply_stylesheet
class MainView(QtWidgets.QWidget):
s_downloadEnd = pyqtSignal(str, bool, str)
def __init__(self, ) -> None:
super().__init__()
self.initView()
self.setMinimumSize(600, 500)
self.setWindowTitle("Tidal-dl")
def __info__(self, msg):
QtWidgets.QMessageBox.information(self,
'Info',
msg,
QtWidgets.QMessageBox.Yes)
def initView(self):
self.c_lineSearch = QtWidgets.QLineEdit()
self.c_btnSearch = QtWidgets.QPushButton("Search")
self.c_btnDownload = QtWidgets.QPushButton("Download")
self.m_supportType = [Type.Album, Type.Playlist, Type.Track, Type.Video]
self.c_combType = QtWidgets.QComboBox()
for item in self.m_supportType:
self.c_combType.addItem(item.name, item)
columnNames = ['#', 'Title', 'Artists', 'Quality']
self.c_tableInfo = QtWidgets.QTableWidget()
self.c_tableInfo.setColumnCount(len(columnNames))
self.c_tableInfo.setRowCount(0)
self.c_tableInfo.setShowGrid(False)
self.c_tableInfo.verticalHeader().setVisible(False)
self.c_tableInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.c_tableInfo.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.c_tableInfo.horizontalHeader().setStretchLastSection(True)
self.c_tableInfo.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
self.c_tableInfo.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.c_tableInfo.setFocusPolicy(Qt.NoFocus)
for index, name in enumerate(columnNames):
item = QtWidgets.QTableWidgetItem(name)
self.c_tableInfo.setHorizontalHeaderItem(index, item)
self.lineGrid = QtWidgets.QHBoxLayout()
self.lineGrid.addWidget(self.c_combType)
self.lineGrid.addWidget(self.c_lineSearch)
self.lineGrid.addWidget(self.c_btnSearch)
self.mainGrid = QtWidgets.QVBoxLayout(self)
self.mainGrid.addLayout(self.lineGrid)
self.mainGrid.addWidget(self.c_tableInfo)
self.mainGrid.addWidget(self.c_btnDownload)
self.c_btnSearch.clicked.connect(self.search)
self.c_btnDownload.clicked.connect(self.download)
self.s_downloadEnd.connect(self.downloadEnd)
def addItem(self, rowIdx: int, colIdx: int, text):
if isinstance(text, str):
item = QtWidgets.QTableWidgetItem(text)
self.c_tableInfo.setItem(rowIdx, colIdx, item)
def search(self):
self.c_tableInfo.setRowCount(0)
self.s_type = self.c_combType.currentData()
self.s_text = self.c_lineSearch.text()
if self.s_text.startswith('http'):
tmpType, tmpId = TIDAL_API.parseUrl(self.s_text)
if tmpType == Type.Null:
self.__info__('Url not support')
return
elif tmpType not in self.m_supportType:
self.__info__(f'Type[{tmpType.name}] not support')
return
tmpData = TIDAL_API.getTypeData(tmpId, tmpType)
if tmpData is None:
self.__info__('Url is wrong!')
return
self.s_type = tmpType
self.s_array = [tmpData]
self.s_result = None
self.c_combType.setCurrentText(tmpType.name)
else:
self.s_result = TIDAL_API.search(self.s_text, self.s_type)
self.s_array = TIDAL_API.getSearchResultItems(self.s_result, self.s_type)
if len(self.s_array) <= 0:
self.__info__('No result')
return
self.c_tableInfo.setRowCount(len(self.s_array))
for index, item in enumerate(self.s_array):
self.addItem(index, 0, str(index + 1))
if self.s_type in [Type.Album, Type.Track]:
self.addItem(index, 1, item.title)
self.addItem(index, 2, TIDAL_API.getArtistsName(item.artists))
self.addItem(index, 3, item.audioQuality)
elif self.s_type in [Type.Video]:
self.addItem(index, 1, item.title)
self.addItem(index, 2, TIDAL_API.getArtistsName(item.artists))
self.addItem(index, 3, item.quality)
elif self.s_type in [Type.Playlist]:
self.addItem(index, 1, item.title)
self.addItem(index, 2, '')
self.addItem(index, 3, '')
def download(self):
index = self.c_tableInfo.currentIndex().row()
if index < 0:
self.__info__('Please select a row first.')
return
self.c_btnDownload.setEnabled(False)
self.c_btnDownload.setText(f"Downloading [{self.s_array[index].title}]...")
def __thread_download__(model: MainView):
try:
type = model.s_type
item = model.s_array[index]
start_type(type, item)
model.s_downloadEnd.emit(item.title, True, '')
except Exception as e:
model.s_downloadEnd.emit(item.title, False, str(e))
_thread.start_new_thread(__thread_download__, (self, ))
def downloadEnd(self, title, result, msg):
self.c_btnDownload.setEnabled(True)
self.c_btnDownload.setText(f"Download")
if result:
self.__info__(f'Download [{title}] finish')
else:
self.__info__(f'Download [{title}] failed:{msg}')
def checkLogin(self):
if not loginByConfig():
self.__info__('Login failed. Please log in using the command line first.')
def startGui():
os.chdir(sys.path[0])
app = QtWidgets.QApplication(sys.argv)
apply_stylesheet(app, theme='dark_blue.xml')
window = MainView()
window.show()
window.checkLogin()
app.exec_()
if __name__ == '__main__':
SETTINGS.read(getProfilePath())
TOKEN.read(getTokenPath())
startGui()
+19 -7
View File
@@ -45,7 +45,7 @@ class TidalAPI(object):
except Exception as e:
if index >= 3:
errmsg += respond.text
raise Exception(errmsg)
def __getItems__(self, path, params={}):
@@ -69,7 +69,7 @@ class TidalAPI(object):
def __getResolutionList__(self, url):
ret = []
array = requests.get(url).text.split("#")
array = requests.get(url).content.decode('utf-8').split("#")
for item in array:
if "RESOLUTION=" not in item:
continue
@@ -209,8 +209,8 @@ class TidalAPI(object):
return self.getMix(id)
return None
def search(self, text: str, type: Type, offset: int, limit: int) -> SearchResult:
typeStr = Type.Album.name.upper() + "S"
def search(self, text: str, type: Type, offset: int = 0, limit: int = 10) -> SearchResult:
typeStr = type.name.upper() + "S"
if type == Type.Null:
typeStr = "ARTISTS,ALBUMS,TRACKS,VIDEOS,PLAYLISTS"
@@ -220,6 +220,19 @@ class TidalAPI(object):
"types": typeStr}
return aigpy.model.dictToModel(self.__get__('search', params=params), SearchResult())
def getSearchResultItems(self, result: SearchResult, type: Type):
if type == Type.Track:
return result.tracks.items
if type == Type.Video:
return result.videos.items
if type == Type.Album:
return result.albums.items
if type == Type.Artist:
return result.artists.items
if type == Type.Playlist:
return result.playlists.items
return []
def getLyrics(self, id) -> Lyrics:
data = self.__get__(f'tracks/{str(id)}/lyrics', urlpre='https://listen.tidal.com/v1/')
return aigpy.model.dictToModel(data, Lyrics())
@@ -365,10 +378,9 @@ class TidalAPI(object):
return item, obj
except:
continue
raise Exception("No result.")
# Singleton
TIDAL_API = TidalAPI()