mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-05 02:55:25 +00:00
Make readme better
This commit is contained in:
parent
0996f8d415
commit
0faa63b3e2
239
README.md
239
README.md
@ -2,10 +2,10 @@
|
||||
<img src="./Src/Assets/min_logo.png">
|
||||
</p>
|
||||
|
||||
|
||||
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 downloading films and series from a variety of supported streaming platforms. [SITE](#website-status-)
|
||||
|
||||
## 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/8vV68UGRc7)
|
||||
|
||||
# Table of Contents
|
||||
@ -18,7 +18,7 @@ You can chat, help improve this repo, or just hang around for some fun in the **
|
||||
* [Requirement](#requirement)
|
||||
* [Usage](#usage-manual)
|
||||
* [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7)
|
||||
* [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux)
|
||||
* [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux)
|
||||
* [Update](#update)
|
||||
* [CONFIGURATION](#configuration)
|
||||
* [DOCKER](#docker)
|
||||
@ -28,55 +28,70 @@ You can chat, help improve this repo, or just hang around for some fun in the **
|
||||
# INSTALLATION
|
||||
|
||||
## Automatic Installation
|
||||
<a id="automatic-installation-os"></a>
|
||||
|
||||
`<a id="automatic-installation-os"></a>`
|
||||
|
||||
### Supported OSs for Automatic Installation 💿
|
||||
|
||||
- Supported ✔️
|
||||
- Not tested ⏳
|
||||
- Not supported ❌
|
||||
|
||||
| OS | Automatic Installation Support |
|
||||
|:--------------------|:--------------------:|
|
||||
| Windows 10/11 | ✔️ |
|
||||
| Windows 7 | ❌ |
|
||||
| Debian Linux | ✔️ |
|
||||
| Arch Linux | ✔️ |
|
||||
| CentOS Stream 9 | ✔️ |
|
||||
| FreeBSD | ⏳ |
|
||||
| MacOS | ✔️ |
|
||||
| Termux | ❌ |
|
||||
| OS | Automatic Installation Support |
|
||||
| :-------------- | :----------------------------: |
|
||||
| Windows 10/11 | ✔️ |
|
||||
| Windows 7 | ❌ |
|
||||
| Debian Linux | ✔️ |
|
||||
| Arch Linux | ✔️ |
|
||||
| CentOS Stream 9 | ✔️ |
|
||||
| FreeBSD | ⏳ |
|
||||
| MacOS | ✔️ |
|
||||
| Termux | ❌ |
|
||||
|
||||
### Installation ⚙️
|
||||
|
||||
Run the following command inside the main directory:
|
||||
|
||||
#### On Windows:
|
||||
|
||||
```powershell
|
||||
.\win_install.bat
|
||||
```
|
||||
|
||||
#### On Linux/MacOS/BSD:
|
||||
|
||||
```bash
|
||||
sudo chmod +x unix_install.sh && ./unix_install.sh
|
||||
```
|
||||
|
||||
<a id="usage-automatic"></a>
|
||||
`<a id="usage-automatic"></a>`
|
||||
|
||||
### Usage 📚
|
||||
|
||||
Run the script with the following command:
|
||||
|
||||
#### On Windows:
|
||||
|
||||
```powershell
|
||||
python .\run.py
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```powershell
|
||||
source .venv/bin/activate && python run.py && deactivate
|
||||
```
|
||||
|
||||
#### On Linux/MacOS/BSD:
|
||||
|
||||
```bash
|
||||
./run.py
|
||||
```
|
||||
|
||||
## Manual Installation
|
||||
<a id="requirement"></a>
|
||||
|
||||
`<a id="requirement"></a>`
|
||||
|
||||
### Requirement 📋
|
||||
|
||||
Make sure you have the following prerequisites installed on your system:
|
||||
@ -93,7 +108,6 @@ Install the required Python libraries using the following command:
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
<a id="usage-manual"></a>
|
||||
### Usage 📚
|
||||
|
||||
Run the script with the following command:
|
||||
@ -110,8 +124,7 @@ python run.py
|
||||
python3 run.py
|
||||
```
|
||||
|
||||
|
||||
## Update
|
||||
### Update
|
||||
|
||||
Keep your script up to date with the latest features by running:
|
||||
|
||||
@ -127,91 +140,150 @@ python update_version.py
|
||||
python3 update_version.py
|
||||
```
|
||||
|
||||
<a id="configuration"></a>
|
||||
## Configuration ⚙️
|
||||
|
||||
You can change some behaviors by tweaking the configuration file.
|
||||
|
||||
<details>
|
||||
<summary><strong>DEFAULT</strong></summary>
|
||||
The configuration file is divided into several main sections:
|
||||
|
||||
* **debug**: Enables or disables debug mode.
|
||||
- **Default Value**: `false`
|
||||
### DEFAULT Settings
|
||||
|
||||
* **root_path**: Path where the script will add movies and TV series folders (see [Path Examples](#Path-examples)).
|
||||
- **Default Value**: `Video`
|
||||
```json
|
||||
{
|
||||
"root_path": "Video",
|
||||
"map_episode_name": "%(tv_name)_S%(season)E%(episode)_%(episode_name)",
|
||||
"special_chars_to_remove": "!@#$%^&*()[]{}<>|`~'\";:,?=+\u00e2\u20ac\u00a6",
|
||||
"not_close": false,
|
||||
"show_trending": false
|
||||
}
|
||||
```
|
||||
|
||||
* **map_episode_name**: Mapping to choose the name of all episodes of TV Shows (see [Episode Name Usage](#Episode-name-usage)).
|
||||
- **Default Value**: `%(tv_name)_S%(season)E%(episode)_%(episode_name)`
|
||||
- `root_path`: Directory where all videos will be saved
|
||||
|
||||
* **not_close**: When activated, prevents the script from closing after its initial execution, allowing it to restart automatically after completing the first run.
|
||||
- **Default Value**: `false`
|
||||
#### Path examples:
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REQUESTS</strong></summary>
|
||||
* Windows: `C:\\MyLibrary\\Folder` or `\\\\MyServer\\MyLibrary` (if you want to use a network folder)
|
||||
* Linux/MacOS: `Desktop/MyLibrary/Folder`
|
||||
`<br/><br/>`
|
||||
- `map_episode_name`: Template for TV series episode filenames
|
||||
|
||||
* **timeout**: The timeout value for requests.
|
||||
- **Default Value**: `15`
|
||||
#### Episode name usage:
|
||||
|
||||
* **verify_ssl**: Whether to verify SSL certificates.
|
||||
- **Default Value**: `false`
|
||||
You can choose different vars:
|
||||
|
||||
* **proxy**: To use proxy create a file with name list_proxy.txt and copy ip and port like "122.114.232.137:8080". They need to be http
|
||||
|
||||
</details>
|
||||
* `%(tv_name)` : Is the name of TV Show
|
||||
* `%(season)` : Is the number of the season
|
||||
* `%(episode)` : Is the number of the episode
|
||||
* `%(episode_name)` : Is the name of the episode
|
||||
`<br/><br/>`
|
||||
- `special_chars_to_remove`: Special characters to be removed from filenames
|
||||
- `not_close`: If true, continues running after downloading
|
||||
- `show_trending`: Display trending content on startup
|
||||
|
||||
<details>
|
||||
<summary><strong>M3U8_DOWNLOAD</strong></summary>
|
||||
#### qBittorrent Configuration
|
||||
|
||||
* **tqdm_use_large_bar**: Whether to use large progress bars during downloads (Downloading %desc: %percentage:.2f %bar %elapsed < %remaining %postfix
|
||||
- **Default Value**: `true`
|
||||
- **Example Value**: `false` with Proc: %percentage:.2f %remaining %postfix
|
||||
```json
|
||||
{
|
||||
"config_qbit_tor": {
|
||||
"host": "192.168.1.59",
|
||||
"port": "8080",
|
||||
"user": "admin",
|
||||
"pass": "adminadmin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **specific_list_audio**: A list of specific audio languages to download.
|
||||
- **Example Value**: `['ita']`
|
||||
To enable qBittorrent integration, follow the setup guide [here](https://github.com/lgallard/qBittorrent-Controller/wiki/How-to-enable-the-qBittorrent-Web-UI).
|
||||
|
||||
* **specific_list_subtitles**: A list of specific subtitle languages to download.
|
||||
- **Example Value**: `['ara', 'baq', 'cat', 'chi', 'cze', 'dan', 'dut', 'eng', 'fil', 'fin', 'forced-ita', 'fre', 'ger', 'glg', 'gre', 'heb', 'hin', 'hun', 'ind', 'ita', 'jpn', 'kan', 'kor', 'mal', 'may', 'nob', 'nor', 'pol', 'por', 'rum', 'rus', 'spa', 'swe', 'tam', 'tel', 'tha', 'tur', 'ukr', 'vie']`
|
||||
### REQUESTS Settings
|
||||
|
||||
* **cleanup_tmp_folder**: Upon final conversion, ensures the removal of all unformatted audio, video tracks, and subtitles from the temporary folder, thereby maintaining cleanliness and efficiency.
|
||||
- **Default Value**: `false`
|
||||
```json
|
||||
{
|
||||
"timeout": 20,
|
||||
"max_retry": 3
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
- `timeout`: Maximum timeout (in seconds) for each request
|
||||
- `max_retry`: Number of retry attempts per segment during M3U8 index download
|
||||
|
||||
<details>
|
||||
<summary><strong>M3U8_PARSER</strong></summary>
|
||||
### BROWSER Settings
|
||||
|
||||
* **force_resolution**: Forces the use of a specific resolution. `-1` means no forced resolution.
|
||||
- **Default Value**: `-1`
|
||||
- **Example Value**: `1080`
|
||||
```json
|
||||
{
|
||||
"headless": false
|
||||
}
|
||||
```
|
||||
|
||||
* **get_only_link**: Print hls m3u8 link and path file.
|
||||
- **Default Value**: `false`
|
||||
- `headless`: Controls whether to run browser in headless mode
|
||||
|
||||
</details>
|
||||
### M3U8_DOWNLOAD Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"tqdm_delay": 0.01,
|
||||
"tqdm_use_large_bar": true,
|
||||
"default_video_workser": 12,
|
||||
"default_audio_workser": 12,
|
||||
"cleanup_tmp_folder": true
|
||||
}
|
||||
```
|
||||
|
||||
- `tqdm_delay`: Delay between progress bar updates
|
||||
- `tqdm_use_large_bar`: Use detailed progress bar (recommended for desktop) set to false for mobile
|
||||
- `default_video_workser`: Number of threads for video download
|
||||
- `default_audio_workser`: Number of threads for audio download
|
||||
- `cleanup_tmp_folder`: Remove temporary .ts files after download
|
||||
|
||||
#### Language Settings
|
||||
|
||||
The following codes can be used for `specific_list_audio` and `specific_list_subtitles`:
|
||||
|
||||
```
|
||||
ara - Arabic eng - English ita - Italian por - Portuguese
|
||||
baq - Basque fil - Filipino jpn - Japanese rum - Romanian
|
||||
cat - Catalan fin - Finnish kan - Kannada rus - Russian
|
||||
chi - Chinese fre - French kor - Korean spa - Spanish
|
||||
cze - Czech ger - German mal - Malayalam swe - Swedish
|
||||
dan - Danish glg - Galician may - Malay tam - Tamil
|
||||
dut - Dutch gre - Greek nob - Norw. Bokm tel - Telugu
|
||||
heb - Hebrew nor - Norwegian tha - Thai
|
||||
forced-ita hin - Hindi pol - Polish tur - Turkish
|
||||
hun - Hungarian ukr - Ukrainian
|
||||
ind - Indonesian vie - Vietnamese
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you're on **Windows** you'll need to use double back slash. On Linux/MacOS, one slash is fine.
|
||||
> Language code availability may vary by site. Some platforms might:
|
||||
>
|
||||
> - Use different language codes
|
||||
> - Support only a subset of these languages
|
||||
> - Offer additional languages not listed here
|
||||
>
|
||||
> Check the specific site's available options if downloads fail.
|
||||
|
||||
#### Path examples:
|
||||
> [!TIP]
|
||||
> You can configure multiple languages by adding them to the lists:
|
||||
>
|
||||
> ```json
|
||||
> "specific_list_audio": ["ita", "eng", "spa"],
|
||||
> "specific_list_subtitles": ["ita", "eng", "spa"]
|
||||
> ```
|
||||
|
||||
* Windows: `C:\\MyLibrary\\Folder` or `\\\\MyServer\\MyLibrary` (if you want to use a network folder).
|
||||
* Linux/MacOS: `Desktop/MyLibrary/Folder`
|
||||
### M3U8_PARSER Settings
|
||||
|
||||
#### Episode name usage:
|
||||
```json
|
||||
{
|
||||
"force_resolution": -1,
|
||||
"get_only_link": false
|
||||
}
|
||||
```
|
||||
|
||||
You can choose different vars:
|
||||
- `force_resolution`: Force specific resolution (-1 for best available, or specify 1080, 720, 360)
|
||||
- `get_only_link`: Return M3U8 playlist/index URL instead of downloading
|
||||
|
||||
* `%(tv_name)` : Is the name of TV Show
|
||||
* `%(season)` : Is the number of the season
|
||||
* `%(episode)` : Is the number of the episode
|
||||
* `%(episode_name)` : Is the name of the episode
|
||||
|
||||
> NOTE: You don't need to add .mp4 at the end
|
||||
|
||||
<a id="docker"></a>
|
||||
## Docker 🐳
|
||||
|
||||
You can run the script in a docker container, to build the image just run
|
||||
@ -245,14 +317,31 @@ make LOCAL_DIR=/path/to/download run-container
|
||||
|
||||
The `run-container` command mounts also the `config.json` file, so any change to the configuration file is reflected immediately without having to rebuild the image.
|
||||
|
||||
<a id="tutorial"></a>
|
||||
### Website Status 🌐
|
||||
|
||||
- Working ✅
|
||||
- Not Working ❌
|
||||
|
||||
| Website | Status |
|
||||
| :----------------- | :----: |
|
||||
| 1337xx | ✅ |
|
||||
| Altadefinizione | ❌ |
|
||||
| AnimeUnity | ✅ |
|
||||
| BitSearch | ✅ |
|
||||
| CB01New | ✅ |
|
||||
| DDLStreamItaly | ✅ |
|
||||
| GuardaSerie | ✅ |
|
||||
| MostraGuarda | ✅ |
|
||||
| PirateBays | ✅ |
|
||||
| StreamingCommunity | ✅ |
|
||||
|
||||
## Tutorial 📖
|
||||
|
||||
[win](https://www.youtube.com/watch?v=mZGqK4wdN-k)
|
||||
[linux](https://www.youtube.com/watch?v=0qUNXPE_mTg)
|
||||
|
||||
<a id="to-do"></a>
|
||||
## To do 📝
|
||||
|
||||
- GUI
|
||||
- Website api
|
||||
- Add other site
|
||||
|
@ -28,7 +28,12 @@ class VideoSource:
|
||||
- url (str): The URL of the video.
|
||||
"""
|
||||
self.url = url
|
||||
self.headers = {'user-agent': get_headers()}
|
||||
self.headers = {
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||
'User-Agent': get_headers()
|
||||
}
|
||||
self.client = httpx.Client()
|
||||
|
||||
def setup(self, url: str) -> None:
|
||||
"""
|
||||
@ -51,7 +56,7 @@ class VideoSource:
|
||||
"""
|
||||
|
||||
try:
|
||||
response = httpx.get(url, headers=self.headers, follow_redirects=True, timeout=max_timeout)
|
||||
response = self.client.get(url, headers=self.headers, follow_redirects=True, timeout=max_timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.text
|
||||
|
@ -16,7 +16,7 @@ from ..Template.Class.SearchType import MediaManager
|
||||
|
||||
|
||||
# Variable
|
||||
from .costant import SITE_NAME
|
||||
from .costant import SITE_NAME, DOMAIN_NOW
|
||||
media_search_manager = MediaManager()
|
||||
table_show_manager = TVShowManager()
|
||||
|
||||
@ -31,16 +31,21 @@ def title_search(title_search: str) -> int:
|
||||
Returns:
|
||||
int: The number of titles found.
|
||||
"""
|
||||
client = httpx.Client()
|
||||
|
||||
# Find new domain if prev dont work
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
#domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
|
||||
# Send request to search for title
|
||||
try:
|
||||
response = httpx.get(
|
||||
url=f"https://{SITE_NAME}.{domain_to_use}/?story={unidecode(title_search.replace(' ', '+'))}&do=search&subaction=search&titleonly=3",
|
||||
headers={'user-agent': get_headers()},
|
||||
response = client.get(
|
||||
url=f"https://{SITE_NAME}.{DOMAIN_NOW}/?story={unidecode(title_search.replace(' ', '+'))}&do=search&subaction=search&titleonly=3",
|
||||
headers={
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||
'User-Agent': get_headers()
|
||||
},
|
||||
timeout=max_timeout
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
@ -5,7 +5,7 @@ from Src.Util.console import console, msg
|
||||
|
||||
|
||||
# Logic class
|
||||
from .site import title_search, run_get_select_title
|
||||
from .site import title_search, run_get_select_title, media_search_manager
|
||||
from .film import download_film
|
||||
|
||||
|
||||
@ -17,15 +17,21 @@ _priority = 2
|
||||
_engineDownload = "mp4"
|
||||
|
||||
|
||||
def search():
|
||||
def search(string_to_search: str = None, get_onylDatabase:bool = False):
|
||||
"""
|
||||
Main function of the application for film and series.
|
||||
"""
|
||||
|
||||
# Make request to site to get content that corrsisponde to that string
|
||||
string_to_search = msg.ask("\n[purple]Insert word to search in all site").strip()
|
||||
if string_to_search is None:
|
||||
string_to_search = msg.ask("\n[purple]Insert word to search in all site").strip()
|
||||
|
||||
# Search on database
|
||||
len_database = title_search(string_to_search)
|
||||
|
||||
# Return list of elements
|
||||
if get_onylDatabase:
|
||||
return media_search_manager
|
||||
|
||||
if len_database > 0:
|
||||
|
||||
# Select title from list
|
||||
|
@ -74,6 +74,7 @@ class PathManager:
|
||||
|
||||
# Create the base path by removing the '.mp4' extension from the output filename
|
||||
self.base_path = str(output_filename).replace(".mp4", "")
|
||||
logging.info(f"class 'PathManager'; set base path: {self.base_path}")
|
||||
|
||||
# Define the path for a temporary directory where segments will be stored
|
||||
self.base_temp = os.path.join(self.base_path, "tmp")
|
||||
@ -106,6 +107,7 @@ class HttpClient:
|
||||
Returns:
|
||||
str: The response body as text if the request is successful, None otherwise.
|
||||
"""
|
||||
logging.info(f"class 'HttpClient'; make request: {url}")
|
||||
try:
|
||||
response = httpx.get(
|
||||
url=url,
|
||||
@ -127,6 +129,7 @@ class HttpClient:
|
||||
Returns:
|
||||
bytes: The response content as bytes if the request is successful, None otherwise.
|
||||
"""
|
||||
logging.info(f"class 'HttpClient'; make request: {url}")
|
||||
try:
|
||||
response = httpx.get(
|
||||
url=url,
|
||||
@ -168,6 +171,7 @@ class ContentExtractor:
|
||||
"""
|
||||
It checks for available audio languages and the specific audio tracks to download.
|
||||
"""
|
||||
logging.info(f"class 'ContentExtractor'; call _collect_audio()")
|
||||
|
||||
# Collect available audio tracks and their corresponding URIs and names
|
||||
self.list_available_audio = self.obj_parse._audio.get_all_uris_and_names()
|
||||
@ -197,6 +201,7 @@ class ContentExtractor:
|
||||
"""
|
||||
It checks for available subtitle languages and the specific subtitles to download.
|
||||
"""
|
||||
logging.info(f"class 'ContentExtractor'; call _collect_subtitle()")
|
||||
|
||||
# Collect available subtitles and their corresponding URIs and names
|
||||
self.list_available_subtitles = self.obj_parse._subtitle.get_all_uris_and_names()
|
||||
@ -226,6 +231,7 @@ class ContentExtractor:
|
||||
"""
|
||||
It identifies the best video quality and displays relevant information to the user.
|
||||
"""
|
||||
logging.info(f"class 'ContentExtractor'; call _collect_video()")
|
||||
|
||||
# Collect custom quality video if a specific resolution is set
|
||||
if FILTER_CUSTOM_REOLUTION != -1:
|
||||
@ -297,6 +303,8 @@ class DownloadTracker:
|
||||
Args:
|
||||
available_video (str): The URL of the video to be downloaded.
|
||||
"""
|
||||
logging.info(f"class 'DownloadTracker'; call add_video() with parameter: {available_video}")
|
||||
|
||||
self.downloaded_video.append({
|
||||
'type': 'video',
|
||||
'url': available_video,
|
||||
@ -310,6 +318,8 @@ class DownloadTracker:
|
||||
Args:
|
||||
list_available_audio (list): A list of available audio track objects.
|
||||
"""
|
||||
logging.info(f"class 'DownloadTracker'; call add_audio() with parameter: {list_available_audio}")
|
||||
|
||||
for obj_audio in list_available_audio:
|
||||
|
||||
# Check if specific audio languages are set for download
|
||||
@ -337,6 +347,7 @@ class DownloadTracker:
|
||||
Args:
|
||||
list_available_subtitles (list): A list of available subtitle objects.
|
||||
"""
|
||||
logging.info(f"class 'DownloadTracker'; call add_subtitle() with parameter: {list_available_subtitles}")
|
||||
|
||||
for obj_subtitle in list_available_subtitles:
|
||||
|
||||
@ -377,6 +388,7 @@ class ContentDownloader:
|
||||
Args:
|
||||
downloaded_video (list): A list containing information about the video to download.
|
||||
"""
|
||||
logging.info(f"class 'ContentDownloader'; call download_video() with parameter: {downloaded_video}")
|
||||
|
||||
# Check if the video file already exists
|
||||
if not os.path.exists(downloaded_video[0].get('path')):
|
||||
@ -407,6 +419,8 @@ class ContentDownloader:
|
||||
Args:
|
||||
downloaded_audio (list): A list containing information about audio tracks to download.
|
||||
"""
|
||||
logging.info(f"class 'ContentDownloader'; call download_audio() with parameter: {downloaded_audio}")
|
||||
|
||||
for obj_audio in downloaded_audio:
|
||||
folder_name = os.path.dirname(obj_audio.get('path'))
|
||||
|
||||
@ -435,6 +449,8 @@ class ContentDownloader:
|
||||
Args:
|
||||
downloaded_subtitle (list): A list containing information about subtitles to download.
|
||||
"""
|
||||
logging.info(f"class 'ContentDownloader'; call download_subtitle() with parameter: {downloaded_subtitle}")
|
||||
|
||||
for obj_subtitle in downloaded_subtitle:
|
||||
sub_language = obj_subtitle.get('language')
|
||||
|
||||
@ -670,6 +686,10 @@ class HLS_Downloader:
|
||||
is_playlist_url (bool): Flag indicating if the m3u8_playlist is a URL.
|
||||
is_index_url (bool): Flag indicating if the m3u8_index is a URL.
|
||||
"""
|
||||
if ((m3u8_playlist == None or m3u8_playlist == "") and output_filename is None) or ((m3u8_index == None or m3u8_index == "") and output_filename is None):
|
||||
logging.info(f"class 'HLS_Downloader'; call __init__(); no parameter")
|
||||
sys.exit(0)
|
||||
|
||||
self.output_filename = self._generate_output_filename(output_filename, m3u8_playlist, m3u8_index)
|
||||
self.path_manager = PathManager(self.output_filename)
|
||||
self.download_tracker = DownloadTracker(self.path_manager)
|
||||
@ -684,6 +704,13 @@ class HLS_Downloader:
|
||||
self.expected_real_time = None
|
||||
self.instace_parserClass = M3U8_Parser()
|
||||
|
||||
self.request_m3u8_playlist = None
|
||||
self.request_m3u8_index = None
|
||||
if (m3u8_playlist == None or m3u8_playlist == ""):
|
||||
self.request_m3u8_index = HttpClient().get(self.m3u8_index)
|
||||
if (m3u8_index == None or m3u8_index == ""):
|
||||
self.request_m3u8_playlist = HttpClient().get(self.m3u8_playlist)
|
||||
|
||||
def _generate_output_filename(self, output_filename, m3u8_playlist, m3u8_index):
|
||||
"""
|
||||
Generates a valid output filename based on provided parameters.
|
||||
@ -699,6 +726,7 @@ class HLS_Downloader:
|
||||
root_path = config_manager.get('DEFAULT', 'root_path')
|
||||
new_filename = None
|
||||
new_folder = os.path.join(root_path, "undefined")
|
||||
logging.info(f"class 'HLS_Downloader'; call _generate_output_filename(); destination folder: {new_folder}")
|
||||
|
||||
# Auto-generate output file name if not present
|
||||
if (output_filename is None) or ("mp4" not in output_filename):
|
||||
@ -727,7 +755,8 @@ class HLS_Downloader:
|
||||
# Parse to only ASCII for compatibility across platforms
|
||||
new_filename = os.path.join(folder, base_name)
|
||||
new_filename = unidecode(new_filename)
|
||||
|
||||
|
||||
logging.info(f"class 'HLS_Downloader'; call _generate_output_filename(); return path: {new_filename}")
|
||||
return new_filename
|
||||
|
||||
def start(self):
|
||||
@ -736,18 +765,17 @@ class HLS_Downloader:
|
||||
"""
|
||||
if os.path.exists(self.output_filename):
|
||||
console.log("[red]Output file already exists.")
|
||||
return
|
||||
return 400
|
||||
|
||||
self.path_manager.create_directories()
|
||||
|
||||
# Determine whether to process a playlist or index
|
||||
if self.m3u8_playlist:
|
||||
if self.m3u8_playlist is not None:
|
||||
if self.request_m3u8_playlist != 404:
|
||||
logging.info(f"class 'HLS_Downloader'; call start(); parse m3u8 data")
|
||||
|
||||
# Parse data from url and check if is a master playlist
|
||||
test_raw_content = HttpClient().get(self.m3u8_playlist)
|
||||
if test_raw_content != 404:
|
||||
self.instace_parserClass.parse_data(uri=self.m3u8_playlist, raw_content=test_raw_content)
|
||||
self.instace_parserClass.parse_data(uri=self.m3u8_playlist, raw_content=self.request_m3u8_playlist)
|
||||
is_masterPlaylist = self.instace_parserClass.is_master_playlist
|
||||
|
||||
# Check if it's a real master playlist
|
||||
@ -766,18 +794,19 @@ class HLS_Downloader:
|
||||
'url': self.m3u8_playlist
|
||||
}
|
||||
|
||||
else:
|
||||
console.log("[red]Error: URL passed to M3U8_Parser is an index playlist; expected a master playlist. Crucimorfo strikes again!")
|
||||
else:
|
||||
console.log("[red]Error: URL passed to M3U8_Parser is an index playlist; expected a master playlist. Crucimorfo strikes again!")
|
||||
console.log("[red]Error: m3u8_playlist failed request")
|
||||
else:
|
||||
console.log("[red]Error: m3u8_playlist is None")
|
||||
|
||||
elif self.m3u8_index:
|
||||
if self.m3u8_index is not None:
|
||||
if self.request_m3u8_index != 404:
|
||||
logging.info(f"class 'HLS_Downloader'; call start(); parse m3u8 data")
|
||||
|
||||
# Parse data from url and check if is a master playlist
|
||||
test_raw_content = HttpClient().get(self.m3u8_index)
|
||||
if test_raw_content != 404:
|
||||
self.instace_parserClass.parse_data(uri=self.m3u8_index, raw_content=test_raw_content)
|
||||
self.instace_parserClass.parse_data(uri=self.m3u8_index, raw_content=self.request_m3u8_index)
|
||||
is_masterPlaylist = self.instace_parserClass.is_master_playlist
|
||||
|
||||
# Check if it's a real index playlist
|
||||
@ -792,12 +821,13 @@ class HLS_Downloader:
|
||||
'url': self.m3u8_index
|
||||
}
|
||||
|
||||
else:
|
||||
console.log("[red]Error: URL passed to M3U8_Parser is an master playlist; expected a index playlist. Crucimorfo strikes again!")
|
||||
else:
|
||||
console.log("[red]Error: URL passed to M3U8_Parser is an master playlist; expected a index playlist. Crucimorfo strikes again!")
|
||||
console.log("[red]Error: m3u8_index failed request")
|
||||
else:
|
||||
console.log("[red]Error: m3u8_index is None")
|
||||
|
||||
|
||||
def _clean(self, out_path: str) -> None:
|
||||
"""
|
||||
Cleans up temporary files and folders after downloading and processing.
|
||||
@ -874,20 +904,20 @@ class HLS_Downloader:
|
||||
else:
|
||||
logging.info("Video file converted already exists.")
|
||||
|
||||
def _process_playlist(self):
|
||||
def _valida_playlist(self):
|
||||
"""
|
||||
Processes the m3u8 playlist to download video, audio, and subtitles.
|
||||
Validates the m3u8 playlist content, saves it to a temporary file, and collects playlist information.
|
||||
"""
|
||||
logging.info("class 'HLS_Downloader'; call _valida_playlist()")
|
||||
|
||||
# Retrieve the m3u8 playlist content
|
||||
if self.is_playlist_url:
|
||||
response_text = HttpClient(headers=headers_index).get(self.m3u8_playlist)
|
||||
|
||||
if response_text != 404:
|
||||
m3u8_playlist_text = response_text
|
||||
if self.request_m3u8_playlist != 404:
|
||||
m3u8_playlist_text = self.request_m3u8_playlist
|
||||
m3u8_url_fixer.set_playlist(self.m3u8_playlist)
|
||||
|
||||
else:
|
||||
logging.info(f"class 'HLS_Downloader'; call _process_playlist(); return 404")
|
||||
return 404
|
||||
|
||||
else:
|
||||
@ -907,6 +937,12 @@ class HLS_Downloader:
|
||||
else:
|
||||
self.content_extractor.start("https://fake.com", m3u8_playlist_text)
|
||||
|
||||
def _process_playlist(self):
|
||||
"""
|
||||
Processes the m3u8 playlist to download video, audio, and subtitles.
|
||||
"""
|
||||
self._valida_playlist()
|
||||
|
||||
# Add downloaded elements to the tracker
|
||||
self.download_tracker.add_video(self.content_extractor.m3u8_index)
|
||||
self.download_tracker.add_audio(self.content_extractor.list_available_audio)
|
||||
@ -941,4 +977,3 @@ class HLS_Downloader:
|
||||
|
||||
# Clean up temporary files and directories
|
||||
self._clean(self.content_joiner.converted_out_path)
|
||||
|
@ -609,7 +609,6 @@ class M3U8_Parser:
|
||||
"""
|
||||
|
||||
try:
|
||||
|
||||
for segment in m3u8_obj.segments:
|
||||
|
||||
# Parse key
|
||||
|
@ -1,5 +1,5 @@
|
||||
__title__ = 'StreamingCommunity'
|
||||
__version__ = 'v1.6.0'
|
||||
__version__ = 'v1.7.0'
|
||||
__author__ = 'Lovi-0'
|
||||
__description__ = 'A command-line program to download film'
|
||||
__copyright__ = 'Copyright 2024'
|
||||
|
@ -9,11 +9,15 @@ sys.path.append(src_path)
|
||||
|
||||
|
||||
# Import
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.logger import Logger
|
||||
from Src.Lib.Downloader import HLS_Downloader
|
||||
|
||||
|
||||
# Test
|
||||
HLS_Downloader(
|
||||
start_message()
|
||||
logger = Logger()
|
||||
print("Return: ", HLS_Downloader(
|
||||
output_filename="",
|
||||
m3u8_playlist=""
|
||||
).start()
|
||||
m3u8_index=""
|
||||
).start())
|
@ -9,11 +9,15 @@ sys.path.append(src_path)
|
||||
|
||||
|
||||
# Import
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.logger import Logger
|
||||
from Src.Lib.Downloader import MP4_downloader
|
||||
|
||||
|
||||
# Test
|
||||
MP4_downloader(
|
||||
start_message()
|
||||
logger = Logger()
|
||||
print("Return: ", MP4_downloader(
|
||||
"",
|
||||
".\Video\undefined.mp4"
|
||||
)
|
||||
))
|
||||
|
@ -9,10 +9,14 @@ sys.path.append(src_path)
|
||||
|
||||
|
||||
# Import
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.logger import Logger
|
||||
from Src.Lib.Downloader import TOR_downloader
|
||||
|
||||
|
||||
# Test
|
||||
start_message()
|
||||
logger = Logger()
|
||||
manager = TOR_downloader()
|
||||
|
||||
magnet_link = "magnet:?x"
|
||||
|
Loading…
x
Reference in New Issue
Block a user