mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-07 12:05:35 +00:00
Fix shorter video and small bar, remove ctrl+c.
This commit is contained in:
parent
2abfe82815
commit
2960b810cd
@ -1,5 +1,6 @@
|
|||||||
# 01.03.24
|
# 01.03.24
|
||||||
|
|
||||||
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from urllib.parse import urljoin, urlparse, parse_qs, urlencode, urlunparse
|
from urllib.parse import urljoin, urlparse, parse_qs, urlencode, urlunparse
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
# Internal utilities
|
# Internal utilities
|
||||||
from Src.Util.headers import get_headers
|
from Src.Util.headers import get_headers
|
||||||
|
from Src.Util.console import console
|
||||||
from Src.Util._jsonConfig import config_manager
|
from Src.Util._jsonConfig import config_manager
|
||||||
|
|
||||||
|
|
||||||
@ -204,4 +206,4 @@ class VideoSource:
|
|||||||
new_url = m._replace(query=new_query) # Replace the old query string with the new one
|
new_url = m._replace(query=new_query) # Replace the old query string with the new one
|
||||||
final_url = urlunparse(new_url) # Construct the final URL from the modified parts
|
final_url = urlunparse(new_url) # Construct the final URL from the modified parts
|
||||||
|
|
||||||
return final_url
|
return final_url
|
||||||
|
@ -202,7 +202,6 @@ class VideoSource:
|
|||||||
logging.error(f"Error getting content: {e}")
|
logging.error(f"Error getting content: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_playlist(self) -> str:
|
def get_playlist(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get playlist.
|
Get playlist.
|
||||||
@ -240,4 +239,4 @@ class VideoSource:
|
|||||||
new_url = m._replace(query=new_query) # Replace the old query string with the new one
|
new_url = m._replace(query=new_query) # Replace the old query string with the new one
|
||||||
final_url = urlunparse(new_url) # Construct the final URL from the modified parts
|
final_url = urlunparse(new_url) # Construct the final URL from the modified parts
|
||||||
|
|
||||||
return final_url
|
return final_url
|
||||||
|
@ -4,4 +4,7 @@ STREAMING_FOLDER = "streamingcommunity"
|
|||||||
MOVIE_FOLDER = "Movie"
|
MOVIE_FOLDER = "Movie"
|
||||||
SERIES_FOLDER = "Serie"
|
SERIES_FOLDER = "Serie"
|
||||||
|
|
||||||
SERVER_IP = ['162.19.231.20', '162.19.255.224', '162.19.254.232', '162.19.254.230', '51.195.107.230', '162.19.255.36', '162.19.228.128', '51.195.107.7', '162.19.253.242', '141.95.0.248', '57.129.4.77', '57.129.7.85']
|
SERVER_IP = ['162.19.255.224', '162.19.255.223', '162.19.254.244', '162.19.254.232', '162.19.254.230',
|
||||||
|
'162.19.253.242', '162.19.249.48', '162.19.245.142', '162.19.231.20', '162.19.229.177',
|
||||||
|
'162.19.228.128', '162.19.228.127', '162.19.228.105', '141.95.1.32', '141.95.1.196',
|
||||||
|
'141.95.1.102', '141.95.0.50', '141.95.0.248', '135.125.237.84', '135.125.233.236']
|
@ -40,6 +40,10 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
|
|||||||
|
|
||||||
logging.info(f"FFMPEG line: {line}")
|
logging.info(f"FFMPEG line: {line}")
|
||||||
|
|
||||||
|
# Capture only error
|
||||||
|
if "rror" in str(line):
|
||||||
|
console.log(f"[red]FFMPEG: {str(line).strip()}")
|
||||||
|
|
||||||
# Check if termination is requested
|
# Check if termination is requested
|
||||||
if terminate_flag.is_set():
|
if terminate_flag.is_set():
|
||||||
break
|
break
|
||||||
|
@ -19,14 +19,18 @@ except: pass
|
|||||||
from Src.Util._jsonConfig import config_manager
|
from Src.Util._jsonConfig import config_manager
|
||||||
from Src.Util.os import check_file_existence, suppress_output
|
from Src.Util.os import check_file_existence, suppress_output
|
||||||
from Src.Util.console import console
|
from Src.Util.console import console
|
||||||
from .util import has_audio_stream, need_to_force_to_ts, check_ffmpeg_input
|
from .util import has_audio_stream, need_to_force_to_ts, check_ffmpeg_input, check_duration_v_a
|
||||||
from .capture import capture_ffmpeg_real_time
|
from .capture import capture_ffmpeg_real_time
|
||||||
|
from ..M3U8.parser import M3U8_Codec
|
||||||
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
DEBUG_MODE = config_manager.get_bool("DEFAULT", "debug")
|
DEBUG_MODE = config_manager.get_bool("DEFAULT", "debug")
|
||||||
DEBUG_FFMPEG = "debug" if DEBUG_MODE else "error"
|
DEBUG_FFMPEG = "debug" if DEBUG_MODE else "error"
|
||||||
USE_CODECS = config_manager.get_bool("M3U8_CONVERSION", "use_codec")
|
USE_CODEC = config_manager.get_bool("M3U8_CONVERSION", "use_codec")
|
||||||
|
USE_VCODEC = config_manager.get_bool("M3U8_CONVERSION", "use_vcodec")
|
||||||
|
USE_ACODEC = config_manager.get_bool("M3U8_CONVERSION", "use_acodec")
|
||||||
|
USE_BITRATE = config_manager.get_bool("M3U8_CONVERSION", "use_bitrate")
|
||||||
USE_GPU = config_manager.get_bool("M3U8_CONVERSION", "use_gpu")
|
USE_GPU = config_manager.get_bool("M3U8_CONVERSION", "use_gpu")
|
||||||
FFMPEG_DEFAULT_PRESET = config_manager.get("M3U8_CONVERSION", "default_preset")
|
FFMPEG_DEFAULT_PRESET = config_manager.get("M3U8_CONVERSION", "default_preset")
|
||||||
CHECK_OUTPUT_CONVERSION = config_manager.get_bool("M3U8_CONVERSION", "check_output_after_ffmpeg")
|
CHECK_OUTPUT_CONVERSION = config_manager.get_bool("M3U8_CONVERSION", "check_output_after_ffmpeg")
|
||||||
@ -263,7 +267,7 @@ def __transcode_with_subtitles(video: str, subtitles_list: List[Dict[str, str]],
|
|||||||
|
|
||||||
|
|
||||||
# --> v 1.1 (new)
|
# --> v 1.1 (new)
|
||||||
def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str = None, bitrate: str = None):
|
def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Joins single ts video file to mp4
|
Joins single ts video file to mp4
|
||||||
@ -281,12 +285,12 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
|
|||||||
logging.error("Missing input video for ffmpeg conversion.")
|
logging.error("Missing input video for ffmpeg conversion.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
# Start command
|
# Start command
|
||||||
ffmpeg_cmd = ['ffmpeg']
|
ffmpeg_cmd = ['ffmpeg']
|
||||||
|
|
||||||
# Enabled the use of gpu
|
# Enabled the use of gpu
|
||||||
ffmpeg_cmd.extend(['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'])
|
if USE_GPU:
|
||||||
|
ffmpeg_cmd.extend(['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'])
|
||||||
|
|
||||||
# Add mpegts to force to detect input file as ts file
|
# Add mpegts to force to detect input file as ts file
|
||||||
if need_to_force_to_ts(video_path):
|
if need_to_force_to_ts(video_path):
|
||||||
@ -294,15 +298,30 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
|
|||||||
ffmpeg_cmd.extend(['-f', 'mpegts'])
|
ffmpeg_cmd.extend(['-f', 'mpegts'])
|
||||||
vcodec = "libx264"
|
vcodec = "libx264"
|
||||||
|
|
||||||
|
|
||||||
# Insert input video path
|
# Insert input video path
|
||||||
ffmpeg_cmd.extend(['-i', video_path])
|
ffmpeg_cmd.extend(['-i', video_path])
|
||||||
|
|
||||||
# Add output args
|
# Add output args
|
||||||
if USE_CODECS:
|
if USE_CODEC:
|
||||||
if vcodec: ffmpeg_cmd.extend(['-c:v', vcodec])
|
if USE_VCODEC:
|
||||||
if acodec: ffmpeg_cmd.extend(['-c:a', acodec])
|
if codec.video_codec_name:
|
||||||
if bitrate: ffmpeg_cmd.extend(['-b:a', str(bitrate)])
|
if not USE_GPU:
|
||||||
|
ffmpeg_cmd.extend(['-c:v', codec.video_codec_name])
|
||||||
|
else:
|
||||||
|
ffmpeg_cmd.extend(['-c:v', 'h264_nvenc'])
|
||||||
|
else:
|
||||||
|
console.log("[red]Cant find vcodec for 'join_audios'")
|
||||||
|
|
||||||
|
if USE_ACODEC:
|
||||||
|
if codec.audio_codec_name:
|
||||||
|
ffmpeg_cmd.extend(['-c:a', codec.audio_codec_name])
|
||||||
|
else:
|
||||||
|
console.log("[red]Cant find acodec for 'join_audios'")
|
||||||
|
|
||||||
|
if USE_BITRATE:
|
||||||
|
ffmpeg_cmd.extend(['-b:v', f'{codec.video_bitrate // 1000}k'])
|
||||||
|
ffmpeg_cmd.extend(['-b:a', f'{codec.audio_bitrate // 1000}k'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ffmpeg_cmd.extend(['-c', 'copy'])
|
ffmpeg_cmd.extend(['-c', 'copy'])
|
||||||
|
|
||||||
@ -312,12 +331,10 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
|
|||||||
else:
|
else:
|
||||||
ffmpeg_cmd.extend(['-preset', 'fast'])
|
ffmpeg_cmd.extend(['-preset', 'fast'])
|
||||||
|
|
||||||
|
|
||||||
# Overwrite
|
# Overwrite
|
||||||
ffmpeg_cmd += [out_path, "-y"]
|
ffmpeg_cmd += [out_path, "-y"]
|
||||||
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
|
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
|
||||||
|
|
||||||
|
|
||||||
# Run join
|
# Run join
|
||||||
if DEBUG_MODE:
|
if DEBUG_MODE:
|
||||||
subprocess.run(ffmpeg_cmd, check=True)
|
subprocess.run(ffmpeg_cmd, check=True)
|
||||||
@ -333,8 +350,6 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
|
|||||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
|
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check file output
|
# Check file output
|
||||||
if CHECK_OUTPUT_CONVERSION:
|
if CHECK_OUTPUT_CONVERSION:
|
||||||
console.log("[red]Check output ffmpeg")
|
console.log("[red]Check output ffmpeg")
|
||||||
@ -347,7 +362,7 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str):
|
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str, codec: M3U8_Codec = None):
|
||||||
"""
|
"""
|
||||||
Joins audio tracks with a video file using FFmpeg.
|
Joins audio tracks with a video file using FFmpeg.
|
||||||
|
|
||||||
@ -362,9 +377,17 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|||||||
logging.error("Missing input video for ffmpeg conversion.")
|
logging.error("Missing input video for ffmpeg conversion.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
video_audio_same_duration = check_duration_v_a(video_path, audio_tracks[0].get('path'))
|
||||||
|
|
||||||
# Start command
|
# Start command
|
||||||
ffmpeg_cmd = ['ffmpeg', '-i', video_path]
|
ffmpeg_cmd = ['ffmpeg']
|
||||||
|
|
||||||
|
# Enabled the use of gpu
|
||||||
|
if USE_GPU:
|
||||||
|
ffmpeg_cmd.extend(['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'])
|
||||||
|
|
||||||
|
# Insert input video path
|
||||||
|
ffmpeg_cmd.extend(['-i', video_path])
|
||||||
|
|
||||||
# Add audio tracks as input
|
# Add audio tracks as input
|
||||||
for i, audio_track in enumerate(audio_tracks):
|
for i, audio_track in enumerate(audio_tracks):
|
||||||
@ -373,7 +396,6 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|||||||
else:
|
else:
|
||||||
logging.error(f"Skip audio join: {audio_track.get('path')} dont exist")
|
logging.error(f"Skip audio join: {audio_track.get('path')} dont exist")
|
||||||
|
|
||||||
|
|
||||||
# Map the video and audio streams
|
# Map the video and audio streams
|
||||||
ffmpeg_cmd.append('-map')
|
ffmpeg_cmd.append('-map')
|
||||||
ffmpeg_cmd.append('0:v') # Map video stream from the first input (video_path)
|
ffmpeg_cmd.append('0:v') # Map video stream from the first input (video_path)
|
||||||
@ -382,18 +404,45 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|||||||
ffmpeg_cmd.append('-map')
|
ffmpeg_cmd.append('-map')
|
||||||
ffmpeg_cmd.append(f'{i}:a') # Map audio streams from subsequent inputs
|
ffmpeg_cmd.append(f'{i}:a') # Map audio streams from subsequent inputs
|
||||||
|
|
||||||
|
|
||||||
# Add output args
|
# Add output args
|
||||||
if USE_CODECS:
|
if USE_CODEC:
|
||||||
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy'])
|
if USE_VCODEC:
|
||||||
|
if codec.video_codec_name:
|
||||||
|
if not USE_GPU:
|
||||||
|
ffmpeg_cmd.extend(['-c:v', codec.video_codec_name])
|
||||||
|
else:
|
||||||
|
ffmpeg_cmd.extend(['-c:v', 'h264_nvenc'])
|
||||||
|
else:
|
||||||
|
console.log("[red]Cant find vcodec for 'join_audios'")
|
||||||
|
|
||||||
|
if USE_ACODEC:
|
||||||
|
if codec.audio_codec_name:
|
||||||
|
ffmpeg_cmd.extend(['-c:a', codec.audio_codec_name])
|
||||||
|
else:
|
||||||
|
console.log("[red]Cant find acodec for 'join_audios'")
|
||||||
|
|
||||||
|
if USE_BITRATE:
|
||||||
|
ffmpeg_cmd.extend(['-b:v', f'{codec.video_bitrate // 1000}k'])
|
||||||
|
ffmpeg_cmd.extend(['-b:a', f'{codec.audio_bitrate // 1000}k'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ffmpeg_cmd.extend(['-c', 'copy'])
|
ffmpeg_cmd.extend(['-c', 'copy'])
|
||||||
|
|
||||||
|
# Ultrafast preset always or fast for gpu
|
||||||
|
if not USE_GPU:
|
||||||
|
ffmpeg_cmd.extend(['-preset', FFMPEG_DEFAULT_PRESET])
|
||||||
|
else:
|
||||||
|
ffmpeg_cmd.extend(['-preset', 'fast'])
|
||||||
|
|
||||||
|
# Use shortest input path for video and audios
|
||||||
|
if not video_audio_same_duration:
|
||||||
|
console.log("[red]Use shortest input.")
|
||||||
|
ffmpeg_cmd.extend(['-shortest', '-strict', 'experimental'])
|
||||||
|
|
||||||
# Overwrite
|
# Overwrite
|
||||||
ffmpeg_cmd += [out_path, "-y"]
|
ffmpeg_cmd += [out_path, "-y"]
|
||||||
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
|
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
|
||||||
|
|
||||||
|
|
||||||
# Run join
|
# Run join
|
||||||
if DEBUG_MODE:
|
if DEBUG_MODE:
|
||||||
subprocess.run(ffmpeg_cmd, check=True)
|
subprocess.run(ffmpeg_cmd, check=True)
|
||||||
@ -409,7 +458,6 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|||||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
|
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
# Check file output
|
# Check file output
|
||||||
if CHECK_OUTPUT_CONVERSION:
|
if CHECK_OUTPUT_CONVERSION:
|
||||||
console.log("[red]Check output ffmpeg")
|
console.log("[red]Check output ffmpeg")
|
||||||
@ -456,7 +504,7 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
|||||||
ffmpeg_cmd += ["-metadata:s:s:{}".format(idx), "title={}".format(subtitle['name'])]
|
ffmpeg_cmd += ["-metadata:s:s:{}".format(idx), "title={}".format(subtitle['name'])]
|
||||||
|
|
||||||
# Add output args
|
# Add output args
|
||||||
if USE_CODECS:
|
if USE_CODEC:
|
||||||
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', 'mov_text'])
|
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', 'mov_text'])
|
||||||
else:
|
else:
|
||||||
ffmpeg_cmd.extend(['-c', 'copy', '-c:s', 'mov_text'])
|
ffmpeg_cmd.extend(['-c', 'copy', '-c:s', 'mov_text'])
|
||||||
|
@ -92,7 +92,7 @@ def format_duration(seconds: float) -> Tuple[int, int, int]:
|
|||||||
return int(hours), int(minutes), int(seconds)
|
return int(hours), int(minutes), int(seconds)
|
||||||
|
|
||||||
|
|
||||||
def print_duration_table(file_path: str) -> None:
|
def print_duration_table(file_path: str, show = True) -> None:
|
||||||
"""
|
"""
|
||||||
Print duration of a video file in hours, minutes, and seconds.
|
Print duration of a video file in hours, minutes, and seconds.
|
||||||
|
|
||||||
@ -104,7 +104,10 @@ def print_duration_table(file_path: str) -> None:
|
|||||||
|
|
||||||
if video_duration is not None:
|
if video_duration is not None:
|
||||||
hours, minutes, seconds = format_duration(video_duration)
|
hours, minutes, seconds = format_duration(video_duration)
|
||||||
console.log(f"[cyan]Duration for [white]([green]{os.path.basename(file_path)}[white]): [yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s")
|
if show:
|
||||||
|
console.print(f"[cyan]Duration for [white]([green]{os.path.basename(file_path)}[white]): [yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s")
|
||||||
|
else:
|
||||||
|
return f"[yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s"
|
||||||
|
|
||||||
|
|
||||||
def get_ffprobe_info(file_path):
|
def get_ffprobe_info(file_path):
|
||||||
@ -210,3 +213,24 @@ def check_ffmpeg_input(input_file):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"An unexpected error occurred: {e}")
|
logging.error(f"An unexpected error occurred: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def check_duration_v_a(video_path, audio_path):
|
||||||
|
"""
|
||||||
|
Check if the duration of the video and audio matches.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- video_path (str): Path to the video file.
|
||||||
|
- audio_path (str): Path to the audio file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- bool: True if the duration of the video and audio matches, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Ottieni la durata del video
|
||||||
|
video_duration = get_video_duration(video_path)
|
||||||
|
|
||||||
|
# Ottieni la durata dell'audio
|
||||||
|
audio_duration = get_video_duration(audio_path)
|
||||||
|
|
||||||
|
# Verifica se le durate corrispondono
|
||||||
|
return video_duration == audio_duration
|
@ -178,7 +178,7 @@ class Downloader():
|
|||||||
|
|
||||||
# Check if there is some audios, else disable download
|
# Check if there is some audios, else disable download
|
||||||
if self.list_available_audio != None:
|
if self.list_available_audio != None:
|
||||||
console.log(f"[cyan]Find audios [white]=> [red]{[obj_audio.get('language') for obj_audio in self.list_available_audio]}")
|
console.print(f"[cyan]Find audios [white]=> [red]{[obj_audio.get('language') for obj_audio in self.list_available_audio]}")
|
||||||
else:
|
else:
|
||||||
console.log("[red]Cant find a list of audios")
|
console.log("[red]Cant find a list of audios")
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class Downloader():
|
|||||||
|
|
||||||
# Check if there is some subtitles, else disable download
|
# Check if there is some subtitles, else disable download
|
||||||
if self.list_available_subtitles != None:
|
if self.list_available_subtitles != None:
|
||||||
console.log(f"[cyan]Find subtitles [white]=> [red]{[obj_sub.get('language') for obj_sub in self.list_available_subtitles]}")
|
console.print(f"[cyan]Find subtitles [white]=> [red]{[obj_sub.get('language') for obj_sub in self.list_available_subtitles]}")
|
||||||
else:
|
else:
|
||||||
console.log("[red]Cant find a list of audios")
|
console.log("[red]Cant find a list of audios")
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ class Downloader():
|
|||||||
logging.info(f"M3U8 index select: {self.m3u8_index}, with resolution: {video_res}")
|
logging.info(f"M3U8 index select: {self.m3u8_index}, with resolution: {video_res}")
|
||||||
|
|
||||||
# Get URI of the best quality and codecs parameters
|
# Get URI of the best quality and codecs parameters
|
||||||
console.log(f"[cyan]Find resolution [white]=> [red]{sorted(list_available_resolution, reverse=True)}")
|
console.print(f"[cyan]Find resolution [white]=> [red]{sorted(list_available_resolution, reverse=True)}")
|
||||||
|
|
||||||
# Fix URL if it is not complete with http:\\site_name.domain\...
|
# Fix URL if it is not complete with http:\\site_name.domain\...
|
||||||
if "http" not in self.m3u8_index:
|
if "http" not in self.m3u8_index:
|
||||||
@ -219,7 +219,7 @@ class Downloader():
|
|||||||
|
|
||||||
# Check if a valid HTTPS URL is obtained
|
# Check if a valid HTTPS URL is obtained
|
||||||
if self.m3u8_index is not None and "https" in self.m3u8_index:
|
if self.m3u8_index is not None and "https" in self.m3u8_index:
|
||||||
console.log(f"[cyan]Found m3u8 index [white]=> [red]{self.m3u8_index}")
|
console.print(f"[cyan]Found m3u8 index [white]=> [red]{self.m3u8_index}")
|
||||||
else:
|
else:
|
||||||
logging.error("[download_m3u8] Can't find a valid m3u8 index")
|
logging.error("[download_m3u8] Can't find a valid m3u8 index")
|
||||||
raise
|
raise
|
||||||
@ -229,7 +229,8 @@ class Downloader():
|
|||||||
logging.info(f"Find codec: {self.codec}")
|
logging.info(f"Find codec: {self.codec}")
|
||||||
|
|
||||||
if self.codec is not None:
|
if self.codec is not None:
|
||||||
console.log(f"[cyan]Find codec [white]=> ([green]'v'[white]: [yellow]{self.codec.video_codec_name}[white], [green]'a'[white]: [yellow]{self.codec.audio_codec_name}[white], [green]'b'[white]: [yellow]{self.codec.bandwidth})")
|
console.print(f"[cyan]Find codec [white]=> ([green]'v'[white]: [yellow]{self.codec.video_codec_name}[white] ([green]b[white]: [yellow]{self.codec.video_bitrate // 1000}k[white]), [green]'a'[white]: [yellow]{self.codec.audio_codec_name}[white] ([green]b[white]: [yellow]{self.codec.audio_bitrate // 1000}k[white]))")
|
||||||
|
|
||||||
|
|
||||||
def __donwload_video__(self, server_ip: list = None):
|
def __donwload_video__(self, server_ip: list = None):
|
||||||
"""
|
"""
|
||||||
@ -263,6 +264,9 @@ class Downloader():
|
|||||||
# Download the video segments
|
# Download the video segments
|
||||||
video_m3u8.download_streams(f"{Colors.MAGENTA}video")
|
video_m3u8.download_streams(f"{Colors.MAGENTA}video")
|
||||||
|
|
||||||
|
# Get time of output file
|
||||||
|
print_duration_table(os.path.join(full_path_video, "0.ts"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
console.log("[cyan]Video [red]already exists.")
|
console.log("[cyan]Video [red]already exists.")
|
||||||
|
|
||||||
@ -309,6 +313,9 @@ class Downloader():
|
|||||||
# Download the audio segments
|
# Download the audio segments
|
||||||
audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}")
|
audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}")
|
||||||
|
|
||||||
|
# Get time of output file
|
||||||
|
print_duration_table(os.path.join(full_path_audio, "0.ts"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
console.log(f"[cyan]Audio [white]([green]{obj_audio.get('language')}[white]) [red]already exists.")
|
console.log(f"[cyan]Audio [white]([green]{obj_audio.get('language')}[white]) [red]already exists.")
|
||||||
|
|
||||||
@ -373,14 +380,14 @@ class Downloader():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Initiate the download of the subtitle content
|
# Initiate the download of the subtitle content
|
||||||
console.log(f"[cyan]Downloading subtitle: [red]{sub_language.lower()}")
|
console.print(f"[cyan]Downloading subtitle: [red]{sub_language.lower()}")
|
||||||
futures.append(executor.submit(self.__save_subtitle_content, m3u8_sub_parser.subtitle[-1], sub_full_path))
|
futures.append(executor.submit(self.__save_subtitle_content, m3u8_sub_parser.subtitle[-1], sub_full_path))
|
||||||
|
|
||||||
# Wait for all downloads to finish
|
# Wait for all downloads to finish
|
||||||
for future in futures:
|
for future in futures:
|
||||||
future.result()
|
future.result()
|
||||||
|
|
||||||
def __join_video__(self, vcodec = 'copy') -> str:
|
def __join_video__(self) -> str:
|
||||||
"""
|
"""
|
||||||
Join downloaded video segments into a single video file.
|
Join downloaded video segments into a single video file.
|
||||||
|
|
||||||
@ -397,10 +404,9 @@ class Downloader():
|
|||||||
join_video(
|
join_video(
|
||||||
video_path = self.downloaded_video[0].get('path'),
|
video_path = self.downloaded_video[0].get('path'),
|
||||||
out_path = path_join_video,
|
out_path = path_join_video,
|
||||||
vcodec = vcodec
|
codec = self.codec
|
||||||
)
|
)
|
||||||
|
|
||||||
print_duration_table(path_join_video)
|
|
||||||
return path_join_video
|
return path_join_video
|
||||||
|
|
||||||
def __join_video_audio__(self) -> str:
|
def __join_video_audio__(self) -> str:
|
||||||
@ -420,10 +426,10 @@ class Downloader():
|
|||||||
join_audios(
|
join_audios(
|
||||||
video_path = self.downloaded_video[0].get('path'),
|
video_path = self.downloaded_video[0].get('path'),
|
||||||
audio_tracks = self.downloaded_audio,
|
audio_tracks = self.downloaded_audio,
|
||||||
out_path = path_join_video_audio
|
out_path = path_join_video_audio,
|
||||||
|
codec = self.codec
|
||||||
)
|
)
|
||||||
|
|
||||||
print_duration_table(path_join_video_audio)
|
|
||||||
return path_join_video_audio
|
return path_join_video_audio
|
||||||
|
|
||||||
def __join_video_subtitles__(self, input_path: str) -> str:
|
def __join_video_subtitles__(self, input_path: str) -> str:
|
||||||
@ -449,7 +455,6 @@ class Downloader():
|
|||||||
path_join_video_subtitle
|
path_join_video_subtitle
|
||||||
)
|
)
|
||||||
|
|
||||||
print_duration_table(path_join_video_subtitle)
|
|
||||||
return path_join_video_subtitle
|
return path_join_video_subtitle
|
||||||
|
|
||||||
def __clean__(self, out_path: str) -> None:
|
def __clean__(self, out_path: str) -> None:
|
||||||
@ -473,7 +478,11 @@ class Downloader():
|
|||||||
os.rename(out_path, self.output_filename)
|
os.rename(out_path, self.output_filename)
|
||||||
|
|
||||||
# Print size of the file
|
# Print size of the file
|
||||||
console.print(Panel(f"[bold green]Download completed![/bold green]\nFile size: [bold red]{format_size(os.path.getsize(self.output_filename))}[/bold red]", title=f"{os.path.basename(self.output_filename.replace('.mp4', ''))}", border_style="green"))
|
console.print(Panel(
|
||||||
|
f"[bold green]Download completed![/bold green]\n"
|
||||||
|
f"File size: [bold red]{format_size(os.path.getsize(self.output_filename))}[/bold red]\n"
|
||||||
|
f"Duration: [bold]{print_duration_table(self.output_filename, show=False)}[/bold]",
|
||||||
|
title=f"{os.path.basename(self.output_filename.replace('.mp4', ''))}", border_style="green"))
|
||||||
|
|
||||||
# Delete all files except the output file
|
# Delete all files except the output file
|
||||||
delete_files_except_one(self.base_path, os.path.basename(self.output_filename))
|
delete_files_except_one(self.base_path, os.path.basename(self.output_filename))
|
||||||
@ -535,7 +544,7 @@ class Downloader():
|
|||||||
there_is_video: bool = (len(self.downloaded_video) > 0)
|
there_is_video: bool = (len(self.downloaded_video) > 0)
|
||||||
there_is_audio: bool = (len(self.downloaded_audio) > 0)
|
there_is_audio: bool = (len(self.downloaded_audio) > 0)
|
||||||
there_is_subtitle: bool = (len(self.downloaded_subtitle) > 0)
|
there_is_subtitle: bool = (len(self.downloaded_subtitle) > 0)
|
||||||
console.log(f"[cyan]Conversion [white]=> ([green]Audio: [yellow]{there_is_audio}[white], [green]Subtitle: [yellow]{there_is_subtitle}[white])")
|
console.print(f"[cyan]Conversion [white]=> ([green]Audio: [yellow]{there_is_audio}[white], [green]Subtitle: [yellow]{there_is_subtitle}[white])")
|
||||||
|
|
||||||
|
|
||||||
# Join audio and video
|
# Join audio and video
|
||||||
|
@ -31,7 +31,6 @@ from ..M3U8 import (
|
|||||||
M3U8_UrlFix
|
M3U8_UrlFix
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
TQDM_MAX_WORKER = config_manager.get_int('M3U8_DOWNLOAD', 'tdqm_workers')
|
TQDM_MAX_WORKER = config_manager.get_int('M3U8_DOWNLOAD', 'tdqm_workers')
|
||||||
TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
|
TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
|
||||||
@ -122,7 +121,7 @@ class M3U8_Segments:
|
|||||||
m3u8_parser = M3U8_Parser()
|
m3u8_parser = M3U8_Parser()
|
||||||
m3u8_parser.parse_data(uri=self.url, raw_content=m3u8_content) # Parse the content of the M3U8 playlist
|
m3u8_parser.parse_data(uri=self.url, raw_content=m3u8_content) # Parse the content of the M3U8 playlist
|
||||||
|
|
||||||
console.log(f"[cyan]There is key: [yellow]{m3u8_parser.keys is not None}")
|
console.print(f"[red]There is key: [yellow]{m3u8_parser.keys is not None}")
|
||||||
|
|
||||||
# Check if there is an encryption key in the playlis
|
# Check if there is an encryption key in the playlis
|
||||||
if m3u8_parser.keys is not None:
|
if m3u8_parser.keys is not None:
|
||||||
@ -166,6 +165,7 @@ class M3U8_Segments:
|
|||||||
|
|
||||||
# Update segments for estimator
|
# Update segments for estimator
|
||||||
self.class_ts_estimator.total_segments = len(self.segments)
|
self.class_ts_estimator.total_segments = len(self.segments)
|
||||||
|
logging.info(f"fSegmnets to donwload: [{len(self.segments)}]")
|
||||||
|
|
||||||
def get_info(self) -> None:
|
def get_info(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -220,12 +220,13 @@ class M3U8_Segments:
|
|||||||
|
|
||||||
# Generate new user agent
|
# Generate new user agent
|
||||||
headers_segments['user-agent'] = get_headers()
|
headers_segments['user-agent'] = get_headers()
|
||||||
|
logging.info(f"Make request to get segmenet: [{index} - {len(self.segments)}]")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Make request and calculate time duration
|
# Make request and calculate time duration
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
response = requests.get(ts_url, headers=headers_segments, verify_ssl=REQUEST_VERIFY_SSL)
|
response = requests.get(ts_url, headers=headers_segments, verify=REQUEST_VERIFY_SSL, timeout=30)
|
||||||
duration = time.time() - start_time
|
duration = time.time() - start_time
|
||||||
|
|
||||||
if response.ok:
|
if response.ok:
|
||||||
@ -267,7 +268,7 @@ class M3U8_Segments:
|
|||||||
while not stop_event.is_set() or not self.segment_queue.empty():
|
while not stop_event.is_set() or not self.segment_queue.empty():
|
||||||
with self.condition:
|
with self.condition:
|
||||||
while self.segment_queue.empty() and not stop_event.is_set():
|
while self.segment_queue.empty() and not stop_event.is_set():
|
||||||
self.condition.wait(timeout=1) # Wait until a new segment is available or stop_event is set
|
self.condition.wait() # Wait until a new segment is available or stop_event is set
|
||||||
|
|
||||||
if stop_event.is_set():
|
if stop_event.is_set():
|
||||||
break
|
break
|
||||||
@ -311,16 +312,6 @@ class M3U8_Segments:
|
|||||||
mininterval=0.01
|
mininterval=0.01
|
||||||
)
|
)
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
|
||||||
self.ctrl_c_detected = True # Set global variable to indicate Ctrl+C detection
|
|
||||||
|
|
||||||
stop_event.set()
|
|
||||||
with self.condition:
|
|
||||||
self.condition.notify_all() # Wake up the writer thread if it's waiting
|
|
||||||
|
|
||||||
# Register the signal handler for Ctrl+C
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=TQDM_MAX_WORKER) as executor:
|
with ThreadPoolExecutor(max_workers=TQDM_MAX_WORKER) as executor:
|
||||||
|
|
||||||
# Start a separate thread to write segments to the file
|
# Start a separate thread to write segments to the file
|
||||||
@ -330,24 +321,13 @@ class M3U8_Segments:
|
|||||||
# Delay the start of each worker
|
# Delay the start of each worker
|
||||||
for index, segment_url in enumerate(self.segments):
|
for index, segment_url in enumerate(self.segments):
|
||||||
|
|
||||||
# Check for Ctrl+C before starting each download task
|
|
||||||
time.sleep(0.03)
|
|
||||||
|
|
||||||
if self.ctrl_c_detected:
|
|
||||||
console.log("[red]Ctrl+C detected. Stopping further downloads.")
|
|
||||||
|
|
||||||
stop_event.set()
|
|
||||||
with self.condition:
|
|
||||||
self.condition.notify_all() # Wake up the writer thread if it's waiting
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
# Submit the download task to the executor
|
# Submit the download task to the executor
|
||||||
executor.submit(self.make_requests_stream, segment_url, index, stop_event, progress_bar)
|
executor.submit(self.make_requests_stream, segment_url, index, stop_event, progress_bar)
|
||||||
|
|
||||||
# Wait for all segments to be downloaded
|
# Wait for all segments to be downloaded
|
||||||
executor.shutdown(wait=True)
|
executor.shutdown()
|
||||||
stop_event.set() # Set the stop event to halt the writer thread
|
stop_event.set() # Set the stop event to halt the writer thread
|
||||||
with self.condition:
|
with self.condition:
|
||||||
self.condition.notify_all() # Wake up the writer thread if it's waiting
|
self.condition.notify_all() # Wake up the writer thread if it's waiting
|
||||||
writer_thread.join() # Wait for the writer thread to finish
|
writer_thread.join() # Wait for the writer thread to finish
|
||||||
|
|
||||||
|
@ -48,10 +48,6 @@ RESOLUTIONS = [
|
|||||||
|
|
||||||
|
|
||||||
class M3U8_Codec:
|
class M3U8_Codec:
|
||||||
"""
|
|
||||||
Represents codec information for an M3U8 playlist.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, bandwidth, codecs):
|
def __init__(self, bandwidth, codecs):
|
||||||
"""
|
"""
|
||||||
Initializes the M3U8Codec object with the provided parameters.
|
Initializes the M3U8Codec object with the provided parameters.
|
||||||
@ -64,20 +60,23 @@ class M3U8_Codec:
|
|||||||
self.codecs = codecs
|
self.codecs = codecs
|
||||||
self.audio_codec = None
|
self.audio_codec = None
|
||||||
self.video_codec = None
|
self.video_codec = None
|
||||||
|
self.video_codec_name = None
|
||||||
|
self.audio_codec_name = None
|
||||||
self.extract_codecs()
|
self.extract_codecs()
|
||||||
self.parse_codecs()
|
self.parse_codecs()
|
||||||
|
self.calculate_bitrates()
|
||||||
|
|
||||||
def extract_codecs(self):
|
def extract_codecs(self):
|
||||||
"""
|
"""
|
||||||
Parses the codecs information to extract audio and video codecs.
|
Parses the codecs information to extract audio and video codecs.
|
||||||
Extracted codecs are set as attributes: audio_codec and video_codec.
|
Extracted codecs are set as attributes: audio_codec and video_codec.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Split the codecs string by comma
|
|
||||||
try:
|
try:
|
||||||
|
# Split the codecs string by comma
|
||||||
codecs_list = self.codecs.split(',')
|
codecs_list = self.codecs.split(',')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Cant split codec list: {self.codecs} with error {e}")
|
logging.error(f"Can't split codec list: {self.codecs} with error {e}")
|
||||||
|
return
|
||||||
|
|
||||||
# Separate audio and video codecs
|
# Separate audio and video codecs
|
||||||
for codec in codecs_list:
|
for codec in codecs_list:
|
||||||
@ -87,7 +86,6 @@ class M3U8_Codec:
|
|||||||
self.audio_codec = codec
|
self.audio_codec = codec
|
||||||
|
|
||||||
def convert_video_codec(self, video_codec_identifier) -> str:
|
def convert_video_codec(self, video_codec_identifier) -> str:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Convert video codec identifier to codec name.
|
Convert video codec identifier to codec name.
|
||||||
|
|
||||||
@ -97,6 +95,9 @@ class M3U8_Codec:
|
|||||||
Returns:
|
Returns:
|
||||||
str: Codec name corresponding to the identifier.
|
str: Codec name corresponding to the identifier.
|
||||||
"""
|
"""
|
||||||
|
if not video_codec_identifier:
|
||||||
|
logging.warning("No video codec identifier provided. Using default codec libx264.")
|
||||||
|
return "libx264" # Default
|
||||||
|
|
||||||
# Extract codec type from the identifier
|
# Extract codec type from the identifier
|
||||||
codec_type = video_codec_identifier.split('.')[0]
|
codec_type = video_codec_identifier.split('.')[0]
|
||||||
@ -107,13 +108,11 @@ class M3U8_Codec:
|
|||||||
|
|
||||||
if codec_name:
|
if codec_name:
|
||||||
return codec_name
|
return codec_name
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.warning(f"No corresponding video codec found for {video_codec_identifier}. Using default codec libx264.")
|
logging.warning(f"No corresponding video codec found for {video_codec_identifier}. Using default codec libx264.")
|
||||||
return "libx264" # Default
|
return "libx264" # Default
|
||||||
|
|
||||||
def convert_audio_codec(self, audio_codec_identifier) -> str:
|
|
||||||
|
|
||||||
|
def convert_audio_codec(self, audio_codec_identifier) -> str:
|
||||||
"""
|
"""
|
||||||
Convert audio codec identifier to codec name.
|
Convert audio codec identifier to codec name.
|
||||||
|
|
||||||
@ -123,6 +122,9 @@ class M3U8_Codec:
|
|||||||
Returns:
|
Returns:
|
||||||
str: Codec name corresponding to the identifier.
|
str: Codec name corresponding to the identifier.
|
||||||
"""
|
"""
|
||||||
|
if not audio_codec_identifier:
|
||||||
|
logging.warning("No audio codec identifier provided. Using default codec aac.")
|
||||||
|
return "aac" # Default
|
||||||
|
|
||||||
# Extract codec type from the identifier
|
# Extract codec type from the identifier
|
||||||
codec_type = audio_codec_identifier.split('.')[0]
|
codec_type = audio_codec_identifier.split('.')[0]
|
||||||
@ -133,25 +135,33 @@ class M3U8_Codec:
|
|||||||
|
|
||||||
if codec_name:
|
if codec_name:
|
||||||
return codec_name
|
return codec_name
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.warning(f"No corresponding audio codec found for {audio_codec_identifier}. Using default codec aac.")
|
logging.warning(f"No corresponding audio codec found for {audio_codec_identifier}. Using default codec aac.")
|
||||||
return "aac" # Default
|
return "aac" # Default
|
||||||
|
|
||||||
def parse_codecs(self):
|
def parse_codecs(self):
|
||||||
"""
|
"""
|
||||||
Parse video and audio codecs.
|
Parse video and audio codecs.
|
||||||
This method updates `video_codec_name` and `audio_codec_name` attributes.
|
This method updates `video_codec_name` and `audio_codec_name` attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.video_codec_name = self.convert_video_codec(self.video_codec)
|
self.video_codec_name = self.convert_video_codec(self.video_codec)
|
||||||
self.audio_codec_name = self.convert_audio_codec(self.audio_codec)
|
self.audio_codec_name = self.convert_audio_codec(self.audio_codec)
|
||||||
|
|
||||||
def __str__(self):
|
def calculate_bitrates(self):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of the M3U8Codec object.
|
Calculate video and audio bitrates based on the available bandwidth.
|
||||||
"""
|
"""
|
||||||
return f"BANDWIDTH={self.bandwidth},RESOLUTION={self.resolution},CODECS=\"{self.codecs}\""
|
if self.bandwidth:
|
||||||
|
|
||||||
|
# Define the video and audio bitrates
|
||||||
|
video_bitrate = int(self.bandwidth * 0.8) # Using 80% of bandwidth for video
|
||||||
|
audio_bitrate = self.bandwidth - video_bitrate
|
||||||
|
|
||||||
|
self.video_bitrate = video_bitrate
|
||||||
|
self.audio_bitrate = audio_bitrate
|
||||||
|
else:
|
||||||
|
logging.warning("No bandwidth provided. Bitrates cannot be calculated.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class M3U8_Video:
|
class M3U8_Video:
|
||||||
|
@ -189,7 +189,7 @@ class ManageRequests:
|
|||||||
timeout: float = HTTP_TIMEOUT,
|
timeout: float = HTTP_TIMEOUT,
|
||||||
retries: int = HTTP_RETRIES,
|
retries: int = HTTP_RETRIES,
|
||||||
params: Optional[Dict[str, str]] = None,
|
params: Optional[Dict[str, str]] = None,
|
||||||
verify_ssl: bool = True,
|
verify: bool = True,
|
||||||
auth: Optional[tuple] = None,
|
auth: Optional[tuple] = None,
|
||||||
proxy: Optional[str] = None,
|
proxy: Optional[str] = None,
|
||||||
cookies: Optional[Dict[str, str]] = None,
|
cookies: Optional[Dict[str, str]] = None,
|
||||||
@ -206,7 +206,7 @@ class ManageRequests:
|
|||||||
- timeout (float, optional): The request timeout. Defaults to HTTP_TIMEOUT.
|
- timeout (float, optional): The request timeout. Defaults to HTTP_TIMEOUT.
|
||||||
- retries (int, optional): The number of retries in case of request failure. Defaults to HTTP_RETRIES.
|
- retries (int, optional): The number of retries in case of request failure. Defaults to HTTP_RETRIES.
|
||||||
- params (Optional[Dict[str, str]], optional): The query parameters for the request. Defaults to None.
|
- params (Optional[Dict[str, str]], optional): The query parameters for the request. Defaults to None.
|
||||||
- verify_ssl (bool, optional): Indicates whether SSL certificate verification should be performed. Defaults to True.
|
- verify (bool, optional): Indicates whether SSL certificate verification should be performed. Defaults to True.
|
||||||
- auth (Optional[tuple], optional): Tuple containing the username and password for basic authentication. Defaults to None.
|
- auth (Optional[tuple], optional): Tuple containing the username and password for basic authentication. Defaults to None.
|
||||||
- proxy (Optional[str], optional): The proxy URL. Defaults to None.
|
- proxy (Optional[str], optional): The proxy URL. Defaults to None.
|
||||||
- cookies (Optional[Dict[str, str]], optional): The cookies to be included in the request. Defaults to None.
|
- cookies (Optional[Dict[str, str]], optional): The cookies to be included in the request. Defaults to None.
|
||||||
@ -218,7 +218,7 @@ class ManageRequests:
|
|||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.retries = retries
|
self.retries = retries
|
||||||
self.params = params
|
self.params = params
|
||||||
self.verify_ssl = verify_ssl
|
self.verify_ssl = verify
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
self.cookies = cookies
|
self.cookies = cookies
|
||||||
|
@ -16,6 +16,7 @@ import platform
|
|||||||
import importlib
|
import importlib
|
||||||
import subprocess
|
import subprocess
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import urllib.request
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
@ -403,6 +404,19 @@ def convert_to_hex(bytes_data: bytes) -> str:
|
|||||||
|
|
||||||
|
|
||||||
# --> OS GET SUMMARY
|
# --> OS GET SUMMARY
|
||||||
|
def check_internet():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Attempt to open a connection to a website to check for internet connection
|
||||||
|
urllib.request.urlopen("http://www.google.com", timeout=1)
|
||||||
|
console.log("[bold green]Internet is available![/bold green]")
|
||||||
|
break
|
||||||
|
|
||||||
|
except urllib.error.URLError:
|
||||||
|
console.log("[bold red]Internet is not available. Waiting...[/bold red]")
|
||||||
|
time.sleep(5)
|
||||||
|
print()
|
||||||
|
|
||||||
def get_executable_version(command):
|
def get_executable_version(command):
|
||||||
try:
|
try:
|
||||||
version_output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode().split('\n')[0]
|
version_output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode().split('\n')[0]
|
||||||
@ -419,7 +433,8 @@ def get_library_version(lib_name):
|
|||||||
return f"{lib_name}-not installed"
|
return f"{lib_name}-not installed"
|
||||||
|
|
||||||
def get_system_summary():
|
def get_system_summary():
|
||||||
|
|
||||||
|
check_internet()
|
||||||
console.print("[bold blue]System Summary[/bold blue][white]:")
|
console.print("[bold blue]System Summary[/bold blue][white]:")
|
||||||
|
|
||||||
# Python version and platform
|
# Python version and platform
|
||||||
|
@ -32,7 +32,10 @@
|
|||||||
},
|
},
|
||||||
"M3U8_CONVERSION": {
|
"M3U8_CONVERSION": {
|
||||||
"use_codec": false,
|
"use_codec": false,
|
||||||
"use_gpu": false,
|
"use_vcodec": true,
|
||||||
|
"use_acodec": true,
|
||||||
|
"use_bitrate": true,
|
||||||
|
"use_gpu": true,
|
||||||
"default_preset": "ultrafast",
|
"default_preset": "ultrafast",
|
||||||
"check_output_after_ffmpeg": false
|
"check_output_after_ffmpeg": false
|
||||||
},
|
},
|
||||||
|
64
run.py
64
run.py
@ -27,34 +27,6 @@ from Src.Api.Altadefinizione import main_film as altadefinizione_film
|
|||||||
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
||||||
|
|
||||||
|
|
||||||
def initialize():
|
|
||||||
"""
|
|
||||||
Initialize the application.
|
|
||||||
Checks Python version, removes temporary folder, and displays start message.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Set terminal size for win 7
|
|
||||||
if platform.system() == "Windows" and "7" in platform.version():
|
|
||||||
os.system('mode 120, 40')
|
|
||||||
|
|
||||||
|
|
||||||
# Check python version
|
|
||||||
if sys.version_info < (3, 7):
|
|
||||||
console.log("[red]Install python version > 3.7.16")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
# Removing temporary folder
|
|
||||||
start_message()
|
|
||||||
|
|
||||||
|
|
||||||
# Attempting GitHub update
|
|
||||||
"""try:
|
|
||||||
git_update()
|
|
||||||
except Exception as e:
|
|
||||||
console.print(f"[blue]Req github [white]=> [red]Failed: {e}")"""
|
|
||||||
|
|
||||||
|
|
||||||
def run_function(func: Callable[..., None], close_console: bool = False) -> None:
|
def run_function(func: Callable[..., None], close_console: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Run a given function indefinitely or once, depending on the value of close_console.
|
Run a given function indefinitely or once, depending on the value of close_console.
|
||||||
@ -63,9 +35,6 @@ def run_function(func: Callable[..., None], close_console: bool = False) -> None
|
|||||||
func (Callable[..., None]): The function to run.
|
func (Callable[..., None]): The function to run.
|
||||||
close_console (bool, optional): Whether to close the console after running the function once. Defaults to False.
|
close_console (bool, optional): Whether to close the console after running the function once. Defaults to False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
initialize()
|
|
||||||
|
|
||||||
if close_console:
|
if close_console:
|
||||||
while 1:
|
while 1:
|
||||||
func()
|
func()
|
||||||
@ -73,11 +42,40 @@ def run_function(func: Callable[..., None], close_console: bool = False) -> None
|
|||||||
func()
|
func()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def initialize():
|
||||||
|
"""
|
||||||
|
Initialize the application.
|
||||||
|
Checks Python version, removes temporary folder, and displays start message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
start_message()
|
||||||
|
|
||||||
|
# Create logger
|
||||||
log_not = Logger()
|
log_not = Logger()
|
||||||
|
|
||||||
|
# Get system info
|
||||||
get_system_summary()
|
get_system_summary()
|
||||||
|
|
||||||
|
# Set terminal size for win 7
|
||||||
|
if platform.system() == "Windows" and "7" in platform.version():
|
||||||
|
os.system('mode 120, 40')
|
||||||
|
|
||||||
|
# Check python version
|
||||||
|
if sys.version_info < (3, 7):
|
||||||
|
console.log("[red]Install python version > 3.7.16")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Attempting GitHub update
|
||||||
|
try:
|
||||||
|
git_update()
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[blue]Req github [white]=> [red]Failed: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
parser = argparse.ArgumentParser(description='Script to download film and series from the internet.')
|
parser = argparse.ArgumentParser(description='Script to download film and series from the internet.')
|
||||||
parser.add_argument('-sa', '--streaming_anime', action='store_true', help='Check into anime category')
|
parser.add_argument('-sa', '--streaming_anime', action='store_true', help='Check into anime category')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user