mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-07 12:05:35 +00:00
Improve organization
This commit is contained in:
parent
92c35b489a
commit
bf7153214f
@ -7,7 +7,7 @@
|
|||||||
This repository provide a simple script designed to facilitate the downloading of films and series from a popular streaming community platform. The script allows users to download individual films, entire series, or specific episodes, providing a seamless experience for content consumers.
|
This repository provide a simple script designed to facilitate the downloading of films and series from a popular streaming community platform. The script allows users to download individual films, entire series, or specific episodes, providing a seamless experience for content consumers.
|
||||||
|
|
||||||
## Join us
|
## Join us
|
||||||
You can chat, help improve this repo, or just hang around for some fun in the **Git_StreamingCommunity** Discord [Server](https://discord.com/invite/qP3nsCXV5z)
|
You can chat, help improve this repo, or just hang around for some fun in the **Git_StreamingCommunity** Discord [Server](https://discord.gg/we8n4tfxFs)
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
* [INSTALLATION](#installation)
|
* [INSTALLATION](#installation)
|
||||||
|
@ -21,6 +21,7 @@ class Episode:
|
|||||||
self.created_at: str = data.get('created_at', '')
|
self.created_at: str = data.get('created_at', '')
|
||||||
self.updated_at: str = data.get('updated_at', '')
|
self.updated_at: str = data.get('updated_at', '')
|
||||||
|
|
||||||
|
|
||||||
class EpisodeManager:
|
class EpisodeManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# 03.03.24
|
# 03.03.24
|
||||||
|
|
||||||
# Import
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
class Image:
|
class Image:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from typing import List, Dict, Union
|
from typing import List, Dict, Union
|
||||||
|
|
||||||
|
|
||||||
class Title:
|
class Title:
|
||||||
def __init__(self, title_data: Dict[str, Union[int, str, None]]):
|
def __init__(self, title_data: Dict[str, Union[int, str, None]]):
|
||||||
"""
|
"""
|
||||||
@ -20,6 +21,7 @@ class Title:
|
|||||||
self.updated_at: str = title_data.get('updated_at')
|
self.updated_at: str = title_data.get('updated_at')
|
||||||
self.episodes_count: int = title_data.get('episodes_count')
|
self.episodes_count: int = title_data.get('episodes_count')
|
||||||
|
|
||||||
|
|
||||||
class TitleManager:
|
class TitleManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
# 01.03.24
|
# 01.03.24
|
||||||
|
|
||||||
# Class import
|
|
||||||
from Src.Util.headers import get_headers
|
|
||||||
from .SeriesType import TitleManager
|
|
||||||
from .EpisodeType import EpisodeManager
|
|
||||||
from .WindowType import WindowVideo, WindowParameter
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import binascii
|
import binascii
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
from urllib.parse import urljoin, urlencode, quote
|
from urllib.parse import urljoin, urlencode, quote
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from Src.Util.headers import get_headers
|
||||||
|
from .SeriesType import TitleManager
|
||||||
|
from .EpisodeType import EpisodeManager
|
||||||
|
from .WindowType import WindowVideo, WindowParameter
|
||||||
|
|
||||||
|
|
||||||
class VideoSource:
|
class VideoSource:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
class WindowVideo:
|
class WindowVideo:
|
||||||
def __init__(self, data: Dict[str, Any]):
|
def __init__(self, data: Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
@ -25,6 +26,7 @@ class WindowVideo:
|
|||||||
self.folder_id: int = data.get('folder_id', '')
|
self.folder_id: int = data.get('folder_id', '')
|
||||||
self.created_at_diff: str = data.get('created_at_diff', '')
|
self.created_at_diff: str = data.get('created_at_diff', '')
|
||||||
|
|
||||||
|
|
||||||
class WindowParameter:
|
class WindowParameter:
|
||||||
def __init__(self, data: Dict[str, Any]):
|
def __init__(self, data: Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
# 11.03.24
|
# 11.03.24
|
||||||
|
|
||||||
# Class import
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.console import console, msg
|
from Src.Util.console import console, msg
|
||||||
from Src.Util.config import config_manager
|
from Src.Util.config import config_manager
|
||||||
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
||||||
from Src.Util.message import start_message
|
from Src.Util.message import start_message
|
||||||
from .Class import VideoSource
|
from .Class import VideoSource
|
||||||
|
|
||||||
# General import
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||||
@ -20,6 +24,7 @@ SERIES_FOLDER = config_manager.get('DEFAULT', 'series_folder_name')
|
|||||||
URL_SITE_NAME = config_manager.get('SITE', 'anime_site_name')
|
URL_SITE_NAME = config_manager.get('SITE', 'anime_site_name')
|
||||||
SITE_DOMAIN = config_manager.get('SITE', 'anime_domain')
|
SITE_DOMAIN = config_manager.get('SITE', 'anime_domain')
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
video_source = VideoSource()
|
video_source = VideoSource()
|
||||||
|
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
# 3.12.23 -> 10.12.23
|
# 3.12.23 -> 10.12.23
|
||||||
|
|
||||||
# Class import
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.console import console
|
from Src.Util.console import console
|
||||||
from Src.Util.config import config_manager
|
from Src.Util.config import config_manager
|
||||||
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
||||||
from Src.Util.message import start_message
|
from Src.Util.message import start_message
|
||||||
from .Class import VideoSource
|
from .Class import VideoSource
|
||||||
|
|
||||||
# General import
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||||
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movies_folder_name')
|
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movies_folder_name')
|
||||||
STREAM_SITE_NAME = config_manager.get('SITE', 'streaming_site_name')
|
STREAM_SITE_NAME = config_manager.get('SITE', 'streaming_site_name')
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
video_source = VideoSource()
|
video_source = VideoSource()
|
||||||
video_source.set_url_base_name(STREAM_SITE_NAME)
|
video_source.set_url_base_name(STREAM_SITE_NAME)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
# 3.12.23 -> 10.12.23
|
# 3.12.23 -> 10.12.23
|
||||||
|
|
||||||
# Class import
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.console import console, msg
|
from Src.Util.console import console, msg
|
||||||
from Src.Util.config import config_manager
|
from Src.Util.config import config_manager
|
||||||
from Src.Util.table import TVShowManager
|
from Src.Util.table import TVShowManager
|
||||||
@ -9,16 +14,13 @@ from Src.Lib.Unidecode import transliterate
|
|||||||
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
from Src.Lib.FFmpeg.my_m3u8 import Downloader
|
||||||
from .Class import VideoSource
|
from .Class import VideoSource
|
||||||
|
|
||||||
# General import
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||||
SERIES_FOLDER = config_manager.get('DEFAULT', 'series_folder_name')
|
SERIES_FOLDER = config_manager.get('DEFAULT', 'series_folder_name')
|
||||||
STREAM_SITE_NAME = config_manager.get('SITE', 'streaming_site_name')
|
STREAM_SITE_NAME = config_manager.get('SITE', 'streaming_site_name')
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
video_source = VideoSource()
|
video_source = VideoSource()
|
||||||
video_source.set_url_base_name(STREAM_SITE_NAME)
|
video_source.set_url_base_name(STREAM_SITE_NAME)
|
||||||
@ -56,6 +58,7 @@ def manage_selection(cmd_insert: str, max_count: int) -> list[int]:
|
|||||||
# Return list of selected seasons)
|
# Return list of selected seasons)
|
||||||
return list_season_select
|
return list_season_select
|
||||||
|
|
||||||
|
|
||||||
def display_episodes_list() -> str:
|
def display_episodes_list() -> str:
|
||||||
"""
|
"""
|
||||||
Display episodes list and handle user input.
|
Display episodes list and handle user input.
|
||||||
@ -133,6 +136,7 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec
|
|||||||
logging.error(f"(donwload_video) Error downloading film: {e}")
|
logging.error(f"(donwload_video) Error downloading film: {e}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
"""
|
"""
|
||||||
Download all episodes of a season.
|
Download all episodes of a season.
|
||||||
@ -174,6 +178,7 @@ def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: boo
|
|||||||
for i_episode in list_episode_select:
|
for i_episode in list_episode_select:
|
||||||
donwload_video(tv_name, index_season_selected, i_episode)
|
donwload_video(tv_name, index_season_selected, i_episode)
|
||||||
|
|
||||||
|
|
||||||
def download_series(tv_id: str, tv_name: str, version: str, domain: str) -> None:
|
def download_series(tv_id: str, tv_name: str, version: str, domain: str) -> None:
|
||||||
"""
|
"""
|
||||||
Download all episodes of a TV series.
|
Download all episodes of a TV series.
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
# 10.12.23
|
# 10.12.23
|
||||||
|
|
||||||
# Class import
|
import sys
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.table import TVShowManager
|
from Src.Util.table import TVShowManager
|
||||||
from Src.Util.headers import get_headers
|
from Src.Util.headers import get_headers
|
||||||
from Src.Util.console import console
|
from Src.Util.console import console
|
||||||
from Src.Util.config import config_manager
|
from Src.Util.config import config_manager
|
||||||
from .Class import MediaManager, MediaItem
|
from .Class import MediaManager, MediaItem
|
||||||
|
|
||||||
# General import
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
GET_TITLES_OF_MOMENT = config_manager.get_bool('DEFAULT', 'get_moment_title')
|
GET_TITLES_OF_MOMENT = config_manager.get_bool('DEFAULT', 'get_moment_title')
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
media_search_manager = MediaManager()
|
media_search_manager = MediaManager()
|
||||||
table_show_manager = TVShowManager()
|
table_show_manager = TVShowManager()
|
||||||
|
|
||||||
|
|
||||||
def get_token(site_name: str, domain: str) -> dict:
|
def get_token(site_name: str, domain: str) -> dict:
|
||||||
"""
|
"""
|
||||||
Function to retrieve session tokens from a specified website.
|
Function to retrieve session tokens from a specified website.
|
||||||
@ -61,6 +67,7 @@ def get_token(site_name: str, domain: str) -> dict:
|
|||||||
'csrf_token': find_csrf_token
|
'csrf_token': find_csrf_token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_moment_titles(domain: str, version: str, prefix: str):
|
def get_moment_titles(domain: str, version: str, prefix: str):
|
||||||
"""
|
"""
|
||||||
Retrieves the title name from a specified domain using the provided version and prefix.
|
Retrieves the title name from a specified domain using the provided version and prefix.
|
||||||
@ -100,6 +107,7 @@ def get_moment_titles(domain: str, version: str, prefix: str):
|
|||||||
logging.error("Error occurred: %s", str(e))
|
logging.error("Error occurred: %s", str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_domain() -> str:
|
def get_domain() -> str:
|
||||||
"""
|
"""
|
||||||
Fetches the domain from a Telegra.ph API response.
|
Fetches the domain from a Telegra.ph API response.
|
||||||
@ -123,6 +131,7 @@ def get_domain() -> str:
|
|||||||
logging.error(f"Error fetching domain: {e}")
|
logging.error(f"Error fetching domain: {e}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def test_site(domain: str) -> str:
|
def test_site(domain: str) -> str:
|
||||||
"""
|
"""
|
||||||
Tests the availability of a website.
|
Tests the availability of a website.
|
||||||
@ -151,6 +160,7 @@ def test_site(domain: str) -> str:
|
|||||||
logging.error(f"Error testing site: {e}")
|
logging.error(f"Error testing site: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_version(text: str) -> str:
|
def get_version(text: str) -> str:
|
||||||
"""
|
"""
|
||||||
Extracts the version from the HTML text of a webpage.
|
Extracts the version from the HTML text of a webpage.
|
||||||
@ -174,6 +184,7 @@ def get_version(text: str) -> str:
|
|||||||
logging.error(f"Error extracting version: {e}")
|
logging.error(f"Error extracting version: {e}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def get_version_and_domain() -> tuple[str, str]:
|
def get_version_and_domain() -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Retrieves the version and domain of a website.
|
Retrieves the version and domain of a website.
|
||||||
@ -215,6 +226,7 @@ def get_version_and_domain() -> tuple[str, str]:
|
|||||||
logging.error(f"Error getting version and domain: {e}")
|
logging.error(f"Error getting version and domain: {e}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def search(title_search: str, domain: str) -> int:
|
def search(title_search: str, domain: str) -> int:
|
||||||
"""
|
"""
|
||||||
Search for titles based on a search query.
|
Search for titles based on a search query.
|
||||||
@ -237,6 +249,7 @@ def search(title_search: str, domain: str) -> int:
|
|||||||
# Return the number of titles found
|
# Return the number of titles found
|
||||||
return media_search_manager.get_length()
|
return media_search_manager.get_length()
|
||||||
|
|
||||||
|
|
||||||
def update_domain_anime():
|
def update_domain_anime():
|
||||||
"""
|
"""
|
||||||
Update the domain for anime streaming site.
|
Update the domain for anime streaming site.
|
||||||
@ -268,6 +281,7 @@ def update_domain_anime():
|
|||||||
# Extract the domain from the URL and update the config
|
# Extract the domain from the URL and update the config
|
||||||
config_manager.set_key('SITE', 'anime_domain', new_site_url.split(".")[-1])
|
config_manager.set_key('SITE', 'anime_domain', new_site_url.split(".")[-1])
|
||||||
|
|
||||||
|
|
||||||
def anime_search(title_search: str) -> int:
|
def anime_search(title_search: str) -> int:
|
||||||
"""
|
"""
|
||||||
Function to perform an anime search using a provided title.
|
Function to perform an anime search using a provided title.
|
||||||
@ -322,6 +336,7 @@ def anime_search(title_search: str) -> int:
|
|||||||
# Return the length of media search manager
|
# Return the length of media search manager
|
||||||
return media_search_manager.get_length()
|
return media_search_manager.get_length()
|
||||||
|
|
||||||
|
|
||||||
def get_select_title() -> MediaItem:
|
def get_select_title() -> MediaItem:
|
||||||
"""
|
"""
|
||||||
Display a selection of titles and prompt the user to choose one.
|
Display a selection of titles and prompt the user to choose one.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# 5.01.24 -> 7.01.24 -> 20.02.24 -> 29.03.24
|
# 5.01.24 -> 7.01.24 -> 20.02.24 -> 29.03.24
|
||||||
|
|
||||||
# Importing modules
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@ -8,16 +7,19 @@ import threading
|
|||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
# Disable specific warnings
|
# Disable specific warnings
|
||||||
from tqdm import TqdmExperimentalWarning
|
from tqdm import TqdmExperimentalWarning
|
||||||
warnings.filterwarnings("ignore", category=TqdmExperimentalWarning)
|
warnings.filterwarnings("ignore", category=TqdmExperimentalWarning)
|
||||||
warnings.filterwarnings("ignore", category=UserWarning, module="cryptography")
|
warnings.filterwarnings("ignore", category=UserWarning, module="cryptography")
|
||||||
|
|
||||||
|
|
||||||
# External libraries
|
# External libraries
|
||||||
import requests
|
import requests
|
||||||
from tqdm.rich import tqdm
|
from tqdm.rich import tqdm
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
|
||||||
# Internal utilities
|
# Internal utilities
|
||||||
from Src.Util.console import console
|
from Src.Util.console import console
|
||||||
from Src.Util.headers import get_headers
|
from Src.Util.headers import get_headers
|
||||||
@ -30,6 +32,7 @@ from Src.Util.os import (
|
|||||||
convert_to_hex
|
convert_to_hex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Logic class
|
# Logic class
|
||||||
from .util import (
|
from .util import (
|
||||||
print_duration_table,
|
print_duration_table,
|
||||||
@ -44,6 +47,7 @@ from .util import (
|
|||||||
M3U8_UrlFix
|
M3U8_UrlFix
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
Download_audio = config_manager.get_bool('M3U8_OPTIONS', 'download_audio')
|
Download_audio = config_manager.get_bool('M3U8_OPTIONS', 'download_audio')
|
||||||
Donwload_subtitles = config_manager.get_bool('M3U8_OPTIONS', 'download_subtitles')
|
Donwload_subtitles = config_manager.get_bool('M3U8_OPTIONS', 'download_subtitles')
|
||||||
@ -59,12 +63,12 @@ TQDM_SHOW_PROGRESS = config_manager.get_bool('M3U8', 'tqdm_show_progress')
|
|||||||
MIN_TS_FILES_IN_FOLDER = config_manager.get_int('M3U8', 'minimum_ts_files_in_folder')
|
MIN_TS_FILES_IN_FOLDER = config_manager.get_int('M3U8', 'minimum_ts_files_in_folder')
|
||||||
REMOVE_SEGMENTS_FOLDER = config_manager.get_bool('M3U8', 'cleanup_tmp_folder')
|
REMOVE_SEGMENTS_FOLDER = config_manager.get_bool('M3U8', 'cleanup_tmp_folder')
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
config_headers = config_manager.get_dict('M3U8_OPTIONS', 'request')
|
config_headers = config_manager.get_dict('M3U8_OPTIONS', 'request')
|
||||||
failed_segments = []
|
failed_segments = []
|
||||||
class_urlFix = M3U8_UrlFix()
|
class_urlFix = M3U8_UrlFix()
|
||||||
|
|
||||||
# [ main class ]
|
|
||||||
|
|
||||||
class M3U8_Segments:
|
class M3U8_Segments:
|
||||||
def __init__(self, url, folder, key=None):
|
def __init__(self, url, folder, key=None):
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
# 03.04.24
|
# 03.04.24
|
||||||
|
|
||||||
# Import
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
# External library
|
# External library
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util.Padding import unpad
|
from Crypto.Util.Padding import unpad
|
||||||
|
|
||||||
|
|
||||||
class AES_ECB:
|
class AES_ECB:
|
||||||
def __init__(self, key: bytes) -> None:
|
def __init__(self, key: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
@ -49,6 +50,7 @@ class AES_ECB:
|
|||||||
decrypted_data = cipher.decrypt(ciphertext)
|
decrypted_data = cipher.decrypt(ciphertext)
|
||||||
return unpad(decrypted_data, AES.block_size)
|
return unpad(decrypted_data, AES.block_size)
|
||||||
|
|
||||||
|
|
||||||
class AES_CBC:
|
class AES_CBC:
|
||||||
def __init__(self, key: bytes, iv: bytes) -> None:
|
def __init__(self, key: bytes, iv: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
@ -91,6 +93,7 @@ class AES_CBC:
|
|||||||
decrypted_data = cipher.decrypt(ciphertext)
|
decrypted_data = cipher.decrypt(ciphertext)
|
||||||
return unpad(decrypted_data, AES.block_size)
|
return unpad(decrypted_data, AES.block_size)
|
||||||
|
|
||||||
|
|
||||||
class AES_CTR:
|
class AES_CTR:
|
||||||
def __init__(self, key: bytes, nonce: bytes) -> None:
|
def __init__(self, key: bytes, nonce: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
@ -132,6 +135,7 @@ class AES_CTR:
|
|||||||
cipher = AES.new(self.key, AES.MODE_CTR, nonce=self.nonce)
|
cipher = AES.new(self.key, AES.MODE_CTR, nonce=self.nonce)
|
||||||
return cipher.decrypt(ciphertext)
|
return cipher.decrypt(ciphertext)
|
||||||
|
|
||||||
|
|
||||||
class M3U8_Decryption:
|
class M3U8_Decryption:
|
||||||
def __init__(self, key: bytes, iv: bytes = None) -> None:
|
def __init__(self, key: bytes, iv: bytes = None) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
# 31.01.24
|
# 31.01.24
|
||||||
|
|
||||||
# Class
|
|
||||||
from Src.Util.console import console
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import ffmpeg
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
import ffmpeg
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from Src.Util.console import console
|
||||||
|
|
||||||
|
|
||||||
def has_audio_stream(video_path: str) -> bool:
|
def has_audio_stream(video_path: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the input video has an audio stream.
|
Check if the input video has an audio stream.
|
||||||
@ -37,6 +41,7 @@ def has_audio_stream(video_path: str) -> bool:
|
|||||||
logging.error(f"Error: {e.stderr}")
|
logging.error(f"Error: {e.stderr}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_video_duration(file_path: str) -> (float):
|
def get_video_duration(file_path: str) -> (float):
|
||||||
"""
|
"""
|
||||||
Get the duration of a video file.
|
Get the duration of a video file.
|
||||||
@ -64,6 +69,7 @@ def get_video_duration(file_path: str) -> (float):
|
|||||||
logging.error(f"Error: {e.stderr}")
|
logging.error(f"Error: {e.stderr}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def format_duration(seconds: float) -> list[int, int, int]:
|
def format_duration(seconds: float) -> list[int, int, int]:
|
||||||
"""
|
"""
|
||||||
Format duration in seconds into hours, minutes, and seconds.
|
Format duration in seconds into hours, minutes, and seconds.
|
||||||
@ -80,6 +86,7 @@ def format_duration(seconds: float) -> list[int, int, int]:
|
|||||||
|
|
||||||
return int(hours), int(minutes), int(seconds)
|
return int(hours), int(minutes), int(seconds)
|
||||||
|
|
||||||
|
|
||||||
def print_duration_table(file_path: str) -> None:
|
def print_duration_table(file_path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Print duration of a video file in hours, minutes, and seconds.
|
Print duration of a video file in hours, minutes, and seconds.
|
||||||
@ -98,7 +105,7 @@ def print_duration_table(file_path: str) -> None:
|
|||||||
# Print the formatted duration
|
# Print the formatted duration
|
||||||
console.log(f"[cyan]Info [green]'{file_path}': [purple]{int(hours)}[red]h [purple]{int(minutes)}[red]m [purple]{int(seconds)}[red]s")
|
console.log(f"[cyan]Info [green]'{file_path}': [purple]{int(hours)}[red]h [purple]{int(minutes)}[red]m [purple]{int(seconds)}[red]s")
|
||||||
|
|
||||||
# SINGLE SUBTITLE
|
|
||||||
def add_subtitle(input_video_path: str, input_subtitle_path: str, output_video_path: str, subtitle_language: str = 'ita', prefix: str = "single_sub") -> str:
|
def add_subtitle(input_video_path: str, input_subtitle_path: str, output_video_path: str, subtitle_language: str = 'ita', prefix: str = "single_sub") -> str:
|
||||||
"""
|
"""
|
||||||
Convert a video with a single subtitle.
|
Convert a video with a single subtitle.
|
||||||
@ -153,7 +160,7 @@ def add_subtitle(input_video_path: str, input_subtitle_path: str, output_video_p
|
|||||||
# Return
|
# Return
|
||||||
return output_video_path
|
return output_video_path
|
||||||
|
|
||||||
# SEGMENTS
|
|
||||||
def concatenate_and_save(file_list_path: str, output_filename: str, video_decoding: str = None, audio_decoding: str = None, prefix: str = "segments", output_directory: str = None) -> str:
|
def concatenate_and_save(file_list_path: str, output_filename: str, video_decoding: str = None, audio_decoding: str = None, prefix: str = "segments", output_directory: str = None) -> str:
|
||||||
"""
|
"""
|
||||||
Concatenate input files and save the output with specified decoding parameters.
|
Concatenate input files and save the output with specified decoding parameters.
|
||||||
@ -222,7 +229,7 @@ def concatenate_and_save(file_list_path: str, output_filename: str, video_decodi
|
|||||||
# Return
|
# Return
|
||||||
return output_file_path
|
return output_file_path
|
||||||
|
|
||||||
# AUDIOS
|
|
||||||
def join_audios(video_path: str, audio_tracks: list[dict[str, str]], prefix: str = "merged") -> str:
|
def join_audios(video_path: str, audio_tracks: list[dict[str, str]], prefix: str = "merged") -> str:
|
||||||
"""
|
"""
|
||||||
Join video with multiple audio tracks and sync them if there are matching segments.
|
Join video with multiple audio tracks and sync them if there are matching segments.
|
||||||
@ -300,7 +307,7 @@ def join_audios(video_path: str, audio_tracks: list[dict[str, str]], prefix: str
|
|||||||
logging.error("[M3U8_Downloader] Ffmpeg error: %s", ffmpeg_error)
|
logging.error("[M3U8_Downloader] Ffmpeg error: %s", ffmpeg_error)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# SUBTITLES
|
|
||||||
def transcode_with_subtitles(video: str, subtitles_list: list[dict[str, str]], output_file: str, prefix: str = "transcoded") -> str:
|
def transcode_with_subtitles(video: str, subtitles_list: list[dict[str, str]], output_file: str, prefix: str = "transcoded") -> str:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
# 24.01.2023
|
# 24.01.2023
|
||||||
|
|
||||||
# Class
|
|
||||||
from Src.Util.console import console
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
@ -12,7 +8,10 @@ import sys
|
|||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
|
|
||||||
# [ func ]
|
# Internal utilities
|
||||||
|
from Src.Util.console import console
|
||||||
|
|
||||||
|
|
||||||
def isAdmin() -> (bool):
|
def isAdmin() -> (bool):
|
||||||
"""
|
"""
|
||||||
Check if the current user has administrative privileges.
|
Check if the current user has administrative privileges.
|
||||||
@ -29,6 +28,7 @@ def isAdmin() -> (bool):
|
|||||||
|
|
||||||
return is_admin
|
return is_admin
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
"""
|
"""
|
||||||
Get the version of FFmpeg installed on the system.
|
Get the version of FFmpeg installed on the system.
|
||||||
@ -55,6 +55,7 @@ def get_version():
|
|||||||
print("Error executing FFmpeg command:", e.output.strip())
|
print("Error executing FFmpeg command:", e.output.strip())
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def download_ffmpeg():
|
def download_ffmpeg():
|
||||||
"""
|
"""
|
||||||
Download FFmpeg binary for Windows and add it to the system PATH.
|
Download FFmpeg binary for Windows and add it to the system PATH.
|
||||||
@ -102,6 +103,7 @@ def download_ffmpeg():
|
|||||||
print(f"Failed to extract FFmpeg zip file: {e}")
|
print(f"Failed to extract FFmpeg zip file: {e}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def check_ffmpeg():
|
def check_ffmpeg():
|
||||||
"""
|
"""
|
||||||
Check if FFmpeg is installed and available on the system PATH.
|
Check if FFmpeg is installed and available on the system PATH.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
# 29.02.24
|
# 29.02.24
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.os import format_size
|
from Src.Util.os import format_size
|
||||||
|
|
||||||
|
|
||||||
class M3U8_Ts_Files:
|
class M3U8_Ts_Files:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
# 29.04.25
|
# 29.04.25
|
||||||
|
|
||||||
# Class import
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Util.headers import get_headers
|
from Src.Util.headers import get_headers
|
||||||
|
|
||||||
# Import
|
|
||||||
from m3u8 import M3U8
|
# External libraries
|
||||||
import logging
|
|
||||||
import requests
|
import requests
|
||||||
|
from m3u8 import M3U8
|
||||||
|
|
||||||
|
|
||||||
class M3U8_Parser:
|
class M3U8_Parser:
|
||||||
def __init__(self, DOWNLOAD_SPECIFIC_SUBTITLE = None):
|
def __init__(self, DOWNLOAD_SPECIFIC_SUBTITLE = None):
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# 29.03.24
|
# 29.03.24
|
||||||
|
|
||||||
# Import
|
|
||||||
import logging
|
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
from urllib.parse import urlparse, urljoin
|
from urllib.parse import urlparse, urljoin
|
||||||
|
|
||||||
|
|
||||||
class M3U8_UrlFix:
|
class M3U8_UrlFix:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
360
Src/Lib/Request/my_requests.py
Normal file
360
Src/Lib/Request/my_requests.py
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
# 04.4.24
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
from typing import Dict, Optional, Union, Unpack
|
||||||
|
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
HTTP_TIMEOUT = 4
|
||||||
|
HTTP_RETRIES = 2
|
||||||
|
HTTP_DELAY = 1
|
||||||
|
|
||||||
|
|
||||||
|
class RequestError(Exception):
|
||||||
|
"""Custom exception class for request errors."""
|
||||||
|
|
||||||
|
def __init__(self, message: str, original_exception: Optional[Exception] = None) -> None:
|
||||||
|
"""
|
||||||
|
Initialize a RequestError instance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (str): The error message.
|
||||||
|
original_exception (Optional[Exception], optional): The original exception that occurred. Defaults to None.
|
||||||
|
"""
|
||||||
|
super().__init__(message)
|
||||||
|
self.original_exception = original_exception
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Return a string representation of the exception."""
|
||||||
|
if self.original_exception:
|
||||||
|
return f"{super().__str__()} Original Exception: {type(self.original_exception).__name__}: {str(self.original_exception)}"
|
||||||
|
else:
|
||||||
|
return super().__str__()
|
||||||
|
|
||||||
|
|
||||||
|
class Response:
|
||||||
|
"""Class representing an HTTP response."""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
status: int,
|
||||||
|
text: str,
|
||||||
|
is_json: bool = False,
|
||||||
|
content: bytes = b"",
|
||||||
|
headers: Optional[Dict[str, str]] = None,
|
||||||
|
cookies: Optional[Dict[str, str]] = None,
|
||||||
|
redirect_url: Optional[str] = None,
|
||||||
|
response_time: Optional[float] = None,
|
||||||
|
timeout: Optional[float] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize a Response object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
status (int): The HTTP status code of the response.
|
||||||
|
text (str): The response content as text.
|
||||||
|
is_json (bool, optional): Indicates if the response content is JSON. Defaults to False.
|
||||||
|
content (bytes, optional): The response content as bytes. Defaults to b"".
|
||||||
|
headers (Optional[Dict[str, str]], optional): The response headers. Defaults to None.
|
||||||
|
cookies (Optional[Dict[str, str]], optional): The cookies set in the response. Defaults to None.
|
||||||
|
redirect_url (Optional[str], optional): The URL if a redirection occurred. Defaults to None.
|
||||||
|
response_time (Optional[float], optional): The time taken to receive the response. Defaults to None.
|
||||||
|
timeout (Optional[float], optional): The request timeout. Defaults to None.
|
||||||
|
"""
|
||||||
|
self.status_code = status
|
||||||
|
self.text = text
|
||||||
|
self.is_json = is_json
|
||||||
|
self.content = content
|
||||||
|
self.headers = headers or {}
|
||||||
|
self.cookies = cookies or {}
|
||||||
|
self.redirect_url = redirect_url
|
||||||
|
self.response_time = response_time
|
||||||
|
self.timeout = timeout
|
||||||
|
self.ok = 200 <= status < 300
|
||||||
|
|
||||||
|
def raise_for_status(self):
|
||||||
|
"""Raise an error if the response status code is not in the 2xx range."""
|
||||||
|
if not self.ok:
|
||||||
|
raise RequestError(f"Request failed with status code {self.status_code}")
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
"""Return the response content as JSON if it is JSON."""
|
||||||
|
if self.is_json:
|
||||||
|
return json.loads(self.text)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ManageRequests:
|
||||||
|
"""Class for managing HTTP requests."""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
url: str,
|
||||||
|
method: str = 'GET',
|
||||||
|
headers: Optional[Dict[str, str]] = None,
|
||||||
|
timeout: float = HTTP_TIMEOUT,
|
||||||
|
retries: int = HTTP_RETRIES,
|
||||||
|
params: Optional[Dict[str, str]] = None,
|
||||||
|
verify_ssl: bool = True,
|
||||||
|
auth: Optional[tuple] = None,
|
||||||
|
proxy: Optional[str] = None,
|
||||||
|
cookies: Optional[Dict[str, str]] = None,
|
||||||
|
redirection_handling: bool = True,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize a ManageRequests object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): The URL to which the request will be sent.
|
||||||
|
method (str, optional): The HTTP method to be used for the request. Defaults to 'GET'.
|
||||||
|
headers (Optional[Dict[str, str]], optional): The request headers. Defaults to None.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
redirection_handling (bool, optional): Indicates whether redirections should be followed. Defaults to True.
|
||||||
|
"""
|
||||||
|
self.url = url
|
||||||
|
self.method = method
|
||||||
|
self.headers = headers or {'User-Agent': 'Mozilla/5.0'}
|
||||||
|
self.timeout = timeout
|
||||||
|
self.retries = retries
|
||||||
|
self.params = params
|
||||||
|
self.verify_ssl = verify_ssl
|
||||||
|
self.auth = auth
|
||||||
|
self.proxy = proxy
|
||||||
|
self.cookies = cookies
|
||||||
|
self.redirection_handling = redirection_handling
|
||||||
|
|
||||||
|
def add_header(self, key: str, value: str) -> None:
|
||||||
|
"""Add a header to the request."""
|
||||||
|
self.headers[key] = value
|
||||||
|
|
||||||
|
def send(self) -> Response:
|
||||||
|
"""Send the HTTP request."""
|
||||||
|
start_time = time.time()
|
||||||
|
self.attempt = 0
|
||||||
|
redirect_url = None
|
||||||
|
|
||||||
|
while self.attempt < self.retries:
|
||||||
|
try:
|
||||||
|
req = self._build_request()
|
||||||
|
response = self._perform_request(req)
|
||||||
|
return self._process_response(response, start_time, redirect_url)
|
||||||
|
except (urllib.error.URLError, urllib.error.HTTPError) as e:
|
||||||
|
self._handle_error(e)
|
||||||
|
attempt += 1
|
||||||
|
|
||||||
|
def _build_request(self) -> urllib.request.Request:
|
||||||
|
"""Build the urllib Request object."""
|
||||||
|
headers = self.headers.copy()
|
||||||
|
if self.params:
|
||||||
|
url = self.url + '?' + urllib.parse.urlencode(self.params)
|
||||||
|
else:
|
||||||
|
url = self.url
|
||||||
|
req = urllib.request.Request(url, headers=headers, method=self.method)
|
||||||
|
if self.auth:
|
||||||
|
req.add_header('Authorization', 'Basic ' + base64.b64encode(f"{self.auth[0]}:{self.auth[1]}".encode()).decode())
|
||||||
|
if self.cookies:
|
||||||
|
cookie_str = '; '.join([f"{name}={value}" for name, value in self.cookies.items()])
|
||||||
|
req.add_header('Cookie', cookie_str)
|
||||||
|
return req
|
||||||
|
|
||||||
|
def _perform_request(self, req: urllib.request.Request) -> urllib.response.addinfourl:
|
||||||
|
"""Perform the HTTP request."""
|
||||||
|
if self.proxy:
|
||||||
|
proxy_handler = urllib.request.ProxyHandler({'http': self.proxy, 'https': self.proxy})
|
||||||
|
opener = urllib.request.build_opener(proxy_handler)
|
||||||
|
urllib.request.install_opener(opener)
|
||||||
|
if not self.verify_ssl:
|
||||||
|
ssl_context = ssl.create_default_context()
|
||||||
|
ssl_context.check_hostname = False
|
||||||
|
ssl_context.verify_mode = ssl.CERT_NONE
|
||||||
|
response = urllib.request.urlopen(req, timeout=self.timeout, context=ssl_context)
|
||||||
|
else:
|
||||||
|
response = urllib.request.urlopen(req, timeout=self.timeout)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _process_response(self, response: urllib.response.addinfourl, start_time: float, redirect_url: Optional[str]) -> Response:
|
||||||
|
"""Process the HTTP response."""
|
||||||
|
response_data = response.read()
|
||||||
|
content_type = response.headers.get('Content-Type', '').lower()
|
||||||
|
is_response_api = "json" in content_type
|
||||||
|
if self.redirection_handling and response.status in (301, 302, 303, 307, 308):
|
||||||
|
location = response.headers.get('Location')
|
||||||
|
logging.info(f"Redirecting to: {location}")
|
||||||
|
redirect_url = location
|
||||||
|
self.url = location
|
||||||
|
return self.send()
|
||||||
|
return self._build_response(response, response_data, start_time, redirect_url, content_type)
|
||||||
|
|
||||||
|
def _build_response(self, response: urllib.response.addinfourl, response_data: bytes, start_time: float, redirect_url: Optional[str], content_type: str) -> Response:
|
||||||
|
"""Build the Response object."""
|
||||||
|
response_time = time.time() - start_time
|
||||||
|
response_headers = dict(response.headers)
|
||||||
|
response_cookies = {}
|
||||||
|
|
||||||
|
for cookie in response.headers.get_all('Set-Cookie', []):
|
||||||
|
cookie_parts = cookie.split(';')
|
||||||
|
cookie_name, cookie_value = cookie_parts[0].split('=')
|
||||||
|
response_cookies[cookie_name.strip()] = cookie_value.strip()
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
status=response.status,
|
||||||
|
text=response_data.decode('latin-1'),
|
||||||
|
is_json=("json" in content_type),
|
||||||
|
content=response_data,
|
||||||
|
headers=response_headers,
|
||||||
|
cookies=response_cookies,
|
||||||
|
redirect_url=redirect_url,
|
||||||
|
response_time=response_time,
|
||||||
|
timeout=self.timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _handle_error(self, e: Union[urllib.error.URLError, urllib.error.HTTPError]) -> None:
|
||||||
|
"""Handle request error."""
|
||||||
|
logging.error(f"Request failed for URL '{self.url}': {str(e)}")
|
||||||
|
if self.attempt < self.retries:
|
||||||
|
logging.info(f"Retrying request for URL '{self.url}' (attempt {self.attempt}/{self.retries})")
|
||||||
|
time.sleep(HTTP_DELAY)
|
||||||
|
else:
|
||||||
|
logging.error(f"Maximum retries reached for URL '{self.url}'")
|
||||||
|
raise RequestError(str(e))
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateRequest:
|
||||||
|
"""Class for validating request inputs."""
|
||||||
|
@staticmethod
|
||||||
|
def validate_url(url: str) -> bool:
|
||||||
|
"""Validate URL format."""
|
||||||
|
url_regex = re.compile(
|
||||||
|
r'^(?:http|ftp)s?://' # http:// or https://
|
||||||
|
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # domain...
|
||||||
|
r'localhost|' # localhost...
|
||||||
|
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or IP
|
||||||
|
r'(?::\d+)?' # optional port
|
||||||
|
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||||
|
return re.match(url_regex, url) is not None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_headers(headers: Dict[str, str]) -> bool:
|
||||||
|
"""Validate header values."""
|
||||||
|
for key, value in headers.items():
|
||||||
|
if not isinstance(key, str) or not isinstance(value, str):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateResponse:
|
||||||
|
"""Class for validating response data."""
|
||||||
|
@staticmethod
|
||||||
|
def is_valid_json(data: str) -> bool:
|
||||||
|
"""Check if response data is a valid JSON."""
|
||||||
|
try:
|
||||||
|
json.loads(data)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class SSLHandler:
|
||||||
|
"""Class for handling SSL certificates."""
|
||||||
|
@staticmethod
|
||||||
|
def load_certificate(custom_cert_path: str) -> None:
|
||||||
|
"""Load custom SSL certificate."""
|
||||||
|
ssl_context = ssl.create_default_context(cafile=custom_cert_path)
|
||||||
|
ssl_context.check_hostname = False
|
||||||
|
ssl_context.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
|
||||||
|
class KwargsRequest():
|
||||||
|
"""Class representing keyword arguments for a request."""
|
||||||
|
url: str
|
||||||
|
headers: Optional[Dict[str, str]] = None
|
||||||
|
timeout: float = HTTP_TIMEOUT
|
||||||
|
retries: int = HTTP_RETRIES
|
||||||
|
params: Optional[Dict[str, str]] = None
|
||||||
|
cookies: Optional[Dict[str, str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class Request:
|
||||||
|
"""Class for making HTTP requests."""
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get(self, url: str, **kwargs: Unpack[KwargsRequest]):
|
||||||
|
"""
|
||||||
|
Send a GET request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): The URL to which the request will be sent.
|
||||||
|
**kwargs: Additional keyword arguments for the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
"""
|
||||||
|
return self._send_request(url, 'GET', **kwargs)
|
||||||
|
|
||||||
|
def post(self, url: str, **kwargs: Unpack[KwargsRequest]):
|
||||||
|
"""
|
||||||
|
Send a POST request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): The URL to which the request will be sent.
|
||||||
|
**kwargs: Additional keyword arguments for the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
"""
|
||||||
|
return self._send_request(url, 'POST', **kwargs)
|
||||||
|
|
||||||
|
def put(self, url: str, **kwargs: Unpack[KwargsRequest]):
|
||||||
|
"""
|
||||||
|
Send a PUT request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): The URL to which the request will be sent.
|
||||||
|
**kwargs: Additional keyword arguments for the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
"""
|
||||||
|
return self._send_request(url, 'PUT', **kwargs)
|
||||||
|
|
||||||
|
def delete(self, url: str, **kwargs: Unpack[KwargsRequest]):
|
||||||
|
"""
|
||||||
|
Send a DELETE request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): The URL to which the request will be sent.
|
||||||
|
**kwargs: Additional keyword arguments for the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
"""
|
||||||
|
return self._send_request(url, 'DELETE', **kwargs)
|
||||||
|
|
||||||
|
def _send_request(self, url: str, method: str, **kwargs: Unpack[KwargsRequest]):
|
||||||
|
"""Send an HTTP request."""
|
||||||
|
# Add validation checks for URL and headers
|
||||||
|
if not ValidateRequest.validate_url(url):
|
||||||
|
raise ValueError("Invalid URL format")
|
||||||
|
|
||||||
|
if 'headers' in kwargs and not ValidateRequest.validate_headers(kwargs['headers']):
|
||||||
|
raise ValueError("Invalid header values")
|
||||||
|
|
||||||
|
return ManageRequests(url, method, **kwargs).send()
|
||||||
|
|
||||||
|
|
||||||
|
# Out
|
||||||
|
request = Request()
|
105
Src/Lib/Request/user_agent.py
Normal file
105
Src/Lib/Request/user_agent.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# 04.4.24
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from .my_requests import request
|
||||||
|
|
||||||
|
|
||||||
|
def get_browser_user_agents_online(browser: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
Retrieve browser user agent strings from a website.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser (str): The name of the browser (e.g., 'chrome', 'firefox', 'safari').
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: List of user agent strings for the specified browser.
|
||||||
|
"""
|
||||||
|
url = f"https://useragentstring.com/pages/{browser}/"
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Make request and find all user agents
|
||||||
|
html = request.get(url).text
|
||||||
|
browser_user_agents = re.findall(r"<a href=\'/.*?>(.+?)</a>", html, re.UNICODE)
|
||||||
|
return [ua for ua in browser_user_agents if "more" not in ua.lower()]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to fetch user agents for '{browser}': {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_agents(browser_name: str, browser_user_agents: Dict[str, List[str]]) -> None:
|
||||||
|
"""
|
||||||
|
Update browser user agents dictionary with new requests.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser_name (str): Name of the browser.
|
||||||
|
browser_user_agents (Dict[str, List[str]]): Dictionary to store browser user agents.
|
||||||
|
"""
|
||||||
|
browser_user_agents[browser_name] = get_browser_user_agents_online(browser_name)
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_user_agent_file() -> None:
|
||||||
|
"""
|
||||||
|
Create or update the user agent file with browser user agents.
|
||||||
|
"""
|
||||||
|
user_agent_file = os.path.join(os.environ.get('TEMP'), 'fake_user_agent.json')
|
||||||
|
|
||||||
|
if not os.path.exists(user_agent_file):
|
||||||
|
browser_user_agents: Dict[str, List[str]] = {}
|
||||||
|
threads = []
|
||||||
|
|
||||||
|
for browser_name in ['chrome', 'firefox', 'safari']:
|
||||||
|
t = threading.Thread(target=update_user_agents, args=(browser_name, browser_user_agents))
|
||||||
|
threads.append(t)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
with open(user_agent_file, 'w') as f:
|
||||||
|
json.dump(browser_user_agents, f, indent=4)
|
||||||
|
logging.info(f"User agent file created at: {user_agent_file}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.info("User agent file already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgentManager:
|
||||||
|
"""
|
||||||
|
Manager class to access browser user agents from a file.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
# Get path to temp file where save all user agents
|
||||||
|
self.user_agent_file = os.path.join(os.environ.get('TEMP'), 'fake_user_agent.json')
|
||||||
|
|
||||||
|
# If file dont exist, creaet it
|
||||||
|
if not os.path.exists(self.user_agent_file):
|
||||||
|
create_or_update_user_agent_file()
|
||||||
|
|
||||||
|
def get_random_user_agent(self, browser: str) -> str:
|
||||||
|
"""
|
||||||
|
Get a random user agent for the specified browser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser (str): The name of the browser ('chrome', 'firefox', 'safari').
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[str]: Random user agent string for the specified browser.
|
||||||
|
"""
|
||||||
|
with open(self.user_agent_file, 'r') as f:
|
||||||
|
browser_user_agents = json.load(f)
|
||||||
|
return random.choice(browser_user_agents.get(browser.lower(), []))
|
||||||
|
|
||||||
|
|
||||||
|
# Output
|
||||||
|
ua = UserAgentManager()
|
@ -1,17 +1,18 @@
|
|||||||
# 04.04.24
|
# 04.04.24
|
||||||
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
Cache = {}
|
Cache = {}
|
||||||
|
|
||||||
|
|
||||||
class UnidecodeError(ValueError):
|
class UnidecodeError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def transliterate_nonascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str:
|
def transliterate_nonascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str:
|
||||||
"""Transliterates non-ASCII characters in a string to their ASCII counterparts.
|
"""Transliterates non-ASCII characters in a string to their ASCII counterparts.
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ def transliterate_nonascii(string: str, errors: str = 'ignore', replace_str: str
|
|||||||
"""
|
"""
|
||||||
return _transliterate(string, errors, replace_str)
|
return _transliterate(string, errors, replace_str)
|
||||||
|
|
||||||
|
|
||||||
def _get_ascii_representation(char: str) -> str:
|
def _get_ascii_representation(char: str) -> str:
|
||||||
"""Obtains the ASCII representation of a Unicode character.
|
"""Obtains the ASCII representation of a Unicode character.
|
||||||
|
|
||||||
@ -79,6 +81,7 @@ def _get_ascii_representation(char: str) -> str:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _transliterate(string: str, errors: str, replace_str: str) -> str:
|
def _transliterate(string: str, errors: str, replace_str: str) -> str:
|
||||||
"""Main transliteration function.
|
"""Main transliteration function.
|
||||||
|
|
||||||
@ -116,6 +119,7 @@ def _transliterate(string: str, errors: str, replace_str: str) -> str:
|
|||||||
|
|
||||||
return ''.join(retval)
|
return ''.join(retval)
|
||||||
|
|
||||||
|
|
||||||
def transliterate_expect_ascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str:
|
def transliterate_expect_ascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str:
|
||||||
"""Transliterates non-ASCII characters in a string, expecting ASCII input.
|
"""Transliterates non-ASCII characters in a string, expecting ASCII input.
|
||||||
|
|
||||||
@ -140,4 +144,6 @@ def transliterate_expect_ascii(string: str, errors: str = 'ignore', replace_str:
|
|||||||
# Otherwise, transliterate non-ASCII characters
|
# Otherwise, transliterate non-ASCII characters
|
||||||
return _transliterate(string, errors, replace_str)
|
return _transliterate(string, errors, replace_str)
|
||||||
|
|
||||||
|
|
||||||
|
# Out
|
||||||
transliterate = transliterate_expect_ascii
|
transliterate = transliterate_expect_ascii
|
||||||
|
@ -10,72 +10,72 @@ data = (
|
|||||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||||
|
|
||||||
'', # 0x80
|
'', # 0x80
|
||||||
'', # 0x81
|
'', # 0x81
|
||||||
'', # 0x82
|
'', # 0x82
|
||||||
'', # 0x83
|
'', # 0x83
|
||||||
'', # 0x84
|
'', # 0x84
|
||||||
'', # 0x85
|
'', # 0x85
|
||||||
'', # 0x86
|
'', # 0x86
|
||||||
'', # 0x87
|
'', # 0x87
|
||||||
'', # 0x88
|
'', # 0x88
|
||||||
'', # 0x89
|
'', # 0x89
|
||||||
'', # 0x8a
|
'', # 0x8a
|
||||||
'', # 0x8b
|
'', # 0x8b
|
||||||
'', # 0x8c
|
'', # 0x8c
|
||||||
'', # 0x8d
|
'', # 0x8d
|
||||||
'', # 0x8e
|
'', # 0x8e
|
||||||
'', # 0x8f
|
'', # 0x8f
|
||||||
'', # 0x90
|
'', # 0x90
|
||||||
'', # 0x91
|
'', # 0x91
|
||||||
'', # 0x92
|
'', # 0x92
|
||||||
'', # 0x93
|
'', # 0x93
|
||||||
'', # 0x94
|
'', # 0x94
|
||||||
'', # 0x95
|
'', # 0x95
|
||||||
'', # 0x96
|
'', # 0x96
|
||||||
'', # 0x97
|
'', # 0x97
|
||||||
'', # 0x98
|
'', # 0x98
|
||||||
'', # 0x99
|
'', # 0x99
|
||||||
'', # 0x9a
|
'', # 0x9a
|
||||||
'', # 0x9b
|
'', # 0x9b
|
||||||
'', # 0x9c
|
'', # 0x9c
|
||||||
'', # 0x9d
|
'', # 0x9d
|
||||||
'', # 0x9e
|
'', # 0x9e
|
||||||
'', # 0x9f
|
'', # 0x9f
|
||||||
' ', # 0xa0
|
' ', # 0xa0
|
||||||
'!', # 0xa1
|
'!', # 0xa1
|
||||||
'C/', # 0xa2
|
'C/', # 0xa2
|
||||||
|
|
||||||
# Not "GBP" - Pound Sign is used for more than just British Pounds.
|
# Not "GBP" - Pound Sign is used for more than just British Pounds.
|
||||||
'PS', # 0xa3
|
'PS', # 0xa3
|
||||||
|
|
||||||
'$?', # 0xa4
|
'$?', # 0xa4
|
||||||
'Y=', # 0xa5
|
'Y=', # 0xa5
|
||||||
'|', # 0xa6
|
'|', # 0xa6
|
||||||
'SS', # 0xa7
|
'SS', # 0xa7
|
||||||
'"', # 0xa8
|
'"', # 0xa8
|
||||||
'(c)', # 0xa9
|
'(c)', # 0xa9
|
||||||
'a', # 0xaa
|
'a', # 0xaa
|
||||||
'<<', # 0xab
|
'<<', # 0xab
|
||||||
'!', # 0xac
|
'!', # 0xac
|
||||||
'', # 0xad
|
'', # 0xad
|
||||||
'(r)', # 0xae
|
'(r)', # 0xae
|
||||||
'-', # 0xaf
|
'-', # 0xaf
|
||||||
'deg', # 0xb0
|
'deg', # 0xb0
|
||||||
'+-', # 0xb1
|
'+-', # 0xb1
|
||||||
|
|
||||||
# These might be combined with other superscript digits (u+2070 - u+2079)
|
# These might be combined with other superscript digits (u+2070 - u+2079)
|
||||||
'2', # 0xb2
|
'2', # 0xb2
|
||||||
'3', # 0xb3
|
'3', # 0xb3
|
||||||
|
|
||||||
'\'', # 0xb4
|
'\'', # 0xb4
|
||||||
'u', # 0xb5
|
'u', # 0xb5
|
||||||
'P', # 0xb6
|
'P', # 0xb6
|
||||||
'*', # 0xb7
|
'*', # 0xb7
|
||||||
',', # 0xb8
|
',', # 0xb8
|
||||||
'1', # 0xb9
|
'1', # 0xb9
|
||||||
'o', # 0xba
|
'o', # 0xba
|
||||||
'>>', # 0xbb
|
'>>', # 0xbb
|
||||||
' 1/4', # 0xbc
|
' 1/4', # 0xbc
|
||||||
' 1/2', # 0xbd
|
' 1/2', # 0xbd
|
||||||
' 3/4', # 0xbe
|
' 3/4', # 0xbe
|
||||||
@ -89,7 +89,7 @@ data = (
|
|||||||
'A', # 0xc4
|
'A', # 0xc4
|
||||||
|
|
||||||
'A', # 0xc5
|
'A', # 0xc5
|
||||||
'AE', # 0xc6
|
'AE', # 0xc6
|
||||||
'C', # 0xc7
|
'C', # 0xc7
|
||||||
'E', # 0xc8
|
'E', # 0xc8
|
||||||
'E', # 0xc9
|
'E', # 0xc9
|
||||||
@ -119,8 +119,8 @@ data = (
|
|||||||
'U', # 0xdc
|
'U', # 0xdc
|
||||||
|
|
||||||
'Y', # 0xdd
|
'Y', # 0xdd
|
||||||
'Th', # 0xde
|
'Th', # 0xde
|
||||||
'ss', # 0xdf
|
'ss', # 0xdf
|
||||||
'a', # 0xe0
|
'a', # 0xe0
|
||||||
'a', # 0xe1
|
'a', # 0xe1
|
||||||
'a', # 0xe2
|
'a', # 0xe2
|
||||||
@ -130,7 +130,7 @@ data = (
|
|||||||
'a', # 0xe4
|
'a', # 0xe4
|
||||||
|
|
||||||
'a', # 0xe5
|
'a', # 0xe5
|
||||||
'ae', # 0xe6
|
'ae', # 0xe6
|
||||||
'c', # 0xe7
|
'c', # 0xe7
|
||||||
'e', # 0xe8
|
'e', # 0xe8
|
||||||
'e', # 0xe9
|
'e', # 0xe9
|
||||||
@ -160,6 +160,6 @@ data = (
|
|||||||
'u', # 0xfc
|
'u', # 0xfc
|
||||||
|
|
||||||
'y', # 0xfd
|
'y', # 0xfd
|
||||||
'th', # 0xfe
|
'th', # 0xfe
|
||||||
'y', # 0xff
|
'y', # 0xff
|
||||||
)
|
)
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
# 01.03.2023
|
# 01.03.2023
|
||||||
|
|
||||||
# Class import
|
|
||||||
from .version import __version__
|
|
||||||
from Src.Util.console import console
|
|
||||||
|
|
||||||
# General import
|
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from .version import __version__
|
||||||
|
from Src.Util.console import console
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
repo_name = "StreamingCommunity_api"
|
repo_name = "StreamingCommunity_api"
|
||||||
repo_user = "ghost6446"
|
repo_user = "ghost6446"
|
||||||
|
@ -4,6 +4,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
|
|
||||||
class ConfigManager:
|
class ConfigManager:
|
||||||
def __init__(self, file_path: str = 'config.json') -> None:
|
def __init__(self, file_path: str = 'config.json') -> None:
|
||||||
"""Initialize the ConfigManager.
|
"""Initialize the ConfigManager.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# 24.02.24
|
# 24.02.24
|
||||||
|
|
||||||
# Import
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.prompt import Prompt
|
from rich.prompt import Prompt
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
@ -1,10 +1,11 @@
|
|||||||
# 3.12.23 -> 10.12.23 -> 20.03.24
|
# 4.04.24
|
||||||
|
|
||||||
# Import
|
import logging
|
||||||
import fake_useragent
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from Src.Lib.Request.user_agent import ua
|
||||||
|
|
||||||
# Variable
|
|
||||||
useragent = fake_useragent.UserAgent(use_external_data=True)
|
|
||||||
|
|
||||||
def get_headers() -> str:
|
def get_headers() -> str:
|
||||||
"""
|
"""
|
||||||
@ -15,4 +16,7 @@ def get_headers() -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Get a random user agent string from the user agent rotator
|
# Get a random user agent string from the user agent rotator
|
||||||
return useragent.firefox
|
random_headers = ua.get_random_user_agent("firefox")
|
||||||
|
|
||||||
|
logging.info(f"Use headers: {random_headers}")
|
||||||
|
return random_headers
|
@ -1,12 +1,13 @@
|
|||||||
# 26.03.24
|
# 26.03.24
|
||||||
|
|
||||||
# Class import
|
|
||||||
from Src.Util.config import config_manager
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from Src.Util.config import config_manager
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
# 3.12.23 -> 19.07.24
|
# 3.12.23 -> 19.07.24
|
||||||
|
|
||||||
# Class import
|
|
||||||
from .config import config_manager
|
|
||||||
from Src.Util.console import console
|
|
||||||
|
|
||||||
# Import
|
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
from Src.Util.console import console
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from .config import config_manager
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
CLEAN = config_manager.get_bool('DEFAULT', 'clean_console')
|
CLEAN = config_manager.get_bool('DEFAULT', 'clean_console')
|
||||||
SHOW = config_manager.get_bool('DEFAULT', 'show_message')
|
SHOW = config_manager.get_bool('DEFAULT', 'show_message')
|
||||||
|
|
||||||
|
|
||||||
def get_os_system():
|
def get_os_system():
|
||||||
"""
|
"""
|
||||||
This function returns the name of the operating system.
|
This function returns the name of the operating system.
|
||||||
@ -19,6 +24,7 @@ def get_os_system():
|
|||||||
os_system = platform.system()
|
os_system = platform.system()
|
||||||
return os_system
|
return os_system
|
||||||
|
|
||||||
|
|
||||||
def start_message():
|
def start_message():
|
||||||
"""
|
"""
|
||||||
Display a start message.
|
Display a start message.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# 24.01.24
|
# 24.01.24
|
||||||
|
|
||||||
# Import
|
|
||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
@ -9,6 +8,7 @@ import hashlib
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def remove_folder(folder_path: str) -> None:
|
def remove_folder(folder_path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Remove a folder if it exists.
|
Remove a folder if it exists.
|
||||||
@ -23,6 +23,7 @@ def remove_folder(folder_path: str) -> None:
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Error removing folder '{folder_path}': {e}")
|
print(f"Error removing folder '{folder_path}': {e}")
|
||||||
|
|
||||||
|
|
||||||
def remove_file(file_path: str) -> None:
|
def remove_file(file_path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Remove a file if it exists
|
Remove a file if it exists
|
||||||
@ -38,9 +39,8 @@ def remove_file(file_path: str) -> None:
|
|||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Error removing file '{file_path}': {e}")
|
print(f"Error removing file '{file_path}': {e}")
|
||||||
#else:
|
|
||||||
# print(f"File '{file_path}' does not exist.")
|
|
||||||
|
|
||||||
def remove_special_characters(filename) -> str:
|
def remove_special_characters(filename) -> str:
|
||||||
"""
|
"""
|
||||||
Removes special characters from a filename to make it suitable for creating a filename in Windows.
|
Removes special characters from a filename to make it suitable for creating a filename in Windows.
|
||||||
@ -60,6 +60,7 @@ def remove_special_characters(filename) -> str:
|
|||||||
|
|
||||||
return cleaned_filename
|
return cleaned_filename
|
||||||
|
|
||||||
|
|
||||||
def move_file_one_folder_up(file_path) -> None:
|
def move_file_one_folder_up(file_path) -> None:
|
||||||
"""
|
"""
|
||||||
Move a file one folder up from its current location.
|
Move a file one folder up from its current location.
|
||||||
@ -83,6 +84,7 @@ def move_file_one_folder_up(file_path) -> None:
|
|||||||
# Move the file
|
# Move the file
|
||||||
os.rename(file_path, new_path)
|
os.rename(file_path, new_path)
|
||||||
|
|
||||||
|
|
||||||
def read_json(path: str):
|
def read_json(path: str):
|
||||||
"""Reads JSON file and returns its content.
|
"""Reads JSON file and returns its content.
|
||||||
|
|
||||||
@ -98,6 +100,7 @@ def read_json(path: str):
|
|||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def save_json(json_obj, path: str) -> None:
|
def save_json(json_obj, path: str) -> None:
|
||||||
"""Saves JSON object to the specified file path.
|
"""Saves JSON object to the specified file path.
|
||||||
|
|
||||||
@ -109,6 +112,7 @@ def save_json(json_obj, path: str) -> None:
|
|||||||
with open(path, 'w') as file:
|
with open(path, 'w') as file:
|
||||||
json.dump(json_obj, file, indent=4) # Adjust the indentation as needed
|
json.dump(json_obj, file, indent=4) # Adjust the indentation as needed
|
||||||
|
|
||||||
|
|
||||||
def clean_json(path: str) -> None:
|
def clean_json(path: str) -> None:
|
||||||
"""Reads JSON data from the file, cleans it, and saves it back.
|
"""Reads JSON data from the file, cleans it, and saves it back.
|
||||||
|
|
||||||
@ -132,6 +136,7 @@ def clean_json(path: str) -> None:
|
|||||||
# Save the modified JSON data back to the file
|
# Save the modified JSON data back to the file
|
||||||
save_json(modified_data, path)
|
save_json(modified_data, path)
|
||||||
|
|
||||||
|
|
||||||
def format_size(size_bytes: float) -> str:
|
def format_size(size_bytes: float) -> str:
|
||||||
"""
|
"""
|
||||||
Format the size in bytes into a human-readable format.
|
Format the size in bytes into a human-readable format.
|
||||||
@ -154,6 +159,7 @@ def format_size(size_bytes: float) -> str:
|
|||||||
# Round the size to two decimal places and return with the appropriate unit
|
# Round the size to two decimal places and return with the appropriate unit
|
||||||
return f"{size_bytes:.2f} {units[unit_index]}"
|
return f"{size_bytes:.2f} {units[unit_index]}"
|
||||||
|
|
||||||
|
|
||||||
def compute_sha1_hash(input_string: str) -> str:
|
def compute_sha1_hash(input_string: str) -> str:
|
||||||
"""
|
"""
|
||||||
Computes the SHA-1 hash of the input string.
|
Computes the SHA-1 hash of the input string.
|
||||||
@ -170,6 +176,7 @@ def compute_sha1_hash(input_string: str) -> str:
|
|||||||
# Return the hashed string
|
# Return the hashed string
|
||||||
return hashed_string
|
return hashed_string
|
||||||
|
|
||||||
|
|
||||||
def decode_bytes(bytes_data: bytes, encodings_to_try: list[str] = None) -> str:
|
def decode_bytes(bytes_data: bytes, encodings_to_try: list[str] = None) -> str:
|
||||||
"""
|
"""
|
||||||
Decode a byte sequence using a list of encodings and return the decoded string.
|
Decode a byte sequence using a list of encodings and return the decoded string.
|
||||||
@ -200,6 +207,7 @@ def decode_bytes(bytes_data: bytes, encodings_to_try: list[str] = None) -> str:
|
|||||||
logging.info("Raw byte data: %s", bytes_data)
|
logging.info("Raw byte data: %s", bytes_data)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def convert_to_hex(bytes_data: bytes) -> str:
|
def convert_to_hex(bytes_data: bytes) -> str:
|
||||||
"""
|
"""
|
||||||
Convert a byte sequence to its hexadecimal representation.
|
Convert a byte sequence to its hexadecimal representation.
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
# 03.03.24
|
# 03.03.24
|
||||||
|
|
||||||
# Class import
|
|
||||||
from .message import start_message
|
|
||||||
|
|
||||||
# Import
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
@ -11,6 +7,11 @@ from rich.prompt import Prompt
|
|||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from .message import start_message
|
||||||
|
|
||||||
|
|
||||||
class TVShowManager:
|
class TVShowManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
12
run.py
12
run.py
@ -1,6 +1,11 @@
|
|||||||
# 10.12.23 -> 31.01.24
|
# 10.12.23 -> 31.01.24
|
||||||
|
|
||||||
# Class
|
import sys
|
||||||
|
import logging
|
||||||
|
import platform
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
from Src.Api import (
|
from Src.Api import (
|
||||||
get_version_and_domain,
|
get_version_and_domain,
|
||||||
download_series,
|
download_series,
|
||||||
@ -19,10 +24,6 @@ from Src.Upload.update import update as git_update
|
|||||||
from Src.Lib.FFmpeg import check_ffmpeg
|
from Src.Lib.FFmpeg import check_ffmpeg
|
||||||
from Src.Util.logger import Logger
|
from Src.Util.logger import Logger
|
||||||
|
|
||||||
# Import
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
import platform
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
DEBUG_MODE = config_manager.get_bool("DEFAULT", "debug")
|
DEBUG_MODE = config_manager.get_bool("DEFAULT", "debug")
|
||||||
@ -31,7 +32,6 @@ SWITCH_TO = config_manager.get_bool('DEFAULT', 'swith_anime')
|
|||||||
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
||||||
|
|
||||||
|
|
||||||
# [ main ]
|
|
||||||
def initialize():
|
def initialize():
|
||||||
"""
|
"""
|
||||||
Initialize the application.
|
Initialize the application.
|
||||||
|
21
update.py
21
update.py
@ -1,16 +1,25 @@
|
|||||||
# 10.12.24
|
# 10.12.24
|
||||||
|
|
||||||
# General imports
|
|
||||||
import requests
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from zipfile import ZipFile
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
|
||||||
|
# Internal utilities
|
||||||
|
from Src.Util.config import config_manager
|
||||||
|
|
||||||
|
|
||||||
|
# External libraries
|
||||||
|
import requests
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
console = Console()
|
console = Console()
|
||||||
local_path = os.path.join(".")
|
local_path = os.path.join(".")
|
||||||
|
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||||
|
|
||||||
|
|
||||||
def move_content(source: str, destination: str) :
|
def move_content(source: str, destination: str) :
|
||||||
"""
|
"""
|
||||||
@ -36,6 +45,7 @@ def move_content(source: str, destination: str) :
|
|||||||
else:
|
else:
|
||||||
shutil.move(source_path, destination_path)
|
shutil.move(source_path, destination_path)
|
||||||
|
|
||||||
|
|
||||||
def keep_specific_items(directory: str, keep_folder: str, keep_file: str):
|
def keep_specific_items(directory: str, keep_folder: str, keep_file: str):
|
||||||
"""
|
"""
|
||||||
Delete all items in the directory except for the specified folder and file.
|
Delete all items in the directory except for the specified folder and file.
|
||||||
@ -66,6 +76,7 @@ def keep_specific_items(directory: str, keep_folder: str, keep_file: str):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
def download_and_extract_latest_commit(author: str, repo_name: str):
|
def download_and_extract_latest_commit(author: str, repo_name: str):
|
||||||
"""
|
"""
|
||||||
Download and extract the latest commit from a GitHub repository.
|
Download and extract the latest commit from a GitHub repository.
|
||||||
@ -115,6 +126,7 @@ def download_and_extract_latest_commit(author: str, repo_name: str):
|
|||||||
else:
|
else:
|
||||||
console.log(f"[red]Failed to fetch commit information. Status code: {response.status_code}")
|
console.log(f"[red]Failed to fetch commit information. Status code: {response.status_code}")
|
||||||
|
|
||||||
|
|
||||||
def main_upload():
|
def main_upload():
|
||||||
"""
|
"""
|
||||||
Main function to upload the latest commit of a GitHub repository.
|
Main function to upload the latest commit of a GitHub repository.
|
||||||
@ -128,10 +140,11 @@ def main_upload():
|
|||||||
if cmd_insert == "yes":
|
if cmd_insert == "yes":
|
||||||
|
|
||||||
# Remove all old file
|
# Remove all old file
|
||||||
keep_specific_items(".", "videos", "upload.py")
|
keep_specific_items(".", ROOT_PATH, "upload.py")
|
||||||
|
|
||||||
download_and_extract_latest_commit(repository_owner, repository_name)
|
download_and_extract_latest_commit(repository_owner, repository_name)
|
||||||
|
|
||||||
|
|
||||||
main_upload()
|
main_upload()
|
||||||
|
|
||||||
# win
|
# win
|
||||||
|
Loading…
x
Reference in New Issue
Block a user