mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-05 02:55:25 +00:00
v1.9.2
This commit is contained in:
parent
0bedb1bf05
commit
eb95a4e5ac
@ -120,8 +120,7 @@ class VideoSource:
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
print("\n")
|
||||
console.print(Panel("[red bold]Coming soon", title="Notification", title_align="left", border_style="yellow"))
|
||||
logging.error(f"Failed to get vixcloud contente with error: {e}")
|
||||
sys.exit(0)
|
||||
|
||||
# Parse response with BeautifulSoup to get content
|
||||
|
@ -117,16 +117,29 @@ def validate_selection(list_season_select: List[int], seasons_count: int) -> Lis
|
||||
Returns:
|
||||
- List[int]: Adjusted list of valid season numbers.
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
|
||||
# Remove any seasons greater than the available seasons
|
||||
valid_seasons = [season for season in list_season_select if 1 <= season <= seasons_count]
|
||||
|
||||
# Remove any seasons greater than the available seasons
|
||||
valid_seasons = [season for season in list_season_select if 1 <= season <= seasons_count]
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_seasons:
|
||||
logging.error(f"Invalid selection: The selected seasons are outside the available range (1-{seasons_count}). Please try again.")
|
||||
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_seasons:
|
||||
print()
|
||||
raise ValueError(f"Invalid selection: The selected seasons are outside the available range (1-{seasons_count}).")
|
||||
# Re-prompt for valid input
|
||||
input_seasons = input(f"Enter valid season numbers (1-{seasons_count}): ")
|
||||
list_season_select = list(map(int, input_seasons.split(',')))
|
||||
continue # Re-prompt the user if the selection is invalid
|
||||
|
||||
return valid_seasons # Return the valid seasons if the input is correct
|
||||
|
||||
except ValueError:
|
||||
logging.error("Error: Please enter valid integers separated by commas.")
|
||||
|
||||
return valid_seasons
|
||||
# Prompt the user for valid input again
|
||||
input_seasons = input(f"Enter valid season numbers (1-{seasons_count}): ")
|
||||
list_season_select = list(map(int, input_seasons.split(',')))
|
||||
|
||||
|
||||
# --> for episode
|
||||
@ -141,13 +154,26 @@ def validate_episode_selection(list_episode_select: List[int], episodes_count: i
|
||||
Returns:
|
||||
- List[int]: Adjusted list of valid episode numbers.
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
|
||||
# Remove any episodes greater than the available episodes
|
||||
valid_episodes = [episode for episode in list_episode_select if 1 <= episode <= episodes_count]
|
||||
# Remove any episodes greater than the available episodes
|
||||
valid_episodes = [episode for episode in list_episode_select if 1 <= episode <= episodes_count]
|
||||
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_episodes:
|
||||
print()
|
||||
raise ValueError(f"Invalid selection: The selected episodes are outside the available range (1-{episodes_count}).")
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_episodes:
|
||||
logging.error(f"Invalid selection: The selected episodes are outside the available range (1-{episodes_count}). Please try again.")
|
||||
|
||||
return valid_episodes
|
||||
# Re-prompt for valid input
|
||||
input_episodes = input(f"Enter valid episode numbers (1-{episodes_count}): ")
|
||||
list_episode_select = list(map(int, input_episodes.split(',')))
|
||||
continue # Re-prompt the user if the selection is invalid
|
||||
|
||||
return valid_episodes
|
||||
|
||||
except ValueError:
|
||||
logging.error("Error: Please enter valid integers separated by commas.")
|
||||
|
||||
# Prompt the user for valid input again
|
||||
input_episodes = input(f"Enter valid episode numbers (1-{episodes_count}): ")
|
||||
list_episode_select = list(map(int, input_episodes.split(',')))
|
@ -22,7 +22,6 @@ from StreamingCommunity.Util.os import (
|
||||
# Logic class
|
||||
from ...FFmpeg import (
|
||||
print_duration_table,
|
||||
get_video_duration_s,
|
||||
join_video,
|
||||
join_audios,
|
||||
join_subtitle
|
||||
@ -52,6 +51,7 @@ GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
headers_index = config_manager.get_dict('REQUESTS', 'user-agent')
|
||||
m3u8_url_fixer = M3U8_UrlFix()
|
||||
list_MissingTs = []
|
||||
|
||||
|
||||
|
||||
@ -397,7 +397,8 @@ class ContentDownloader:
|
||||
self.expected_real_time = video_m3u8.expected_real_time
|
||||
|
||||
# Download the video streams and print status
|
||||
video_m3u8.download_streams(f"{Colors.MAGENTA}video", "video")
|
||||
info_dw = video_m3u8.download_streams(f"{Colors.MAGENTA}video", "video")
|
||||
list_MissingTs.append(info_dw)
|
||||
|
||||
# Print duration information of the downloaded video
|
||||
#print_duration_table(downloaded_video[0].get('path'))
|
||||
@ -427,7 +428,8 @@ class ContentDownloader:
|
||||
audio_m3u8.get_info()
|
||||
|
||||
# Download the audio segments and print status
|
||||
audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}", "audio")
|
||||
info_dw = audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}", f"audio_{obj_audio.get('language')}")
|
||||
list_MissingTs.append(info_dw)
|
||||
|
||||
# Print duration information of the downloaded audio
|
||||
#print_duration_table(obj_audio.get('path'))
|
||||
@ -838,40 +840,33 @@ class HLS_Downloader:
|
||||
|
||||
# Rename the output file to the desired output filename if it does not already exist
|
||||
if not os.path.exists(self.output_filename):
|
||||
missing_ts = False
|
||||
missing_info = ""
|
||||
|
||||
# Rename the converted file to the specified output filename
|
||||
os.rename(out_path, self.output_filename)
|
||||
|
||||
# Get duration information for the output file
|
||||
end_output_time = print_duration_table(self.output_filename, description=False, return_string=False)
|
||||
|
||||
# Calculate file size and duration for reporting
|
||||
formatted_size = internet_manager.format_file_size(os.path.getsize(self.output_filename))
|
||||
formatted_duration = print_duration_table(self.output_filename, description=False, return_string=True)
|
||||
|
||||
expected_real_seconds = dict_to_seconds(self.content_downloader.expected_real_time)
|
||||
end_output_seconds = dict_to_seconds(end_output_time)
|
||||
|
||||
# Check if the downloaded content is complete based on expected duration
|
||||
if expected_real_seconds is not None:
|
||||
missing_ts = not (expected_real_seconds - 3 <= end_output_seconds <= expected_real_seconds + 3)
|
||||
else:
|
||||
missing_ts = "Undefined"
|
||||
|
||||
# Second check for missing segments
|
||||
if not missing_ts:
|
||||
if get_video_duration_s(self.output_filename) < int(expected_real_seconds) - 5:
|
||||
# Collect info about type missing
|
||||
for item in list_MissingTs:
|
||||
if int(item['nFailed']) >= 1:
|
||||
missing_ts = True
|
||||
missing_info += f"[red]TS Failed: {item['nFailed']} {item['type']} tracks[/red]\n"
|
||||
|
||||
# Prepare the report panel content
|
||||
print("")
|
||||
panel_content = (
|
||||
f"[bold green]Download completed![/bold green]\n"
|
||||
f"[cyan]File size: [bold red]{formatted_size}[/bold red]\n"
|
||||
f"[cyan]Duration: [bold]{formatted_duration}[/bold]\n"
|
||||
f"[cyan]Missing TS: [bold red]{missing_ts}[/bold red]"
|
||||
f"[cyan]Duration: [bold]{formatted_duration}[/bold]"
|
||||
)
|
||||
|
||||
if missing_ts:
|
||||
panel_content += f"\n{missing_info}"
|
||||
|
||||
# Display the download completion message
|
||||
console.print(Panel(
|
||||
panel_content,
|
||||
|
@ -35,7 +35,7 @@ from ...M3U8 import (
|
||||
M3U8_Parser,
|
||||
M3U8_UrlFix
|
||||
)
|
||||
from ...FFmpeg.util import print_duration_table
|
||||
from ...FFmpeg.util import print_duration_table, format_duration
|
||||
from .proxyes import main_test_proxy
|
||||
|
||||
# Config
|
||||
@ -98,6 +98,7 @@ class M3U8_Segments:
|
||||
# OTHER INFO
|
||||
self.info_maxRetry = 0
|
||||
self.info_nRetry = 0
|
||||
self.info_nFailed = 0
|
||||
|
||||
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
|
||||
"""
|
||||
@ -133,6 +134,7 @@ class M3U8_Segments:
|
||||
hex_content = binascii.hexlify(response.content).decode('utf-8')
|
||||
byte_content = bytes.fromhex(hex_content)
|
||||
|
||||
#console.print(f"[cyan]Find key: [red]{hex_content}")
|
||||
return byte_content
|
||||
|
||||
def parse_data(self, m3u8_content: str) -> None:
|
||||
@ -332,6 +334,8 @@ class M3U8_Segments:
|
||||
console.log(f"[red]Final retry failed for segment: {index}")
|
||||
self.queue.put((index, None)) # Marker for failed segment
|
||||
progress_bar.update(1)
|
||||
self.info_nFailed += 1
|
||||
|
||||
#break
|
||||
|
||||
sleep_time = backoff_factor * (2 ** attempt)
|
||||
@ -426,10 +430,10 @@ class M3U8_Segments:
|
||||
AUDIO_WORKERS = DEFAULT_AUDIO_WORKERS
|
||||
|
||||
# Differnt workers for audio and video
|
||||
if "video" == str(type):
|
||||
if "video" in str(type):
|
||||
TQDM_MAX_WORKER = VIDEO_WORKERS
|
||||
|
||||
if "audio" == str(type):
|
||||
if "audio" in str(type):
|
||||
TQDM_MAX_WORKER = AUDIO_WORKERS
|
||||
|
||||
console.print(f"[cyan]Video workers[white]: [green]{VIDEO_WORKERS} [white]| [cyan]Audio workers[white]: [green]{AUDIO_WORKERS}")
|
||||
@ -526,11 +530,6 @@ class M3U8_Segments:
|
||||
if self.download_interrupted:
|
||||
console.log("[red] Download was manually stopped.")
|
||||
|
||||
# Optional: Delete partial download
|
||||
if os.path.exists(self.tmp_file_path):
|
||||
os.remove(self.tmp_file_path)
|
||||
sys.exit(0)
|
||||
|
||||
# Clean up
|
||||
self.stop_event.set()
|
||||
writer_thread.join(timeout=30)
|
||||
@ -549,12 +548,18 @@ class M3U8_Segments:
|
||||
file_size = os.path.getsize(self.tmp_file_path)
|
||||
if file_size == 0:
|
||||
raise Exception("Output file is empty")
|
||||
|
||||
console.print(f"[cyan]Max retry per URL[white]: [green]{self.info_maxRetry}[green] [white]| [cyan]Total retry done[white]: [green]{self.info_nRetry}[green] [white]| [cyan]Duration: {print_duration_table(self.tmp_file_path, None, True)} \n")
|
||||
|
||||
# Get expected time
|
||||
ex_hours, ex_minutes, ex_seconds = format_duration(self.expected_real_time_s)
|
||||
ex_formatted_duration = f"[yellow]{int(ex_hours)}[red]h [yellow]{int(ex_minutes)}[red]m [yellow]{int(ex_seconds)}[red]s"
|
||||
console.print(f"[cyan]Max retry per URL[white]: [green]{self.info_maxRetry}[green] [white]| [cyan]Total retry done[white]: [green]{self.info_nRetry}[green] [white]| [cyan]Missing TS: [red]{self.info_nFailed} [white]| [cyan]Duration: {print_duration_table(self.tmp_file_path, None, True)} [white]| [cyan]Expected duation: {ex_formatted_duration} \n")
|
||||
|
||||
if self.info_nRetry >= len(self.segments) * (1/3.33):
|
||||
console.print(
|
||||
"[yellow]⚠ Warning:[/yellow] Too many retries detected! "
|
||||
"Consider reducing the number of [cyan]workers[/cyan] in the [magenta]config.json[/magenta] file. "
|
||||
"This will impact [bold]performance[/bold]."
|
||||
)
|
||||
)
|
||||
|
||||
# Info to return
|
||||
return {'type': type, 'nFailed': self.info_nFailed}
|
@ -1,4 +1,4 @@
|
||||
# 18.04.24
|
||||
|
||||
from .command import join_video, join_audios, join_subtitle
|
||||
from .util import print_duration_table, get_video_duration_s
|
||||
from .util import print_duration_table, get_video_duration
|
||||
|
@ -79,7 +79,7 @@ def get_video_duration(file_path: str) -> float:
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error get video duration: {e}")
|
||||
logging.error(f"Get video duration error: {e}")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@ -144,7 +144,6 @@ def print_duration_table(file_path: str, description: str = "Duration", return_s
|
||||
- str: The formatted duration string if return_string is True.
|
||||
- dict: A dictionary with keys 'h', 'm', 's' representing hours, minutes, and seconds if return_string is False.
|
||||
"""
|
||||
|
||||
video_duration = get_video_duration(file_path)
|
||||
|
||||
if video_duration is not None:
|
||||
|
@ -1,5 +1,5 @@
|
||||
__title__ = 'StreamingCommunity'
|
||||
__version__ = '1.9.0'
|
||||
__version__ = '1.9.2'
|
||||
__author__ = 'Lovi-0'
|
||||
__description__ = 'A command-line program to download film'
|
||||
__copyright__ = 'Copyright 2024'
|
||||
|
@ -309,9 +309,8 @@ class InternManager():
|
||||
class OsSummary:
|
||||
|
||||
def __init__(self):
|
||||
ffmpeg_path, ffprobe_path = check_ffmpeg()
|
||||
self.ffmpeg_path = ffmpeg_path
|
||||
self.ffprobe_path = ffprobe_path
|
||||
self.ffmpeg_path = None
|
||||
self.ffprobe_path = None
|
||||
|
||||
def get_executable_version(self, command: list):
|
||||
"""
|
||||
@ -454,13 +453,17 @@ class OsSummary:
|
||||
else:
|
||||
command = 'which'
|
||||
|
||||
# Locate ffmpeg and ffprobe
|
||||
# Locate ffmpeg and ffprobe from path enviroment
|
||||
if self.ffmpeg_path != None and "binary" not in self.ffmpeg_path:
|
||||
self.ffmpeg_path = self.check_ffmpeg_location([command, 'ffmpeg'])
|
||||
|
||||
if self.ffprobe_path != None and "binary" not in self.ffprobe_path:
|
||||
self.ffprobe_path = self.check_ffmpeg_location([command, 'ffprobe'])
|
||||
|
||||
# Locate ffmpeg from bin installation
|
||||
if self.ffmpeg_path is None or self.ffprobe_path is None:
|
||||
self.ffmpeg_path, self.ffprobe_path = check_ffmpeg()
|
||||
|
||||
if self.ffmpeg_path is None or self.ffprobe_path is None:
|
||||
console.log("[red]Cant locate ffmpeg or ffprobe")
|
||||
sys.exit(0)
|
||||
@ -501,7 +504,6 @@ class OsSummary:
|
||||
logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
|
||||
|
||||
|
||||
|
||||
# OTHER
|
||||
os_manager = OsManager()
|
||||
internet_manager = InternManager()
|
||||
|
@ -16,7 +16,7 @@ from StreamingCommunity.Util.message import start_message
|
||||
from StreamingCommunity.Util.console import console, msg
|
||||
from StreamingCommunity.Util._jsonConfig import config_manager
|
||||
from StreamingCommunity.Upload.update import update as git_update
|
||||
from StreamingCommunity.Util.os import OsSummary
|
||||
from StreamingCommunity.Util.os import os_summary
|
||||
from StreamingCommunity.Lib.TMBD import tmdb
|
||||
from StreamingCommunity.Util.logger import Logger
|
||||
|
||||
@ -108,7 +108,6 @@ def initialize():
|
||||
start_message()
|
||||
|
||||
# Get system info
|
||||
os_summary = OsSummary()
|
||||
os_summary.get_system_summary()
|
||||
|
||||
# Set terminal size for win 7
|
||||
@ -133,7 +132,7 @@ def initialize():
|
||||
print()
|
||||
tmdb.display_trending_tv_shows()
|
||||
print()
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user