Fix shorter video and small bar, remove ctrl+c.

This commit is contained in:
Ghost 2024-06-01 14:20:50 +02:00
parent 2abfe82815
commit 2960b810cd
13 changed files with 222 additions and 127 deletions

View File

@ -1,5 +1,6 @@
# 01.03.24
import sys
import logging
from urllib.parse import urljoin, urlparse, parse_qs, urlencode, urlunparse
@ -11,6 +12,7 @@ from bs4 import BeautifulSoup
# Internal utilities
from Src.Util.headers import get_headers
from Src.Util.console import console
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
final_url = urlunparse(new_url) # Construct the final URL from the modified parts
return final_url
return final_url

View File

@ -202,7 +202,6 @@ class VideoSource:
logging.error(f"Error getting content: {e}")
raise
def get_playlist(self) -> str:
"""
Get playlist.
@ -240,4 +239,4 @@ class VideoSource:
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
return final_url
return final_url

View File

@ -4,4 +4,7 @@ STREAMING_FOLDER = "streamingcommunity"
MOVIE_FOLDER = "Movie"
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']

View File

@ -40,6 +40,10 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
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
if terminate_flag.is_set():
break

View File

@ -19,14 +19,18 @@ except: pass
from Src.Util._jsonConfig import config_manager
from Src.Util.os import check_file_existence, suppress_output
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 ..M3U8.parser import M3U8_Codec
# Config
DEBUG_MODE = config_manager.get_bool("DEFAULT", "debug")
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")
FFMPEG_DEFAULT_PRESET = config_manager.get("M3U8_CONVERSION", "default_preset")
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)
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
@ -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.")
sys.exit(0)
# Start command
ffmpeg_cmd = ['ffmpeg']
# 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
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'])
vcodec = "libx264"
# Insert input video path
ffmpeg_cmd.extend(['-i', video_path])
# Add output args
if USE_CODECS:
if vcodec: ffmpeg_cmd.extend(['-c:v', vcodec])
if acodec: ffmpeg_cmd.extend(['-c:a', acodec])
if bitrate: ffmpeg_cmd.extend(['-b:a', str(bitrate)])
if USE_CODEC:
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:
ffmpeg_cmd.extend(['-c', 'copy'])
@ -312,12 +331,10 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str =
else:
ffmpeg_cmd.extend(['-preset', 'fast'])
# Overwrite
ffmpeg_cmd += [out_path, "-y"]
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
# Run join
if DEBUG_MODE:
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")
print()
# Check file output
if CHECK_OUTPUT_CONVERSION:
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)
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.
@ -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.")
sys.exit(0)
video_audio_same_duration = check_duration_v_a(video_path, audio_tracks[0].get('path'))
# 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
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:
logging.error(f"Skip audio join: {audio_track.get('path')} dont exist")
# Map the video and audio streams
ffmpeg_cmd.append('-map')
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(f'{i}:a') # Map audio streams from subsequent inputs
# Add output args
if USE_CODECS:
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy'])
if USE_CODEC:
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:
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
ffmpeg_cmd += [out_path, "-y"]
logging.info(f"FFmpeg command: {ffmpeg_cmd}")
# Run join
if DEBUG_MODE:
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")
print()
# Check file output
if CHECK_OUTPUT_CONVERSION:
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'])]
# Add output args
if USE_CODECS:
if USE_CODEC:
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', 'mov_text'])
else:
ffmpeg_cmd.extend(['-c', 'copy', '-c:s', 'mov_text'])

View File

@ -92,7 +92,7 @@ def format_duration(seconds: float) -> Tuple[int, int, int]:
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.
@ -104,7 +104,10 @@ def print_duration_table(file_path: str) -> None:
if video_duration is not None:
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):
@ -210,3 +213,24 @@ def check_ffmpeg_input(input_file):
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
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

View File

@ -178,7 +178,7 @@ class Downloader():
# Check if there is some audios, else disable download
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:
console.log("[red]Cant find a list of audios")
@ -191,7 +191,7 @@ class Downloader():
# Check if there is some subtitles, else disable download
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:
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}")
# 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\...
if "http" not in self.m3u8_index:
@ -219,7 +219,7 @@ class Downloader():
# Check if a valid HTTPS URL is obtained
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:
logging.error("[download_m3u8] Can't find a valid m3u8 index")
raise
@ -229,7 +229,8 @@ class Downloader():
logging.info(f"Find codec: {self.codec}")
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):
"""
@ -263,6 +264,9 @@ class Downloader():
# Download the video segments
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:
console.log("[cyan]Video [red]already exists.")
@ -309,6 +313,9 @@ class Downloader():
# Download the audio segments
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:
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
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))
# Wait for all downloads to finish
for future in futures:
future.result()
def __join_video__(self, vcodec = 'copy') -> str:
def __join_video__(self) -> str:
"""
Join downloaded video segments into a single video file.
@ -397,10 +404,9 @@ class Downloader():
join_video(
video_path = self.downloaded_video[0].get('path'),
out_path = path_join_video,
vcodec = vcodec
codec = self.codec
)
print_duration_table(path_join_video)
return path_join_video
def __join_video_audio__(self) -> str:
@ -420,10 +426,10 @@ class Downloader():
join_audios(
video_path = self.downloaded_video[0].get('path'),
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
def __join_video_subtitles__(self, input_path: str) -> str:
@ -449,7 +455,6 @@ class Downloader():
path_join_video_subtitle
)
print_duration_table(path_join_video_subtitle)
return path_join_video_subtitle
def __clean__(self, out_path: str) -> None:
@ -473,7 +478,11 @@ class Downloader():
os.rename(out_path, self.output_filename)
# 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_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_audio: bool = (len(self.downloaded_audio) > 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

View File

@ -31,7 +31,6 @@ from ..M3U8 import (
M3U8_UrlFix
)
# Config
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')
@ -122,7 +121,7 @@ class M3U8_Segments:
m3u8_parser = M3U8_Parser()
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
if m3u8_parser.keys is not None:
@ -166,6 +165,7 @@ class M3U8_Segments:
# Update segments for estimator
self.class_ts_estimator.total_segments = len(self.segments)
logging.info(f"fSegmnets to donwload: [{len(self.segments)}]")
def get_info(self) -> None:
"""
@ -220,12 +220,13 @@ class M3U8_Segments:
# Generate new user agent
headers_segments['user-agent'] = get_headers()
logging.info(f"Make request to get segmenet: [{index} - {len(self.segments)}]")
try:
# Make request and calculate time duration
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
if response.ok:
@ -267,7 +268,7 @@ class M3U8_Segments:
while not stop_event.is_set() or not self.segment_queue.empty():
with self.condition:
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():
break
@ -311,16 +312,6 @@ class M3U8_Segments:
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:
# Start a separate thread to write segments to the file
@ -330,24 +321,13 @@ class M3U8_Segments:
# Delay the start of each worker
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
executor.submit(self.make_requests_stream, segment_url, index, stop_event, progress_bar)
# 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
with self.condition:
self.condition.notify_all() # Wake up the writer thread if it's waiting
writer_thread.join() # Wait for the writer thread to finish

View File

@ -48,10 +48,6 @@ RESOLUTIONS = [
class M3U8_Codec:
"""
Represents codec information for an M3U8 playlist.
"""
def __init__(self, bandwidth, codecs):
"""
Initializes the M3U8Codec object with the provided parameters.
@ -64,20 +60,23 @@ class M3U8_Codec:
self.codecs = codecs
self.audio_codec = None
self.video_codec = None
self.video_codec_name = None
self.audio_codec_name = None
self.extract_codecs()
self.parse_codecs()
self.calculate_bitrates()
def extract_codecs(self):
"""
Parses the codecs information to extract audio and video codecs.
Extracted codecs are set as attributes: audio_codec and video_codec.
"""
# Split the codecs string by comma
try:
# Split the codecs string by comma
codecs_list = self.codecs.split(',')
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
for codec in codecs_list:
@ -87,7 +86,6 @@ class M3U8_Codec:
self.audio_codec = codec
def convert_video_codec(self, video_codec_identifier) -> str:
"""
Convert video codec identifier to codec name.
@ -97,6 +95,9 @@ class M3U8_Codec:
Returns:
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
codec_type = video_codec_identifier.split('.')[0]
@ -107,13 +108,11 @@ class M3U8_Codec:
if codec_name:
return codec_name
else:
logging.warning(f"No corresponding video codec found for {video_codec_identifier}. Using default codec libx264.")
return "libx264" # Default
def convert_audio_codec(self, audio_codec_identifier) -> str:
return "libx264" # Default
def convert_audio_codec(self, audio_codec_identifier) -> str:
"""
Convert audio codec identifier to codec name.
@ -123,6 +122,9 @@ class M3U8_Codec:
Returns:
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
codec_type = audio_codec_identifier.split('.')[0]
@ -133,25 +135,33 @@ class M3U8_Codec:
if codec_name:
return codec_name
else:
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):
"""
Parse video and audio codecs.
This method updates `video_codec_name` and `audio_codec_name` attributes.
"""
self.video_codec_name = self.convert_video_codec(self.video_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:

View File

@ -189,7 +189,7 @@ class ManageRequests:
timeout: float = HTTP_TIMEOUT,
retries: int = HTTP_RETRIES,
params: Optional[Dict[str, str]] = None,
verify_ssl: bool = True,
verify: bool = True,
auth: Optional[tuple] = None,
proxy: Optional[str] = None,
cookies: Optional[Dict[str, str]] = None,
@ -206,7 +206,7 @@ class ManageRequests:
- 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.
- 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.
- 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.
@ -218,7 +218,7 @@ class ManageRequests:
self.timeout = timeout
self.retries = retries
self.params = params
self.verify_ssl = verify_ssl
self.verify_ssl = verify
self.auth = auth
self.proxy = proxy
self.cookies = cookies

View File

@ -16,6 +16,7 @@ import platform
import importlib
import subprocess
import contextlib
import urllib.request
import importlib.metadata
from typing import List
@ -403,6 +404,19 @@ def convert_to_hex(bytes_data: bytes) -> str:
# --> 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):
try:
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"
def get_system_summary():
check_internet()
console.print("[bold blue]System Summary[/bold blue][white]:")
# Python version and platform

View File

@ -32,7 +32,10 @@
},
"M3U8_CONVERSION": {
"use_codec": false,
"use_gpu": false,
"use_vcodec": true,
"use_acodec": true,
"use_bitrate": true,
"use_gpu": true,
"default_preset": "ultrafast",
"check_output_after_ffmpeg": false
},

64
run.py
View File

@ -27,34 +27,6 @@ from Src.Api.Altadefinizione import main_film as altadefinizione_film
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:
"""
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.
close_console (bool, optional): Whether to close the console after running the function once. Defaults to False.
"""
initialize()
if close_console:
while 1:
func()
@ -73,11 +42,40 @@ def run_function(func: Callable[..., None], close_console: bool = False) -> None
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()
# Get system info
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
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')