mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-07 12:05:35 +00:00
Add RRC
This commit is contained in:
parent
1ede152923
commit
f7aee0e922
@ -61,8 +61,8 @@ def download_film(select_title: MediaItem) -> str:
|
|||||||
|
|
||||||
# Download the film using the m3u8 playlist, and output filename
|
# Download the film using the m3u8 playlist, and output filename
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, title_name)
|
output_path=os.path.join(mp4_path, title_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
if TELEGRAM_BOT:
|
if TELEGRAM_BOT:
|
||||||
|
@ -49,8 +49,8 @@ def download_film(select_title: MediaItem) -> str:
|
|||||||
|
|
||||||
# Download the film using the m3u8 playlist, and output filename
|
# Download the film using the m3u8 playlist, and output filename
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, title_name)
|
output_path=os.path.join(mp4_path, title_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
if "error" in r_proc.keys():
|
if "error" in r_proc.keys():
|
||||||
|
@ -58,8 +58,8 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|||||||
|
|
||||||
# Download the film using the m3u8 playlist, and output filename
|
# Download the film using the m3u8 playlist, and output filename
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, mp4_name)
|
output_path=os.path.join(mp4_path, mp4_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return r_proc['path']
|
return r_proc['path'], r_proc['stopped']
|
||||||
|
|
||||||
|
|
||||||
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False) -> None:
|
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False) -> None:
|
||||||
@ -91,7 +91,11 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
|
|||||||
|
|
||||||
# Download all episodes without asking
|
# Download all episodes without asking
|
||||||
for i_episode in range(1, episodes_count + 1):
|
for i_episode in range(1, episodes_count + 1):
|
||||||
download_video(index_season_selected, i_episode, scape_info_serie)
|
path, stopped = download_video(index_season_selected, i_episode, scape_info_serie)
|
||||||
|
|
||||||
|
if stopped:
|
||||||
|
break
|
||||||
|
|
||||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -107,11 +111,11 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Download selected episodes
|
# Download selected episodes
|
||||||
stopped = bool(False)
|
|
||||||
for i_episode in list_episode_select:
|
for i_episode in list_episode_select:
|
||||||
|
path, stopped = download_video(index_season_selected, i_episode, scape_info_serie)
|
||||||
|
|
||||||
if stopped:
|
if stopped:
|
||||||
break
|
break
|
||||||
download_video(index_season_selected, i_episode, scape_info_serie)
|
|
||||||
|
|
||||||
|
|
||||||
def download_series(dict_serie: MediaItem) -> None:
|
def download_series(dict_serie: MediaItem) -> None:
|
||||||
|
@ -77,8 +77,8 @@ def download_film(movie_details: Json_film) -> str:
|
|||||||
|
|
||||||
# Download the film using the m3u8 playlist, and output filename
|
# Download the film using the m3u8 playlist, and output filename
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, title_name)
|
output_path=os.path.join(mp4_path, title_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
if "error" in r_proc.keys():
|
if "error" in r_proc.keys():
|
||||||
|
@ -66,8 +66,8 @@ def download_film(select_title: MediaItem) -> str:
|
|||||||
|
|
||||||
# Download the film using the m3u8 playlist, and output filename
|
# Download the film using the m3u8 playlist, and output filename
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, title_name)
|
output_path=os.path.join(mp4_path, title_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
if TELEGRAM_BOT:
|
if TELEGRAM_BOT:
|
||||||
|
@ -72,8 +72,8 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|||||||
|
|
||||||
# Download the episode
|
# Download the episode
|
||||||
r_proc = HLS_Downloader(
|
r_proc = HLS_Downloader(
|
||||||
m3u8_playlist=master_playlist,
|
m3u8_url=master_playlist,
|
||||||
output_filename=os.path.join(mp4_path, mp4_name)
|
output_path=os.path.join(mp4_path, mp4_name)
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
if "error" in r_proc.keys():
|
if "error" in r_proc.keys():
|
||||||
@ -82,7 +82,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return r_proc['path']
|
return r_proc['path'], r_proc['stopped']
|
||||||
|
|
||||||
def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource, download_all: bool = False) -> None:
|
def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource, download_all: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
@ -105,7 +105,11 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide
|
|||||||
|
|
||||||
# Download all episodes without asking
|
# Download all episodes without asking
|
||||||
for i_episode in range(1, episodes_count + 1):
|
for i_episode in range(1, episodes_count + 1):
|
||||||
download_video(index_season_selected, i_episode, scrape_serie, video_source)
|
path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
|
||||||
|
|
||||||
|
if stopped:
|
||||||
|
break
|
||||||
|
|
||||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -122,7 +126,10 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide
|
|||||||
|
|
||||||
# Download selected episodes if not stopped
|
# Download selected episodes if not stopped
|
||||||
for i_episode in list_episode_select:
|
for i_episode in list_episode_select:
|
||||||
download_video(index_season_selected, i_episode, scrape_serie, video_source)[1]
|
path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
|
||||||
|
|
||||||
|
if stopped:
|
||||||
|
break
|
||||||
|
|
||||||
def download_series(select_season: MediaItem, version: str) -> None:
|
def download_series(select_season: MediaItem, version: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -72,6 +72,7 @@ class HLSClient:
|
|||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.content if return_content else response.text
|
return response.content if return_content else response.text
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Attempt {attempt+1} failed: {str(e)}")
|
logging.error(f"Attempt {attempt+1} failed: {str(e)}")
|
||||||
time.sleep(1.5 ** attempt)
|
time.sleep(1.5 ** attempt)
|
||||||
@ -100,8 +101,10 @@ class PathManager:
|
|||||||
root = config_manager.get('DEFAULT', 'root_path')
|
root = config_manager.get('DEFAULT', 'root_path')
|
||||||
hash_name = compute_sha1_hash(self.m3u8_url) + ".mp4"
|
hash_name = compute_sha1_hash(self.m3u8_url) + ".mp4"
|
||||||
return os.path.join(root, "undefined", hash_name)
|
return os.path.join(root, "undefined", hash_name)
|
||||||
|
|
||||||
if not path.endswith(".mp4"):
|
if not path.endswith(".mp4"):
|
||||||
path += ".mp4"
|
path += ".mp4"
|
||||||
|
|
||||||
return os_manager.get_sanitize_path(path)
|
return os_manager.get_sanitize_path(path)
|
||||||
|
|
||||||
def setup_directories(self):
|
def setup_directories(self):
|
||||||
@ -112,7 +115,6 @@ class PathManager:
|
|||||||
|
|
||||||
def move_final_file(self, final_file: str):
|
def move_final_file(self, final_file: str):
|
||||||
"""Moves the final merged file to the desired output location."""
|
"""Moves the final merged file to the desired output location."""
|
||||||
os.makedirs(os.path.dirname(self.output_path), exist_ok=True)
|
|
||||||
if os.path.exists(self.output_path):
|
if os.path.exists(self.output_path):
|
||||||
os.remove(self.output_path)
|
os.remove(self.output_path)
|
||||||
shutil.move(final_file, self.output_path)
|
shutil.move(final_file, self.output_path)
|
||||||
@ -144,6 +146,7 @@ class M3U8Manager:
|
|||||||
content = self.client.request(self.m3u8_url)
|
content = self.client.request(self.m3u8_url)
|
||||||
if not content:
|
if not content:
|
||||||
raise ValueError("Failed to fetch M3U8 content")
|
raise ValueError("Failed to fetch M3U8 content")
|
||||||
|
|
||||||
self.parser.parse_data(uri=self.m3u8_url, raw_content=content)
|
self.parser.parse_data(uri=self.m3u8_url, raw_content=content)
|
||||||
self.url_fixer.set_playlist(self.m3u8_url)
|
self.url_fixer.set_playlist(self.m3u8_url)
|
||||||
self.is_master = self.parser.is_master_playlist
|
self.is_master = self.parser.is_master_playlist
|
||||||
@ -245,49 +248,78 @@ class DownloadManager:
|
|||||||
self.client = client
|
self.client = client
|
||||||
self.url_fixer = url_fixer
|
self.url_fixer = url_fixer
|
||||||
self.missing_segments = []
|
self.missing_segments = []
|
||||||
|
self.stopped = False
|
||||||
|
|
||||||
def download_video(self, video_url: str):
|
def download_video(self, video_url: str):
|
||||||
"""Downloads video segments from the M3U8 playlist."""
|
"""Downloads video segments from the M3U8 playlist."""
|
||||||
video_full_url = self.url_fixer.generate_full_url(video_url)
|
video_full_url = self.url_fixer.generate_full_url(video_url)
|
||||||
video_tmp_dir = os.path.join(self.temp_dir, 'video')
|
video_tmp_dir = os.path.join(self.temp_dir, 'video')
|
||||||
|
|
||||||
downloader = M3U8_Segments(url=video_full_url, tmp_folder=video_tmp_dir)
|
downloader = M3U8_Segments(url=video_full_url, tmp_folder=video_tmp_dir)
|
||||||
result = downloader.download_streams("Video", "video")
|
result = downloader.download_streams("Video", "video")
|
||||||
self.missing_segments.append(result)
|
self.missing_segments.append(result)
|
||||||
|
|
||||||
|
if result.get('stopped', False):
|
||||||
|
self.stopped = True
|
||||||
|
return self.stopped
|
||||||
|
|
||||||
def download_audio(self, audio: Dict):
|
def download_audio(self, audio: Dict):
|
||||||
"""Downloads audio segments for a specific language track."""
|
"""Downloads audio segments for a specific language track."""
|
||||||
|
if self.stopped:
|
||||||
|
return True
|
||||||
|
|
||||||
audio_full_url = self.url_fixer.generate_full_url(audio['uri'])
|
audio_full_url = self.url_fixer.generate_full_url(audio['uri'])
|
||||||
audio_tmp_dir = os.path.join(self.temp_dir, 'audio', audio['language'])
|
audio_tmp_dir = os.path.join(self.temp_dir, 'audio', audio['language'])
|
||||||
|
|
||||||
downloader = M3U8_Segments(url=audio_full_url, tmp_folder=audio_tmp_dir)
|
downloader = M3U8_Segments(url=audio_full_url, tmp_folder=audio_tmp_dir)
|
||||||
result = downloader.download_streams(f"Audio {audio['language']}", "audio")
|
result = downloader.download_streams(f"Audio {audio['language']}", "audio")
|
||||||
self.missing_segments.append(result)
|
self.missing_segments.append(result)
|
||||||
|
|
||||||
|
if result.get('stopped', False):
|
||||||
|
self.stopped = True
|
||||||
|
return self.stopped
|
||||||
|
|
||||||
def download_subtitle(self, sub: Dict):
|
def download_subtitle(self, sub: Dict):
|
||||||
"""Downloads and saves subtitle file for a specific language."""
|
"""Downloads and saves subtitle file for a specific language."""
|
||||||
|
if self.stopped:
|
||||||
|
return True
|
||||||
|
|
||||||
content = self.client.request(sub['uri'])
|
content = self.client.request(sub['uri'])
|
||||||
if content:
|
if content:
|
||||||
sub_path = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
|
sub_path = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
|
||||||
with open(sub_path, 'w', encoding='utf-8') as f:
|
with open(sub_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
|
return self.stopped
|
||||||
|
|
||||||
def download_all(self, video_url: str, audio_streams: List[Dict], sub_streams: List[Dict]):
|
def download_all(self, video_url: str, audio_streams: List[Dict], sub_streams: List[Dict]):
|
||||||
"""
|
"""
|
||||||
Downloads all selected streams (video, audio, subtitles).
|
Downloads all selected streams (video, audio, subtitles).
|
||||||
Skips already downloaded content to support resume functionality.
|
|
||||||
"""
|
"""
|
||||||
video_file = os.path.join(self.temp_dir, 'video', '0.ts')
|
video_file = os.path.join(self.temp_dir, 'video', '0.ts')
|
||||||
if not os.path.exists(video_file):
|
if not os.path.exists(video_file):
|
||||||
self.download_video(video_url)
|
if self.download_video(video_url):
|
||||||
|
return True
|
||||||
|
|
||||||
for audio in audio_streams:
|
for audio in audio_streams:
|
||||||
|
if self.stopped:
|
||||||
|
break
|
||||||
|
|
||||||
audio_file = os.path.join(self.temp_dir, 'audio', audio['language'], '0.ts')
|
audio_file = os.path.join(self.temp_dir, 'audio', audio['language'], '0.ts')
|
||||||
if not os.path.exists(audio_file):
|
if not os.path.exists(audio_file):
|
||||||
self.download_audio(audio)
|
if self.download_audio(audio):
|
||||||
|
return True
|
||||||
|
|
||||||
for sub in sub_streams:
|
for sub in sub_streams:
|
||||||
|
if self.stopped:
|
||||||
|
break
|
||||||
|
|
||||||
sub_file = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
|
sub_file = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
|
||||||
if not os.path.exists(sub_file):
|
if not os.path.exists(sub_file):
|
||||||
self.download_subtitle(sub)
|
if self.download_subtitle(sub):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return self.stopped
|
||||||
|
|
||||||
|
|
||||||
class MergeManager:
|
class MergeManager:
|
||||||
@ -324,12 +356,14 @@ class MergeManager:
|
|||||||
out_path=os.path.join(self.temp_dir, 'video.mp4'),
|
out_path=os.path.join(self.temp_dir, 'video.mp4'),
|
||||||
codec=self.parser.codec
|
codec=self.parser.codec
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if MERGE_AUDIO and self.audio_streams:
|
if MERGE_AUDIO and self.audio_streams:
|
||||||
audio_tracks = [{
|
audio_tracks = [{
|
||||||
'path': os.path.join(self.temp_dir, 'audio', a['language'], '0.ts'),
|
'path': os.path.join(self.temp_dir, 'audio', a['language'], '0.ts'),
|
||||||
'name': a['language']
|
'name': a['language']
|
||||||
} for a in self.audio_streams]
|
} for a in self.audio_streams]
|
||||||
|
|
||||||
merged_audio_path = os.path.join(self.temp_dir, 'merged_audio.mp4')
|
merged_audio_path = os.path.join(self.temp_dir, 'merged_audio.mp4')
|
||||||
merged_file = join_audios(
|
merged_file = join_audios(
|
||||||
video_path=video_file,
|
video_path=video_file,
|
||||||
@ -337,17 +371,20 @@ class MergeManager:
|
|||||||
out_path=merged_audio_path,
|
out_path=merged_audio_path,
|
||||||
codec=self.parser.codec
|
codec=self.parser.codec
|
||||||
)
|
)
|
||||||
|
|
||||||
if MERGE_SUBTITLE and self.sub_streams:
|
if MERGE_SUBTITLE and self.sub_streams:
|
||||||
sub_tracks = [{
|
sub_tracks = [{
|
||||||
'path': os.path.join(self.temp_dir, 'subs', f"{s['language']}.vtt"),
|
'path': os.path.join(self.temp_dir, 'subs', f"{s['language']}.vtt"),
|
||||||
'language': s['language']
|
'language': s['language']
|
||||||
} for s in self.sub_streams]
|
} for s in self.sub_streams]
|
||||||
|
|
||||||
merged_subs_path = os.path.join(self.temp_dir, 'final.mp4')
|
merged_subs_path = os.path.join(self.temp_dir, 'final.mp4')
|
||||||
merged_file = join_subtitle(
|
merged_file = join_subtitle(
|
||||||
video_path=merged_file,
|
video_path=merged_file,
|
||||||
subtitles_list=sub_tracks,
|
subtitles_list=sub_tracks,
|
||||||
out_path=merged_subs_path
|
out_path=merged_subs_path
|
||||||
)
|
)
|
||||||
|
|
||||||
return merged_file
|
return merged_file
|
||||||
|
|
||||||
|
|
||||||
@ -382,7 +419,8 @@ class HLS_Downloader:
|
|||||||
'path': self.path_manager.output_path,
|
'path': self.path_manager.output_path,
|
||||||
'url': self.m3u8_url,
|
'url': self.m3u8_url,
|
||||||
'is_master': False,
|
'is_master': False,
|
||||||
'error': 'File already exists'
|
'error': 'File already exists',
|
||||||
|
'stopped': False
|
||||||
}
|
}
|
||||||
if TELEGRAM_BOT:
|
if TELEGRAM_BOT:
|
||||||
bot.send_message(response)
|
bot.send_message(response)
|
||||||
@ -400,12 +438,23 @@ class HLS_Downloader:
|
|||||||
client=self.client,
|
client=self.client,
|
||||||
url_fixer=self.m3u8_manager.url_fixer
|
url_fixer=self.m3u8_manager.url_fixer
|
||||||
)
|
)
|
||||||
self.download_manager.download_all(
|
|
||||||
|
# Check if download was stopped
|
||||||
|
download_stopped = self.download_manager.download_all(
|
||||||
video_url=self.m3u8_manager.video_url,
|
video_url=self.m3u8_manager.video_url,
|
||||||
audio_streams=self.m3u8_manager.audio_streams,
|
audio_streams=self.m3u8_manager.audio_streams,
|
||||||
sub_streams=self.m3u8_manager.sub_streams
|
sub_streams=self.m3u8_manager.sub_streams
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if download_stopped:
|
||||||
|
return {
|
||||||
|
'path': None,
|
||||||
|
'url': self.m3u8_url,
|
||||||
|
'is_master': self.m3u8_manager.is_master,
|
||||||
|
'error': 'Download stopped by user',
|
||||||
|
'stopped': True
|
||||||
|
}
|
||||||
|
|
||||||
self.merge_manager = MergeManager(
|
self.merge_manager = MergeManager(
|
||||||
temp_dir=self.path_manager.temp_dir,
|
temp_dir=self.path_manager.temp_dir,
|
||||||
parser=self.m3u8_manager.parser,
|
parser=self.m3u8_manager.parser,
|
||||||
@ -422,18 +471,21 @@ class HLS_Downloader:
|
|||||||
return {
|
return {
|
||||||
'path': self.path_manager.output_path,
|
'path': self.path_manager.output_path,
|
||||||
'url': self.m3u8_url,
|
'url': self.m3u8_url,
|
||||||
'is_master': self.m3u8_manager.is_master
|
'is_master': self.m3u8_manager.is_master,
|
||||||
|
'stopped': False
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
console.print(f"[red]Download failed: {error_msg}[/red]")
|
console.print(f"[red]Download failed: {error_msg}[/red]")
|
||||||
logging.error("Download error", exc_info=True)
|
logging.error("Download error", exc_info=True)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'path': None,
|
'path': None,
|
||||||
'url': self.m3u8_url,
|
'url': self.m3u8_url,
|
||||||
'is_master': getattr(self.m3u8_manager, 'is_master', None),
|
'is_master': getattr(self.m3u8_manager, 'is_master', None),
|
||||||
'error': error_msg
|
'error': error_msg,
|
||||||
|
'stopped': False
|
||||||
}
|
}
|
||||||
|
|
||||||
def _print_summary(self):
|
def _print_summary(self):
|
||||||
@ -466,6 +518,7 @@ class HLS_Downloader:
|
|||||||
if missing_ts:
|
if missing_ts:
|
||||||
panel_content += f"\n{missing_info}"
|
panel_content += f"\n{missing_info}"
|
||||||
os.rename(self.path_manager.output_path, self.path_manager.output_path.replace(".mp4", "_failed.mp4"))
|
os.rename(self.path_manager.output_path, self.path_manager.output_path.replace(".mp4", "_failed.mp4"))
|
||||||
|
|
||||||
console.print(Panel(
|
console.print(Panel(
|
||||||
panel_content,
|
panel_content,
|
||||||
title=f"{os.path.basename(self.path_manager.output_path.replace('.mp4', ''))}",
|
title=f"{os.path.basename(self.path_manager.output_path.replace('.mp4', ''))}",
|
||||||
|
@ -47,7 +47,6 @@ PROXY_START_MAX = config_manager.get_float('REQUESTS', 'proxy_start_max')
|
|||||||
DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
|
DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
|
||||||
DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
|
DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
|
||||||
MAX_TIMEOOUT = config_manager.get_int("REQUESTS", "timeout")
|
MAX_TIMEOOUT = config_manager.get_int("REQUESTS", "timeout")
|
||||||
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ class M3U8_Segments:
|
|||||||
|
|
||||||
# Util class
|
# Util class
|
||||||
self.decryption: M3U8_Decryption = None
|
self.decryption: M3U8_Decryption = None
|
||||||
self.class_ts_estimator = M3U8_Ts_Estimator(0)
|
self.class_ts_estimator = M3U8_Ts_Estimator(0, self)
|
||||||
self.class_url_fixer = M3U8_UrlFix(url)
|
self.class_url_fixer = M3U8_UrlFix(url)
|
||||||
|
|
||||||
# Sync
|
# Sync
|
||||||
@ -88,6 +87,8 @@ class M3U8_Segments:
|
|||||||
self.info_maxRetry = 0
|
self.info_maxRetry = 0
|
||||||
self.info_nRetry = 0
|
self.info_nRetry = 0
|
||||||
self.info_nFailed = 0
|
self.info_nFailed = 0
|
||||||
|
self.active_retries = 0
|
||||||
|
self.active_retries_lock = threading.Lock()
|
||||||
|
|
||||||
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
|
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
|
||||||
key_uri = urljoin(self.url, m3u8_parser.keys.get('uri'))
|
key_uri = urljoin(self.url, m3u8_parser.keys.get('uri'))
|
||||||
@ -232,11 +233,18 @@ class M3U8_Segments:
|
|||||||
self.queue.put((index, None)) # Marker for failed segment
|
self.queue.put((index, None)) # Marker for failed segment
|
||||||
progress_bar.update(1)
|
progress_bar.update(1)
|
||||||
self.info_nFailed += 1
|
self.info_nFailed += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.active_retries_lock:
|
||||||
|
self.active_retries += 1
|
||||||
|
|
||||||
sleep_time = backoff_factor * (2 ** attempt)
|
sleep_time = backoff_factor * (2 ** attempt)
|
||||||
logging.info(f"Retrying segment {index} in {sleep_time} seconds...")
|
logging.info(f"Retrying segment {index} in {sleep_time} seconds...")
|
||||||
time.sleep(sleep_time)
|
time.sleep(sleep_time)
|
||||||
|
|
||||||
|
with self.active_retries_lock:
|
||||||
|
self.active_retries -= 1
|
||||||
|
|
||||||
def write_segments_to_file(self):
|
def write_segments_to_file(self):
|
||||||
"""
|
"""
|
||||||
Writes segments to file with additional verification.
|
Writes segments to file with additional verification.
|
||||||
@ -296,11 +304,7 @@ class M3U8_Segments:
|
|||||||
- description: Description to insert on tqdm bar
|
- description: Description to insert on tqdm bar
|
||||||
- type (str): Type of download: 'video' or 'audio'
|
- type (str): Type of download: 'video' or 'audio'
|
||||||
"""
|
"""
|
||||||
|
self.get_info()
|
||||||
if TELEGRAM_BOT:
|
|
||||||
# Viene usato per lo screen
|
|
||||||
console.log("####")
|
|
||||||
|
|
||||||
self.setup_interrupt_handler()
|
self.setup_interrupt_handler()
|
||||||
|
|
||||||
progress_bar = tqdm(
|
progress_bar = tqdm(
|
||||||
|
@ -110,6 +110,8 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
|||||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
|
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
|
||||||
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str, codec: M3U8_Codec = None):
|
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str, codec: M3U8_Codec = None):
|
||||||
"""
|
"""
|
||||||
@ -204,6 +206,8 @@ 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()
|
||||||
|
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
|
||||||
def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_path: str):
|
def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_path: str):
|
||||||
"""
|
"""
|
||||||
@ -256,3 +260,5 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
|||||||
with suppress_output():
|
with suppress_output():
|
||||||
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
|
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
return out_path
|
@ -22,7 +22,7 @@ TQDM_USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
|
|||||||
|
|
||||||
|
|
||||||
class M3U8_Ts_Estimator:
|
class M3U8_Ts_Estimator:
|
||||||
def __init__(self, total_segments: int):
|
def __init__(self, total_segments: int, segments_instance=None):
|
||||||
"""
|
"""
|
||||||
Initialize the M3U8_Ts_Estimator object.
|
Initialize the M3U8_Ts_Estimator object.
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ class M3U8_Ts_Estimator:
|
|||||||
self.ts_file_sizes = []
|
self.ts_file_sizes = []
|
||||||
self.now_downloaded_size = 0
|
self.now_downloaded_size = 0
|
||||||
self.total_segments = total_segments
|
self.total_segments = total_segments
|
||||||
|
self.segments_instance = segments_instance
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
self.speed = {"upload": "N/A", "download": "N/A"}
|
self.speed = {"upload": "N/A", "download": "N/A"}
|
||||||
|
|
||||||
@ -102,7 +103,6 @@ class M3U8_Ts_Estimator:
|
|||||||
return "Error"
|
return "Error"
|
||||||
|
|
||||||
def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
|
def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
|
||||||
"""Updates the progress bar with download information."""
|
|
||||||
try:
|
try:
|
||||||
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
|
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
|
||||||
|
|
||||||
@ -120,21 +120,25 @@ class M3U8_Ts_Estimator:
|
|||||||
if len(speed_data) >= 2:
|
if len(speed_data) >= 2:
|
||||||
average_internet_speed = speed_data[0]
|
average_internet_speed = speed_data[0]
|
||||||
average_internet_unit = speed_data[1]
|
average_internet_unit = speed_data[1]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
average_internet_speed = "N/A"
|
average_internet_speed = "N/A"
|
||||||
average_internet_unit = ""
|
average_internet_unit = ""
|
||||||
|
|
||||||
|
# Retrieve retry count from segments_instance
|
||||||
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
||||||
progress_str = (
|
progress_str = (
|
||||||
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
||||||
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
|
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
|
||||||
f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit} "
|
||||||
|
f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Retrieve retry count from segments_instance
|
||||||
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
||||||
progress_str = (
|
progress_str = (
|
||||||
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
||||||
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
|
||||||
|
f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
|
||||||
)
|
)
|
||||||
|
|
||||||
progress_counter.set_postfix_str(progress_str)
|
progress_counter.set_postfix_str(progress_str)
|
||||||
|
@ -17,7 +17,7 @@ class RequestManager:
|
|||||||
if not hasattr(self, 'initialized'):
|
if not hasattr(self, 'initialized'):
|
||||||
self.json_file = json_file
|
self.json_file = json_file
|
||||||
self.initialized = True
|
self.initialized = True
|
||||||
self.on_response_callback = None # Aggiungi un campo per il callback
|
self.on_response_callback = None
|
||||||
|
|
||||||
def create_request(self, type: str) -> str:
|
def create_request(self, type: str) -> str:
|
||||||
request_data = {
|
request_data = {
|
||||||
|
@ -6,40 +6,26 @@ import json
|
|||||||
session_data = {}
|
session_data = {}
|
||||||
|
|
||||||
def set_session(value):
|
def set_session(value):
|
||||||
"""
|
|
||||||
Salva lo script_id nella sessione.
|
|
||||||
"""
|
|
||||||
session_data['script_id'] = value
|
session_data['script_id'] = value
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
def get_session():
|
||||||
"""
|
|
||||||
Restituisce lo script_id dalla sessione, o 'unknown' se non presente.
|
|
||||||
"""
|
|
||||||
return session_data.get('script_id', 'unknown')
|
return session_data.get('script_id', 'unknown')
|
||||||
|
|
||||||
|
def updateScriptId(screen_id, titolo):
|
||||||
def update_script_id(screen_id, titolo):
|
|
||||||
"""
|
|
||||||
Aggiorna il titolo di uno script in base allo screen_id.
|
|
||||||
"""
|
|
||||||
json_file = "scripts.json"
|
json_file = "scripts.json"
|
||||||
try:
|
try:
|
||||||
# Apre il file JSON e carica i dati
|
|
||||||
with open(json_file, 'r') as f:
|
with open(json_file, 'r') as f:
|
||||||
scripts_data = json.load(f)
|
scripts_data = json.load(f)
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
scripts_data = []
|
scripts_data = []
|
||||||
|
|
||||||
# Cerca lo script con lo screen_id corrispondente
|
# cerco lo script con lo screen_id
|
||||||
for script in scripts_data:
|
for script in scripts_data:
|
||||||
if script["screen_id"] == screen_id:
|
if script["screen_id"] == screen_id:
|
||||||
|
# se trovo il match, aggiorno il titolo
|
||||||
# Se trovato, aggiorna il titolo
|
|
||||||
script["titolo"] = titolo
|
script["titolo"] = titolo
|
||||||
|
|
||||||
# Salva i dati aggiornati nel file JSON
|
# aggiorno il file json
|
||||||
with open(json_file, 'w') as f:
|
with open(json_file, 'w') as f:
|
||||||
json.dump(scripts_data, f, indent=4)
|
json.dump(scripts_data, f, indent=4)
|
||||||
|
|
||||||
@ -47,26 +33,20 @@ def update_script_id(screen_id, titolo):
|
|||||||
|
|
||||||
print(f"Screen_id {screen_id} non trovato.")
|
print(f"Screen_id {screen_id} non trovato.")
|
||||||
|
|
||||||
|
def deleteScriptId(screen_id):
|
||||||
def delete_script_id(screen_id):
|
|
||||||
"""
|
|
||||||
Elimina uno script in base allo screen_id.
|
|
||||||
"""
|
|
||||||
json_file = "scripts.json"
|
json_file = "scripts.json"
|
||||||
try:
|
try:
|
||||||
with open(json_file, 'r') as f:
|
with open(json_file, 'r') as f:
|
||||||
scripts_data = json.load(f)
|
scripts_data = json.load(f)
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
scripts_data = []
|
scripts_data = []
|
||||||
|
|
||||||
# Cerca lo script con lo screen_id corrispondente
|
|
||||||
for script in scripts_data:
|
for script in scripts_data:
|
||||||
if script["screen_id"] == screen_id:
|
if script["screen_id"] == screen_id:
|
||||||
|
# se trovo il match, elimino lo script
|
||||||
# Se trovato, rimuove lo script
|
|
||||||
scripts_data.remove(script)
|
scripts_data.remove(script)
|
||||||
|
|
||||||
|
# aggiorno il file json
|
||||||
with open(json_file, 'w') as f:
|
with open(json_file, 'w') as f:
|
||||||
json.dump(scripts_data, f, indent=4)
|
json.dump(scripts_data, f, indent=4)
|
||||||
|
|
||||||
|
@ -559,18 +559,3 @@ class TelegramBot:
|
|||||||
|
|
||||||
def get_bot_instance():
|
def get_bot_instance():
|
||||||
return TelegramBot.get_instance()
|
return TelegramBot.get_instance()
|
||||||
|
|
||||||
|
|
||||||
# Esempio di utilizzo
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
# Usa le variabili
|
|
||||||
token = os.getenv("TOKEN_TELEGRAM")
|
|
||||||
authorized_user_id = os.getenv("AUTHORIZED_USER_ID")
|
|
||||||
|
|
||||||
TOKEN = token
|
|
||||||
AUTHORIZED_USER_ID = int(authorized_user_id)
|
|
||||||
|
|
||||||
# Inizializza il bot
|
|
||||||
bot = TelegramBot.init_bot(TOKEN, AUTHORIZED_USER_ID)
|
|
||||||
bot.run()
|
|
@ -25,8 +25,7 @@ def update():
|
|||||||
"""
|
"""
|
||||||
Check for updates on GitHub and display relevant information.
|
Check for updates on GitHub and display relevant information.
|
||||||
"""
|
"""
|
||||||
|
console.print("\n[cyan]→ [green]Checking GitHub version ...")
|
||||||
console.print("[green]Checking GitHub version [white]...")
|
|
||||||
|
|
||||||
# Make the GitHub API requests and handle potential errors
|
# Make the GitHub API requests and handle potential errors
|
||||||
try:
|
try:
|
||||||
@ -59,7 +58,7 @@ def update():
|
|||||||
if str(__version__).replace('v', '') != str(last_version).replace('v', '') :
|
if str(__version__).replace('v', '') != str(last_version).replace('v', '') :
|
||||||
console.print(f"[red]New version available: [yellow]{last_version} \n")
|
console.print(f"[red]New version available: [yellow]{last_version} \n")
|
||||||
else:
|
else:
|
||||||
console.print(f" [yellow]Everything is up to date \n")
|
console.print(f" [red]Everything is up to date \n")
|
||||||
|
|
||||||
console.print(f"[red]{__title__} has been downloaded [yellow]{total_download_count} [red]times, but only [yellow]{percentual_stars}% [red]of users have starred it.\n\
|
console.print(f"[red]{__title__} has been downloaded [yellow]{total_download_count} [red]times, but only [yellow]{percentual_stars}% [red]of users have starred it.\n\
|
||||||
[cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
|
[cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
|
||||||
|
@ -492,7 +492,7 @@ class OsSummary:
|
|||||||
else:
|
else:
|
||||||
logging.info(f"Library: {installed_version}")
|
logging.info(f"Library: {installed_version}")
|
||||||
|
|
||||||
console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
|
#console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
|
||||||
logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
|
logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ from StreamingCommunity.Lib.Downloader import HLS_Downloader
|
|||||||
start_message()
|
start_message()
|
||||||
logger = Logger()
|
logger = Logger()
|
||||||
print("Return: ", HLS_Downloader(
|
print("Return: ", HLS_Downloader(
|
||||||
output_filename="test.mp4",
|
output_path="test.mp4",
|
||||||
m3u8_playlist="https://acdn.ak-stream-videoplatform.sky.it/hls/2024/11/21/968275/master.m3u8"
|
m3u8_url="https://acdn.ak-stream-videoplatform.sky.it/hls/2024/11/21/968275/master.m3u8"
|
||||||
).start())
|
).start())
|
@ -3,7 +3,7 @@
|
|||||||
"debug": false,
|
"debug": false,
|
||||||
"log_file": "app.log",
|
"log_file": "app.log",
|
||||||
"log_to_file": false,
|
"log_to_file": false,
|
||||||
"show_message": false,
|
"show_message": true,
|
||||||
"clean_console": true,
|
"clean_console": true,
|
||||||
"root_path": "Video",
|
"root_path": "Video",
|
||||||
"movie_folder_name": "Movie",
|
"movie_folder_name": "Movie",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"proxy_start_max": 0.5
|
"proxy_start_max": 0.5
|
||||||
},
|
},
|
||||||
"M3U8_DOWNLOAD": {
|
"M3U8_DOWNLOAD": {
|
||||||
"tqdm_delay": 0.15,
|
"tqdm_delay": 0.12,
|
||||||
"default_video_workser": 12,
|
"default_video_workser": 12,
|
||||||
"default_audio_workser": 12,
|
"default_audio_workser": 12,
|
||||||
"merge_audio": true,
|
"merge_audio": true,
|
||||||
|
17
test_run.py
17
test_run.py
@ -2,13 +2,20 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
from StreamingCommunity.run import main
|
from StreamingCommunity.run import main
|
||||||
|
from StreamingCommunity.Util._jsonConfig import config_manager
|
||||||
from StreamingCommunity.TelegramHelp.request_manager import RequestManager
|
from StreamingCommunity.TelegramHelp.request_manager import RequestManager
|
||||||
from StreamingCommunity.TelegramHelp.session import set_session
|
from StreamingCommunity.TelegramHelp.session import set_session
|
||||||
|
|
||||||
# Svuoto il file
|
# Svuoto il file
|
||||||
request_manager = RequestManager()
|
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
||||||
request_manager.clear_file()
|
|
||||||
script_id = sys.argv[1] if len(sys.argv) > 1 else "unknown"
|
|
||||||
|
|
||||||
set_session(script_id)
|
if TELEGRAM_BOT:
|
||||||
main(script_id)
|
request_manager = RequestManager()
|
||||||
|
request_manager.clear_file()
|
||||||
|
script_id = sys.argv[1] if len(sys.argv) > 1 else "unknown"
|
||||||
|
|
||||||
|
set_session(script_id)
|
||||||
|
main(script_id)
|
||||||
|
|
||||||
|
else:
|
||||||
|
main(0)
|
Loading…
x
Reference in New Issue
Block a user