22
33import os
44import sys
5- import dbus
65import struct
76import yt_dlp
87
98import subprocess as sp
109
1110from multiprocessing import Process
12- from PyQt5 .QtWidgets import QApplication , QFileDialog
11+
12+ from PyQt5 .QtCore import (
13+ QThread ,
14+ pyqtSignal
15+ )
16+
17+ from PyQt5 .QtWidgets import (
18+ QLabel ,
19+ QWidget ,
20+ QFileDialog ,
21+ QPushButton ,
22+ QVBoxLayout ,
23+ QProgressBar ,
24+ QApplication
25+ )
1326
1427PREFER_EXT = {
1528 'video' : 'mp4' ,
1629 'audio' : 'mp3'
1730}
1831
19- class DLoadTracker :
20- def __init__ (self ):
21- self .bus = dbus .SessionBus ()
22- self .interface = 'org.kde.JobViewV2'
23- self .server = self .bus .get_object ('org.kde.kuiserver' , '/JobViewServer' )
24- self .path = self .server .requestView ('mpvnet' , 'download' , 1 )
25- self .job = self .bus .get_object ('org.kde.kuiserver' , self .path )
26- self .set_info ('Initializing...' )
32+ class DLoader (QWidget ):
33+ def __init__ (self , url , ydl_opts , info ):
34+ super ().__init__ ()
35+ self .info_label = QLabel ()
36+ self .info_label .setText (info )
37+ self .progress_bar = QProgressBar ()
38+ self .progress_bar .setValue (0 )
39+ self .cancel_button = QPushButton ('Cancel' )
40+ self .cancel_button .clicked .connect (self .cancel_download )
41+ self .vbox = QVBoxLayout ()
42+ self .vbox .addWidget (self .info_label )
43+ self .vbox .addWidget (self .progress_bar )
44+ self .vbox .addWidget (self .cancel_button )
45+ self .thread = DLoaderThread (url , ydl_opts )
46+ self .thread .signal_progress .connect (self .set_progress )
47+ self .setWindowTitle ('mpvnet' )
48+ self .setFixedSize (self .sizeHint ())
49+ self .setLayout (self .vbox )
50+ self .show ()
51+
52+ def start_download (self ):
53+ self .thread .start ()
54+
55+ def cancel_download (self ):
56+ self .thread .interrupt = True
2757
2858 def set_progress (self , progress ):
29- self .job .setPercent (progress , dbus_interface = self .interface )
30-
31- def set_info (self , info ):
32- self .job .setInfoMessage (info , dbus_interface = self .interface )
59+ self .progress_bar .setValue (progress )
60+
61+ class DLoaderThread (QThread ):
62+ signal_progress = pyqtSignal (int )
63+ def __init__ (self , url , ydl_opts ):
64+ super ().__init__ ()
65+ self .url = url
66+ self .ydl_opts = ydl_opts
67+ self .ydl_opts .update ({
68+ 'progress_hooks' : [self .progress_hook ],
69+ })
70+ self .interrupt = False
3371
34- def terminate (self , info ):
35- self .job .terminate (info , dbus_interface = self .interface )
72+ def run (self ):
73+ with yt_dlp .YoutubeDL (self .ydl_opts .copy ()) as ydl :
74+ try :
75+ ydl .download (self .url )
76+ except yt_dlp .DownloadCancelled :
77+ pass
78+ finally :
79+ QApplication .quit ()
3680
3781 def progress_hook (self , stream ):
3882 if stream ['status' ] == 'downloading' :
83+ if self .interrupt :
84+ raise yt_dlp .DownloadCancelled
3985 progress = int (float (stream ['_percent_str' ].replace ('%' , '' )))
4086 if progress >= 0 and progress <= 100 :
41- self .set_progress (progress )
87+ self .signal_progress . emit (progress )
4288
43- class DLoadPathChooser :
89+ class DLoaderPathChooser :
4490 def __init__ (self , default_path ):
4591 self .path = None
4692 self .default_path = default_path
@@ -50,7 +96,7 @@ def choose_path (self):
5096 dialog = QFileDialog ()
5197 dialog .setAcceptMode (QFileDialog .AcceptSave )
5298 dialog .selectFile (self .default_path )
53- if dialog .exec_ ():
99+ if dialog .exec ():
54100 self .path = dialog .selectedFiles ()[0 ]
55101
56102def recv_msg ():
@@ -71,6 +117,7 @@ def run_loader(loader_args_raw):
71117 os .environ ['LD_PRELOAD' ] = '/usr/lib/libvulkan.so.1'
72118 os .chdir (os .path .expanduser ("~" ))
73119 loader_args = loader_args_raw .split ()
120+ mpv_head = f'mpv --profile=pseudo-gui --geometry=x50%'
74121 mpv_tail = f'--ytdl-format=\' { loader_args [2 ]} \' \' { loader_args [3 ]} \' '
75122 ydl_opts = {
76123 'quiet' : True ,
@@ -83,22 +130,19 @@ def run_loader(loader_args_raw):
83130 stop_loader (0 , 'return_error_invalid_url' )
84131 if loader_args [0 ] == 'mpv' :
85132 if loader_args [1 ] == 'video' :
86- sp .Popen (f'mpv --geometry=x50% { mpv_tail } ' , shell = True )
133+ sp .Popen (f'{ mpv_head } { mpv_tail } ' , shell = True )
87134 else :
88- sp .Popen (f'konsole -e "mpv -- no-video { mpv_tail } " ' , shell = True )
135+ sp .Popen (f'{ mpv_head } -- no-video { mpv_tail } ' , shell = True )
89136 else :
90- tracker = DLoadTracker ()
91137 title = media_info ['title' ]
92138 for symbol in ['/' , '"' , '\' ' ]:
93139 title = title .replace (symbol , '_' )
94- path_chooser = DLoadPathChooser (title )
140+ path_chooser = DLoaderPathChooser (title )
95141 path_chooser .choose_path ()
96142 if not path_chooser .path :
97- tracker .terminate ('Action cancelled' )
98143 stop_loader (0 , 'return_normal' )
99144 ydl_opts .update ({
100145 'format' : loader_args [2 ],
101- 'progress_hooks' : [tracker .progress_hook ],
102146 'outtmpl' : f'{ path_chooser .path } .%(ext)s' ,
103147 })
104148 if loader_args [1 ] == 'video' :
@@ -112,16 +156,14 @@ def run_loader(loader_args_raw):
112156 'preferredcodec' : PREFER_EXT ['audio' ],
113157 }]
114158 })
115- tracker . set_info (
159+ info_str = str (
116160 f'Downloading { loader_args [1 ]} : ' +
117161 f'"{ path_chooser .path } .{ PREFER_EXT [loader_args [1 ]]} "'
118162 )
119- with yt_dlp .YoutubeDL (ydl_opts .copy ()) as ydl :
120- try :
121- ydl .download (loader_args [3 ])
122- except dbus .exceptions .DBusException :
123- stop_loader (0 , 'return_normal' )
124- tracker .terminate ('Download complete' )
163+ app = QApplication ([])
164+ downloader = DLoader (loader_args [3 ], ydl_opts , info_str )
165+ downloader .start_download ()
166+ app .exec ()
125167 stop_loader (0 , 'return_normal' )
126168
127169def stop_loader (return_code , return_msg ):
0 commit comments