Spaces:
Build error
Build error
| import gradio as gr | |
| import yt_dlp | |
| import os | |
| import re | |
| import time | |
| import json | |
| import subprocess | |
| from datetime import datetime | |
| # Add imports for the other engines | |
| from pytube import YouTube as PytubeYouTube | |
| try: | |
| from pytubefix import YouTube as PytubefixYouTube | |
| except ImportError: | |
| PytubefixYouTube = None | |
| def time_to_seconds(time_str): | |
| if not time_str: | |
| return 0 | |
| if re.match(r'^\d+$', time_str): | |
| return int(time_str) | |
| elif re.match(r'^\d+:\d+$', time_str): | |
| minutes, seconds = time_str.split(':') | |
| return int(minutes) * 60 + int(seconds) | |
| elif re.match(r'^\d+:\d+:\d+$', time_str): | |
| hours, minutes, seconds = time_str.split(':') | |
| return int(hours) * 3600 + int(minutes) * 60 + int(seconds) | |
| return 0 | |
| def seconds_to_time_str(seconds): | |
| hours, remainder = divmod(seconds, 3600) | |
| minutes, seconds = divmod(remainder, 60) | |
| return f"{hours:02d}:{minutes:02d}:{seconds:02d}" | |
| # ====== YT-DLP Implementation ====== | |
| def get_video_formats(info): | |
| """Extract available video formats from yt-dlp info""" | |
| formats = [] | |
| for f in info.get('formats', []): | |
| if f.get('resolution') != 'audio only' and f.get('vcodec') != 'none': | |
| height = f.get('height') | |
| if height: | |
| formats.append({ | |
| 'format_id': f.get('format_id'), | |
| 'resolution': f"{height}p", | |
| 'fps': f.get('fps'), | |
| 'vcodec': f.get('vcodec'), | |
| 'acodec': f.get('acodec'), | |
| 'ext': f.get('ext'), | |
| 'filesize': f.get('filesize'), | |
| 'has_audio': f.get('acodec') != 'none' | |
| }) | |
| return formats | |
| def fetch_video_info_ytdlp(video_url, cookies_file=None): | |
| try: | |
| ydl_opts = { | |
| 'quiet': True, | |
| 'no_warnings': True, | |
| 'skip_download': True, | |
| } | |
| # Add cookies file if provided | |
| if cookies_file and os.path.exists(cookies_file): | |
| ydl_opts['cookiefile'] = cookies_file | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(video_url, download=False) | |
| # Extract basic information | |
| title = info.get('title', '') | |
| length = info.get('duration', 0) | |
| author = info.get('uploader', '') | |
| views = info.get('view_count', 0) | |
| description = info.get('description', '') | |
| thumbnail = info.get('thumbnail', '') | |
| duration_str = seconds_to_time_str(length) | |
| formats = get_video_formats(info) | |
| all_resolutions = set() | |
| for f in formats: | |
| if f.get('resolution'): | |
| all_resolutions.add(f.get('resolution')) | |
| qualities = sorted(list(all_resolutions), | |
| key=lambda x: int(x.replace('p', ''))) | |
| print(f"Available qualities: {qualities}") | |
| if not qualities: | |
| qualities = ["360p", "480p", "720p", "1080p"] | |
| default_quality = qualities[-1] if qualities else "1080p" | |
| return "", title, author, f"{views:,}", duration_str, description[:500] + "..." if len(description) > 500 else description, thumbnail, gr.update(visible=True), gr.update(choices=qualities, value=default_quality) | |
| except Exception as e: | |
| return f"错误: {str(e)}", "", "", "", "", "", None, gr.update(visible=False), gr.update(choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| def download_video_segment_ytdlp(video_url, start_time, end_time, quality, fps, progress=gr.Progress(), cookies_file=None): | |
| try: | |
| start_seconds = time_to_seconds(start_time) | |
| end_seconds = time_to_seconds(end_time) | |
| if end_seconds <= start_seconds: | |
| return "结束时间必须大于开始时间" | |
| # Extract video information | |
| progress(0.1, "正在获取视频信息...") | |
| ydl_opts = { | |
| 'quiet': True, | |
| 'no_warnings': True, | |
| 'skip_download': True, | |
| } | |
| # Add cookies file if provided | |
| if cookies_file and os.path.exists(cookies_file): | |
| ydl_opts['cookiefile'] = cookies_file | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(video_url, download=False) | |
| title = info.get('title', 'video') | |
| safe_title = re.sub(r'[^\w\-_]', '_', title) | |
| formats = get_video_formats(info) | |
| resolution = quality | |
| fps_value = int(fps.replace("fps", "")) | |
| progress(0.3, f"正在选择视频流 (质量: {quality}, 帧率: {fps})...") | |
| selected_format = None | |
| for f in formats: | |
| if f['resolution'] == quality and f['fps'] and f['fps'] >= fps_value and f['has_audio']: | |
| selected_format = f | |
| break | |
| if not selected_format: | |
| progress(0.3, "正在选择最佳视频和音频流...") | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| temp_video = f"temp_video_{timestamp}.mp4" | |
| temp_audio = f"temp_audio_{timestamp}.mp4" | |
| temp_output = f"temp_combined_{timestamp}.mp4" | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频...") | |
| video_ydl_opts = { | |
| 'format': f'bestvideo[height<={quality.replace("p", "")}]', | |
| 'outtmpl': temp_video, | |
| 'quiet': True | |
| } | |
| # Add cookies file if provided | |
| if cookies_file and os.path.exists(cookies_file): | |
| video_ydl_opts['cookiefile'] = cookies_file | |
| with yt_dlp.YoutubeDL(video_ydl_opts) as ydl: | |
| ydl.download([video_url]) | |
| progress(0.5, "正在下载音频...") | |
| audio_ydl_opts = { | |
| 'format': 'bestaudio', | |
| 'outtmpl': temp_audio, | |
| 'quiet': True | |
| } | |
| with yt_dlp.YoutubeDL(audio_ydl_opts) as ydl: | |
| ydl.download([video_url]) | |
| progress(0.6, "正在合并视频和音频...") | |
| output_file_temp = os.path.join(os.getcwd(), temp_output) | |
| cmd_combine = [ | |
| 'ffmpeg', '-y', | |
| '-i', temp_video, | |
| '-i', temp_audio, | |
| '-c', 'copy', | |
| output_file_temp | |
| ] | |
| subprocess.run(cmd_combine, check=True) | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd_cut = [ | |
| 'ffmpeg', '-y', | |
| '-i', output_file_temp, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd_cut, check=True) | |
| os.remove(temp_video) | |
| os.remove(temp_audio) | |
| os.remove(output_file_temp) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| format_id = selected_format['format_id'] | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| temp_filename = f"temp_{timestamp}.mp4" | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频...") | |
| ydl_opts = { | |
| 'format': format_id, | |
| 'outtmpl': temp_filename, | |
| 'quiet': True | |
| } | |
| # Add cookies file if provided | |
| if cookies_file and os.path.exists(cookies_file): | |
| ydl_opts['cookiefile'] = cookies_file | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| ydl.download([video_url]) | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd = [ | |
| 'ffmpeg', '-y', | |
| '-i', temp_filename, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd, check=True) | |
| os.remove(temp_filename) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| except Exception as e: | |
| return f"错误: {str(e)}" | |
| # ====== PyTube Implementation ====== | |
| def fetch_video_info_pytube(video_url): | |
| try: | |
| yt = PytubeYouTube(video_url) | |
| title = yt.title | |
| length = yt.length | |
| author = yt.author | |
| views = yt.views | |
| description = yt.description | |
| thumbnail = yt.thumbnail_url | |
| duration_str = seconds_to_time_str(length) | |
| progressive_streams = yt.streams.filter(progressive=True) | |
| adaptive_streams = yt.streams.filter(adaptive=True, only_video=True) | |
| all_resolutions = set() | |
| for s in progressive_streams: | |
| if s.resolution: | |
| all_resolutions.add(s.resolution) | |
| for s in adaptive_streams: | |
| if s.resolution: | |
| all_resolutions.add(s.resolution) | |
| qualities = sorted(list(all_resolutions), | |
| key=lambda x: int(x.replace('p', ''))) | |
| print(f"Available qualities: {qualities}") | |
| if not qualities: | |
| qualities = ["360p", "480p", "720p", "1080p"] | |
| default_quality = qualities[-1] if qualities else "1080p" | |
| return "", title, author, f"{views:,}", duration_str, description[:500] + "..." if len(description) > 500 else description, thumbnail, gr.update(visible=True), gr.update(choices=qualities, value=default_quality) | |
| except Exception as e: | |
| return f"错误: {str(e)}", "", "", "", "", "", None, gr.update(visible=False), gr.update(choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| def download_video_segment_pytube(video_url, start_time, end_time, quality, fps, progress=gr.Progress()): | |
| try: | |
| start_seconds = time_to_seconds(start_time) | |
| end_seconds = time_to_seconds(end_time) | |
| if end_seconds <= start_seconds: | |
| return "结束时间必须大于开始时间" | |
| progress(0.1, "正在获取视频信息...") | |
| yt = PytubeYouTube(video_url) | |
| title = yt.title | |
| safe_title = re.sub(r'[^\w\-_]', '_', title) | |
| resolution = quality | |
| fps_value = int(fps.replace("fps", "")) | |
| progress(0.3, f"正在选择视频流 (质量: {quality}, 帧率: {fps})...") | |
| streams = yt.streams.filter(progressive=True, res=quality) | |
| if not streams: | |
| print(f"No progressive stream found for {quality}, trying adaptive...") | |
| video_streams = yt.streams.filter(adaptive=True, only_video=True, res=quality) | |
| if video_streams: | |
| video_stream = video_streams.first() | |
| audio_stream = yt.streams.filter(only_audio=True).first() | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| temp_video = f"temp_video_{timestamp}.mp4" | |
| temp_audio = f"temp_audio_{timestamp}.mp4" | |
| temp_output = f"temp_combined_{timestamp}.mp4" | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频流...") | |
| video_path = video_stream.download(filename=temp_video) | |
| progress(0.5, "正在下载音频流...") | |
| audio_path = audio_stream.download(filename=temp_audio) | |
| progress(0.6, "正在合并视频和音频...") | |
| output_file_temp = os.path.join(os.getcwd(), temp_output) | |
| cmd_combine = [ | |
| 'ffmpeg', '-y', | |
| '-i', video_path, | |
| '-i', audio_path, | |
| '-c', 'copy', | |
| output_file_temp | |
| ] | |
| subprocess.run(cmd_combine, check=True) | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd_cut = [ | |
| 'ffmpeg', '-y', | |
| '-i', output_file_temp, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd_cut, check=True) | |
| os.remove(video_path) | |
| os.remove(audio_path) | |
| os.remove(output_file_temp) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| else: | |
| streams = yt.streams.filter(progressive=True).order_by('resolution').desc() | |
| if not streams: | |
| return "无法找到合适的视频流" | |
| video_stream = streams.first() | |
| print(f"Selected stream: {video_stream.resolution} {video_stream.fps}fps") | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频...") | |
| download_path = video_stream.download(filename=f"temp_{output_filename}") | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd = [ | |
| 'ffmpeg', '-y', | |
| '-i', download_path, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd, check=True) | |
| os.remove(download_path) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| except Exception as e: | |
| return f"错误: {str(e)}" | |
| # ====== PyTubeFix Implementation ====== | |
| def fetch_video_info_pytubefix(video_url, po_token="", visitor_data=""): | |
| try: | |
| if PytubefixYouTube is None: | |
| return "错误: pytubefix 未安装", "", "", "", "", "", None, gr.update(visible=False), gr.update(choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| # Create YouTube object with token verification if tokens are provided | |
| if po_token and visitor_data: | |
| yt = PytubefixYouTube( | |
| video_url, | |
| use_po_token=True, | |
| po_token_verifier=lambda: (visitor_data, po_token) | |
| ) | |
| else: | |
| yt = PytubefixYouTube(video_url) | |
| title = yt.title | |
| length = yt.length | |
| author = yt.author | |
| views = yt.views | |
| description = yt.description | |
| thumbnail = yt.thumbnail_url | |
| duration_str = seconds_to_time_str(length) | |
| progressive_streams = yt.streams.filter(progressive=True) | |
| adaptive_streams = yt.streams.filter(adaptive=True, only_video=True) | |
| all_resolutions = set() | |
| for s in progressive_streams: | |
| if s.resolution: | |
| all_resolutions.add(s.resolution) | |
| for s in adaptive_streams: | |
| if s.resolution: | |
| all_resolutions.add(s.resolution) | |
| qualities = sorted(list(all_resolutions), | |
| key=lambda x: int(x.replace('p', ''))) | |
| print(f"Available qualities: {qualities}") | |
| if not qualities: | |
| qualities = ["360p", "480p", "720p", "1080p"] | |
| default_quality = qualities[-1] if qualities else "1080p" | |
| return "", title, author, f"{views:,}", duration_str, description[:500] + "..." if len(description) > 500 else description, thumbnail, gr.update(visible=True), gr.update(choices=qualities, value=default_quality) | |
| except Exception as e: | |
| return f"错误: {str(e)}", "", "", "", "", "", None, gr.update(visible=False), gr.update(choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| def download_video_segment_pytubefix(video_url, start_time, end_time, quality, fps, progress=gr.Progress(), po_token="", visitor_data=""): | |
| try: | |
| if PytubefixYouTube is None: | |
| return "错误: pytubefix 未安装" | |
| start_seconds = time_to_seconds(start_time) | |
| end_seconds = time_to_seconds(end_time) | |
| if end_seconds <= start_seconds: | |
| return "结束时间必须大于开始时间" | |
| progress(0.1, "正在获取视频信息...") | |
| # Create YouTube object with token verification if tokens are provided | |
| if po_token and visitor_data: | |
| yt = PytubefixYouTube( | |
| video_url, | |
| use_po_token=True, | |
| po_token_verifier=lambda: (visitor_data, po_token) | |
| ) | |
| else: | |
| yt = PytubefixYouTube(video_url) | |
| title = yt.title | |
| safe_title = re.sub(r'[^\w\-_]', '_', title) | |
| resolution = quality | |
| fps_value = int(fps.replace("fps", "")) | |
| progress(0.3, f"正在选择视频流 (质量: {quality}, 帧率: {fps})...") | |
| streams = yt.streams.filter(progressive=True, res=quality) | |
| if not streams: | |
| print(f"No progressive stream found for {quality}, trying adaptive...") | |
| video_streams = yt.streams.filter(adaptive=True, only_video=True, res=quality) | |
| if video_streams: | |
| video_stream = video_streams.first() | |
| audio_stream = yt.streams.filter(only_audio=True).first() | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| temp_video = f"temp_video_{timestamp}.mp4" | |
| temp_audio = f"temp_audio_{timestamp}.mp4" | |
| temp_output = f"temp_combined_{timestamp}.mp4" | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频流...") | |
| video_path = video_stream.download(filename=temp_video) | |
| progress(0.5, "正在下载音频流...") | |
| audio_path = audio_stream.download(filename=temp_audio) | |
| progress(0.6, "正在合并视频和音频...") | |
| output_file_temp = os.path.join(os.getcwd(), temp_output) | |
| cmd_combine = [ | |
| 'ffmpeg', '-y', | |
| '-i', video_path, | |
| '-i', audio_path, | |
| '-c', 'copy', | |
| output_file_temp | |
| ] | |
| subprocess.run(cmd_combine, check=True) | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd_cut = [ | |
| 'ffmpeg', '-y', | |
| '-i', output_file_temp, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd_cut, check=True) | |
| os.remove(video_path) | |
| os.remove(audio_path) | |
| os.remove(output_file_temp) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| else: | |
| streams = yt.streams.filter(progressive=True).order_by('resolution').desc() | |
| if not streams: | |
| return "无法找到合适的视频流" | |
| video_stream = streams.first() | |
| print(f"Selected stream: {video_stream.resolution} {video_stream.fps}fps") | |
| timestamp = datetime.now().strftime("%Y%m%d%H%M%S") | |
| output_filename = f"{safe_title}_{timestamp}_{start_time.replace(':', '')}_{end_time.replace(':', '')}.mp4" | |
| progress(0.4, "正在下载视频...") | |
| download_path = video_stream.download(filename=f"temp_{output_filename}") | |
| progress(0.7, "正在剪辑视频片段...") | |
| output_file = os.path.join(os.getcwd(), output_filename) | |
| cmd = [ | |
| 'ffmpeg', '-y', | |
| '-i', download_path, | |
| '-ss', str(start_seconds), | |
| '-to', str(end_seconds), | |
| '-c:v', 'copy', | |
| '-c:a', 'copy', | |
| output_file | |
| ] | |
| subprocess.run(cmd, check=True) | |
| os.remove(download_path) | |
| progress(1.0, "已完成!") | |
| return output_file | |
| except Exception as e: | |
| return f"错误: {str(e)}" | |
| # ====== Unified Interface Functions ====== | |
| def fetch_video_info(video_url, engine, po_token="", visitor_data="", cookies_file=None): | |
| if engine == "yt_dlp": | |
| return fetch_video_info_ytdlp(video_url, cookies_file) | |
| elif engine == "pytube": | |
| return fetch_video_info_pytube(video_url) | |
| elif engine == "pytubefix": | |
| return fetch_video_info_pytubefix(video_url, po_token, visitor_data) | |
| else: | |
| return f"错误: 未知引擎 {engine}", "", "", "", "", "", None, gr.update(visible=False), gr.update(choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| def download_video_segment(video_url, start_time, end_time, quality, fps, engine, po_token="", visitor_data="", cookies_file=None, progress=gr.Progress()): | |
| if engine == "yt_dlp": | |
| return download_video_segment_ytdlp(video_url, start_time, end_time, quality, fps, progress, cookies_file) | |
| elif engine == "pytube": | |
| return download_video_segment_pytube(video_url, start_time, end_time, quality, fps, progress) | |
| elif engine == "pytubefix": | |
| return download_video_segment_pytubefix(video_url, start_time, end_time, quality, fps, progress, po_token, visitor_data) | |
| else: | |
| return f"错误: 未知引擎 {engine}" | |
| # ====== Gradio UI ====== | |
| with gr.Blocks(title="YouTube 视频剪辑器", theme="NoCrypt/miku") as demo: | |
| gr.Markdown("# YouTube 视频剪辑工具") | |
| gr.Markdown("输入 YouTube 视频链接,查看视频信息并下载指定时间段的视频片段") | |
| with gr.Accordion(label="下载引擎", open=False): | |
| engine = gr.Radio( | |
| label="选择用于下载视频的库", | |
| choices=["yt_dlp", "pytube", "pytubefix"], | |
| value="yt_dlp" | |
| ) | |
| with gr.Group(visible=True) as ytdlp_group: | |
| cookies_file = gr.File( | |
| label="Cookies 文件 (可选 - Netscape 格式,用于访问受限视频)", | |
| file_count="single", | |
| type="filepath", # Change from "file" to "filepath" | |
| file_types=[".txt"] | |
| ) | |
| with gr.Group(visible=False) as pytubefix_group: | |
| po_token = gr.Textbox(label="poToken (optional)", placeholder="INNERTUBE_API_KEY value") | |
| visitor_data = gr.Textbox(label="visitorData (optional)", placeholder="CgtsZG1hLXVpLW...") | |
| with gr.Row(): | |
| video_url = gr.Textbox(label="YouTube 视频链接", placeholder="https://www.youtube.com/watch?v=...") | |
| fetch_button = gr.Button("获取视频信息", variant="primary") | |
| error_message = gr.Textbox(label="状态", visible=True, interactive=False) | |
| with gr.Group(visible=False) as video_info_group: | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| thumbnail = gr.Image(label="缩略图") | |
| with gr.Column(scale=3): | |
| title = gr.Textbox(label="标题", interactive=False) | |
| author = gr.Textbox(label="作者", interactive=False) | |
| with gr.Row(): | |
| views = gr.Textbox(label="播放量", interactive=False) | |
| duration = gr.Textbox(label="时长", interactive=False) | |
| description = gr.Textbox(label="描述", interactive=False, lines=5) | |
| gr.Markdown("### 选择要下载的视频片段") | |
| with gr.Row(): | |
| start_time = gr.Textbox(label="开始时间 (HH:MM:SS)", placeholder="00:01:30") | |
| end_time = gr.Textbox(label="结束时间 (HH:MM:SS)", placeholder="00:02:30") | |
| with gr.Row(): | |
| quality = gr.Dropdown(label="视频质量", choices=["360p", "480p", "720p", "1080p"], value="1080p") | |
| fps = gr.Radio(label="帧率", choices=["30fps", "60fps"], value="60fps") | |
| download_button = gr.Button("下载视频片段", variant="primary") | |
| output_file = gr.File(label="下载结果") | |
| def update_engine_groups(engine_value): | |
| return gr.update(visible=(engine_value == "pytubefix")), gr.update(visible=(engine_value == "yt_dlp")) | |
| engine.change( | |
| fn=update_engine_groups, | |
| inputs=[engine], | |
| outputs=[pytubefix_group, ytdlp_group] | |
| ) | |
| fetch_button.click( | |
| fn=fetch_video_info, | |
| inputs=[video_url, engine, po_token, visitor_data, cookies_file], | |
| outputs=[error_message, title, author, views, duration, description, thumbnail, video_info_group, quality] | |
| ) | |
| download_button.click( | |
| fn=download_video_segment, | |
| inputs=[video_url, start_time, end_time, quality, fps, engine, po_token, visitor_data, cookies_file], | |
| outputs=[output_file] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |