11from pathlib import Path
2- from platform import system
32from shutil import which
4- from subprocess import Popen
3+ from platform import system
4+ from subprocess import Popen , run
5+ from textwrap import dedent
56
67__all__ = ["FFMPEG" ]
78
89
910class FFMPEG :
11+ SYSTEM = system ()
12+
13+ # 常见终端及其执行模板
14+ linux_terminal_templates = {
15+ # GNOME Terminal (Ubuntu)
16+ "gnome-terminal" : ["gnome-terminal" , "--" , "bash" , "-c" , "{cmd}; exec bash" ],
17+ # Deepin Terminal
18+ "deepin-terminal" : ["deepin-terminal" , "--" , "bash" , "-c" , "{cmd}; exec bash" ],
19+ # XFCE4 Terminal (MX Linux 默认)
20+ "xfce4-terminal" : [
21+ "xfce4-terminal" ,
22+ "--hold" ,
23+ "-e" ,
24+ 'bash -c "{cmd}; exec bash"' ,
25+ ],
26+ # Konsole (KDE)
27+ "konsole" : ["konsole" , "-e" , "bash" , "-i" , "-c" , "{cmd}; bash" ],
28+ # Terminator
29+ "terminator" : ["terminator" , "-x" , "bash" , "-c" , "{cmd}; exec bash" ],
30+ }
31+
1032 def __init__ (self , path : str ):
1133 self .path = self .__check_ffmpeg_path (Path (path ))
12- self .state = bool (self .path )
13- self .command , self .shell = self ._check_system_type ()
34+ self .support = {
35+ "Darwin" : self .generate_command_darwin ,
36+ "Linux" : self .generate_command_linux ,
37+ "Windows" : self .generate_command_windows ,
38+ }
39+ self .run_command = self .support .get (self .SYSTEM , None )
40+ self .state = bool (self .path ) if self .run_command else False
1441
1542 @staticmethod
16- def _check_system_type ():
17- if (s := system ()) == "Darwin" : # macOS
18- return ["open" , "-a" , "Terminal" ], False
19- elif s == "Windows" : # Windows
20- return ["start" , "cmd" , "/k" ], True
21- elif s == "Linux" : # Linux
22- return ["x-terminal-emulator" ], False
43+ def generate_command_darwin (command : list ) -> None :
44+ script = dedent (f"""
45+ tell application "Terminal"
46+ do script "{ " " .join (command ).replace ('"' , '\\ "' )} "
47+ activate
48+ end tell
49+ """ )
50+ Popen (["osascript" , "-e" , script ])
51+
52+ @staticmethod
53+ def generate_command_windows (command : list ) -> None :
54+ Popen (
55+ " " .join (
56+ [
57+ "start" ,
58+ "cmd" ,
59+ "/k" ,
60+ ]
61+ + command
62+ ),
63+ shell = True ,
64+ )
65+
66+ @classmethod
67+ def generate_command_linux (cls , command : list ) -> None :
68+ # TODO: Linux 系统尚未测试
69+ command = " " .join (command )
70+ print ("ffmpeg command:" , command )
71+ for term , template in cls .linux_terminal_templates .items ():
72+ if which (term ):
73+ # 填充命令并执行
74+ filled = [
75+ part .format (cmd = command ) if "{cmd}" in part else part
76+ for part in template
77+ ]
78+ run (
79+ filled ,
80+ )
2381
2482 def __check_ffmpeg_path (self , path : Path ):
25- # return None # 调试使用
2683 return self .__check_system_ffmpeg () or self .__check_system_ffmpeg (path )
2784
2885 def download (self , data : list [tuple ], proxy , user_agent ):
@@ -33,66 +90,60 @@ def download(self, data: list[tuple], proxy, user_agent):
3390 proxy ,
3491 user_agent ,
3592 )
36- Popen ( command , shell = self .shell )
93+ self .run_command ( command )
3794
3895 def __generate_command (
3996 self ,
4097 url ,
4198 file ,
4299 proxy ,
43100 user_agent ,
44- ) -> str :
45- command = self .command .copy ()
46- command .extend (
47- [
48- self .path ,
49- "-hide_banner" ,
50- "-rw_timeout" ,
51- f"{ 30 * 1000 * 1000 } " ,
52- "-loglevel" ,
53- "info" ,
54- "-protocol_whitelist" ,
55- "rtmp,crypto,file,http,https,tcp,tls,udp,rtp,httpproxy" ,
56- "-analyzeduration" ,
57- f"{ 10 * 1000 * 1000 } " ,
58- "-probesize" ,
59- f"{ 10 * 1000 * 1000 } " ,
60- "-fflags" ,
61- "+discardcorrupt" ,
62- "-user_agent" ,
63- f'"{ user_agent } "' ,
64- "-i" ,
65- f'"{ url } "' ,
66- "-bufsize" ,
67- "10240k" ,
68- "-map" ,
69- "0" ,
70- "-c:v" ,
71- "copy" ,
72- "-c:a" ,
73- "copy" ,
74- "-sn" ,
75- "-dn" ,
76- "-reconnect_delay_max" ,
77- "60" ,
78- "-reconnect_streamed" ,
79- "-reconnect_at_eof" ,
80- "-max_muxing_queue_size" ,
81- "128" ,
82- "-correct_ts_overflow" ,
83- "1" ,
84- "-f" ,
85- "mp4" ,
86- ]
87- )
101+ ) -> list :
102+ command = [
103+ self .path ,
104+ "-hide_banner" ,
105+ "-rw_timeout" ,
106+ f"{ 30 * 1000 * 1000 } " ,
107+ "-loglevel" ,
108+ "info" ,
109+ "-protocol_whitelist" ,
110+ "rtmp,crypto,file,http,https,tcp,tls,udp,rtp,httpproxy" ,
111+ "-analyzeduration" ,
112+ f"{ 10 * 1000 * 1000 } " ,
113+ "-probesize" ,
114+ f"{ 10 * 1000 * 1000 } " ,
115+ "-fflags" ,
116+ "+discardcorrupt" ,
117+ "-user_agent" ,
118+ f'"{ user_agent } "' ,
119+ "-i" ,
120+ f'"{ url } "' ,
121+ "-bufsize" ,
122+ "10240k" ,
123+ "-map" ,
124+ "0" ,
125+ "-c:v" ,
126+ "copy" ,
127+ "-c:a" ,
128+ "copy" ,
129+ "-sn" ,
130+ "-dn" ,
131+ "-reconnect_delay_max" ,
132+ "60" ,
133+ "-reconnect_streamed" ,
134+ "-reconnect_at_eof" ,
135+ "-max_muxing_queue_size" ,
136+ "128" ,
137+ "-correct_ts_overflow" ,
138+ "1" ,
139+ "-f" ,
140+ "mp4" ,
141+ ]
88142 if proxy :
89- for insert_index , item in enumerate (
90- ("-http_proxy" , proxy ), start = len (self .command ) + 2
91- ):
143+ for insert_index , item in enumerate (("-http_proxy" , proxy ), start = 2 ):
92144 command .insert (insert_index , item )
93145 command .append (f'"{ file } "' )
94- # print(" ".join(command)) # 调试使用
95- return " " .join (command )
146+ return command
96147
97148 @staticmethod
98149 def __check_system_ffmpeg (path : Path = None ):
0 commit comments