Merge branch 'Lovi-0:main' into main

This commit is contained in:
Francesco Grazioso 2024-06-08 13:04:38 +02:00 committed by GitHub
commit 7be9f7176e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 70 additions and 120 deletions

View File

@ -96,12 +96,12 @@ You can change some behaviors by tweaking the configuration file.
* **timeout**: The timeout value for requests. * **timeout**: The timeout value for requests.
- **Default Value**: `10` - **Default Value**: `10`
* **max_retry**: Maximum number of retries for requests.
- **Default Value**: `3`
* **verify_ssl**: Whether to verify SSL certificates. * **verify_ssl**: Whether to verify SSL certificates.
- **Default Value**: `false` - **Default Value**: `false`
* **proxy**: The proxy to use for requests. (Note: This parameter works only with HTTP and HTTPS protocols.)
- **Example Value**: `[{'protocol': 'http', 'ip': '123.45.67.89', 'port': '8080', 'username': 'your_username', 'password': 'your_password'}, {'protocol': 'https', 'ip': '123.45.67.89', 'port': '8080', 'username': 'your_username', 'password': 'your_password'}]`
</details> </details>
<details> <details>

View File

@ -3,5 +3,3 @@
STREAMING_FOLDER = "streamingcommunity" STREAMING_FOLDER = "streamingcommunity"
MOVIE_FOLDER = "Movie" MOVIE_FOLDER = "Movie"
SERIES_FOLDER = "Serie" SERIES_FOLDER = "Serie"
STATIC_IP_SERVER = ['57.129.7.85', '57.129.7.188', '57.129.7.174', '57.129.4.77', '57.129.16.196', '57.129.16.156', '57.129.16.139', '57.129.16.135', '57.129.13.175', '57.129.13.157', '51.38.112.237', '51.195.107.7', '51.195.107.230', '162.19.255.78', '162.19.255.36', '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

@ -18,7 +18,7 @@ from .Core.Vix_player.player import VideoSource
# Config # Config
ROOT_PATH = config_manager.get('DEFAULT', 'root_path') ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
from .costant import STREAMING_FOLDER, MOVIE_FOLDER, STATIC_IP_SERVER from .costant import STREAMING_FOLDER, MOVIE_FOLDER
# Variable # Variable
@ -59,4 +59,4 @@ def download_film(id_film: str, title_name: str, domain: str):
Downloader( Downloader(
m3u8_playlist = master_playlist, m3u8_playlist = master_playlist,
output_filename = os.path.join(mp4_path, mp4_format) output_filename = os.path.join(mp4_path, mp4_format)
).start(STATIC_IP_SERVER) ).start()

View File

@ -20,7 +20,7 @@ from .Core.Util import manage_selection, map_episode_title
# Config # Config
ROOT_PATH = config_manager.get('DEFAULT', 'root_path') ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
from .costant import STREAMING_FOLDER, SERIES_FOLDER, STATIC_IP_SERVER from .costant import STREAMING_FOLDER, SERIES_FOLDER
# Variable # Variable
@ -96,7 +96,7 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec
Downloader( Downloader(
m3u8_playlist = master_playlist, m3u8_playlist = master_playlist,
output_filename = os.path.join(mp4_path, mp4_name) output_filename = os.path.join(mp4_path, mp4_name)
).start(STATIC_IP_SERVER) ).start()
def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: bool = False) -> None: def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: bool = False) -> None:

View File

@ -232,15 +232,12 @@ class Downloader():
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]))") 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):
""" """
Downloads and manages video segments. Downloads and manages video segments.
This method downloads video segments if necessary and updates This method downloads video segments if necessary and updates
the list of downloaded video segments. the list of downloaded video segments.
Args:
- server_ip (list): A list of IP addresses to use in requests.
""" """
# Construct full path for the video segment directory # Construct full path for the video segment directory
@ -256,7 +253,6 @@ class Downloader():
# Create an instance of M3U8_Segments to handle video segments # Create an instance of M3U8_Segments to handle video segments
video_m3u8 = M3U8_Segments(self.m3u8_index, full_path_video) video_m3u8 = M3U8_Segments(self.m3u8_index, full_path_video)
video_m3u8.add_server_ip(server_ip)
# Get information about the video segments # Get information about the video segments
video_m3u8.get_info() video_m3u8.get_info()
@ -270,15 +266,12 @@ class Downloader():
else: else:
console.log("[cyan]Video [red]already exists.") console.log("[cyan]Video [red]already exists.")
def __donwload_audio__(self, server_ip: list = None): def __donwload_audio__(self):
""" """
Downloads and manages audio segments. Downloads and manages audio segments.
This method iterates over available audio tracks, downloads them if necessary, and updates This method iterates over available audio tracks, downloads them if necessary, and updates
the list of downloaded audio tracks. the list of downloaded audio tracks.
Args:
- server_ip (list): A list of IP addresses to use in requests.
""" """
# Iterate over each available audio track # Iterate over each available audio track
@ -305,7 +298,6 @@ class Downloader():
# If the audio segment directory doesn't exist, download audio segments # If the audio segment directory doesn't exist, download audio segments
audio_m3u8 = M3U8_Segments(obj_audio.get('uri'), full_path_audio) audio_m3u8 = M3U8_Segments(obj_audio.get('uri'), full_path_audio)
audio_m3u8.add_server_ip(server_ip)
# Get information about the audio segments # Get information about the audio segments
audio_m3u8.get_info() audio_m3u8.get_info()
@ -494,12 +486,9 @@ class Downloader():
else: else:
logging.info("Video file converted already exist.") logging.info("Video file converted already exist.")
def start(self, server_ip: list = None) -> None: def start(self) -> None:
""" """
Start the process of fetching, downloading, joining, and cleaning up the video. Start the process of fetching, downloading, joining, and cleaning up the video.
Args:
- server_ip (list): A list of IP addresses to use in requests.
""" """
# Check if file already exist # Check if file already exist
@ -532,9 +521,9 @@ class Downloader():
# Start all download ... # Start all download ...
if DOWNLOAD_VIDEO: if DOWNLOAD_VIDEO:
self.__donwload_video__(server_ip) self.__donwload_video__()
if DOWNLOAD_AUDIO: if DOWNLOAD_AUDIO:
self.__donwload_audio__(server_ip) self.__donwload_audio__()
if DOWNLOAD_SUBTITLE: if DOWNLOAD_SUBTITLE:
self.__download_subtitle__() self.__download_subtitle__()

View File

@ -13,6 +13,7 @@ from urllib.parse import urljoin, urlparse, urlunparse
# External libraries # External libraries
import requests import requests
from requests.exceptions import HTTPError, ConnectionError, Timeout, RequestException
from tqdm import tqdm from tqdm import tqdm
@ -36,16 +37,19 @@ from ..M3U8 import (
import urllib3 import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 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')
REQUEST_VERIFY_SSL = config_manager.get_bool('REQUESTS', 'verify_ssl') REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
PROXY_LIST = config_manager.get_list('REQUESTS', 'proxy')
# Variable # Variable
headers_index = config_manager.get_dict('REQUESTS', 'index') headers_index = config_manager.get_dict('REQUESTS', 'index')
headers_segments = config_manager.get_dict('REQUESTS', 'segments') headers_segments = config_manager.get_dict('REQUESTS', 'segments')
session = requests.Session()
session.verify = config_manager.get_bool('REQUESTS', 'verify_ssl')
class M3U8_Segments: class M3U8_Segments:
@ -58,7 +62,6 @@ class M3U8_Segments:
- tmp_folder (str): The temporary folder to store downloaded segments. - tmp_folder (str): The temporary folder to store downloaded segments.
""" """
self.url = url self.url = url
self.fake_proxy = False
self.tmp_folder = tmp_folder self.tmp_folder = tmp_folder
self.tmp_file_path = os.path.join(self.tmp_folder, "0.ts") self.tmp_file_path = os.path.join(self.tmp_folder, "0.ts")
os.makedirs(self.tmp_folder, exist_ok=True) os.makedirs(self.tmp_folder, exist_ok=True)
@ -73,17 +76,6 @@ class M3U8_Segments:
self.segment_queue = queue.PriorityQueue() # Priority queue to maintain the order of segments self.segment_queue = queue.PriorityQueue() # Priority queue to maintain the order of segments
self.condition = threading.Condition() # Condition variable for thread synchronization self.condition = threading.Condition() # Condition variable for thread synchronization
def add_server_ip(self, list_ip):
"""
Add server IP addresses
Args:
list_ip (list): A list of IP addresses to be added.
"""
if list_ip is not None:
self.fake_proxy = True
self.fake_proxy_ip = list_ip
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes: def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
""" """
Retrieves the encryption key from the M3U8 playlist. Retrieves the encryption key from the M3U8 playlist.
@ -115,41 +107,6 @@ class M3U8_Segments:
logging.info(f"Key: ('hex': {hex_content}, 'byte': {byte_content})") logging.info(f"Key: ('hex': {hex_content}, 'byte': {byte_content})")
return byte_content return byte_content
def __test_ip(self, url_to_test: str):
"""
Tests each proxy IP by sending a request to a corresponding segment URL.
"""
failed_ips = []
for i in range(len(self.fake_proxy_ip)):
try:
response = requests.get(url_to_test, verify=False, retries=0)
if response == None:
logging.error(f"[Work] to make request using: {url_to_test}")
failed_ips.append(i)
except:
# Log the error and add the IP to the list of failed IPs
logging.error(f"[Fail] to make request using IP in this request: {url_to_test}")
failed_ips.append(i)
# Remove the failed IPs from the fake_proxy_ip list
self.fake_proxy_ip = [ip for j, ip in enumerate(self.fake_proxy_ip) if j not in failed_ips]
# Exit the program if 50% requests failed
if len(failed_ips) / 2 > len(self.fake_proxy_ip):
logging.error("All requests with ip failed.")
# Set to not use proxy
self.fake_proxy_ip = None
self.fake_proxy = False
return False
def parse_data(self, m3u8_content: str) -> None: def parse_data(self, m3u8_content: str) -> None:
""" """
Parses the M3U8 content to extract segment information. Parses the M3U8 content to extract segment information.
@ -190,25 +147,6 @@ class M3U8_Segments:
self.segments[i] = self.class_url_fixer.generate_full_url(segment_url) self.segments[i] = self.class_url_fixer.generate_full_url(segment_url)
logging.info(f"Generated new URL: {self.segments[i]}, from: {segment_url}") logging.info(f"Generated new URL: {self.segments[i]}, from: {segment_url}")
# Change IP address of server
if self.fake_proxy:
for i in range(len(self.segments)):
segment_url = self.segments[i]
# Set to not use proxy if 50% failed
if not self.__test_ip(segment_url):
console.log("[red]Cant use proxy switch to normal url.")
self.fake_proxy = False
break
self.segments[i] = self.__gen_proxy__(segment_url, self.segments.index(segment_url))
# Save new playlist of segment
path_m3u8_file = os.path.join(self.tmp_folder, "playlist_fix.m3u8")
with open(path_m3u8_file, "w") as file:
for item in self.segments:
file.write(f"{item}\n")
# 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"Segmnets to donwload: [{len(self.segments)}]") logging.info(f"Segmnets to donwload: [{len(self.segments)}]")
@ -231,28 +169,37 @@ class M3U8_Segments:
# Parse the text from the M3U8 index file # Parse the text from the M3U8 index file
self.parse_data(response.text) self.parse_data(response.text)
def __gen_proxy__(self, url: str, url_index: int) -> str: def get_proxy(self, index):
""" """
Change the IP address of the provided URL based on the given index. Returns the proxy configuration for the given index.
Args: Args:
- url (str): The original URL that needs its IP address replaced. - index (int): The index to select the proxy from the PROXY_LIST.
- url_index (int): The index used to select a new IP address from the list of FAKE_PROXY_IP.
Returns: Returns:
str: The modified URL with the new IP address. - dict: A dictionary containing the proxy scheme and proxy URL.
""" """
if self.fake_proxy: try:
new_ip_address = self.fake_proxy_ip[url_index % len(self.fake_proxy_ip)] # Select the proxy from the list using the index
new_proxy = PROXY_LIST[index % len(PROXY_LIST)]
proxy_scheme = new_proxy["protocol"]
# Parse the original URL and replace the hostname with the new IP address # Construct the proxy URL based on the presence of user and pass keys
parsed_url = urlparse(url)._replace(netloc=new_ip_address) if "user" in new_proxy and "pass" in new_proxy:
proxy_url = f"{proxy_scheme}://{new_proxy['user']}:{new_proxy['pass']}@{new_proxy['ip']}:{new_proxy['port']}"
elif "user" in new_proxy:
proxy_url = f"{proxy_scheme}://{new_proxy['user']}@{new_proxy['ip']}:{new_proxy['port']}"
else:
proxy_url = f"{proxy_scheme}://{new_proxy['ip']}:{new_proxy['port']}"
return urlunparse(parsed_url) logging.info(f"Proxy URL generated: {proxy_url}")
return {proxy_scheme: proxy_url}
else: except KeyError as e:
return url logging.error(f"KeyError: Missing required key {e} in proxy configuration.")
except Exception as e:
logging.error(f"An unexpected error occurred while generating proxy URL: {e}")
def make_requests_stream(self, ts_url: str, index: int, progress_bar: tqdm) -> None: def make_requests_stream(self, ts_url: str, index: int, progress_bar: tqdm) -> None:
""" """
@ -269,9 +216,23 @@ class M3U8_Segments:
try: try:
# Make request and calculate time duration
start_time = time.time() start_time = time.time()
response = requests.get(ts_url, headers=headers_segments, verify=REQUEST_VERIFY_SSL, timeout=15)
# Generate proxy
if len(PROXY_LIST) > 0:
# Make request
proxy = self.get_proxy(index)
response = session.get(ts_url, headers=headers_segments, timeout=REQUEST_TIMEOUT, proxies=proxy)
response.raise_for_status()
else:
# Make request
response = session.get(ts_url, headers=headers_segments, timeout=REQUEST_TIMEOUT)
response.raise_for_status()
# Calculate duration
duration = time.time() - start_time duration = time.time() - start_time
logging.info(f"Make request to get segment: [{index} - {len(self.segments)}] in: {duration}, len data: {len(response.content)}") logging.info(f"Make request to get segment: [{index} - {len(self.segments)}] in: {duration}, len data: {len(response.content)}")
@ -293,8 +254,10 @@ class M3U8_Segments:
else: else:
logging.error(f"Failed to download segment: {ts_url}") logging.error(f"Failed to download segment: {ts_url}")
except (HTTPError, ConnectionError, Timeout, RequestException) as e:
logging.error(f"Request-related exception while downloading segment: {e}")
except Exception as e: except Exception as e:
logging.error(f"Exception while downloading segment: {e}") logging.error(f"An unexpected exception occurred while download segment: {e}")
# Update bar # Update bar
progress_bar.update(1) progress_bar.update(1)

View File

@ -10,11 +10,11 @@
"not_close": false "not_close": false
}, },
"REQUESTS": { "REQUESTS": {
"timeout": 10, "timeout": 5,
"max_retry": 3,
"verify_ssl": false, "verify_ssl": false,
"index": {"user-agent": ""}, "index": {"user-agent": ""},
"segments": { "user-agent": ""} "segments": { "user-agent": ""},
"proxy": []
}, },
"M3U8_DOWNLOAD": { "M3U8_DOWNLOAD": {
"tdqm_workers": 4, "tdqm_workers": 4,