mirror of
https://github.com/yaronzz/Tidal-Media-Downloader.git
synced 2026-01-16 16:04:25 -05:00
update gui
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from urllib import request
|
||||
import requests
|
||||
|
||||
import aigpy
|
||||
import lyricsgenius
|
||||
@@ -318,7 +318,7 @@ def convert(srcPath, stream):
|
||||
return srcPath
|
||||
|
||||
|
||||
def downloadTrack(track: Track, album=None, playlist=None, userProgress=None):
|
||||
def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, partSize=1048576):
|
||||
try:
|
||||
msg, stream = API.getStreamUrl(track.id, CONF.audioQuality)
|
||||
if not aigpy.string.isNull(msg) or stream is None:
|
||||
@@ -326,6 +326,8 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None):
|
||||
return False, msg
|
||||
if CONF.showTrackInfo:
|
||||
Printf.track(track, stream)
|
||||
if userProgress is not None:
|
||||
userProgress.updateStream(stream)
|
||||
path = getTrackPath(CONF, track, stream, album, playlist)
|
||||
|
||||
# check exist
|
||||
@@ -337,6 +339,7 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None):
|
||||
logging.info("[DL Track] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.url)
|
||||
tool = aigpy.download.DownloadTool(path + '.part', [stream.url])
|
||||
tool.setUserProgress(userProgress)
|
||||
tool.setPartSize(partSize)
|
||||
check, err = tool.start(CONF.showProgress)
|
||||
if not check:
|
||||
Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')')
|
||||
@@ -377,7 +380,7 @@ def downloadVideo(video: Video, album=None, playlist=None):
|
||||
path = getVideoPath(CONF, video, album, playlist)
|
||||
|
||||
logging.info("[DL Video] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.m3u8Url)
|
||||
m3u8content = request.get(stream.m3u8Url).content
|
||||
m3u8content = requests.get(stream.m3u8Url).content
|
||||
if m3u8content is None:
|
||||
Printf.err(video.title + ' get m3u8 content failed.')
|
||||
return False, "Get m3u8 content failed"
|
||||
@@ -488,7 +491,7 @@ def getBasePath(model):
|
||||
if isinstance(model, tidal_dl.model.Playlist):
|
||||
return getPlaylistPath(CONF, model)
|
||||
if isinstance(model, tidal_dl.model.Track):
|
||||
return getAlbumPath(CONF, model)
|
||||
return getAlbumPath(CONF, model.album)
|
||||
if isinstance(model, tidal_dl.model.Video):
|
||||
filePath = getVideoPath(CONF, model, model.album, model.playlist)
|
||||
return aigpy.pathHelper.getDirName(filePath)
|
||||
|
||||
@@ -27,3 +27,6 @@ class ListWidget(QListWidget):
|
||||
# item.setSizeHint(QSize(widget.width(), widget.height()))
|
||||
self.addItem(item)
|
||||
self.setItemWidget(item, widget)
|
||||
|
||||
def setAdjustMode(self):
|
||||
self.setResizeMode(QListWidget.Adjust)
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
@Contact : yaronhuang@foxmail.com
|
||||
@Desc :
|
||||
"""
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QWidget, QScrollArea, QVBoxLayout
|
||||
from PyQt5.QtGui import QResizeEvent
|
||||
|
||||
|
||||
class ScrollWidget(QScrollArea):
|
||||
def __init__(self):
|
||||
super(ScrollWidget, self).__init__()
|
||||
|
||||
self._numWidget = 0
|
||||
self._layout = QVBoxLayout()
|
||||
self._layout.addStretch(1)
|
||||
|
||||
@@ -26,4 +27,12 @@ class ScrollWidget(QScrollArea):
|
||||
self.setWidgetResizable(True)
|
||||
|
||||
def addWidgetItem(self, widget: QWidget):
|
||||
self._layout.insertWidget(0, widget)
|
||||
self._layout.insertWidget(self._numWidget, widget)
|
||||
self._numWidget += 1
|
||||
|
||||
def resizeEvent(self, e: QResizeEvent):
|
||||
super().resizeEvent(e)
|
||||
width = e.size().width()
|
||||
if width > 0:
|
||||
self._mainW.setMaximumWidth(width)
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ QWidget#BaseWidget {
|
||||
background: var(--Color_Default);
|
||||
}
|
||||
|
||||
|
||||
|
||||
QWidget#TaskItemView {
|
||||
background: var(--Color_Default);
|
||||
border-style: solid;
|
||||
@@ -543,6 +545,15 @@ QLabel#BoldLabel {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QLabel#ItalicLabel {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
QLabel#TagLabel {
|
||||
background: var(--Color_DangerPressed);
|
||||
}
|
||||
|
||||
|
||||
/* QFrame */
|
||||
QFrame#VLineQFrame
|
||||
{
|
||||
@@ -678,6 +689,20 @@ QListWidget#TaskContentListWidget::item:selected
|
||||
background: var(--Color_Default);
|
||||
}
|
||||
|
||||
QWidget#DownloadItemsWidget {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: 1px solid var(--Color_Border);
|
||||
border-bottom: none;
|
||||
background: var(--Color_Default);
|
||||
outline:none;
|
||||
}
|
||||
|
||||
QWidget#DownloadItemView {
|
||||
border-style: none;
|
||||
margin-top: 2px;
|
||||
background: var(--Color_Default);
|
||||
}
|
||||
|
||||
|
||||
QListWidget#DownloadItemsListWidget {
|
||||
|
||||
@@ -50,6 +50,8 @@ class LabelStyle(Enum):
|
||||
SearchErr = 5,
|
||||
Icon = 6,
|
||||
Bold = 7,
|
||||
Italic = 9,
|
||||
Tag = 10
|
||||
|
||||
|
||||
class ThemeStyle(Enum):
|
||||
|
||||
@@ -19,15 +19,21 @@ class DownloadItemView(QWidget):
|
||||
def __init__(self):
|
||||
super(DownloadItemView, self).__init__()
|
||||
self.__initView__()
|
||||
self.setObjectName('DownloadItemView')
|
||||
self.setAttribute(Qt.WA_StyledBackground)
|
||||
|
||||
def __initView__(self):
|
||||
self._indexLabel = Label('1')
|
||||
self._codecLabel = Label('', LabelStyle.Tag)
|
||||
self._titleLabel = Label('title', LabelStyle.Bold)
|
||||
self._ownLabel = Label('own')
|
||||
self._ownLabel = Label('own', LabelStyle.Italic)
|
||||
self._ownLabel.setMaximumWidth(200)
|
||||
self._actionLabel = Label('')
|
||||
self._actionLabel = Label('', LabelStyle.Italic)
|
||||
self._actionLabel.setFixedWidth(80)
|
||||
|
||||
self._errLabel = Label('')
|
||||
self._errLabel.setVisible(False)
|
||||
|
||||
self._sizeLabel = Label('/')
|
||||
self._speedLabel = Label('')
|
||||
|
||||
@@ -37,22 +43,28 @@ class DownloadItemView(QWidget):
|
||||
self._progress.setFixedWidth(300)
|
||||
self._progress.setRange(0, 100)
|
||||
|
||||
titleLayout = QHBoxLayout()
|
||||
titleLayout.setSpacing(3)
|
||||
titleLayout.setContentsMargins(0, 0, 0, 0)
|
||||
titleLayout.addWidget(self._indexLabel, Qt.AlignLeft)
|
||||
titleLayout.addWidget(self._titleLabel, Qt.AlignLeft)
|
||||
titleLayout.addStretch(50)
|
||||
titleLayout.addWidget(self._codecLabel, Qt.AlignRight)
|
||||
titleLayout.addWidget(self._ownLabel, Qt.AlignRight)
|
||||
|
||||
speedLayout = QHBoxLayout()
|
||||
speedLayout.setSpacing(30)
|
||||
speedLayout.addWidget(self._sizeLabel)
|
||||
speedLayout.addWidget(self._speedLabel)
|
||||
|
||||
grid = QGridLayout(self)
|
||||
grid.addWidget(self._indexLabel, 0, 0, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
grid.addWidget(self._titleLabel, 0, 1, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
grid.addWidget(self._ownLabel, 0, 2, Qt.AlignRight | Qt.AlignVCenter)
|
||||
grid.addWidget(self._progress, 0, 3, Qt.AlignRight | Qt.AlignVCenter)
|
||||
grid.addWidget(self._actionLabel, 0, 4, Qt.AlignRight | Qt.AlignVCenter)
|
||||
grid.addWidget(self._errLabel, 1, 1, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
grid.setColumnStretch(1, 1)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.setSpacing(30)
|
||||
layout.addWidget(self._sizeLabel)
|
||||
layout.addWidget(self._speedLabel)
|
||||
|
||||
grid.addLayout(layout, 1, 3, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
grid.setContentsMargins(0,0,0,0)
|
||||
grid.setSpacing(2)
|
||||
grid.addLayout(titleLayout, 0, 0, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
grid.addWidget(self._progress, 0, 1, Qt.AlignRight | Qt.AlignVCenter)
|
||||
grid.addWidget(self._actionLabel, 0, 2, Qt.AlignRight | Qt.AlignVCenter)
|
||||
grid.addWidget(self._errLabel, 1, 0, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
grid.addLayout(speedLayout, 1, 1, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
def setLabel(self, index, title, own):
|
||||
self._indexLabel.setText(str(index))
|
||||
@@ -61,6 +73,7 @@ class DownloadItemView(QWidget):
|
||||
|
||||
def setErrmsg(self, msg):
|
||||
self._errLabel.setText(msg)
|
||||
self._errLabel.setVisible(len(msg) > 0)
|
||||
|
||||
def setAction(self, msg):
|
||||
self._actionLabel.setText(msg)
|
||||
@@ -70,7 +83,10 @@ class DownloadItemView(QWidget):
|
||||
pass
|
||||
|
||||
def setSize(self, curSize: str, totalSize: str):
|
||||
self._sizeLabel.setText(f'{curSize}/{totalSize}')
|
||||
self._sizeLabel.setText(f'{curSize} / {totalSize}')
|
||||
|
||||
def setSpeed(self, speed: str):
|
||||
self._speedLabel.setText(speed)
|
||||
|
||||
def setCodec(self, codec: str):
|
||||
self._codecLabel.setText(codec)
|
||||
|
||||
@@ -127,7 +127,7 @@ class SearchView(QWidget):
|
||||
item.title,
|
||||
item.artists[0].name,
|
||||
str(item.releaseDate),
|
||||
API.getDurationString(item.duration)]
|
||||
getDurationString(item.duration)]
|
||||
datas.append(rowData)
|
||||
|
||||
elif stype == Type.Track:
|
||||
|
||||
@@ -29,7 +29,7 @@ class TaskItemView(QWidget):
|
||||
def __initView__(self):
|
||||
layout = QVBoxLayout()
|
||||
layout.addLayout(self.__initHead__(), Qt.AlignTop)
|
||||
layout.addWidget(self.__initList__(), Qt.AlignTop)
|
||||
layout.addWidget(self.__initList__())
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
@@ -40,8 +40,10 @@ class TaskItemView(QWidget):
|
||||
self._btnOpen = PushButton('', ButtonStyle.TaskOpen)
|
||||
self._btnExpand = PushButton('', ButtonStyle.TaskExpand)
|
||||
self._btnExpand.clicked.connect(self.__expandClick__)
|
||||
btnLayout = createHBoxLayout([self._btnRetry, self._btnCancel,
|
||||
self._btnDelete, self._btnOpen,
|
||||
btnLayout = createHBoxLayout([self._btnRetry,
|
||||
self._btnCancel,
|
||||
#self._btnDelete,
|
||||
self._btnOpen,
|
||||
self._btnExpand])
|
||||
|
||||
self._titleLabel = Label('', LabelStyle.PageTitle)
|
||||
@@ -49,6 +51,8 @@ class TaskItemView(QWidget):
|
||||
self._errLabel = Label()
|
||||
self._errLabel.hide()
|
||||
labelLayout = createVBoxLayout([self._titleLabel, self._descLabel, self._errLabel])
|
||||
labelLayout.insertStretch(0, 1)
|
||||
labelLayout.addStretch(1)
|
||||
|
||||
self._picLabel = Label('', LabelStyle.Icon)
|
||||
self._picLabel.setMinimumHeight(64)
|
||||
@@ -61,8 +65,10 @@ class TaskItemView(QWidget):
|
||||
return headLayout
|
||||
|
||||
def __initList__(self):
|
||||
self._list = ListWidget(ListWidgetStyle.DownloadItems)
|
||||
self._list.setMinimumHeight(200)
|
||||
self._list = QWidget()
|
||||
self._list.setObjectName("DownloadItemsWidget")
|
||||
self._listLayout = QVBoxLayout(self._list)
|
||||
self._listLayout.setSpacing(0)
|
||||
return self._list
|
||||
|
||||
def setLabel(self, title, desc):
|
||||
@@ -78,7 +84,7 @@ class TaskItemView(QWidget):
|
||||
self._picLabel.setPixmap(pic.scaled(64, 64))
|
||||
|
||||
def addListItem(self, view):
|
||||
self._list.addWidgetItem(view)
|
||||
self._listLayout.addWidget(view)
|
||||
|
||||
def __expandClick__(self):
|
||||
if self._list.isHidden():
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
import aigpy
|
||||
from abc import ABC, ABCMeta
|
||||
from enum import Enum
|
||||
from pickle import FALSE
|
||||
@@ -23,26 +24,39 @@ from tidal_gui.viewModel.viewModel import ViewModel
|
||||
|
||||
|
||||
class DownloadStatus(Enum):
|
||||
Wait = 0,
|
||||
Running = 1,
|
||||
Finish = 2,
|
||||
Error = 3,
|
||||
Cancel = 4,
|
||||
WAIT = 0,
|
||||
RUNNING = 1,
|
||||
SUCCESS = 2,
|
||||
ERROR = 3,
|
||||
CANCEL = 4,
|
||||
|
||||
|
||||
_endStatus_ = [DownloadStatus.Finish, DownloadStatus.Error, DownloadStatus.Cancel]
|
||||
_endStatus_ = [DownloadStatus.SUCCESS, DownloadStatus.ERROR, DownloadStatus.CANCEL]
|
||||
|
||||
|
||||
class Progress(UserProgress):
|
||||
def __init__(self, model):
|
||||
super().__init__()
|
||||
self.model = model
|
||||
self.curStr = ''
|
||||
self.maxStr = ''
|
||||
|
||||
def __toMBStr__(self, num):
|
||||
size = aigpy.memory.convert(num, aigpy.memory.Unit.BYTE, aigpy.memory.Unit.MB)
|
||||
return str(round(size, 2)) + ' MB'
|
||||
|
||||
def updateCurNum(self):
|
||||
self.model.update(self.curNum, self.maxNum)
|
||||
per = self.curNum * 100 / self.maxNum
|
||||
self.curStr = self.__toMBStr__(self.curNum)
|
||||
self.model.SIGNAL_REFRESH_VIEW.emit('updateCurNum', {'per': per,
|
||||
'curStr': self.curStr,
|
||||
'maxStr': self.maxStr})
|
||||
|
||||
def updateMaxNum(self):
|
||||
pass
|
||||
self.maxStr = self.__toMBStr__(self.maxNum)
|
||||
|
||||
def updateStream(self, stream):
|
||||
self.model.SIGNAL_REFRESH_VIEW.emit('updateStream', {'stream': stream})
|
||||
|
||||
|
||||
class DownloadItemModel(ViewModel):
|
||||
@@ -53,13 +67,27 @@ class DownloadItemModel(ViewModel):
|
||||
self.basePath = basePath
|
||||
self.isTrack = isinstance(data, Track)
|
||||
self.progress = Progress(self)
|
||||
self.__setStatus__(DownloadStatus.Wait)
|
||||
self.__setStatus__(DownloadStatus.WAIT)
|
||||
|
||||
if self.isTrack:
|
||||
self.__initTrack__(index)
|
||||
else:
|
||||
self.__initVideo__(index)
|
||||
|
||||
self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__)
|
||||
|
||||
def __refresh__(self, stype: str, object):
|
||||
if stype == "updateCurNum":
|
||||
per = object['per']
|
||||
curStr = object['curStr']
|
||||
maxStr = object['maxStr']
|
||||
self.view.setSize(curStr, maxStr)
|
||||
self.view.setProgress(per)
|
||||
elif stype == "updateStream":
|
||||
codec = object['stream'].codec
|
||||
self.view.setCodec(codec)
|
||||
|
||||
|
||||
def __setStatus__(self, status: DownloadStatus, desc: str = ''):
|
||||
self.status = status
|
||||
if desc == '':
|
||||
@@ -68,7 +96,7 @@ class DownloadItemModel(ViewModel):
|
||||
self.view.setAction(status.name + '-' + desc)
|
||||
|
||||
def __setErrStatus__(self, errmsg: str):
|
||||
self.status = DownloadStatus.Error
|
||||
self.status = DownloadStatus.ERROR
|
||||
self.view.setAction(self.status.name)
|
||||
self.view.setErrmsg(errmsg)
|
||||
|
||||
@@ -82,33 +110,28 @@ class DownloadItemModel(ViewModel):
|
||||
own = getArtistsNames(self.data.artists)
|
||||
self.view.setLabel(index, title, own)
|
||||
|
||||
def update(self, curNum, maxNum):
|
||||
per = curNum * 100 / maxNum
|
||||
self.view.setProgress(per)
|
||||
|
||||
def isInWait(self):
|
||||
return self.status == DownloadStatus.Wait
|
||||
return self.status == DownloadStatus.WAIT
|
||||
|
||||
def stopDownload(self):
|
||||
if self.status not in _endStatus_:
|
||||
self.__setStatus__(DownloadStatus.Cancel)
|
||||
self.__setStatus__(DownloadStatus.CANCEL)
|
||||
|
||||
def retry(self):
|
||||
self.__setStatus__(DownloadStatus.Wait)
|
||||
if self.status in [DownloadStatus.ERROR, DownloadStatus.CANCEL]:
|
||||
self.__setStatus__(DownloadStatus.WAIT)
|
||||
|
||||
def download(self):
|
||||
self.__setStatus__(DownloadStatus.Running)
|
||||
self.__setStatus__(DownloadStatus.RUNNING)
|
||||
|
||||
if self.isTrack:
|
||||
check, msg = downloadTrack(self.data, self.data.album, self.data.playlist, self.progress)
|
||||
else:
|
||||
check, msg = downloadVideo(self.data)
|
||||
|
||||
|
||||
if check is False:
|
||||
self.__setErrStatus__(msg)
|
||||
else:
|
||||
self.__setStatus__(DownloadStatus.Finish)
|
||||
return
|
||||
|
||||
self.view.setProgress(100)
|
||||
self.__setStatus__(DownloadStatus.Finish)
|
||||
|
||||
self.__setStatus__(DownloadStatus.SUCCESS)
|
||||
|
||||
@@ -49,7 +49,7 @@ class TaskItemModel(ViewModel):
|
||||
def __refresh__(self, stype: str, obj):
|
||||
if stype == "setPic":
|
||||
self.view.setPic(obj)
|
||||
elif stype == "addListItem":
|
||||
elif stype == "addListItems":
|
||||
for index, item in enumerate(obj):
|
||||
downItem = DownloadItemModel(index + 1, item, self.path)
|
||||
self.view.addListItem(downItem.view)
|
||||
@@ -84,7 +84,7 @@ class TaskItemModel(ViewModel):
|
||||
self.view.setLabel(title, desc)
|
||||
|
||||
def __thread_func__(model: TaskItemModel, album: Album):
|
||||
cover = API.getCoverData(album.cover, '1280', '1280')
|
||||
cover = API.getCoverData(album.cover)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('setPic', cover)
|
||||
|
||||
msg, tracks, videos = API.getItems(album.id, Type.Album)
|
||||
@@ -97,17 +97,69 @@ class TaskItemModel(ViewModel):
|
||||
for item in videos:
|
||||
item.album = album
|
||||
|
||||
model.SIGNAL_REFRESH_VIEW.emit('addListItem', tracks + videos)
|
||||
print('__initAlbum__')
|
||||
model.SIGNAL_REFRESH_VIEW.emit('addListItems', tracks + videos)
|
||||
time.sleep(1)
|
||||
|
||||
_thread.start_new_thread(__thread_func__, (self, data))
|
||||
|
||||
def __initTrack__(self, data: Track):
|
||||
pass
|
||||
title = data.title
|
||||
desc = f"by {getArtistsNames(data.artists)} " \
|
||||
f"{getDurationString(data.duration)} "
|
||||
self.view.setLabel(title, desc)
|
||||
|
||||
def __thread_func__(model: TaskItemModel, track: Track):
|
||||
mag, track.album = API.getAlbum(track.album.id)
|
||||
model.path = getBasePath(track)
|
||||
cover = API.getCoverData(track.album.cover)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('setPic', cover)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('addListItems', [track])
|
||||
time.sleep(1)
|
||||
|
||||
_thread.start_new_thread(__thread_func__, (self, data))
|
||||
|
||||
def __initVideo__(self, data: Video):
|
||||
pass
|
||||
self.path = getBasePath(data)
|
||||
|
||||
title = data.title
|
||||
desc = f"by {getArtistsNames(data.artists)} " \
|
||||
f"{getDurationString(data.duration)} "
|
||||
self.view.setLabel(title, desc)
|
||||
|
||||
def __thread_func__(model: TaskItemModel, video: Video):
|
||||
cover = API.getCoverData(video.imageID)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('setPic', cover)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('addListItems', [video])
|
||||
time.sleep(1)
|
||||
|
||||
_thread.start_new_thread(__thread_func__, (self, data))
|
||||
|
||||
def __initPlaylist__(self, data: Playlist):
|
||||
pass
|
||||
self.path = getBasePath(data)
|
||||
|
||||
title = data.title
|
||||
desc = f"{getDurationString(data.duration)} " \
|
||||
f"Track-{data.numberOfTracks} " \
|
||||
f"Video-{data.numberOfVideos}"
|
||||
self.view.setLabel(title, desc)
|
||||
|
||||
def __thread_func__(model: TaskItemModel, playlist: Playlist):
|
||||
cover = API.getCoverData(playlist.squareImage)
|
||||
model.SIGNAL_REFRESH_VIEW.emit('setPic', cover)
|
||||
|
||||
msg, tracks, videos = API.getItems(playlist.uuid, Type.Playlist)
|
||||
if not aigpy.stringHelper.isNull(msg):
|
||||
model.view.setErrmsg(msg)
|
||||
return
|
||||
|
||||
for item in tracks:
|
||||
mag, album = API.getAlbum(item.album.id)
|
||||
item.playlist = playlist
|
||||
item.album = album
|
||||
for item in videos:
|
||||
item.playlist = playlist
|
||||
|
||||
model.SIGNAL_REFRESH_VIEW.emit('addListItems', tracks + videos)
|
||||
time.sleep(1)
|
||||
|
||||
_thread.start_new_thread(__thread_func__, (self, data))
|
||||
|
||||
Reference in New Issue
Block a user