From c622cbbdf02852675fd0f05b4472a68fa55f6e17 Mon Sep 17 00:00:00 2001
From: Dark1291
Date: Wed, 5 Feb 2025 13:20:20 +0100
Subject: [PATCH] Add USSR (#247)
* Add betterer formattation and ffmpeg installation testing
* Update readme
* Better formattation
* Update readme with tg
* Change tg folder
* Add RRC
* Rename ...
---
README.md | 108 +-
.../Api/Site/1337xx/__init__.py | 30 +-
StreamingCommunity/Api/Site/1337xx/site.py | 25 -
StreamingCommunity/Api/Site/1337xx/title.py | 20 +-
.../Site/altadefinizionegratis/__init__.py | 45 +-
.../Api/Site/altadefinizionegratis/costant.py | 4 +-
.../Api/Site/altadefinizionegratis/film.py | 61 +-
.../Api/Site/altadefinizionegratis/site.py | 20 +-
.../Api/Site/animeunity/__init__.py | 47 +-
.../Api/Site/animeunity/costant.py | 4 +-
.../Api/Site/animeunity/film_serie.py | 68 +-
.../Api/Site/animeunity/site.py | 21 +-
StreamingCommunity/Api/Site/cb01new/film.py | 26 +-
.../Api/Site/guardaserie/series.py | 37 +-
.../Api/Site/mostraguarda/film.py | 31 +-
.../Api/Site/streamingcommunity/__init__.py | 41 +-
.../Api/Site/streamingcommunity/costant.py | 4 +-
.../Api/Site/streamingcommunity/film.py | 64 +-
.../Api/Site/streamingcommunity/series.py | 128 +-
.../Api/Site/streamingcommunity/site.py | 23 +-
StreamingCommunity/HelpTg/config.json | 91 --
StreamingCommunity/HelpTg/request_manager.py | 79 --
StreamingCommunity/HelpTg/requirements.txt | 14 -
.../Lib/Downloader/HLS/downloader.py | 1249 ++++++-----------
.../Lib/Downloader/HLS/segments.py | 18 +-
.../Lib/Downloader/MP4/downloader.py | 51 +-
.../Lib/Downloader/TOR/downloader.py | 1 +
StreamingCommunity/Lib/FFmpeg/command.py | 8 +-
StreamingCommunity/Lib/M3U8/estimator.py | 16 +-
.../{HelpTg => TelegramHelp}/__init__.py | 0
.../TelegramHelp/request_manager.py | 82 ++
.../{HelpTg => TelegramHelp}/session.py | 20 +-
.../{HelpTg => TelegramHelp}/telegram_bot.py | 372 ++---
StreamingCommunity/Upload/update.py | 5 +-
StreamingCommunity/Upload/version.py | 2 +-
StreamingCommunity/Util/_jsonConfig.py | 2 +-
StreamingCommunity/Util/message.py | 46 +-
StreamingCommunity/Util/os.py | 5 +-
StreamingCommunity/Util/table.py | 2 +-
StreamingCommunity/run.py | 99 +-
Test/Download/HLS.py | 4 +-
Test/Util/ffmpegVersion.py | 29 +
Test/Util/{oss.py => osPath.py} | 0
config.json | 16 +-
requirements.txt | 6 +-
test_run.py | 20 +-
46 files changed, 1203 insertions(+), 1841 deletions(-)
delete mode 100644 StreamingCommunity/HelpTg/config.json
delete mode 100644 StreamingCommunity/HelpTg/request_manager.py
delete mode 100644 StreamingCommunity/HelpTg/requirements.txt
rename StreamingCommunity/{HelpTg => TelegramHelp}/__init__.py (100%)
create mode 100644 StreamingCommunity/TelegramHelp/request_manager.py
rename StreamingCommunity/{HelpTg => TelegramHelp}/session.py (69%)
mode change 100755 => 100644
rename StreamingCommunity/{HelpTg => TelegramHelp}/telegram_bot.py (58%)
create mode 100644 Test/Util/ffmpegVersion.py
rename Test/Util/{oss.py => osPath.py} (100%)
diff --git a/README.md b/README.md
index 2a17245..34e7bec 100644
--- a/README.md
+++ b/README.md
@@ -9,23 +9,23 @@
-
-
+
+
-
-
+
+
-
+
-
-
+
+
@@ -44,7 +44,10 @@
- π© [Request](#requests-settings)
- π₯ [Download](#m3u8_download-settings)
- π [Parser](#m3u8_parser-settings)
+- π [Command](#command)
+- π» [Examples of terminal](#examples-of-terminal-usage)
- π³ [Docker](#docker)
+- π [Telegram Usage](#telegram-usage)
- π [Tutorial](#tutorials)
- π [To do](#to-do)
- π¬ [Support](#support)
@@ -55,16 +58,13 @@
# Installation
-
+
-
-
-
-
+
-
+
@@ -286,25 +286,37 @@ The configuration file is divided into several main sections:
`
`
- `movie_folder_name`: The name of the subdirectory where movies will be stored.
+ * Can be changed from terminal with `--movie_folder_name`
+
+
- `serie_folder_name`: The name of the subdirectory where TV series will be stored.
+ * Can be changed from terminal with `--serie_folder_name`
+
- `map_episode_name`: Template for TV series episode filenames
### Episode name usage:
You can choose different vars:
-
-
* `%(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
`
`
+ * Can be changed from terminal with `--map_episode_name`
+
- `add_siteName`: If set to true, appends the site_name to the root path before the movie and serie folders.
-- `disable_searchDomain`: If set to true, disables the search for a new domain for all sites.
-- `not_close`: If set to true, keeps the program running after the download is complete.
+ * Can be changed from terminal with `--add_siteName true/false`
+
+- `disable_searchDomain`: If set to true, disables the search for a new domain for all sites.
+ * Can be changed from terminal with `--disable_searchDomain true/false`
+
+
+- `not_close`: If set to true, keeps the program running after the download is complete.
+ * Can be changed from terminal with `--not_close true/false`
+
### qBittorrent Configuration
@@ -350,7 +362,13 @@ The configuration file is divided into several main sections:
- `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
+ * Can be changed from terminal with `--default_video_worker `
+
+
- `default_audio_workser`: Number of threads for audio download
+ * Can be changed from terminal with `--default_audio_worker `
+
+
- `cleanup_tmp_folder`: Remove temporary .ts files after download
> [!IMPORTANT]
@@ -361,6 +379,9 @@ The configuration file is divided into several main sections:
### Language Settings
The following codes can be used for `specific_list_audio` and `specific_list_subtitles`:
+* Can be changed from terminal with `--specific_list_audio ita,eng` for audio
+* Can be changed from terminal with `--specific_list_subtitles eng,spa` for subtitles
+
```
ara - Arabic eng - English ita - Italian por - Portuguese
@@ -432,6 +453,21 @@ You can download VLC Media Player from the [official website](https://www.videol
- Enter a season number followed by `-*` to download from that season to the end.
* **Example:** `3-*` will download from *Season 3* to the final season.
+# Examples of terminal usage
+
+```bash
+# Change video and audio workers
+python test_run.py --default_video_worker 8 --default_audio_worker 8
+
+# Set specific languages
+python test_run.py --specific_list_audio ita,eng --specific_list_subtitles eng,spa
+
+# Keep console open after download
+python test_run.py --not_close true
+
+# Disable domain search and add site name
+python test_run.py --disable_searchDomain true --add_siteName true
+```
# Docker
@@ -466,15 +502,43 @@ 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.
+# Telegram Usage
+
+## Configuration
+
+You need to create an .env file and enter your Telegram token
+
+and user ID to authorize only one user to use it
+
+## .env Example:
+
+```
+TOKEN_TELEGRAM=IlTuo2131TOKEN$12D3Telegram
+AUTHORIZED_USER_ID=12345678
+DEBUG=False
+```
+
+## Install Python Dependencies
+
+```bash
+pip install -r requirements.txt
+```
+
+## On Linux/MacOS:
+
+```bash
+python3 telegram_bot.py
+```
+
# Website Status
| Website | Status |
|:-------------------|:------:|
| [1337xx](https://1337xx.to/) | β
|
-| [AltadefinizioneGratis](https://altadefinizionegratis.site/) | β
|
+| [AltadefinizioneGratis](https://altadefinizionegratis.pro/) | β
|
| [AnimeUnity](https://animeunity.so/) | β
|
| [Ilcorsaronero](https://ilcorsaronero.link/) | β
|
-| [CB01New](https://cb01new.video/) | β
|
+| [CB01New](https://cb01new.media/) | β
|
| [DDLStreamItaly](https://ddlstreamitaly.co/) | β
|
| [GuardaSerie](https://guardaserie.meme/) | β
|
| [MostraGuarda](https://mostraguarda.stream/) | β
|
@@ -490,7 +554,7 @@ The `run-container` command mounts also the `config.json` file, so any change to
# To Do
-- Finish [website API](https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1)
+- Finish [website API](https://github.com/Abc-dc/StreamingCommunity/tree/test_gui_1)
# Contributing
@@ -508,6 +572,6 @@ This software is provided "as is", without warranty of any kind, express or impl
## Contributors
-
-
+
+
diff --git a/StreamingCommunity/Api/Site/1337xx/__init__.py b/StreamingCommunity/Api/Site/1337xx/__init__.py
index bad0b7c..f0d5725 100644
--- a/StreamingCommunity/Api/Site/1337xx/__init__.py
+++ b/StreamingCommunity/Api/Site/1337xx/__init__.py
@@ -1,5 +1,7 @@
# 02.07.24
+import sys
+import subprocess
from urllib.parse import quote_plus
@@ -11,12 +13,6 @@ from StreamingCommunity.Util.console import console, msg
from .site import title_search, run_get_select_title, media_search_manager
from .title import download_title
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-import sys
-import subprocess
# Variable
indice = 8
@@ -31,25 +27,9 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
"""
Main function of the application for film and series.
"""
- if TELEGRAM_BOT:
- bot = get_bot_instance()
-
- # Chiedi la scelta all'utente con il bot Telegram
- string_to_search = bot.ask(
- "key_search",
- f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
- None
- )
-
- if string_to_search == 'back':
- # Riavvia lo script
- # Chiude il processo attuale e avvia una nuova istanza dello script
- subprocess.Popen([sys.executable] + sys.argv)
- sys.exit()
- else:
- if string_to_search is None:
+ if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
-
+
# Search on database
len_database = title_search(quote_plus(string_to_search))
@@ -69,4 +49,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
- search()
+ search()
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/1337xx/site.py b/StreamingCommunity/Api/Site/1337xx/site.py
index a472616..6d683cb 100644
--- a/StreamingCommunity/Api/Site/1337xx/site.py
+++ b/StreamingCommunity/Api/Site/1337xx/site.py
@@ -17,10 +17,6 @@ from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.Util import search_domain
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-
# Variable
from .costant import SITE_NAME, DOMAIN_NOW
@@ -40,10 +36,6 @@ def title_search(word_to_search: str) -> int:
Returns:
- int: The number of titles found.
"""
-
- if TELEGRAM_BOT:
- bot = get_bot_instance()
-
media_search_manager.clear()
table_show_manager.clear()
@@ -69,10 +61,6 @@ def title_search(word_to_search: str) -> int:
# Create soup and find table
soup = BeautifulSoup(response.text, "html.parser")
- if TELEGRAM_BOT:
- # Inizializza la lista delle scelte
- choices = []
-
for tr in soup.find_all('tr'):
try:
@@ -85,22 +73,9 @@ def title_search(word_to_search: str) -> int:
'size': tr.find_all("td")[-2].get_text(strip=True)
}
- if TELEGRAM_BOT:
- # Crea una stringa formattata per ogni scelta con numero
- choice_text = f"{len(choices)} - {title_info.get('name')} ({title_info.get('type')}) - {title_info.get('date')}"
- choices.append(choice_text)
-
- media_search_manager.add_media(title_info)
-
except Exception as e:
print(f"Error parsing a film entry: {e}")
- if TELEGRAM_BOT:
- # Se ci sono scelte, inviale a Telegram
- if choices:
- # Invio a telegram la lista
- bot.send_message(f"Lista dei risultati:", choices)
-
# Return the number of titles found
return media_search_manager.get_length()
diff --git a/StreamingCommunity/Api/Site/1337xx/title.py b/StreamingCommunity/Api/Site/1337xx/title.py
index c299f40..c0fd43e 100644
--- a/StreamingCommunity/Api/Site/1337xx/title.py
+++ b/StreamingCommunity/Api/Site/1337xx/title.py
@@ -23,11 +23,6 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Config
from .costant import DOMAIN_NOW, SITE_NAME, MOVIE_FOLDER
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.HelpTg.session import get_session, updateScriptId, deleteScriptId
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
def download_title(select_title: MediaItem):
"""
@@ -36,13 +31,6 @@ def download_title(select_title: MediaItem):
Parameters:
- select_title (MediaItem): The media item to be downloaded. This should be an instance of the MediaItem class, containing attributes like `name` and `url`.
"""
- if TELEGRAM_BOT:
- bot = get_bot_instance()
- bot.send_message(f"Download in corso:\n{select_title.name}", None)
- script_id = get_session()
- if script_id != "unknown":
- updateScriptId(script_id, select_title.name)
-
start_message()
console.print(f"[yellow]Download: [red]{select_title.name} \n")
print()
@@ -72,10 +60,4 @@ def download_title(select_title: MediaItem):
manager = TOR_downloader()
manager.add_magnet_link(final_url)
manager.start_download()
- manager.move_downloaded_files(mp4_path)
-
- if TELEGRAM_BOT:
- # Delete script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
+ manager.move_downloaded_files(mp4_path)
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py b/StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py
index 40c86df..df2737f 100644
--- a/StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py
+++ b/StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py
@@ -1,20 +1,19 @@
# 26.05.24
+import sys
+import subprocess
from urllib.parse import quote_plus
# Internal utilities
from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
from .site import title_search, run_get_select_title, media_search_manager
from .film import download_film
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
# Variable
indice = 2
@@ -22,33 +21,34 @@ _useFor = "film"
_deprecate = False
_priority = 2
_engineDownload = "hls"
-from .costant import SITE_NAME
+from .costant import SITE_NAME, TELEGRAM_BOT
def search(string_to_search: str = None, get_onylDatabase: bool = False):
"""
Main function of the application for film and series.
"""
-
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
- if string_to_search is None:
- # Chiedi la scelta all'utente con il bot Telegram
- string_to_search = bot.ask(
- "key_search",
- f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
- None
- )
+ if string_to_search is None:
+
+ # Chiedi la scelta all'utente con il bot Telegram
+ string_to_search = bot.ask(
+ "key_search",
+ f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
+ None
+ )
+
+ if string_to_search == 'back':
+ # Riavvia lo script
+ # Chiude il processo attuale e avvia una nuova istanza dello script
+ subprocess.Popen([sys.executable] + sys.argv)
+ sys.exit()
- if string_to_search == 'back':
- # Riavvia lo script
- # Chiude il processo attuale e avvia una nuova istanza dello script
- subprocess.Popen([sys.executable] + sys.argv)
- sys.exit()
else:
- if string_to_search is None:
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
+ if string_to_search is None:
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
# Search on database
len_database = title_search(quote_plus(string_to_search))
@@ -66,9 +66,8 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
download_film(select_title)
else:
-
if TELEGRAM_BOT:
- bot.send_message(f"Nessun risultato trovato riprova", None)
+ bot.send_message(f"Nessun risultato trovato riprova", None)
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
diff --git a/StreamingCommunity/Api/Site/altadefinizionegratis/costant.py b/StreamingCommunity/Api/Site/altadefinizionegratis/costant.py
index cb76a8b..e4d4d9a 100644
--- a/StreamingCommunity/Api/Site/altadefinizionegratis/costant.py
+++ b/StreamingCommunity/Api/Site/altadefinizionegratis/costant.py
@@ -16,4 +16,6 @@ MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_fold
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
- MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))
\ No newline at end of file
+ MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))
+
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/altadefinizionegratis/film.py b/StreamingCommunity/Api/Site/altadefinizionegratis/film.py
index 7a91b53..d7ff463 100644
--- a/StreamingCommunity/Api/Site/altadefinizionegratis/film.py
+++ b/StreamingCommunity/Api/Site/altadefinizionegratis/film.py
@@ -1,19 +1,18 @@
# 26.05.24
import os
-import time
# Internal utilities
-from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.Util.console import console
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Lib.Downloader import HLS_Downloader
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.session import get_session, updateScriptId, deleteScriptId
# Logic class
-from StreamingCommunity.Api.Template.Util import execute_search
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
@@ -22,13 +21,7 @@ from StreamingCommunity.Api.Player.supervideo import VideoSource
# Config
-from .costant import MOVIE_FOLDER
-
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.HelpTg.session import get_session, updateScriptId, deleteScriptId
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
+from .costant import MOVIE_FOLDER, TELEGRAM_BOT
def download_film(select_title: MediaItem) -> str:
@@ -42,17 +35,14 @@ def download_film(select_title: MediaItem) -> str:
Return:
- str: output path
"""
-
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
+ bot.send_message(f"Download in corso:\n{select_title.name}", None)
- # Invio a telegram
- bot.send_message(f"Download in corso:\n{select_title.name}", None)
-
- # Get script_id
- script_id = get_session()
- if script_id != "unknown":
- updateScriptId(script_id, select_title.name)
+ # Get script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ updateScriptId(script_id, select_title.name)
# Start message and display film information
start_message()
@@ -71,26 +61,21 @@ def download_film(select_title: MediaItem) -> str:
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, title_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, title_name)
).start()
-
- """if r_proc == 404:
- time.sleep(2)
-
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
if TELEGRAM_BOT:
- # Delete script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
+
+ # Delete script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ deleteScriptId(script_id)
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- return os.path.join(mp4_path, title_name)
\ No newline at end of file
+ return r_proc['path']
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/altadefinizionegratis/site.py b/StreamingCommunity/Api/Site/altadefinizionegratis/site.py
index 8c03bbe..3b1a1a3 100644
--- a/StreamingCommunity/Api/Site/altadefinizionegratis/site.py
+++ b/StreamingCommunity/Api/Site/altadefinizionegratis/site.py
@@ -26,7 +26,7 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
from StreamingCommunity.Util._jsonConfig import config_manager
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
@@ -42,7 +42,7 @@ def title_search(title_search: str) -> int:
int: The number of titles found.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
media_search_manager.clear()
table_show_manager.clear()
@@ -71,9 +71,9 @@ def title_search(title_search: str) -> int:
# Create soup and find table
soup = BeautifulSoup(response.text, "html.parser")
+ # Inizializza la lista delle scelte
if TELEGRAM_BOT:
- # Inizializza la lista delle scelte
- choices = []
+ choices = []
for row in soup.find_all('div', class_='col-lg-3 col-md-3 col-xs-4'):
try:
@@ -94,19 +94,17 @@ def title_search(title_search: str) -> int:
media_search_manager.add_media(film_info)
if TELEGRAM_BOT:
- # Crea una stringa formattata per ogni scelta con numero
- choice_text = f"{len(choices)} - {film_info.get('name')} ({film_info.get('url')}) {film_info.get('score')}"
- choices.append(choice_text)
+ # Crea una stringa formattata per ogni scelta con numero
+ choice_text = f"{len(choices)} - {film_info.get('name')} ({film_info.get('url')}) {film_info.get('score')}"
+ choices.append(choice_text)
except AttributeError as e:
print(f"Error parsing a film entry: {e}")
if TELEGRAM_BOT:
- # Se ci sono scelte, inviale a Telegram
- if choices:
- # Invio a telegram la lista
- bot.send_message(f"Lista dei risultati:", choices)
+ if choices:
+ bot.send_message(f"Lista dei risultati:", choices)
# Return the number of titles found
return media_search_manager.get_length()
diff --git a/StreamingCommunity/Api/Site/animeunity/__init__.py b/StreamingCommunity/Api/Site/animeunity/__init__.py
index 6c83ca5..5a12833 100644
--- a/StreamingCommunity/Api/Site/animeunity/__init__.py
+++ b/StreamingCommunity/Api/Site/animeunity/__init__.py
@@ -1,23 +1,19 @@
# 21.05.24
+import sys
+import subprocess
from urllib.parse import quote_plus
# Internal utilities
from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
from .site import title_search, run_get_select_title, media_search_manager
from .film_serie import download_film, download_series
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-import sys
-import subprocess
-
# Variable
indice = 1
@@ -25,30 +21,32 @@ _useFor = "anime"
_deprecate = False
_priority = 2
_engineDownload = "mp4"
-from .costant import SITE_NAME
+from .costant import SITE_NAME, TELEGRAM_BOT
def search(string_to_search: str = None, get_onylDatabase: bool = False):
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
- if string_to_search is None:
- # Chiedi la scelta all'utente con il bot Telegram
- string_to_search = bot.ask(
- "key_search",
- f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
- None
- )
+ if string_to_search is None:
+
+ # Chiedi la scelta all'utente con il bot Telegram
+ string_to_search = bot.ask(
+ "key_search",
+ f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
+ None
+ )
+
+ if string_to_search == 'back':
+ # Riavvia lo script
+ # Chiude il processo attuale e avvia una nuova istanza dello script
+ subprocess.Popen([sys.executable] + sys.argv)
+ sys.exit()
- if string_to_search == 'back':
- # Riavvia lo script
- # Chiude il processo attuale e avvia una nuova istanza dello script
- subprocess.Popen([sys.executable] + sys.argv)
- sys.exit()
else:
- if string_to_search is None:
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
+ if string_to_search is None:
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
# Search on database
len_database = title_search(string_to_search)
@@ -69,9 +67,8 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
download_series(select_title)
else:
-
if TELEGRAM_BOT:
- bot.send_message(f"Nessun risultato trovato riprova", None)
+ bot.send_message(f"Nessun risultato trovato riprova", None)
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
diff --git a/StreamingCommunity/Api/Site/animeunity/costant.py b/StreamingCommunity/Api/Site/animeunity/costant.py
index 4bb90f8..1702282 100644
--- a/StreamingCommunity/Api/Site/animeunity/costant.py
+++ b/StreamingCommunity/Api/Site/animeunity/costant.py
@@ -16,4 +16,6 @@ ANIME_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'anime_fold
if config_manager.get_bool("DEFAULT", "add_siteName"):
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))
- ANIME_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'anime_folder_name'))
\ No newline at end of file
+ ANIME_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'anime_folder_name'))
+
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/animeunity/film_serie.py b/StreamingCommunity/Api/Site/animeunity/film_serie.py
index 84bd5af..92b74ae 100644
--- a/StreamingCommunity/Api/Site/animeunity/film_serie.py
+++ b/StreamingCommunity/Api/Site/animeunity/film_serie.py
@@ -10,6 +10,8 @@ from StreamingCommunity.Util.console import console, msg
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
from StreamingCommunity.Lib.Downloader import MP4_downloader
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.session import get_session, updateScriptId, deleteScriptId
# Logic class
@@ -23,14 +25,10 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSourceAnime
# Variable
-from .costant import SITE_NAME, ANIME_FOLDER, MOVIE_FOLDER
+from .costant import SITE_NAME, ANIME_FOLDER, MOVIE_FOLDER, TELEGRAM_BOT
KILL_HANDLER = bool(False)
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.HelpTg.session import get_session, updateScriptId, deleteScriptId
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
+
def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_source: VideoSourceAnime) -> tuple[str,bool]:
"""
@@ -44,9 +42,8 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
- bool: kill handler status
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
-
# Get information about the selected episode
obj_episode = scrape_serie.get_info_episode(index_select)
@@ -57,13 +54,13 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
console.print("[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
if TELEGRAM_BOT:
- # Invio a telegram
- bot.send_message(f"Download in corso:\nTitolo:{scrape_serie.series_name}\nEpisodio: {obj_episode.number}", None)
+ bot.send_message(f"Download in corso:\nTitolo:{scrape_serie.series_name}\nEpisodio: {obj_episode.number}", None)
+
+ # Get script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ updateScriptId(script_id, f"{scrape_serie.series_name} - E{obj_episode.number}")
- # Get script_id
- script_id = get_session()
- if script_id != "unknown":
- updateScriptId(script_id, f"{scrape_serie.series_name} - E{obj_episode.number}")
# Collect mp4 url
video_source.get_embed(obj_episode.id)
@@ -72,19 +69,15 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
title_name = f"{scrape_serie.series_name}_EP_{obj_episode.number}.mp4"
if scrape_serie.is_series:
- mp4_path = os_manager.get_sanitize_path(
- os.path.join(ANIME_FOLDER, scrape_serie.series_name)
- )
+ mp4_path = os_manager.get_sanitize_path(os.path.join(ANIME_FOLDER, scrape_serie.series_name))
+
else:
- mp4_path = os_manager.get_sanitize_path(
- os.path.join(MOVIE_FOLDER, scrape_serie.series_name)
- )
+ mp4_path = os_manager.get_sanitize_path(os.path.join(MOVIE_FOLDER, scrape_serie.series_name))
# Create output folder
os_manager.create_path(mp4_path)
# Start downloading
-
r_proc = MP4_downloader(
url=str(video_source.src_mp4).strip(),
path=os.path.join(mp4_path, title_name)
@@ -109,7 +102,8 @@ def download_series(select_title: MediaItem):
- tv_name (str): The name of the TV series.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
+
scrape_serie = ScrapeSerieAnime(SITE_NAME)
video_source = VideoSourceAnime(SITE_NAME)
@@ -121,19 +115,19 @@ def download_series(select_title: MediaItem):
console.print(f"[cyan]Episodes find: [red]{episoded_count}")
if TELEGRAM_BOT:
- console.print(f"\n[cyan]Insert media [red]index [yellow]or [red](*) [cyan]to download all media [yellow]or [red][1-2] [cyan]or [red][3-*] [cyan]for a range of media")
+ console.print(f"\n[cyan]Insert media [red]index [yellow]or [red](*) [cyan]to download all media [yellow]or [red][1-2] [cyan]or [red][3-*] [cyan]for a range of media")
+ bot.send_message(f"Episodi trovati: {episoded_count}", None)
- # Invio a telegram
- bot.send_message(f"Episodi trovati: {episoded_count}", None)
+ last_command = bot.ask(
+ "select_title",
+ f"Inserisci l'indice del media o (*) per scaricare tutti i media, oppure [1-2] o [3-*] per un intervallo di media.",
+ None
+ )
- last_command = bot.ask(
- "select_title",
- f"Inserisci l'indice del media o (*) per scaricare tutti i media, oppure [1-2] o [3-*] per un intervallo di media.",
- None
- )
else:
- # Prompt user to select an episode index
- last_command = msg.ask("\n[cyan]Insert media [red]index [yellow]or [red](*) [cyan]to download all media [yellow]or [red][1-2] [cyan]or [red][3-*] [cyan]for a range of media")
+
+ # Prompt user to select an episode index
+ last_command = msg.ask("\n[cyan]Insert media [red]index [yellow]or [red](*) [cyan]to download all media [yellow]or [red][1-2] [cyan]or [red][3-*] [cyan]for a range of media")
# Manage user selection
list_episode_select = manage_selection(last_command, episoded_count)
@@ -151,12 +145,12 @@ def download_series(select_title: MediaItem):
kill_handler= download_episode(i_episode-1, scrape_serie, video_source)[1]
if TELEGRAM_BOT:
- bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
+ bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
- # Get script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
+ # Get script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ deleteScriptId(script_id)
def download_film(select_title: MediaItem):
diff --git a/StreamingCommunity/Api/Site/animeunity/site.py b/StreamingCommunity/Api/Site/animeunity/site.py
index 1ffc46b..d36411f 100644
--- a/StreamingCommunity/Api/Site/animeunity/site.py
+++ b/StreamingCommunity/Api/Site/animeunity/site.py
@@ -12,6 +12,7 @@ from bs4 import BeautifulSoup
from StreamingCommunity.Util.console import console
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Util.table import TVShowManager
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
@@ -19,14 +20,9 @@ from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.Util import search_domain
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-
# Variable
-from .costant import SITE_NAME, DOMAIN_NOW
+from .costant import SITE_NAME, DOMAIN_NOW, TELEGRAM_BOT
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
@@ -109,7 +105,7 @@ def title_search(title: str) -> int:
- int: A number containing the length of media search manager.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
media_search_manager.clear()
table_show_manager.clear()
@@ -154,9 +150,9 @@ def title_search(title: str) -> int:
except Exception as e:
console.print(f"Site: {SITE_NAME}, request search error: {e}")
+ # Inizializza la lista delle scelte
if TELEGRAM_BOT:
- # Inizializza la lista delle scelte
- choices = []
+ choices = []
for dict_title in response.json()['records']:
try:
@@ -180,11 +176,10 @@ def title_search(title: str) -> int:
except Exception as e:
print(f"Error parsing a film entry: {e}")
+
if TELEGRAM_BOT:
- # Se ci sono scelte, inviale a Telegram
- if choices:
- # Invio a telegram la lista
- bot.send_message(f"Lista dei risultati:", choices)
+ if choices:
+ bot.send_message(f"Lista dei risultati:", choices)
# Return the length of media search manager
return media_search_manager.get_length()
diff --git a/StreamingCommunity/Api/Site/cb01new/film.py b/StreamingCommunity/Api/Site/cb01new/film.py
index b886cac..249329d 100644
--- a/StreamingCommunity/Api/Site/cb01new/film.py
+++ b/StreamingCommunity/Api/Site/cb01new/film.py
@@ -7,12 +7,10 @@ import os
from StreamingCommunity.Util.console import console
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Lib.Downloader import HLS_Downloader
# Logic class
-from StreamingCommunity.Api.Template.Util import execute_search
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
@@ -34,8 +32,6 @@ def download_film(select_title: MediaItem) -> str:
Return:
- str: output path
"""
-
- # Start message and display film information
start_message()
console.print(f"[yellow]Download: [red]{select_title.name} \n")
console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
@@ -53,20 +49,14 @@ def download_film(select_title: MediaItem) -> str:
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, title_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, title_name)
).start()
- """if r_proc == 404:
- time.sleep(2)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
-
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
-
- return os.path.join(mp4_path, title_name)
\ No newline at end of file
+ return r_proc['path']
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/guardaserie/series.py b/StreamingCommunity/Api/Site/guardaserie/series.py
index 93a2658..7ce0442 100644
--- a/StreamingCommunity/Api/Site/guardaserie/series.py
+++ b/StreamingCommunity/Api/Site/guardaserie/series.py
@@ -7,13 +7,12 @@ import sys
# Internal utilities
from StreamingCommunity.Util.console import console, msg
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Util.table import TVShowManager
from StreamingCommunity.Lib.Downloader import HLS_Downloader
# Logic class
-from StreamingCommunity.Api.Template.Util import manage_selection, map_episode_title, dynamic_format_number, validate_selection, validate_episode_selection, execute_search
+from StreamingCommunity.Api.Template.Util import manage_selection, map_episode_title, dynamic_format_number, validate_selection, validate_episode_selection
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
@@ -46,6 +45,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
obj_episode = scape_info_serie.list_episodes[index_episode_selected - 1]
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.get('name')}\n")
console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
+
# Define filename and path for the downloaded video
mp4_name = f"{map_episode_title(scape_info_serie.tv_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4"
mp4_path = os.path.join(SERIES_FOLDER, scape_info_serie.tv_name, f"S{index_season_selected}")
@@ -58,23 +58,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, mp4_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, mp4_name)
).start()
- """if r_proc == 404:
- time.sleep(2)
-
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- return os.path.join(mp4_path, mp4_name)
+ return r_proc['path'], r_proc['stopped']
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False) -> None:
@@ -96,7 +91,11 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
# Download all episodes without asking
for i_episode in range(1, episodes_count + 1):
- download_video(index_season_selected, i_episode, scape_info_serie)
+ path, stopped = download_video(index_season_selected, i_episode, scape_info_serie)
+
+ if stopped:
+ break
+
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
else:
@@ -112,11 +111,11 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
return
# Download selected episodes
- stopped = bool(False)
for i_episode in list_episode_select:
+ path, stopped = download_video(index_season_selected, i_episode, scape_info_serie)
+
if stopped:
break
- download_video(index_season_selected, i_episode, scape_info_serie)
def download_series(dict_serie: MediaItem) -> None:
@@ -197,4 +196,4 @@ def display_episodes_list(obj_episode_manager) -> str:
console.print("\n[red]Quit [white]...")
sys.exit(0)
- return last_command
+ return last_command
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/mostraguarda/film.py b/StreamingCommunity/Api/Site/mostraguarda/film.py
index 2e04228..2d9ff36 100644
--- a/StreamingCommunity/Api/Site/mostraguarda/film.py
+++ b/StreamingCommunity/Api/Site/mostraguarda/film.py
@@ -2,7 +2,6 @@
import os
import sys
-import time
import logging
@@ -12,18 +11,13 @@ from bs4 import BeautifulSoup
# Internal utilities
-from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.Util.console import console
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Util.headers import get_headers
from StreamingCommunity.Lib.Downloader import HLS_Downloader
-# Logic class
-from StreamingCommunity.Api.Template.Util import execute_search
-
-
# Player
from StreamingCommunity.Api.Player.supervideo import VideoSource
@@ -51,6 +45,7 @@ def download_film(movie_details: Json_film) -> str:
start_message()
console.print(f"[yellow]Download: [red]{movie_details.title} \n")
console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
+
# Make request to main site
try:
url = f"https://{SITE_NAME}.{DOMAIN_NOW}/set-movie-a/{movie_details.imdb_id}"
@@ -82,20 +77,14 @@ def download_film(movie_details: Json_film) -> str:
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, title_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, title_name)
).start()
- """if r_proc == 404:
- time.sleep(2)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
-
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
-
- return os.path.join(mp4_path, title_name)
\ No newline at end of file
+ return r_proc['path']
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/__init__.py b/StreamingCommunity/Api/Site/streamingcommunity/__init__.py
index a0ad193..83aeb15 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/__init__.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/__init__.py
@@ -1,11 +1,13 @@
# 21.05.24
-from urllib.parse import quote_plus
-import subprocess
import sys
+import subprocess
+from urllib.parse import quote_plus
+
# Internal utilities
from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
@@ -13,10 +15,6 @@ from .site import get_version_and_domain, title_search, run_get_select_title, me
from .film import download_film
from .series import download_series
-# Telegram bot instance
-from StreamingCommunity.Util._jsonConfig import config_manager
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
# Variable
indice = 0
@@ -24,30 +22,31 @@ _useFor = "film_serie"
_deprecate = False
_priority = 1
_engineDownload = "hls"
-from .costant import SITE_NAME
+from .costant import SITE_NAME, TELEGRAM_BOT
def search(string_to_search: str = None, get_onylDatabase: bool = False):
"""
Main function of the application for film and series.
"""
-
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
- if string_to_search is None:
- # Chiedi la scelta all'utente con il bot Telegram
- string_to_search = bot.ask(
- "key_search",
- f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
- None
- )
+ if string_to_search is None:
+
+ # Chiedi la scelta all'utente con il bot Telegram
+ string_to_search = bot.ask(
+ "key_search",
+ f"Inserisci la parola da cercare\noppure π back per tornare alla scelta: ",
+ None
+ )
- if string_to_search == 'back':
- # Riavvia lo script
- # Chiude il processo attuale e avvia una nuova istanza dello script
- subprocess.Popen([sys.executable] + sys.argv)
- sys.exit()
+ if string_to_search == 'back':
+ # Riavvia lo script
+ # Chiude il processo attuale e avvia una nuova istanza dello script
+ subprocess.Popen([sys.executable] + sys.argv)
+ sys.exit()
+
else:
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/costant.py b/StreamingCommunity/Api/Site/streamingcommunity/costant.py
index cb76a8b..bb5f066 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/costant.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/costant.py
@@ -16,4 +16,6 @@ MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_fold
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
- MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))
\ No newline at end of file
+ MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))
+
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/film.py b/StreamingCommunity/Api/Site/streamingcommunity/film.py
index 80e1bc4..790c3ea 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/film.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/film.py
@@ -1,19 +1,18 @@
# 3.12.23
import os
-import time
# Internal utilities
-from StreamingCommunity.Util.console import console, msg
+from StreamingCommunity.Util.console import console
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Lib.Downloader import HLS_Downloader
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.session import get_session, updateScriptId, deleteScriptId
# Logic class
-from StreamingCommunity.Api.Template.Util import execute_search
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
@@ -22,13 +21,7 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource
# Variable
-from .costant import SITE_NAME, MOVIE_FOLDER
-
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.HelpTg.session import get_session, updateScriptId, deleteScriptId
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
+from .costant import SITE_NAME, MOVIE_FOLDER, TELEGRAM_BOT
def download_film(select_title: MediaItem) -> str:
@@ -43,18 +36,16 @@ def download_film(select_title: MediaItem) -> str:
- str: output path
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
-
- # Invio a telegram
- bot.send_message(f"Download in corso:\n{select_title.name}", None)
+ bot = get_bot_instance()
+ bot.send_message(f"Download in corso:\n{select_title.name}", None)
- # Viene usato per lo screen
- console.print(f"## Download: [red]{select_title.name} ##")
+ # Viene usato per lo screen
+ console.print(f"## Download: [red]{select_title.name} ##")
- # Get script_id
- script_id = get_session()
- if script_id != "unknown":
- updateScriptId(script_id, select_title.name)
+ # Get script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ updateScriptId(script_id, select_title.name)
# Start message and display film information
start_message()
@@ -75,26 +66,21 @@ def download_film(select_title: MediaItem) -> str:
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, title_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, title_name)
).start()
if TELEGRAM_BOT:
- # Delete script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
+
+ # Delete script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ deleteScriptId(script_id)
- """if r_proc == 404:
- time.sleep(2)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
-
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
-
- return os.path.join(mp4_path, title_name)
\ No newline at end of file
+ return r_proc['path']
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/series.py b/StreamingCommunity/Api/Site/streamingcommunity/series.py
index 50a3a73..e94d962 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/series.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/series.py
@@ -2,20 +2,20 @@
import os
import sys
-import time
# Internal utilities
from StreamingCommunity.Util.console import console, msg
from StreamingCommunity.Util.message import start_message
-from StreamingCommunity.Util.call_stack import get_call_stack
from StreamingCommunity.Util.table import TVShowManager
from StreamingCommunity.Lib.Downloader import HLS_Downloader
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.session import get_session, updateScriptId, deleteScriptId
# Logic class
from .util.ScrapeSerie import ScrapeSerie
-from StreamingCommunity.Api.Template.Util import manage_selection, map_episode_title, dynamic_format_number, validate_selection, validate_episode_selection, execute_search
+from StreamingCommunity.Api.Template.Util import manage_selection, map_episode_title, dynamic_format_number, validate_selection, validate_episode_selection
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
@@ -24,13 +24,7 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource
# Variable
-from .costant import SITE_NAME, SERIES_FOLDER
-
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-from StreamingCommunity.HelpTg.session import get_session, updateScriptId, deleteScriptId
-from StreamingCommunity.Util._jsonConfig import config_manager
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
+from .costant import SITE_NAME, SERIES_FOLDER, TELEGRAM_BOT
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource) -> tuple[str,bool]:
@@ -54,18 +48,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
print()
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
- # Invio a telegram
- bot.send_message(
- f"Download in corso\nSerie: {scrape_serie.series_name}\nStagione: {index_season_selected}\nEpisodio: {index_episode_selected}\nTitolo: {obj_episode.name}",
- None
- )
+ # Invio a telegram
+ bot.send_message(
+ f"Download in corso\nSerie: {scrape_serie.series_name}\nStagione: {index_season_selected}\nEpisodio: {index_episode_selected}\nTitolo: {obj_episode.name}",
+ None
+ )
- # Get script_id
+ # Get script_id and update it
script_id = get_session()
if script_id != "unknown":
- # Update script_id
updateScriptId(script_id, f"{scrape_serie.series_name} - S{index_season_selected} - E{index_episode_selected} - {obj_episode.name}")
# Define filename and path for the downloaded video
@@ -79,26 +72,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
# Download the episode
r_proc = HLS_Downloader(
- m3u8_playlist=master_playlist,
- output_filename=os.path.join(mp4_path, mp4_name)
+ m3u8_url=master_playlist,
+ output_path=os.path.join(mp4_path, mp4_name)
).start()
- #bot.send_message(f"Serie scaricata tutta", None)
+ if "error" in r_proc.keys():
+ try:
+ os.remove(r_proc['path'])
+ except:
+ pass
- """if r_proc == 404:
- time.sleep(2)
-
- # Re call search function
- if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n":
- frames = get_call_stack()
- execute_search(frames[-4])"""
-
- if r_proc != None:
- console.print("[green]Result: ")
- console.print(r_proc)
- #bot.send_message(f"Episodio scaricato", None)
-
- return os.path.join(mp4_path, mp4_name)
+ return r_proc['path'], r_proc['stopped']
def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource, download_all: bool = False) -> None:
"""
@@ -109,8 +93,6 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide
- download_all (bool): Download all episodes in the season.
"""
- #bot = get_bot_instance()
-
# Clean memory of all episodes and get the number of the season
scrape_serie.episode_manager.clear()
@@ -123,10 +105,12 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide
# Download all episodes without asking
for i_episode in range(1, episodes_count + 1):
- download_video(index_season_selected, i_episode, scrape_serie, video_source)
- console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
+ path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
- #bot.send_message(f"Finito di scaricare la stagione: {index_season_selected}", None)
+ if stopped:
+ break
+
+ console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
else:
@@ -138,15 +122,14 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
except ValueError as e:
console.print(f"[red]{str(e)}")
- #bot.send_message(f"{str(e)}", None)
return
# Download selected episodes if not stopped
- stopped = bool(False)
for i_episode in list_episode_select:
+ path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
+
if stopped:
break
- stopped=download_video(index_season_selected, i_episode, scrape_serie, video_source)[1]
def download_series(select_season: MediaItem, version: str) -> None:
"""
@@ -158,7 +141,7 @@ def download_series(select_season: MediaItem, version: str) -> None:
- version (str): Version of the site.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
# Start message and set up video source
start_message()
@@ -179,21 +162,22 @@ def download_series(select_season: MediaItem, version: str) -> None:
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
if TELEGRAM_BOT:
- console.print("\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
+ console.print("\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end")
- bot.send_message(f"Stagioni trovate: {seasons_count}", None)
+ bot.send_message(f"Stagioni trovate: {seasons_count}", None)
+
+ index_season_selected = bot.ask(
+ "select_title_episode",
+ "Inserisci il numero della stagione (es. 1), * per scaricare tutte le stagioni, (es. 1-2) per un intervallo di stagioni, o (es. 3-*) per scaricare dalla stagione specificata fino alla fine",
+ None
+ )
- index_season_selected = bot.ask(
- "select_title_episode",
- "Inserisci il numero della stagione (es. 1), * per scaricare tutte le stagioni, (es. 1-2) per un intervallo di stagioni, o (es. 3-*) per scaricare dalla stagione specificata fino alla fine",
- None
- )
else:
- index_season_selected = msg.ask(
- "\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
- "[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
- )
+ index_season_selected = msg.ask(
+ "\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
+ "[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
+ )
# Manage and validate the selection
list_season_select = manage_selection(index_season_selected, seasons_count)
@@ -216,11 +200,12 @@ def download_series(select_season: MediaItem, version: str) -> None:
download_episode(i_season, scrape_serie, video_source, download_all=False)
if TELEGRAM_BOT:
- bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
- # Get script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
+ bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
+
+ # Get script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ deleteScriptId(script_id)
def display_episodes_list(scrape_serie) -> str:
@@ -231,7 +216,7 @@ def display_episodes_list(scrape_serie) -> str:
last_command (str): Last command entered by the user.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
+ bot = get_bot_instance()
# Set up table for displaying episodes
table_show_manager = TVShowManager()
@@ -247,7 +232,8 @@ def display_episodes_list(scrape_serie) -> str:
# Populate the table with episodes information
if TELEGRAM_BOT:
- choices = []
+ choices = []
+
for i, media in enumerate(scrape_serie.episode_manager.episodes):
table_show_manager.add_tv_show({
'Index': str(media.number),
@@ -256,20 +242,12 @@ def display_episodes_list(scrape_serie) -> str:
})
if TELEGRAM_BOT:
- # Creazione della stringa per il messaggio Telegram
- choice_text = f"{media.number} - {media.name} ({media.duration} min)"
- choices.append(choice_text)
+ choice_text = f"{media.number} - {media.name} ({media.duration} min)"
+ choices.append(choice_text)
if TELEGRAM_BOT:
- # creo episoded_count
- #episoded_count = len(scrape_serie.episode_manager.episodes)
-
- # Invio a telegram
- #bot.send_message(f"Episodi trovati: {episoded_count}", None)
-
- # Invia la lista degli episodi al bot Telegram
- if choices:
- bot.send_message(f"Lista episodi:", choices)
+ if choices:
+ bot.send_message(f"Lista episodi:", choices)
# Run the table and handle user input
last_command = table_show_manager.run()
@@ -278,4 +256,4 @@ def display_episodes_list(scrape_serie) -> str:
console.print("\n[red]Quit [white]...")
sys.exit(0)
- return last_command
+ return last_command
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/site.py b/StreamingCommunity/Api/Site/streamingcommunity/site.py
index be84384..0064865 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/site.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/site.py
@@ -15,7 +15,7 @@ from StreamingCommunity.Util.console import console
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Util.headers import get_headers
from StreamingCommunity.Util.table import TVShowManager
-
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
@@ -25,11 +25,8 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Config
-from .costant import SITE_NAME, DOMAIN_NOW
+from .costant import SITE_NAME, DOMAIN_NOW, TELEGRAM_BOT
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
# Variable
media_search_manager = MediaManager()
@@ -123,13 +120,12 @@ def title_search(title_search: str, domain: str) -> int:
except Exception as e:
console.print(f"Site: {SITE_NAME}, request search error: {e}")
+ # Prepara le scelte per l'utente
if TELEGRAM_BOT:
- # Prepara le scelte per l'utente
- choices = []
+ choices = []
for i, dict_title in enumerate(response.json()['data']):
try:
-
media_search_manager.add_media({
'id': dict_title.get('id'),
'slug': dict_title.get('slug'),
@@ -138,18 +134,17 @@ def title_search(title_search: str, domain: str) -> int:
'date': dict_title.get('last_air_date'),
'score': dict_title.get('score')
})
+
if TELEGRAM_BOT:
- # Crea una stringa formattata per ogni scelta con numero
- choice_text = f"{i} - {dict_title.get('name')} ({dict_title.get('type')}) - {dict_title.get('last_air_date')}"
- choices.append(choice_text)
+ choice_text = f"{i} - {dict_title.get('name')} ({dict_title.get('type')}) - {dict_title.get('last_air_date')}"
+ choices.append(choice_text)
except Exception as e:
print(f"Error parsing a film entry: {e}")
if TELEGRAM_BOT:
- if choices:
- # Invio a telegram la lista
- bot.send_message(f"Lista dei risultati:", choices)
+ if choices:
+ bot.send_message(f"Lista dei risultati:", choices)
# Return the number of titles found
return media_search_manager.get_length()
diff --git a/StreamingCommunity/HelpTg/config.json b/StreamingCommunity/HelpTg/config.json
deleted file mode 100644
index 3ad11ca..0000000
--- a/StreamingCommunity/HelpTg/config.json
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "DEFAULT": {
- "debug": false,
- "log_file": "app.log",
- "log_to_file": true,
- "show_message": true,
- "clean_console": true,
- "root_path": "Video",
- "movie_folder_name": "Movie",
- "serie_folder_name": "TV",
- "anime_folder_name": "Anime",
- "map_episode_name": "E%(episode)_%(episode_name)",
- "config_qbit_tor": {
- "host": "192.168.1.99",
- "port": "7060",
- "user": "admin",
- "pass": "adminadmin"
- },
- "add_siteName": false,
- "disable_searchDomain": false,
- "not_close": false,
- "telegram_bot": true
- },
- "REQUESTS": {
- "timeout": 30,
- "max_retry": 8,
- "proxy_start_min": 0.1,
- "proxy_start_max": 0.5
- },
- "M3U8_DOWNLOAD": {
- "tqdm_delay": 0.12,
- "default_video_workser": 12,
- "default_audio_workser": 12,
- "merge_audio": true,
- "specific_list_audio": [
- "ita"
- ],
- "merge_subs": false,
- "specific_list_subtitles": [
- "eng",
- "spa"
- ],
- "cleanup_tmp_folder": true
- },
- "M3U8_CONVERSION": {
- "use_codec": false,
- "use_vcodec": true,
- "use_acodec": true,
- "use_bitrate": true,
- "use_gpu": false,
- "default_preset": "ultrafast"
- },
- "M3U8_PARSER": {
- "force_resolution": -1,
- "get_only_link": false
- },
- "SITE": {
- "streamingcommunity": {
- "domain": "paris"
- },
- "altadefinizionegratis": {
- "domain": "site"
- },
- "guardaserie": {
- "domain": "meme"
- },
- "mostraguarda": {
- "domain": "stream"
- },
- "ddlstreamitaly": {
- "domain": "co",
- "extra": {
- "ips4_device_key": "",
- "ips4_member_id": "",
- "ips4_login_key": ""
- }
- },
- "animeunity": {
- "domain": "so"
- },
- "cb01new": {
- "domain": "mobi"
- },
- "1337xx": {
- "domain": "to"
- },
- "ilcorsaronero": {
- "domain": "link"
- }
- }
-}
\ No newline at end of file
diff --git a/StreamingCommunity/HelpTg/request_manager.py b/StreamingCommunity/HelpTg/request_manager.py
deleted file mode 100644
index 963bbad..0000000
--- a/StreamingCommunity/HelpTg/request_manager.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import json
-import time
-from typing import Optional
-
-class RequestManager:
- _instance = None
-
- def __new__(cls, *args, **kwargs):
- if not cls._instance:
- cls._instance = super().__new__(cls)
- return cls._instance
-
- def __init__(self, json_file: str = "active_requests.json"):
- if not hasattr(self, 'initialized'):
- self.json_file = json_file
- self.initialized = True
- self.on_response_callback = None # Aggiungi un campo per il callback
-
- def create_request(self, type: str) -> str:
- request_data = {
- "type": type,
- "response": None,
- "timestamp": time.time()
- }
-
- # Aggiungi il tipo al salvataggio della richiesta
- with open(self.json_file, "w") as f:
- json.dump(request_data, f)
-
- return "Ok"
-
- def save_response(self, message_text: str) -> bool:
- try:
- # Carica il file JSON
- with open(self.json_file, "r") as f:
- data = json.load(f)
-
- # Controlla se esiste la chiave 'type' e se la risposta Γ¨ presente
- if "type" in data and "response" in data:
- data["response"] = message_text # Aggiorna la risposta
-
- # Scrivi il file JSON aggiornato
- with open(self.json_file, "w") as f:
- json.dump(data, f, indent=4) # Formatta il file JSON
-
- return True
- else:
- return False
-
- except (FileNotFoundError, json.JSONDecodeError) as e:
- print(f"β οΈ save_response - errore: {e}")
- return False
-
- def get_response(self) -> Optional[str]:
- try:
- with open(self.json_file, "r") as f:
- data = json.load(f)
- # Verifica se esiste la chiave "response"
- if "response" in data:
- response = data["response"] # Ottieni la risposta direttamente
- if response is not None and self.on_response_callback:
- # Se la risposta Γ¨ disponibile, chiama il callback
- self.on_response_callback(response)
- return response
-
- except (FileNotFoundError, json.JSONDecodeError) as e:
- print(f"get_response - errore: {e}")
- return None
-
- def clear_file(self) -> bool:
- try:
- # Svuota il file JSON scrivendo un oggetto vuoto
- with open(self.json_file, "w") as f:
- json.dump({}, f)
- print(f"File {self.json_file} Γ¨ stato svuotato con successo.")
- return True
- except Exception as e:
- print(f"β οΈ clear_file - errore: {e}")
- return False
\ No newline at end of file
diff --git a/StreamingCommunity/HelpTg/requirements.txt b/StreamingCommunity/HelpTg/requirements.txt
deleted file mode 100644
index 01f23e8..0000000
--- a/StreamingCommunity/HelpTg/requirements.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-ο»Ώhttpx
-bs4
-rich
-tqdm
-m3u8
-psutil
-unidecode
-jsbeautifier
-pathvalidate
-pycryptodomex
-googlesearch-python
-fake-useragent
-qbittorrent-api
-python-qbittorrent
\ No newline at end of file
diff --git a/StreamingCommunity/Lib/Downloader/HLS/downloader.py b/StreamingCommunity/Lib/Downloader/HLS/downloader.py
index 601f4c7..2ee4b4e 100644
--- a/StreamingCommunity/Lib/Downloader/HLS/downloader.py
+++ b/StreamingCommunity/Lib/Downloader/HLS/downloader.py
@@ -2,10 +2,10 @@
import os
import re
-import sys
import time
import logging
-import subprocess
+import shutil
+from typing import Any, Dict, List, Optional
# External libraries
@@ -16,12 +16,13 @@ import httpx
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Util.headers import get_headers
from StreamingCommunity.Util.console import console, Panel
-from StreamingCommunity.Util.color import Colors
from StreamingCommunity.Util.os import (
compute_sha1_hash,
os_manager,
internet_manager
)
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+
# Logic class
from ...FFmpeg import (
@@ -30,19 +31,12 @@ from ...FFmpeg import (
join_audios,
join_subtitle
)
-from ...M3U8 import (
- M3U8_Parser,
- M3U8_Codec,
- M3U8_UrlFix
-)
+from ...M3U8 import M3U8_Parser, M3U8_UrlFix
from .segments import M3U8_Segments
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
# Config
-DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_audio')
+DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_audio')
DOWNLOAD_SPECIFIC_SUBTITLE = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_subtitles')
MERGE_AUDIO = config_manager.get_bool('M3U8_DOWNLOAD', 'merge_audio')
MERGE_SUBTITLE = config_manager.get_bool('M3U8_DOWNLOAD', 'merge_subs')
@@ -50,934 +44,483 @@ REMOVE_SEGMENTS_FOLDER = config_manager.get_bool('M3U8_DOWNLOAD', 'cleanup_tmp_f
FILTER_CUSTOM_REOLUTION = config_manager.get_int('M3U8_PARSER', 'force_resolution')
GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
RETRY_LIMIT = config_manager.get_int('REQUESTS', 'max_retry')
+MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
-
-# Variable
-max_timeout = config_manager.get_int("REQUESTS", "timeout")
-m3u8_url_fixer = M3U8_UrlFix()
-list_MissingTs = []
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-class HttpClient:
- def __init__(self, headers: dict = None):
- self.headers = headers or {'User-Agent': get_headers()}
- self.client = httpx.Client(headers=self.headers, timeout=max_timeout, follow_redirects=True)
+class HLSClient:
+ """Client for making HTTP requests to HLS endpoints with retry mechanism."""
+ def __init__(self):
+ self.headers = {'User-Agent': get_headers()}
- def _make_request(self, url: str, return_content: bool = False):
+ def request(self, url: str, return_content: bool = False) -> Optional[httpx.Response]:
+ """
+ Makes HTTP GET requests with retry logic.
+
+ Args:
+ url: Target URL to request
+ return_content: If True, returns response content instead of text
+
+ Returns:
+ Response content/text or None if all retries fail
+ """
+ client = httpx.Client(headers=self.headers, timeout=MAX_TIMEOUT, follow_redirects=True)
for attempt in range(RETRY_LIMIT):
try:
- response = self.client.get(url)
+ response = client.get(url)
response.raise_for_status()
return response.content if return_content else response.text
+
except Exception as e:
- logging.error(f"Attempt {attempt+1} failed for {url}: {str(e)}")
+ logging.error(f"Attempt {attempt+1} failed: {str(e)}")
time.sleep(1.5 ** attempt)
return None
- def get(self, url: str) -> str:
- return self._make_request(url)
-
- def get_content(self, url: str) -> bytes:
- return self._make_request(url, return_content=True)
-
class PathManager:
- def __init__(self, output_filename):
+ """Manages file paths and directories for downloaded content."""
+ def __init__(self, m3u8_url: str, output_path: Optional[str]):
"""
- Initializes the PathManager with the output filename.
-
Args:
- output_filename (str): The name of the output file (should end with .mp4).
+ m3u8_url: Source M3U8 playlist URL
+ output_path: Desired output path for the final video file
"""
- self.output_filename = output_filename
+ self.m3u8_url = m3u8_url
+ self.output_path = self._sanitize_output_path(output_path)
+ base_name = os.path.basename(self.output_path).replace(".mp4", "")
+ self.temp_dir = os.path.join(os.path.dirname(self.output_path), f"{base_name}_tmp")
+
+ def _sanitize_output_path(self, path: Optional[str]) -> str:
+ """
+ Ensures output path is valid and follows expected format.
+ Creates a hash-based filename if no path is provided.
+ """
+ if not path:
+ root = config_manager.get('DEFAULT', 'root_path')
+ hash_name = compute_sha1_hash(self.m3u8_url) + ".mp4"
+ return os.path.join(root, "undefined", hash_name)
- # 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}")
+ if not path.endswith(".mp4"):
+ path += ".mp4"
+
+ return os_manager.get_sanitize_path(path)
+
+ def setup_directories(self):
+ """Creates necessary directories for temporary files (video, audio, subtitles)."""
+ os.makedirs(self.temp_dir, exist_ok=True)
+ for subdir in ['video', 'audio', 'subs']:
+ os.makedirs(os.path.join(self.temp_dir, subdir), exist_ok=True)
+
+ def move_final_file(self, final_file: str):
+ """Moves the final merged file to the desired output location."""
+ if os.path.exists(self.output_path):
+ os.remove(self.output_path)
+ shutil.move(final_file, self.output_path)
+
+ def cleanup(self):
+ """Removes temporary directories if configured to do so."""
+ if REMOVE_SEGMENTS_FOLDER:
+ os_manager.remove_folder(self.temp_dir)
+
+
+class M3U8Manager:
+ """Handles M3U8 playlist parsing and stream selection."""
+ def __init__(self, m3u8_url: str, client: HLSClient):
+ self.m3u8_url = m3u8_url
+ self.client = client
+ self.parser = M3U8_Parser()
+ self.url_fixer = M3U8_UrlFix()
+ self.video_url = None
+ self.video_res = None
+ self.audio_streams = []
+ self.sub_streams = []
+ self.is_master = False
+
+ def parse(self):
+ """
+ Fetches and parses the M3U8 playlist content.
+ Determines if it's a master playlist (index) or media playlist.
+ """
+ content = self.client.request(self.m3u8_url)
+ if not content:
+ raise ValueError("Failed to fetch M3U8 content")
- # Define the path for a temporary directory where segments will be stored
- self.base_temp = os.path.join(self.base_path, "tmp")
- self.video_segments_path = os.path.join(self.base_temp, "video")
- self.audio_segments_path = os.path.join(self.base_temp, "audio")
- self.subtitle_segments_path = os.path.join(self.base_temp, "subtitle")
+ self.parser.parse_data(uri=self.m3u8_url, raw_content=content)
+ self.url_fixer.set_playlist(self.m3u8_url)
+ self.is_master = self.parser.is_master_playlist
- def create_directories(self):
+ def select_streams(self):
"""
- Creates the necessary directories for storing video, audio, and subtitle segments.
+ Selects video, audio, and subtitle streams based on configuration.
+ If it's a master playlist, only selects video stream.
"""
+ if not self.is_master:
+ # For master playlist, only get the video stream
+ if FILTER_CUSTOM_REOLUTION != -1:
+ self.video_url, self.video_res = self.parser._video.get_custom_uri(y_resolution=FILTER_CUSTOM_REOLUTION)
+ else:
+ self.video_url, self.video_res = self.parser._video.get_best_uri()
- os.makedirs(self.base_temp, exist_ok=True)
- os.makedirs(self.video_segments_path, exist_ok=True)
- os.makedirs(self.audio_segments_path, exist_ok=True)
- os.makedirs(self.subtitle_segments_path, exist_ok=True)
-
-
-class ContentExtractor:
- def start(self, obj_parse: M3U8_Parser):
- """
- Starts the extraction process by parsing the M3U8 playlist and collecting audio, subtitle, and video data.
-
- Args:
- obj_parse (str): The M3U8_Parser obj of the M3U8 playlist.
- """
- self.obj_parse = obj_parse
- self._collect_audio()
- self._collect_subtitle()
- self._collect_video()
-
- def _collect_audio(self):
- """
- 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()
-
- # Check if there are any audio tracks available; if not, disable download
- if self.list_available_audio is not None:
-
- # Extract available languages from the audio tracks
- available_languages = [obj_audio.get('language') for obj_audio in self.list_available_audio]
- set_language = DOWNLOAD_SPECIFIC_AUDIO
- downloadable_languages = list(set(available_languages) & set(set_language))
-
- # Only show if there is something available
- if len(available_languages) > 0:
- console.print(f"[cyan bold]Audio β[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
- f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
- f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
-
+ # Don't process audio or subtitles for master playlist
+ self.audio_streams = []
+ self.sub_streams = []
+
else:
- console.log("[red]Can't find a list of audios")
+ # For media playlist, process all streams as before
+ if FILTER_CUSTOM_REOLUTION != -1:
+ self.video_url, self.video_res = self.parser._video.get_custom_uri(y_resolution=FILTER_CUSTOM_REOLUTION)
+ else:
+ self.video_url, self.video_res = self.parser._video.get_best_uri()
- def _collect_subtitle(self):
- """
- It checks for available subtitle languages and the specific subtitles to download.
- """
- logging.info(f"class 'ContentExtractor'; call _collect_subtitle()")
+ self.audio_streams = [
+ s for s in (self.parser._audio.get_all_uris_and_names() or [])
+ if s.get('language') in DOWNLOAD_SPECIFIC_AUDIO
+ ]
- # Collect available subtitles and their corresponding URIs and names
- self.list_available_subtitles = self.obj_parse._subtitle.get_all_uris_and_names()
-
- # Check if there are any subtitles available; if not, disable download
- if self.list_available_subtitles is not None:
-
- # Extract available languages from the subtitles
- available_languages = [obj_subtitle.get('language') for obj_subtitle in self.list_available_subtitles]
- set_language = DOWNLOAD_SPECIFIC_SUBTITLE
- downloadable_languages = list(set(available_languages) & set(set_language))
-
- # Only show if there is something available
- if len(available_languages) > 0:
- console.print(f"[cyan bold]Subtitle β[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
- f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
- f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
+ self.sub_streams = [
+ s for s in (self.parser._subtitle.get_all_uris_and_names() or [])
+ if s.get('language') in DOWNLOAD_SPECIFIC_SUBTITLE
+ ]
+ def log_selection(self):
+ if FILTER_CUSTOM_REOLUTION == -1:
+ set_resolution = "Best"
else:
- console.log("[red]Can't find a list of subtitles")
-
- def _collect_video(self):
- """
- It identifies the best video quality and displays relevant information to the user.
- """
- logging.info(f"class 'ContentExtractor'; call _collect_video()")
- set_resolution = "Best"
-
- # Collect custom quality video if a specific resolution is set
- if FILTER_CUSTOM_REOLUTION != -1:
- self.m3u8_index, video_res = self.obj_parse._video.get_custom_uri(y_resolution=FILTER_CUSTOM_REOLUTION)
set_resolution = f"{FILTER_CUSTOM_REOLUTION}p"
- else:
+ tuple_available_resolution = self.parser._video.get_list_resolution()
+ list_available_resolution = [f"{r[0]}x{r[1]}" for r in tuple_available_resolution]
- # Otherwise, get the best available video quality
- self.m3u8_index, video_res = self.obj_parse._video.get_best_uri()
-
- self.codec: M3U8_Codec = self.obj_parse.codec
+ console.print(
+ f"[cyan bold]Video β[/cyan bold] [green]Available:[/green] [purple]{', '.join(list_available_resolution)}[/purple] | "
+ f"[red]Set:[/red] [purple]{set_resolution}[/purple] | "
+ f"[yellow]Downloadable:[/yellow] [purple]{self.video_res[0]}x{self.video_res[1]}[/purple]"
+ )
- # List all available resolutions
- tuple_available_resolution = self.obj_parse._video.get_list_resolution()
- list_available_resolution = [str(resolution[0]) + "x" + str(resolution[1]) for resolution in tuple_available_resolution]
- logging.info(f"M3U8 index selected: {self.m3u8_index}, with resolution: {video_res}")
-
- # Create a formatted table to display video info
- console.print(f"[cyan bold]Video β[/cyan bold] [green]Available:[/green] [purple]{', '.join(list_available_resolution)}[/purple] | "
- f"[red]Set:[/red] [purple]{set_resolution}[/purple] | "
- f"[yellow]Downloadable:[/yellow] [purple]{video_res[0]}x{video_res[1]}[/purple]")
-
- if self.codec is not None:
-
- # Generate the string for available codec information
+ if self.parser.codec is not None:
available_codec_info = (
- f"[green]v[/green]: [yellow]{self.codec.video_codec_name}[/yellow] "
- f"([green]b[/green]: [yellow]{self.codec.video_bitrate // 1000}k[/yellow]), "
- f"[green]a[/green]: [yellow]{self.codec.audio_codec_name}[/yellow] "
- f"([green]b[/green]: [yellow]{self.codec.audio_bitrate // 1000}k[/yellow])"
+ f"[green]v[/green]: [yellow]{self.parser.codec.video_codec_name}[/yellow] "
+ f"([green]b[/green]: [yellow]{self.parser.codec.video_bitrate // 1000}k[/yellow]), "
+ f"[green]a[/green]: [yellow]{self.parser.codec.audio_codec_name}[/yellow] "
+ f"([green]b[/green]: [yellow]{self.parser.codec.audio_bitrate // 1000}k[/yellow])"
)
+ set_codec_info = available_codec_info if config_manager.get_bool("M3U8_CONVERSION", "use_codec") else "[purple]copy[/purple]"
- # Determine what to display for "Set"
- # If the codec usage is enabled in the configuration, use the detailed codec info
- # Otherwise, display "copy"
- if config_manager.get_bool("M3U8_CONVERSION", "use_codec"):
- set_codec_info = available_codec_info
- else:
- set_codec_info = "[purple]copy[/purple]"
-
- # Print the formatted result with "Available" and "Set" information
console.print(
f"[bold cyan]Codec β[/bold cyan] [green]Available:[/green] {available_codec_info} | "
f"[red]Set:[/red] {set_codec_info}"
)
+ available_subtitles = self.parser._subtitle.get_all_uris_and_names() or []
+ available_sub_languages = [sub.get('language') for sub in available_subtitles]
+ downloadable_sub_languages = list(set(available_sub_languages) & set(DOWNLOAD_SPECIFIC_SUBTITLE))
+ if available_sub_languages:
+ console.print(
+ f"[cyan bold]Subtitle β[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_sub_languages)}[/purple] | "
+ f"[red]Set:[/red] [purple]{', '.join(DOWNLOAD_SPECIFIC_SUBTITLE)}[/purple] | "
+ f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_sub_languages)}[/purple]"
+ )
- # Fix the URL if it does not include the full protocol
- if "http" not in self.m3u8_index:
-
- # Generate the full URL
- self.m3u8_index = m3u8_url_fixer.generate_full_url(self.m3u8_index)
- logging.info(f"Generated index URL: {self.m3u8_index}")
-
- # Check if a valid HTTPS URL is obtained
- if self.m3u8_index is not None and "https" in self.m3u8_index:
- #console.print(f"[cyan]Found m3u8 index [white]=> [red]{self.m3u8_index}")
- print()
-
- else:
- logging.error("[download_m3u8] Can't find a valid m3u8 index")
- raise ValueError("Invalid m3u8 index URL")
-
+ available_audio = self.parser._audio.get_all_uris_and_names() or []
+ available_audio_languages = [audio.get('language') for audio in available_audio]
+ downloadable_audio_languages = list(set(available_audio_languages) & set(DOWNLOAD_SPECIFIC_AUDIO))
+ if available_audio_languages:
+ console.print(
+ f"[cyan bold]Audio β[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_audio_languages)}[/purple] | "
+ f"[red]Set:[/red] [purple]{', '.join(DOWNLOAD_SPECIFIC_AUDIO)}[/purple] | "
+ f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_audio_languages)}[/purple]"
+ )
print("")
-class DownloadTracker:
- def __init__(self, path_manager: PathManager):
+class DownloadManager:
+ """Manages downloading of video, audio, and subtitle streams."""
+ def __init__(self, temp_dir: str, client: HLSClient, url_fixer: M3U8_UrlFix):
"""
- Initializes the DownloadTracker with paths for audio, subtitle, and video segments.
-
Args:
- path_manager (PathManager): An instance of the PathManager class to manage file paths.
+ temp_dir: Directory for storing temporary files
+ client: HLSClient instance for making requests
+ url_fixer: URL fixer instance for generating complete URLs
"""
+ self.temp_dir = temp_dir
+ self.client = client
+ self.url_fixer = url_fixer
+ self.missing_segments = []
+ self.stopped = False
- # Initialize lists to track downloaded audio, subtitles, and video
- self.downloaded_audio = []
- self.downloaded_subtitle = []
- self.downloaded_video = []
+ def download_video(self, video_url: str):
+ """Downloads video segments from the M3U8 playlist."""
+ video_full_url = self.url_fixer.generate_full_url(video_url)
+ video_tmp_dir = os.path.join(self.temp_dir, 'video')
- self.video_segment_path = path_manager.video_segments_path
- self.audio_segments_path = path_manager.audio_segments_path
- self.subtitle_segments_path = path_manager.subtitle_segments_path
+ downloader = M3U8_Segments(url=video_full_url, tmp_folder=video_tmp_dir)
+ result = downloader.download_streams("Video", "video")
+ self.missing_segments.append(result)
- def add_video(self, available_video):
- """
- Adds a single video to the list of downloaded videos.
+ if result.get('stopped', False):
+ self.stopped = True
+ return self.stopped
- 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,
- 'path': os.path.join(self.video_segment_path, "0.ts")
- })
-
- def add_audio(self, list_available_audio):
- """
- Adds available audio tracks to the list of downloaded audio.
-
- 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
- if len(DOWNLOAD_SPECIFIC_AUDIO) > 0:
-
- # Skip this audio track if its language is not in the specified list
- if obj_audio.get('language') not in DOWNLOAD_SPECIFIC_AUDIO:
- continue
-
- # Construct the full path for the audio segment directory
- full_path_audio = os.path.join(self.audio_segments_path, obj_audio.get('language'))
-
- # Append the audio information to the downloaded audio list
- self.downloaded_audio.append({
- 'type': 'audio',
- 'url': obj_audio.get('uri'),
- 'language': obj_audio.get('language'),
- 'path': os.path.join(full_path_audio, "0.ts")
- })
-
- def add_subtitle(self, list_available_subtitles):
- """
- Adds available subtitles to the list of downloaded subtitles.
-
- 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:
-
- # Check if specific subtitle languages are set for download
- if len(DOWNLOAD_SPECIFIC_SUBTITLE) > 0:
-
- # Skip this subtitle if its language is not in the specified list
- if obj_subtitle.get('language') not in DOWNLOAD_SPECIFIC_SUBTITLE:
- continue
-
- sub_language = obj_subtitle.get('language')
-
- # Construct the full path for the subtitle file
- sub_full_path = os.path.join(self.subtitle_segments_path, sub_language + ".vtt")
-
- self.downloaded_subtitle.append({
- 'type': 'sub',
- 'url': obj_subtitle.get('uri'),
- 'language': obj_subtitle.get('language'),
- 'path': sub_full_path
- })
-
-
-class ContentDownloader:
- def __init__(self):
- """
- Initializes the ContentDownloader class.
-
- Attributes:
- expected_real_time (float): Expected real-time duration of the video download.
- """
- self.expected_real_time = None
-
- def download_video(self, downloaded_video):
- """
- Downloads the video if it doesn't already exist.
-
- 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')):
- folder_name = os.path.dirname(downloaded_video[0].get('path'))
-
- # Create an instance of M3U8_Segments to handle video segments download
- video_m3u8 = M3U8_Segments(downloaded_video[0].get('url'), folder_name)
-
- # Get information about the video segments (e.g., duration, ts files to download)
- video_m3u8.get_info()
-
- # Store the expected real-time duration of the video
- self.expected_real_time = video_m3u8.expected_real_time
-
- # Download the video streams and print status
- info_dw = video_m3u8.download_streams(f"{Colors.MAGENTA}video", "video")
- list_MissingTs.append(info_dw)
- self.stopped=list_MissingTs.pop()
-
- else:
- console.log("[cyan]Video [red]already exists.")
-
- def download_audio(self, downloaded_audio):
- """
- Downloads audio tracks if they don't already exist.
-
- 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'))
-
- # Check if the audio file already exists
- if not os.path.exists(obj_audio.get('path')):
-
- # Create an instance of M3U8_Segments to handle audio segments download
- audio_m3u8 = M3U8_Segments(obj_audio.get('url'), folder_name)
-
- # Get information about the audio segments (e.g., duration, ts files to download)
- audio_m3u8.get_info()
-
- # Download the audio segments and print status
- info_dw = audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}", f"audio_{obj_audio.get('language')}")
- list_MissingTs.append(info_dw)
- self.stopped=list_MissingTs.pop()
-
- else:
- console.log(f"[cyan]Audio [white]([green]{obj_audio.get('language')}[white]) [red]already exists.")
-
- def download_subtitle(self, downloaded_subtitle):
- """
- Downloads subtitle files if they don't already exist.
-
- 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')
-
- # Check if the subtitle file already exists
- if os.path.exists(obj_subtitle.get("path")):
- console.log(f"[cyan]Subtitle [white]([green]{sub_language}[white]) [red]already exists.")
- continue # Skip to the next subtitle if it exists
-
- # Parse the M3U8 file to get the subtitle URI
- m3u8_sub_parser = M3U8_Parser()
- url = obj_subtitle.get('url')
- success = False
-
- for attempt in range(RETRY_LIMIT):
- try:
- response = httpx.get(url, headers={'user-agent': get_headers()}, timeout=20)
- response.raise_for_status()
- m3u8_sub_parser.parse_data(
- uri=obj_subtitle.get('uri'),
- raw_content=response.text
- )
- success = True
- break
-
- except httpx.RequestError as e:
- logging.warning(f"Attempt {attempt + 1} failed for URL {url}: {e}")
- time.sleep(2)
-
- if not success:
- console.log(f"[red]Failed to download subtitle data for: {sub_language}")
- continue
-
- # Print the status of the subtitle download
- console.print(f"[cyan] - Downloading subtitle: [red]{sub_language.lower()}")
-
- # Download the subtitle content with retry
- subtitle_content = None
- for attempt in range(RETRY_LIMIT):
- try:
- response = httpx.get(m3u8_sub_parser.subtitle[-1], headers={'user-agent': get_headers()}, timeout=20)
- response.raise_for_status()
- subtitle_content = response.content
- break
-
- except httpx.RequestError as e:
- logging.warning(f"Attempt {attempt + 1} failed for subtitle content URL: {e}")
- time.sleep(2)
-
- if subtitle_content is None:
- console.log(f"[red]Failed to download subtitle content for: {sub_language}")
- continue
-
- # Write the content to the specified file
- try:
- with open(obj_subtitle.get("path"), "wb") as f:
- f.write(subtitle_content)
- #console.log(f"[green]Subtitle downloaded successfully: {sub_language}")
-
- except Exception as e:
- logging.error(f"Failed to write subtitle file for {sub_language}: {e}")
- #console.log(f"[red]Error writing subtitle file: {sub_language}")
-
-
-class ContentJoiner:
- def __init__(self, path_manager):
- """
- Initializes the ContentJoiner class.
-
- Args:
- path_manager (PathManager): An instance of PathManager to manage output paths.
- """
- self.path_manager: PathManager = path_manager
-
- def setup(self, downloaded_video, downloaded_audio, downloaded_subtitle, codec = None):
- """
- Sets up the content joiner with downloaded media files.
-
- Args:
- downloaded_video (list): List of downloaded video information.
- downloaded_audio (list): List of downloaded audio information.
- downloaded_subtitle (list): List of downloaded subtitle information.
- """
- self.downloaded_video = downloaded_video
- self.downloaded_audio = downloaded_audio
- self.downloaded_subtitle = downloaded_subtitle
- self.codec = codec
+ def download_audio(self, audio: Dict):
+ """Downloads audio segments for a specific language track."""
+ if self.stopped:
+ return True
- # Initialize flags to check if media is available
- self.converted_out_path = None
- self.there_is_video = len(downloaded_video) > 0
- self.there_is_audio = len(downloaded_audio) > 0
- self.there_is_subtitle = len(downloaded_subtitle) > 0
+ audio_full_url = self.url_fixer.generate_full_url(audio['uri'])
+ audio_tmp_dir = os.path.join(self.temp_dir, 'audio', audio['language'])
- # Start the joining process
- self.conversione()
+ downloader = M3U8_Segments(url=audio_full_url, tmp_folder=audio_tmp_dir)
+ result = downloader.download_streams(f"Audio {audio['language']}", "audio")
+ self.missing_segments.append(result)
- def conversione(self):
+ if result.get('stopped', False):
+ self.stopped = True
+ return self.stopped
+
+ def download_subtitle(self, sub: Dict):
+ """Downloads and saves subtitle file for a specific language."""
+ if self.stopped:
+ return True
+
+ content = self.client.request(sub['uri'])
+ if content:
+ sub_path = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
+ with open(sub_path, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+ return self.stopped
+
+ def download_all(self, video_url: str, audio_streams: List[Dict], sub_streams: List[Dict]):
"""
- Handles the joining of video, audio, and subtitles based on availability.
+ Downloads all selected streams (video, audio, subtitles).
"""
+ video_file = os.path.join(self.temp_dir, 'video', '0.ts')
+ if not os.path.exists(video_file):
+ if self.download_video(video_url):
+ return True
+
+ for audio in audio_streams:
+ if self.stopped:
+ break
- # Join audio and video if audio is available
- if self.there_is_audio:
- if MERGE_AUDIO:
+ audio_file = os.path.join(self.temp_dir, 'audio', audio['language'], '0.ts')
+ if not os.path.exists(audio_file):
+ if self.download_audio(audio):
+ return True
- # Join video with audio tracks
- self.converted_out_path = self._join_video_audio()
+ for sub in sub_streams:
+ if self.stopped:
+ break
- else:
+ sub_file = os.path.join(self.temp_dir, 'subs', f"{sub['language']}.vtt")
+ if not os.path.exists(sub_file):
+ if self.download_subtitle(sub):
+ return True
+
+ return self.stopped
- # Process each available audio track
- for obj_audio in self.downloaded_audio:
- language = obj_audio.get('language')
- path = obj_audio.get('path')
- # Set the new path for regular audio
- new_path = self.path_manager.output_filename.replace(".mp4", f"_{language}.mp4")
-
- try:
-
- # Rename the audio file to the new path
- os.rename(path, new_path)
- logging.info(f"Audio moved to {new_path}")
-
- except Exception as e:
- logging.error(f"Failed to move audio {path} to {new_path}: {e}")
-
- # Convert video if available
- if self.there_is_video:
- self.converted_out_path = self._join_video()
-
- # If no audio but video is available, join video
- else:
- if self.there_is_video:
- self.converted_out_path = self._join_video()
-
- # Join subtitles if available
- if self.there_is_subtitle:
- if MERGE_SUBTITLE:
- if self.converted_out_path is not None:
- self.converted_out_path = self._join_video_subtitles(self.converted_out_path)
-
- else:
-
- # Process each available subtitle track
- for obj_sub in self.downloaded_subtitle:
- language = obj_sub.get('language')
- path = obj_sub.get('path')
- forced = 'forced' in language
-
- # Adjust the language name and set the new path based on forced status
- if forced:
- language = language.replace("forced-", "")
- new_path = self.path_manager.output_filename.replace(".mp4", f".{language}.forced.vtt")
- else:
- new_path = self.path_manager.output_filename.replace(".mp4", f".{language}.vtt")
-
- try:
- # Rename the subtitle file to the new path
- os.rename(path, new_path)
- logging.info(f"Subtitle moved to {new_path}")
-
- except Exception as e:
- logging.error(f"Failed to move subtitle {path} to {new_path}: {e}")
-
- def _join_video(self):
+class MergeManager:
+ """Handles merging of video, audio, and subtitle streams."""
+ def __init__(self, temp_dir: str, parser: M3U8_Parser, audio_streams: List[Dict], sub_streams: List[Dict]):
"""
- Joins video segments into a single video file.
-
- Returns:
- str: The path to the joined video file.
- """
- path_join_video = os.path.join(self.path_manager.base_path, "v_v.mp4")
- logging.info(f"JOIN video path: {path_join_video}")
-
- # Check if the joined video file already exists
- if not os.path.exists(path_join_video):
-
- # Join the video segments into a single video file
- join_video(
- video_path=self.downloaded_video[0].get('path'),
- out_path=path_join_video,
- codec=self.codec
- )
-
- else:
- console.log("[red]Output join video already exists.")
-
- return path_join_video
-
- def _join_video_audio(self):
- """
- Joins video segments with audio tracks into a single video with audio file.
-
- Returns:
- str: The path to the joined video with audio file.
- """
- path_join_video_audio = os.path.join(self.path_manager.base_path, "v_a.mp4")
- logging.info(f"JOIN audio path: {path_join_video_audio}")
-
- # Check if the joined video with audio file already exists
- if not os.path.exists(path_join_video_audio):
-
- # Set codec to None if not defined in class
- #if not hasattr(self, 'codec'):
- # self.codec = None
-
- # Join the video with audio segments
- join_audios(
- video_path=self.downloaded_video[0].get('path'),
- audio_tracks=self.downloaded_audio,
- out_path=path_join_video_audio,
- codec=self.codec
- )
-
- else:
- console.log("[red]Output join video and audio already exists.")
-
- return path_join_video_audio
-
- def _join_video_subtitles(self, input_path):
- """
- Joins subtitles with the video.
-
Args:
- input_path (str): The path to the video file to which subtitles will be added.
-
- Returns:
- str: The path to the video with subtitles file.
+ temp_dir: Directory containing temporary files
+ parser: M3U8 parser instance with codec information
+ audio_streams: List of audio streams to merge
+ sub_streams: List of subtitle streams to merge
"""
- path_join_video_subtitle = os.path.join(self.path_manager.base_path, "v_s.mp4")
- logging.info(f"JOIN subtitle path: {path_join_video_subtitle}")
+ self.temp_dir = temp_dir
+ self.parser = parser
+ self.audio_streams = audio_streams
+ self.sub_streams = sub_streams
- # Check if the video with subtitles file already exists
- if not os.path.exists(path_join_video_subtitle):
+ def merge(self) -> str:
+ """
+ Merges downloaded streams into final video file.
+ Returns path to the final merged file.
+
+ Process:
+ 1. If no audio/subs, just process video
+ 2. If audio exists, merge with video
+ 3. If subtitles exist, add them to the video
+ """
+ video_file = os.path.join(self.temp_dir, 'video', '0.ts')
+ merged_file = video_file
- # Join the video with subtitles
- join_subtitle(
- input_path,
- self.downloaded_subtitle,
- path_join_video_subtitle
+ if not self.audio_streams and not self.sub_streams:
+ merged_file = join_video(
+ video_path=video_file,
+ out_path=os.path.join(self.temp_dir, 'video.mp4'),
+ codec=self.parser.codec
)
- return path_join_video_subtitle
+ else:
+ if MERGE_AUDIO and self.audio_streams:
+ audio_tracks = [{
+ 'path': os.path.join(self.temp_dir, 'audio', a['language'], '0.ts'),
+ 'name': a['language']
+ } for a in self.audio_streams]
+
+ merged_audio_path = os.path.join(self.temp_dir, 'merged_audio.mp4')
+ merged_file = join_audios(
+ video_path=video_file,
+ audio_tracks=audio_tracks,
+ out_path=merged_audio_path,
+ codec=self.parser.codec
+ )
+
+ if MERGE_SUBTITLE and self.sub_streams:
+ sub_tracks = [{
+ 'path': os.path.join(self.temp_dir, 'subs', f"{s['language']}.vtt"),
+ 'language': s['language']
+ } for s in self.sub_streams]
+
+ merged_subs_path = os.path.join(self.temp_dir, 'final.mp4')
+ merged_file = join_subtitle(
+ video_path=merged_file,
+ subtitles_list=sub_tracks,
+ out_path=merged_subs_path
+ )
+
+ return merged_file
class HLS_Downloader:
- stopped = bool(False)
-
- def __init__(self, output_filename: str=None, m3u8_playlist: str=None, m3u8_index: str=None, is_playlist_url: bool=True, is_index_url: bool=True):
+ """Main class for HLS video download and processing."""
+ def __init__(self, m3u8_url: str, output_path: Optional[str] = None):
+ self.m3u8_url = m3u8_url
+ self.path_manager = PathManager(m3u8_url, output_path)
+ self.client = HLSClient()
+ self.m3u8_manager = M3U8Manager(m3u8_url, self.client)
+ self.download_manager: Optional[DownloadManager] = None
+ self.merge_manager: Optional[MergeManager] = None
+
+ def start(self) -> Dict[str, Any]:
"""
- Initializes the HLS_Downloader class.
-
- Args:
- output_filename (str): The desired output filename for the downloaded content.
- m3u8_playlist (str): The URL or content of the m3u8 playlist.
- m3u8_index (str): The index URL for m3u8 streams.
- 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)
- self.content_extractor = ContentExtractor()
- self.content_downloader = ContentDownloader()
- self.content_joiner = ContentJoiner(self.path_manager)
-
- self.m3u8_playlist = m3u8_playlist
- self.m3u8_index = m3u8_index
- self.is_playlist_url = is_playlist_url
- self.is_index_url = is_index_url
- 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.
-
- Args:
- output_filename (str): The desired output filename.
- m3u8_playlist (str): The m3u8 playlist URL or content.
- m3u8_index (str): The m3u8 index URL.
-
+ Main execution flow with handling for both index and playlist M3U8s.
+
Returns:
- str: The generated output filename.
- """
- 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}")
+ Dict containing:
+ - path: Output file path
+ - url: Original M3U8 URL
+ - is_master: Whether the M3U8 was a master playlist
+ Or raises an exception if there's an error
+ """
+ if TELEGRAM_BOT:
+ bot = get_bot_instance()
- # Auto-generate output file name if not present
- if (output_filename is None) or ("mp4" not in output_filename):
- if m3u8_playlist is not None:
- new_filename = os.path.join(new_folder, compute_sha1_hash(m3u8_playlist) + ".mp4")
- else:
- new_filename = os.path.join(new_folder, compute_sha1_hash(m3u8_index) + ".mp4")
+ try:
+ if os.path.exists(self.path_manager.output_path):
+ console.print(f"[red]Output file {self.path_manager.output_path} already exists![/red]")
+ response = {
+ 'path': self.path_manager.output_path,
+ 'url': self.m3u8_url,
+ 'is_master': False,
+ 'error': 'File already exists',
+ 'stopped': False
+ }
+ if TELEGRAM_BOT:
+ bot.send_message(response)
+ return response
- else:
+ self.path_manager.setup_directories()
- # Check if output_filename contains a folder path
- folder, base_name = os.path.split(output_filename)
+ # Parse M3U8 and determine if it's a master playlist
+ self.m3u8_manager.parse()
+ self.m3u8_manager.select_streams()
+ self.m3u8_manager.log_selection()
+
+ self.download_manager = DownloadManager(
+ temp_dir=self.path_manager.temp_dir,
+ client=self.client,
+ url_fixer=self.m3u8_manager.url_fixer
+ )
- # If no folder is specified, default to 'undefined'
- if not folder:
- folder = new_folder
-
- # Sanitize base name and folder
- folder = os_manager.get_sanitize_path(folder)
- base_name = os_manager.get_sanitize_file(base_name)
- os_manager.create_path(folder)
-
- # Parse to only ASCII for compatibility across platforms
- new_filename = os.path.join(folder, base_name)
-
- logging.info(f"class 'HLS_Downloader'; call _generate_output_filename(); return path: {new_filename}")
- return new_filename
-
- def start(self):
- """
- Initiates the downloading process. Checks if the output file already exists and proceeds with processing the playlist or index.
- """
- if TELEGRAM_BOT:
- bot = get_bot_instance()
-
- if os.path.exists(self.output_filename):
- console.log("[red]Output file already exists.")
- if TELEGRAM_BOT:
- bot.send_message(f"Contenuto giΓ scaricato!", None)
- 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")
-
- 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
- if is_masterPlaylist:
- if not GET_ONLY_LINK:
- r_proc = self._process_playlist()
-
- if r_proc == 404:
- return 404
- else:
- return None
-
- else:
- return {
- 'path': self.output_filename,
- '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(f"[red]Error: m3u8_playlist failed request for: {self.m3u8_playlist}")
- 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")
-
- 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
- if not is_masterPlaylist:
- if not GET_ONLY_LINK:
- self._process_index()
- return None
-
- else:
- return {
- 'path': self.output_filename,
- '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: m3u8_index failed request")
- else:
- console.log("[red]Error: m3u8_index is None")
-
- # Reset
- self._reset()
-
- def _clean(self, out_path: str) -> None:
- """
- Cleans up temporary files and folders after downloading and processing.
-
- Args:
- out_path (str): The path of the output file to be cleaned up.
- """
- if TELEGRAM_BOT:
- bot = get_bot_instance()
-
- # Check if the final output file exists
- logging.info(f"Check if end file converted exists: {out_path}")
- if out_path is None or not os.path.isfile(out_path):
- logging.error("Video file converted does not exist.")
- sys.exit(0)
-
- # Rename the output file to the desired output filename if it does not already exist
- if not os.path.exists(self.output_filename):
- missing_ts = False
- missing_info = ""
-
- # Rename the converted file to the specified output filename
- os.rename(out_path, self.output_filename)
-
- # Calculate file size and duration for reporting
- formatted_size = internet_manager.format_file_size(os.path.getsize(self.output_filename))
- formatted_duration = print_duration_table(self.output_filename, description=False, return_string=True)
-
- # Collect info about type missing
- for item in list_MissingTs:
- if int(item['nFailed']) >= 1:
- missing_ts = True
- missing_info += f"[red]TS Failed: {item['nFailed']} {item['type']} tracks[/red]\n"
-
- # Prepare the report panel content
- print("")
- panel_content = (
- f"[bold green]Download completed![/bold green]\n"
- f"[cyan]File size: [bold red]{formatted_size}[/bold red]\n"
- f"[cyan]Duration: [bold]{formatted_duration}[/bold]\n"
- f"[cyan]Output: [bold]{os.path.abspath(self.output_filename)}[/bold]"
+ # Check if download was stopped
+ download_stopped = self.download_manager.download_all(
+ video_url=self.m3u8_manager.video_url,
+ audio_streams=self.m3u8_manager.audio_streams,
+ sub_streams=self.m3u8_manager.sub_streams
)
- if missing_ts:
- panel_content += f"\n{missing_info}"
+ if download_stopped:
+ return {
+ 'path': None,
+ 'url': self.m3u8_url,
+ 'is_master': self.m3u8_manager.is_master,
+ 'error': 'Download stopped by user',
+ 'stopped': True
+ }
- # Display the download completion message
- console.print(Panel(
- panel_content,
- title=f"{os.path.basename(self.output_filename.replace('.mp4', ''))}",
- border_style="green"
- ))
+ self.merge_manager = MergeManager(
+ temp_dir=self.path_manager.temp_dir,
+ parser=self.m3u8_manager.parser,
+ audio_streams=self.m3u8_manager.audio_streams,
+ sub_streams=self.m3u8_manager.sub_streams
+ )
- if TELEGRAM_BOT:
- message = f"Download completato\nDimensione: {formatted_size}\nDurata: {formatted_duration}\nPercorso: {os.path.abspath(self.output_filename)}"
- # Rimuovere i tag di colore usando una regex
- clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
- # Invio a telegram
- bot.send_message(clean_message, None)
+ final_file = self.merge_manager.merge()
+ self.path_manager.move_final_file(final_file)
+ self.path_manager.cleanup()
- # Handle missing segments
- if missing_ts:
- os.rename(self.output_filename, self.output_filename.replace(".mp4", "_failed.mp4"))
+ self._print_summary()
- # Delete all temporary files except for the output file
- os_manager.remove_files_except_one(self.path_manager.base_path, os.path.basename(self.output_filename.replace(".mp4", "_failed.mp4")))
+ return {
+ 'path': self.path_manager.output_path,
+ 'url': self.m3u8_url,
+ 'is_master': self.m3u8_manager.is_master,
+ 'stopped': False
+ }
- # Remove the base folder if specified
- if REMOVE_SEGMENTS_FOLDER:
- os_manager.remove_folder(self.path_manager.base_path)
-
- else:
- logging.info("Video file converted already exists.")
-
- def _valida_playlist(self):
- """
- 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:
- 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:
- m3u8_playlist_text = self.m3u8_playlist
-
- # Check if the m3u8 content is valid
- if m3u8_playlist_text is None:
- console.log("[red]Playlist m3u8 to download is empty.")
- sys.exit(0)
-
- # Save the m3u8 playlist text to a temporary file
- open(os.path.join(self.path_manager.base_temp, "playlist.m3u8"), "w+", encoding="utf-8").write(m3u8_playlist_text)
-
- # Collect information about the playlist
- if self.is_playlist_url:
- self.content_extractor.start(self.instace_parserClass)
- 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)
- self.download_tracker.add_subtitle(self.content_extractor.list_available_subtitles)
-
- # Download each type of content
- if len(self.download_tracker.downloaded_video) > 0:
- self.content_downloader.download_video(self.download_tracker.downloaded_video)
- if len(self.download_tracker.downloaded_audio) > 0:
- self.content_downloader.download_audio(self.download_tracker.downloaded_audio)
- if len(self.download_tracker.downloaded_subtitle) > 0:
- self.content_downloader.download_subtitle(self.download_tracker.downloaded_subtitle)
-
- # Join downloaded content
- self.content_joiner.setup(self.download_tracker.downloaded_video, self.download_tracker.downloaded_audio, self.download_tracker.downloaded_subtitle, self.content_extractor.codec)
-
- # Clean up temporary files and directories
- self._clean(self.content_joiner.converted_out_path)
+ except Exception as e:
+ error_msg = str(e)
+ console.print(f"[red]Download failed: {error_msg}[/red]")
+ logging.error("Download error", exc_info=True)
+
+ return {
+ 'path': None,
+ 'url': self.m3u8_url,
+ 'is_master': getattr(self.m3u8_manager, 'is_master', None),
+ 'error': error_msg,
+ 'stopped': False
+ }
- def _process_index(self):
- """
- Processes the m3u8 index to download only video.
- """
- m3u8_url_fixer.set_playlist(self.m3u8_index)
+ def _print_summary(self):
+ """Prints download summary including file size, duration, and any missing segments."""
+ if TELEGRAM_BOT:
+ bot = get_bot_instance()
- # Download video
- self.download_tracker.add_video(self.m3u8_index)
- self.content_downloader.download_video(self.download_tracker.downloaded_video)
-
- # Join video
- self.content_joiner.setup(self.download_tracker.downloaded_video, [], [])
+ missing_ts = False
+ missing_info = ""
+ for item in self.download_manager.missing_segments:
+ if int(item['nFailed']) >= 1:
+ missing_ts = True
+ missing_info += f"[red]TS Failed: {item['nFailed']} {item['type']} tracks[/red]\n"
- # Clean up temporary files and directories
- self._clean(self.content_joiner.converted_out_path)
+ file_size = internet_manager.format_file_size(os.path.getsize(self.path_manager.output_path))
+ duration = print_duration_table(self.path_manager.output_path, description=False, return_string=True)
- def _reset(self):
- global list_MissingTs, m3u8_url_fixer
+ panel_content = (
+ f"[bold green]Download completed![/bold green]\n"
+ f"[cyan]File size: [bold red]{file_size}[/bold red]\n"
+ f"[cyan]Duration: [bold]{duration}[/bold]\n"
+ f"[cyan]Output: [bold]{os.path.abspath(self.path_manager.output_path)}[/bold]"
+ )
- m3u8_url_fixer.reset_playlist()
- list_MissingTs = []
+ if TELEGRAM_BOT:
+ message = f"Download completato\nDimensione: {file_size}\nDurata: {duration}\nPercorso: {os.path.abspath(self.path_manager.output_path)}"
+ clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
+ bot.send_message(clean_message, None)
+
+ if missing_ts:
+ panel_content += f"\n{missing_info}"
+ os.rename(self.path_manager.output_path, self.path_manager.output_path.replace(".mp4", "_failed.mp4"))
+
+ console.print(Panel(
+ panel_content,
+ title=f"{os.path.basename(self.path_manager.output_path.replace('.mp4', ''))}",
+ border_style="green"
+ ))
\ No newline at end of file
diff --git a/StreamingCommunity/Lib/Downloader/HLS/segments.py b/StreamingCommunity/Lib/Downloader/HLS/segments.py
index 51d3532..b17b453 100644
--- a/StreamingCommunity/Lib/Downloader/HLS/segments.py
+++ b/StreamingCommunity/Lib/Downloader/HLS/segments.py
@@ -47,7 +47,6 @@ PROXY_START_MAX = config_manager.get_float('REQUESTS', 'proxy_start_max')
DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
MAX_TIMEOOUT = config_manager.get_int("REQUESTS", "timeout")
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
@@ -70,7 +69,7 @@ class M3U8_Segments:
# Util class
self.decryption: M3U8_Decryption = None
- self.class_ts_estimator = M3U8_Ts_Estimator(0)
+ self.class_ts_estimator = M3U8_Ts_Estimator(0, self)
self.class_url_fixer = M3U8_UrlFix(url)
# Sync
@@ -88,6 +87,8 @@ class M3U8_Segments:
self.info_maxRetry = 0
self.info_nRetry = 0
self.info_nFailed = 0
+ self.active_retries = 0
+ self.active_retries_lock = threading.Lock()
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
key_uri = urljoin(self.url, m3u8_parser.keys.get('uri'))
@@ -232,10 +233,17 @@ class M3U8_Segments:
self.queue.put((index, None)) # Marker for failed segment
progress_bar.update(1)
self.info_nFailed += 1
+ return
+
+ with self.active_retries_lock:
+ self.active_retries += 1
sleep_time = backoff_factor * (2 ** attempt)
logging.info(f"Retrying segment {index} in {sleep_time} seconds...")
time.sleep(sleep_time)
+
+ with self.active_retries_lock:
+ self.active_retries -= 1
def write_segments_to_file(self):
"""
@@ -296,11 +304,7 @@ class M3U8_Segments:
- description: Description to insert on tqdm bar
- type (str): Type of download: 'video' or 'audio'
"""
-
- if TELEGRAM_BOT:
- # Viene usato per lo screen
- console.log("####")
-
+ self.get_info()
self.setup_interrupt_handler()
progress_bar = tqdm(
diff --git a/StreamingCommunity/Lib/Downloader/MP4/downloader.py b/StreamingCommunity/Lib/Downloader/MP4/downloader.py
index 9afd4b4..acae652 100644
--- a/StreamingCommunity/Lib/Downloader/MP4/downloader.py
+++ b/StreamingCommunity/Lib/Downloader/MP4/downloader.py
@@ -1,12 +1,11 @@
# 09.06.24
import os
-import signal, re
+import re
import sys
-import ssl
-import certifi
+import signal
import logging
-import atexit
+
# External libraries
import httpx
@@ -19,6 +18,7 @@ from StreamingCommunity.Util.color import Colors
from StreamingCommunity.Util.console import console, Panel
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Util.os import internet_manager
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
@@ -29,16 +29,15 @@ from ...FFmpeg import print_duration_table
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
-TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-
# Config
GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
TQDM_USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
+
+
#Ending constant
KILL_HANDLER = bool(False)
@@ -54,14 +53,13 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
- headers_ (dict, optional): Custom headers for the request.
"""
if TELEGRAM_BOT:
- bot = get_bot_instance()
- # Viene usato per lo screen
- console.log("####")
+ bot = get_bot_instance()
+ console.log("####")
if os.path.exists(path):
console.log("[red]Output file already exists.")
if TELEGRAM_BOT:
- bot.send_message(f"Contenuto giΓ scaricato!", None)
+ bot.send_message(f"Contenuto giΓ scaricato!", None)
return 400
# Early return for link-only mode
@@ -94,8 +92,8 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
try:
# Create a custom transport that bypasses SSL verification
transport = httpx.HTTPTransport(
- verify=False, # Disable SSL certificate verification
- http2=True # Optional: enable HTTP/2 support
+ verify=False,
+ http2=True
)
# Download with streaming and progress tracking
@@ -137,7 +135,6 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
Parameters:
- args (tuple): The signal arguments (to prevent errors).
"""
-
if(downloaded MAX_DOWNLOAD_SIZE:
# break
@@ -172,11 +169,9 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
))
if TELEGRAM_BOT:
- message = f"Download completato\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
- # Rimuovere i tag di colore usando una regex
- clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
- # Invio a telegram
- bot.send_message(clean_message, None)
+ message = f"Download completato\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
+ clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
+ bot.send_message(clean_message, None)
return path
@@ -184,16 +179,6 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
console.print("[bold red]Download failed or file is empty.[/bold red]")
return None
- except httpx.HTTPStatusError as http_err:
- logging.error(f"HTTP error occurred: {http_err}")
- console.print(f"[bold red]HTTP Error: {http_err}[/bold red]")
- return None
-
- except httpx.RequestError as req_err:
- logging.error(f"Request error: {req_err}")
- console.print(f"[bold red]Request Error: {req_err}[/bold red]")
- return None
-
except Exception as e:
logging.error(f"Unexpected error during download: {e}")
console.print(f"[bold red]Unexpected Error: {e}[/bold red]")
diff --git a/StreamingCommunity/Lib/Downloader/TOR/downloader.py b/StreamingCommunity/Lib/Downloader/TOR/downloader.py
index d5a79c0..ea45fd8 100644
--- a/StreamingCommunity/Lib/Downloader/TOR/downloader.py
+++ b/StreamingCommunity/Lib/Downloader/TOR/downloader.py
@@ -27,6 +27,7 @@ PORT = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['port'])
USERNAME = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['user'])
PASSWORD = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['pass'])
+
# Config
TQDM_USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
diff --git a/StreamingCommunity/Lib/FFmpeg/command.py b/StreamingCommunity/Lib/FFmpeg/command.py
index 4129c88..7665a8f 100644
--- a/StreamingCommunity/Lib/FFmpeg/command.py
+++ b/StreamingCommunity/Lib/FFmpeg/command.py
@@ -110,6 +110,8 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
print()
+ return out_path
+
def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: str, codec: M3U8_Codec = None):
"""
@@ -204,6 +206,8 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
print()
+ return out_path
+
def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_path: str):
"""
@@ -255,4 +259,6 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
console.log(f"[purple]FFmpeg [white][[cyan]Join subtitle[white]] ...")
with suppress_output():
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
- print()
\ No newline at end of file
+ print()
+
+ return out_path
\ No newline at end of file
diff --git a/StreamingCommunity/Lib/M3U8/estimator.py b/StreamingCommunity/Lib/M3U8/estimator.py
index 543e39b..e9abab4 100644
--- a/StreamingCommunity/Lib/M3U8/estimator.py
+++ b/StreamingCommunity/Lib/M3U8/estimator.py
@@ -22,7 +22,7 @@ TQDM_USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
class M3U8_Ts_Estimator:
- def __init__(self, total_segments: int):
+ def __init__(self, total_segments: int, segments_instance=None):
"""
Initialize the M3U8_Ts_Estimator object.
@@ -32,6 +32,7 @@ class M3U8_Ts_Estimator:
self.ts_file_sizes = []
self.now_downloaded_size = 0
self.total_segments = total_segments
+ self.segments_instance = segments_instance
self.lock = threading.Lock()
self.speed = {"upload": "N/A", "download": "N/A"}
@@ -102,7 +103,6 @@ class M3U8_Ts_Estimator:
return "Error"
def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
- """Updates the progress bar with download information."""
try:
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
@@ -120,21 +120,25 @@ class M3U8_Ts_Estimator:
if len(speed_data) >= 2:
average_internet_speed = speed_data[0]
average_internet_unit = speed_data[1]
-
else:
average_internet_speed = "N/A"
average_internet_unit = ""
+ # Retrieve retry count from segments_instance
+ retry_count = self.segments_instance.active_retries if self.segments_instance else 0
progress_str = (
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
- f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
+ f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit} "
+ f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
)
-
else:
+ # Retrieve retry count from segments_instance
+ retry_count = self.segments_instance.active_retries if self.segments_instance else 0
progress_str = (
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
- f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
+ f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
+ f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
)
progress_counter.set_postfix_str(progress_str)
diff --git a/StreamingCommunity/HelpTg/__init__.py b/StreamingCommunity/TelegramHelp/__init__.py
similarity index 100%
rename from StreamingCommunity/HelpTg/__init__.py
rename to StreamingCommunity/TelegramHelp/__init__.py
diff --git a/StreamingCommunity/TelegramHelp/request_manager.py b/StreamingCommunity/TelegramHelp/request_manager.py
new file mode 100644
index 0000000..51dd5f3
--- /dev/null
+++ b/StreamingCommunity/TelegramHelp/request_manager.py
@@ -0,0 +1,82 @@
+# 04.02.25
+# Made by: @GiuPic
+
+import json
+import time
+from typing import Optional
+
+class RequestManager:
+ _instance = None
+
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = super().__new__(cls)
+ return cls._instance
+
+ def __init__(self, json_file: str = "active_requests.json"):
+ if not hasattr(self, 'initialized'):
+ self.json_file = json_file
+ self.initialized = True
+ self.on_response_callback = None
+
+ def create_request(self, type: str) -> str:
+ request_data = {
+ "type": type,
+ "response": None,
+ "timestamp": time.time()
+ }
+
+ with open(self.json_file, "w") as f:
+ json.dump(request_data, f)
+
+ return "Ok"
+
+ def save_response(self, message_text: str) -> bool:
+ try:
+ # Carica il file JSON
+ with open(self.json_file, "r") as f:
+ data = json.load(f)
+
+ # Controlla se esiste la chiave 'type' e se la risposta Γ¨ presente
+ if "type" in data and "response" in data:
+ data["response"] = message_text # Aggiorna la risposta
+
+ with open(self.json_file, "w") as f:
+ json.dump(data, f, indent=4)
+
+ return True
+ else:
+ return False
+
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ print(f"β οΈ save_response - errore: {e}")
+ return False
+
+ def get_response(self) -> Optional[str]:
+ try:
+ with open(self.json_file, "r") as f:
+ data = json.load(f)
+
+ # Verifica se esiste la chiave "response"
+ if "response" in data:
+ response = data["response"] # Ottieni la risposta direttamente
+
+ if response is not None and self.on_response_callback:
+ self.on_response_callback(response)
+
+ return response
+
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ print(f"get_response - errore: {e}")
+ return None
+
+ def clear_file(self) -> bool:
+ try:
+ with open(self.json_file, "w") as f:
+ json.dump({}, f)
+ print(f"File {self.json_file} Γ¨ stato svuotato con successo.")
+ return True
+
+ except Exception as e:
+ print(f"β οΈ clear_file - errore: {e}")
+ return False
\ No newline at end of file
diff --git a/StreamingCommunity/HelpTg/session.py b/StreamingCommunity/TelegramHelp/session.py
old mode 100755
new mode 100644
similarity index 69%
rename from StreamingCommunity/HelpTg/session.py
rename to StreamingCommunity/TelegramHelp/session.py
index b81add8..7a26034
--- a/StreamingCommunity/HelpTg/session.py
+++ b/StreamingCommunity/TelegramHelp/session.py
@@ -1,24 +1,22 @@
+# 04.02.25
+# Made by: @GiuPic
+
import json
session_data = {}
def set_session(value):
- # salvo script_id in session_data
session_data['script_id'] = value
def get_session():
- # controllo se script_id Γ¨ presente in session_data
return session_data.get('script_id', 'unknown')
def updateScriptId(screen_id, titolo):
- # definisco il nome del file json
json_file = "scripts.json"
try:
- # apro il file json
with open(json_file, 'r') as f:
scripts_data = json.load(f)
except FileNotFoundError:
- # Se il file non esiste, inizializzo la lista vuota
scripts_data = []
# cerco lo script con lo screen_id
@@ -26,37 +24,33 @@ def updateScriptId(screen_id, titolo):
if script["screen_id"] == screen_id:
# se trovo il match, aggiorno il titolo
script["titolo"] = titolo
+
# aggiorno il file json
with open(json_file, 'w') as f:
json.dump(scripts_data, f, indent=4)
- #print(f"Titolo aggiornato per screen_id {screen_id}")
+
return
- # se non trovo nessuno script con lo screen_id
print(f"Screen_id {screen_id} non trovato.")
-# creo la funzione che elimina lo script con lo screen_id specificato
def deleteScriptId(screen_id):
- # definisco il nome del file json
json_file = "scripts.json"
try:
- # apro il file json
with open(json_file, 'r') as f:
scripts_data = json.load(f)
except FileNotFoundError:
- # Se il file non esiste, inizializzo la lista vuota
scripts_data = []
- # cerco lo script con lo screen_id
for script in scripts_data:
if script["screen_id"] == screen_id:
# se trovo il match, elimino lo script
scripts_data.remove(script)
+
# aggiorno il file json
with open(json_file, 'w') as f:
json.dump(scripts_data, f, indent=4)
+
print(f"Script eliminato per screen_id {screen_id}")
return
- # se non trovo nessuno script con lo screen_id
print(f"Screen_id {screen_id} non trovato.")
\ No newline at end of file
diff --git a/StreamingCommunity/HelpTg/telegram_bot.py b/StreamingCommunity/TelegramHelp/telegram_bot.py
similarity index 58%
rename from StreamingCommunity/HelpTg/telegram_bot.py
rename to StreamingCommunity/TelegramHelp/telegram_bot.py
index 4672a92..6b09cac 100644
--- a/StreamingCommunity/HelpTg/telegram_bot.py
+++ b/StreamingCommunity/TelegramHelp/telegram_bot.py
@@ -1,13 +1,24 @@
-import telebot
-from telebot import types
+# 04.02.25
+# Made by: @GiuPic
+
+import os
+import re
+import sys
import time
import uuid
-import subprocess
-import os, re, sys
import json
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
-from StreamingCommunity.HelpTg.request_manager import RequestManager
import threading
+import subprocess
+
+
+# External libraries
+import telebot
+
+
+# Fix import
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
+from StreamingCommunity.TelegramHelp.request_manager import RequestManager
+
# Funzione per caricare variabili da un file .env
def load_env(file_path="../../.env"):
@@ -21,6 +32,7 @@ def load_env(file_path="../../.env"):
# Carica le variabili
load_env()
+
class TelegramBot:
_instance = None
_config_file = "../../bot_config.json"
@@ -30,11 +42,15 @@ class TelegramBot:
if cls._instance is None:
# Prova a caricare la configurazione e inizializzare il bot
if os.path.exists(cls._config_file):
- with open(cls._config_file, 'r') as f:
+ with open(cls._config_file, "r") as f:
config = json.load(f)
- cls._instance = cls.init_bot(config['token'], config['authorized_user_id'])
+ cls._instance = cls.init_bot(
+ config["token"], config["authorized_user_id"]
+ )
else:
- raise Exception("Bot non ancora inizializzato. Chiamare prima init_bot() con token e authorized_user_id")
+ raise Exception(
+ "Bot non ancora inizializzato. Chiamare prima init_bot() con token e authorized_user_id"
+ )
return cls._instance
@classmethod
@@ -42,16 +58,12 @@ class TelegramBot:
if cls._instance is None:
cls._instance = cls(token, authorized_user_id)
# Salva la configurazione
- config = {
- 'token': token,
- 'authorized_user_id': authorized_user_id
- }
- with open(cls._config_file, 'w') as f:
+ config = {"token": token, "authorized_user_id": authorized_user_id}
+ with open(cls._config_file, "w") as f:
json.dump(config, f)
return cls._instance
def __init__(self, token, authorized_user_id):
-
def monitor_scripts():
while True:
try:
@@ -68,25 +80,41 @@ class TelegramBot:
for script in scripts_data:
screen_exists = False
try:
- existing_screens = subprocess.check_output(["screen", "-list"]).decode('utf-8')
+ existing_screens = subprocess.check_output(
+ ["screen", "-list"]
+ ).decode("utf-8")
if script["screen_id"] in existing_screens:
screen_exists = True
except subprocess.CalledProcessError:
pass # Se il comando fallisce, significa che non ci sono screen attivi.
if screen_exists:
- if "titolo" not in script and script["status"] == "running" and (current_time - script["start_time"]) > 600:
+ if (
+ "titolo" not in script
+ and script["status"] == "running"
+ and (current_time - script["start_time"]) > 600
+ ):
# Prova a terminare la sessione screen
try:
- subprocess.check_output(["screen", "-S", script["screen_id"], "-X", "quit"])
- print(f"β
La sessione screen con ID {script['screen_id']} Γ¨ stata fermata automaticamente.")
+ subprocess.check_output(
+ ["screen", "-S", script["screen_id"], "-X", "quit"]
+ )
+ print(
+ f"β
La sessione screen con ID {script['screen_id']} Γ¨ stata fermata automaticamente."
+ )
except subprocess.CalledProcessError:
- print(f"β οΈ Impossibile fermare la sessione screen con ID {script['screen_id']}.")
- print(f"β οΈ Lo script con ID {script['screen_id']} ha superato i 10 minuti e verrΓ rimosso.")
+ print(
+ f"β οΈ Impossibile fermare la sessione screen con ID {script['screen_id']}."
+ )
+ print(
+ f"β οΈ Lo script con ID {script['screen_id']} ha superato i 10 minuti e verrΓ rimosso."
+ )
else:
scripts_data_to_save.append(script)
else:
- print(f"β οΈ La sessione screen con ID {script['screen_id']} non esiste piΓΉ e verrΓ rimossa.")
+ print(
+ f"β οΈ La sessione screen con ID {script['screen_id']} non esiste piΓΉ e verrΓ rimossa."
+ )
# Salva la lista aggiornata, senza gli script scaduti o le screen non esistenti
with open("../../scripts.json", "w") as f:
@@ -98,9 +126,10 @@ class TelegramBot:
monitor_thread = threading.Thread(target=monitor_scripts, daemon=True)
monitor_thread.start()
-
if TelegramBot._instance is not None:
- raise Exception("Questa classe Γ¨ un singleton! Usa get_instance() per ottenere l'istanza.")
+ raise Exception(
+ "Questa classe Γ¨ un singleton! Usa get_instance() per ottenere l'istanza."
+ )
self.token = token
self.authorized_user_id = authorized_user_id
@@ -113,67 +142,30 @@ class TelegramBot:
def register_handlers(self):
- """ @self.bot.message_handler(commands=['start'])
+ """@self.bot.message_handler(commands=['start'])
def start(message):
- self.handle_start(message) """
+ self.handle_start(message)"""
- @self.bot.message_handler(commands=['get_id'])
+ @self.bot.message_handler(commands=["get_id"])
def get_id(message):
self.handle_get_id(message)
- @self.bot.message_handler(commands=['start'])
+ @self.bot.message_handler(commands=["start"])
def start_script(message):
self.handle_start_script(message)
- @self.bot.message_handler(commands=['list'])
+ @self.bot.message_handler(commands=["list"])
def list_scripts(message):
self.handle_list_scripts(message)
- @self.bot.message_handler(commands=['stop'])
+ @self.bot.message_handler(commands=["stop"])
def stop_script(message):
self.handle_stop_script(message)
- @self.bot.message_handler(commands=['screen'])
+ @self.bot.message_handler(commands=["screen"])
def screen_status(message):
self.handle_screen_status(message)
- """ @self.bot.message_handler(commands=['replay'])
- def send_welcome(message):
- # Crea una tastiera personalizzata
- markup = types.ReplyKeyboardMarkup(row_width=2)
- itembtn1 = types.KeyboardButton('Start')
- itembtn2 = types.KeyboardButton('Lista')
- markup.add(itembtn1, itembtn2)
-
- # Invia un messaggio con la tastiera
- self.bot.send_message(message.chat.id, "Scegli un'opzione:", reply_markup=markup) """
-
- """@self.bot.message_handler(commands=['inline'])
- def send_welcome(message):
- # Crea una tastiera inline
- markup = types.InlineKeyboardMarkup()
- itembtn1 = types.InlineKeyboardButton('Azione 1', callback_data='action1')
- itembtn2 = types.InlineKeyboardButton('Azione 2', callback_data='action2')
- itembtn3 = types.InlineKeyboardButton('Azione 3', callback_data='action3')
- markup.add(itembtn1, itembtn2, itembtn3)
-
- # Invia un messaggio con la tastiera inline
- self.bot.send_message(message.chat.id, "Scegli un'opzione:", reply_markup=markup)
-
- # Gestisce le callback delle tastiere inline
- @self.bot.callback_query_handler(func=lambda call: True)
- def handle_callback(call):
- if call.data == 'action1':
- self.bot.answer_callback_query(call.id, "Hai scelto Azione 1!")
- self.bot.send_message(call.message.chat.id, "Hai eseguito Azione 1!")
- elif call.data == 'action2':
- self.bot.answer_callback_query(call.id, "Hai scelto Azione 2!")
- self.bot.send_message(call.message.chat.id, "Hai eseguito Azione 2!")
- elif call.data == 'action3':
- self.bot.answer_callback_query(call.id, "Hai scelto Azione 3!")
- self.bot.send_message(call.message.chat.id, "Hai eseguito Azione 3!")
- """
-
@self.bot.message_handler(func=lambda message: True)
def handle_all_messages(message):
self.handle_response(message)
@@ -188,7 +180,11 @@ class TelegramBot:
return
print(f"Il tuo ID utente Γ¨: `{message.from_user.id}`")
- self.bot.send_message(message.chat.id, f"Il tuo ID utente Γ¨: `{message.from_user.id}`", parse_mode="Markdown")
+ self.bot.send_message(
+ message.chat.id,
+ f"Il tuo ID utente Γ¨: `{message.from_user.id}`",
+ parse_mode="Markdown",
+ )
def handle_start_script(self, message):
if not self.is_authorized(message.from_user.id):
@@ -197,10 +193,6 @@ class TelegramBot:
return
screen_id = str(uuid.uuid4())[:8]
- #screen_id = '0000'
-
- # Impostare a True per avviare il test_run.py in modalitΓ verbose per il debug
-
debug_mode = os.getenv("DEBUG")
verbose = debug_mode
@@ -210,16 +202,28 @@ class TelegramBot:
# Verifica se lo screen con il nome esiste giΓ
try:
subprocess.check_output(["screen", "-list"])
- existing_screens = subprocess.check_output(["screen", "-list"]).decode('utf-8')
+ existing_screens = subprocess.check_output(["screen", "-list"]).decode(
+ "utf-8"
+ )
if screen_id in existing_screens:
print(f"β οΈ Lo script con ID {screen_id} Γ¨ giΓ in esecuzione.")
- self.bot.send_message(message.chat.id, f"β οΈ Lo script con ID {screen_id} Γ¨ giΓ in esecuzione.")
+ self.bot.send_message(
+ message.chat.id,
+ f"β οΈ Lo script con ID {screen_id} Γ¨ giΓ in esecuzione.",
+ )
return
except subprocess.CalledProcessError:
pass # Se il comando fallisce, significa che non ci sono screen attivi.
# Crea la sessione screen e avvia lo script al suo interno
- command = ["screen", "-dmS", screen_id, "python3", "../../test_run.py", screen_id]
+ command = [
+ "screen",
+ "-dmS",
+ screen_id,
+ "python3",
+ "../../test_run.py",
+ screen_id,
+ ]
# Avvia il comando tramite subprocess
subprocess.Popen(command)
@@ -229,7 +233,7 @@ class TelegramBot:
"screen_id": screen_id,
"start_time": time.time(),
"status": "running",
- "user_id": message.from_user.id
+ "user_id": message.from_user.id,
}
# Salvataggio nel file JSON
@@ -237,7 +241,7 @@ class TelegramBot:
# Carica i dati esistenti o crea una nuova lista
try:
- with open(json_file, 'r') as f:
+ with open(json_file, "r") as f:
scripts_data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
scripts_data = []
@@ -246,65 +250,61 @@ class TelegramBot:
scripts_data.append(script_info)
# Scrivi il file aggiornato
- with open(json_file, 'w') as f:
+ with open(json_file, "w") as f:
json.dump(scripts_data, f, indent=4)
def handle_list_scripts(self, message):
- if not self.is_authorized(message.from_user.id):
- print(f"β Non sei autorizzato.")
- self.bot.send_message(message.chat.id, "β Non sei autorizzato.")
- return
+ if not self.is_authorized(message.from_user.id):
+ print(f"β Non sei autorizzato.")
+ self.bot.send_message(message.chat.id, "β Non sei autorizzato.")
+ return
- try:
- with open("../../scripts.json", "r") as f:
- scripts_data = json.load(f)
- except (FileNotFoundError, json.JSONDecodeError):
- scripts_data = []
+ try:
+ with open("../../scripts.json", "r") as f:
+ scripts_data = json.load(f)
+ except (FileNotFoundError, json.JSONDecodeError):
+ scripts_data = []
- if not scripts_data:
- print(f"β οΈ Nessuno script registrato.")
- self.bot.send_message(message.chat.id, "β οΈ Nessuno script registrato.")
- return
+ if not scripts_data:
+ print(f"β οΈ Nessuno script registrato.")
+ self.bot.send_message(message.chat.id, "β οΈ Nessuno script registrato.")
+ return
- current_time = time.time()
- msg = ["π₯οΈ **Script Registrati:**\n"]
+ current_time = time.time()
+ msg = ["π₯οΈ **Script Registrati:**\n"]
- for script in scripts_data:
- # Calcola la durata
- duration = current_time - script["start_time"]
- if "end_time" in script:
- duration = script["end_time"] - script["start_time"]
+ for script in scripts_data:
+ # Calcola la durata
+ duration = current_time - script["start_time"]
+ if "end_time" in script:
+ duration = script["end_time"] - script["start_time"]
- # Formatta la durata
- hours, rem = divmod(duration, 3600)
- minutes, seconds = divmod(rem, 60)
- duration_str = f"{int(hours)}h {int(minutes)}m {int(seconds)}s"
+ # Formatta la durata
+ hours, rem = divmod(duration, 3600)
+ minutes, seconds = divmod(rem, 60)
+ duration_str = f"{int(hours)}h {int(minutes)}m {int(seconds)}s"
- # Icona stato
- status_icons = {
- "running": "π’",
- "stopped": "π΄",
- "completed": "βͺ"
- }
+ # Icona stato
+ status_icons = {"running": "π’", "stopped": "π΄", "completed": "βͺ"}
- # Costruisci riga
- line = (
- f"β’ ID: `{script['screen_id']}`\n"
- f"β’ Stato: {status_icons.get(script['status'], 'β«')}\n"
- f"β’ Stop: `/stop {script['screen_id']}`\n"
- f"β’ Screen: `/screen {script['screen_id']}`\n"
- f"β’ Durata: {duration_str}\n"
- f"β’ Download:\n{script.get('titolo', 'N/A')}\n"
- )
- msg.append(line)
+ # Costruisci riga
+ line = (
+ f"β’ ID: `{script['screen_id']}`\n"
+ f"β’ Stato: {status_icons.get(script['status'], 'β«')}\n"
+ f"β’ Stop: `/stop {script['screen_id']}`\n"
+ f"β’ Screen: `/screen {script['screen_id']}`\n"
+ f"β’ Durata: {duration_str}\n"
+ f"β’ Download:\n{script.get('titolo', 'N/A')}\n"
+ )
+ msg.append(line)
- # Formatta la risposta finale
- final_msg = "\n".join(msg)
- if len(final_msg) > 4000:
- final_msg = final_msg[:4000] + "\n[...] (messaggio troncato)"
+ # Formatta la risposta finale
+ final_msg = "\n".join(msg)
+ if len(final_msg) > 4000:
+ final_msg = final_msg[:4000] + "\n[...] (messaggio troncato)"
- print(f"{final_msg}")
- self.bot.send_message(message.chat.id, final_msg, parse_mode="Markdown")
+ print(f"{final_msg}")
+ self.bot.send_message(message.chat.id, final_msg, parse_mode="Markdown")
def handle_stop_script(self, message):
if not self.is_authorized(message.from_user.id):
@@ -324,7 +324,9 @@ class TelegramBot:
if not running_scripts:
print(f"β οΈ Nessuno script attivo da fermare.")
- self.bot.send_message(message.chat.id, "β οΈ Nessuno script attivo da fermare.")
+ self.bot.send_message(
+ message.chat.id, "β οΈ Nessuno script attivo da fermare."
+ )
return
msg = "π₯οΈ **Script Attivi:**\n"
@@ -344,12 +346,18 @@ class TelegramBot:
scripts_data = []
# Filtra la lista eliminando lo script con l'ID specificato
- new_scripts_data = [script for script in scripts_data if script["screen_id"] != screen_id]
+ new_scripts_data = [
+ script for script in scripts_data if script["screen_id"] != screen_id
+ ]
if len(new_scripts_data) == len(scripts_data):
# Nessun elemento rimosso, quindi ID non trovato
print(f"β οΈ Nessuno script attivo con ID `{screen_id}`.")
- self.bot.send_message(message.chat.id, f"β οΈ Nessuno script attivo con ID `{screen_id}`.", parse_mode="Markdown")
+ self.bot.send_message(
+ message.chat.id,
+ f"β οΈ Nessuno script attivo con ID `{screen_id}`.",
+ parse_mode="Markdown",
+ )
return
# Terminare la sessione screen
@@ -357,8 +365,14 @@ class TelegramBot:
subprocess.check_output(["screen", "-S", screen_id, "-X", "quit"])
print(f"β
La sessione screen con ID {screen_id} Γ¨ stata fermata.")
except subprocess.CalledProcessError:
- print(f"β οΈ Impossibile fermare la sessione screen con ID `{screen_id}`.")
- self.bot.send_message(message.chat.id, f"β οΈ Impossibile fermare la sessione screen con ID `{screen_id}`.", parse_mode="Markdown")
+ print(
+ f"β οΈ Impossibile fermare la sessione screen con ID `{screen_id}`."
+ )
+ self.bot.send_message(
+ message.chat.id,
+ f"β οΈ Impossibile fermare la sessione screen con ID `{screen_id}`.",
+ parse_mode="Markdown",
+ )
return
# Salva la lista aggiornata senza lo script eliminato
@@ -366,22 +380,13 @@ class TelegramBot:
json.dump(new_scripts_data, f, indent=4)
print(f"β
Script `{screen_id}` terminato con successo!")
- self.bot.send_message(message.chat.id, f"β
Script `{screen_id}` terminato con successo!", parse_mode="Markdown")
+ self.bot.send_message(
+ message.chat.id,
+ f"β
Script `{screen_id}` terminato con successo!",
+ parse_mode="Markdown",
+ )
def handle_response(self, message):
- """ if message.text == 'Start':
- self.handle_start_script(self, message)
- elif message.text == 'Lista':
- self.handle_list_scripts(self, message)
- elif message.text == 'Azione 3':
- self.bot.send_message(message.chat.id, "Hai scelto Azione 3!")
- else:
- self.bot.send_message(message.chat.id, "Comando non riconosciuto.")
-
- if not self.is_authorized(message.from_user.id):
- self.bot.reply_to(message, "β Non sei autorizzato.")
- return """
-
text = message.text
if self.request_manager.save_response(text):
print(f"π₯ Risposta salvata correttamente per il tipo {text}")
@@ -393,7 +398,9 @@ class TelegramBot:
command_parts = message.text.split()
if len(command_parts) < 2:
print(f"β οΈ ID mancante nel comando. Usa: /screen ")
- self.bot.send_message(message.chat.id, "β οΈ ID mancante nel comando. Usa: /screen ")
+ self.bot.send_message(
+ message.chat.id, "β οΈ ID mancante nel comando. Usa: /screen "
+ )
return
screen_id = command_parts[1]
@@ -401,35 +408,55 @@ class TelegramBot:
try:
# Cattura l'output della screen
- subprocess.run(["screen", "-X", "-S", screen_id, "hardcopy", "-h", temp_file], check=True)
+ subprocess.run(
+ ["screen", "-X", "-S", screen_id, "hardcopy", "-h", temp_file],
+ check=True,
+ )
except subprocess.CalledProcessError as e:
print(f"β Errore durante la cattura dell'output della screen: {e}")
- self.bot.send_message(message.chat.id, f"β Errore durante la cattura dell'output della screen: {e}")
+ self.bot.send_message(
+ message.chat.id,
+ f"β Errore durante la cattura dell'output della screen: {e}",
+ )
return
if not os.path.exists(temp_file):
print(f"β Impossibile catturare l'output della screen.")
- self.bot.send_message(message.chat.id, f"β Impossibile catturare l'output della screen.")
+ self.bot.send_message(
+ message.chat.id, f"β Impossibile catturare l'output della screen."
+ )
return
try:
# Leggi il file con la codifica corretta
- with open(temp_file, 'r', encoding='latin-1') as file:
+ with open(temp_file, "r", encoding="latin-1") as file:
screen_output = file.read()
# Pulisci l'output
- cleaned_output = re.sub(r'[\x00-\x1F\x7F]', '', screen_output) # Rimuovi caratteri di controllo
- cleaned_output = cleaned_output.replace('\n\n', '\n') # Rimuovi newline multipli
+ cleaned_output = re.sub(
+ r"[\x00-\x1F\x7F]", "", screen_output
+ ) # Rimuovi caratteri di controllo
+ cleaned_output = cleaned_output.replace(
+ "\n\n", "\n"
+ ) # Rimuovi newline multipli
# Estrarre tutte le parti da "Download:" fino a "Video" o "Subtitle", senza includerli
- download_matches = re.findall(r"Download: (.*?)(?:Video|Subtitle)", cleaned_output)
+ download_matches = re.findall(
+ r"Download: (.*?)(?:Video|Subtitle)", cleaned_output
+ )
if download_matches:
# Serie TV e Film StreamingCommunity
proc_matches = re.findall(r"Proc: ([\d\.]+%)", cleaned_output)
# Creare una stringa unica con tutti i risultati
- result_string = "\n".join([f"Download: {download_matches[i].strip()}\nDownload al {proc_matches[i]}" for i in range(len(download_matches)) if i < len(proc_matches)])
+ result_string = "\n".join(
+ [
+ f"Download: {download_matches[i].strip()}\nDownload al {proc_matches[i]}"
+ for i in range(len(download_matches))
+ if i < len(proc_matches)
+ ]
+ )
if result_string != "":
cleaned_output = result_string
@@ -461,11 +488,18 @@ class TelegramBot:
# Invia l'output pulito
print(f"π Output della screen {screen_id}:\n{cleaned_output}")
- self._send_long_message(message.chat.id, f"π Output della screen {screen_id}:\n{cleaned_output}")
+ self._send_long_message(
+ message.chat.id, f"π Output della screen {screen_id}:\n{cleaned_output}"
+ )
except Exception as e:
- print(f"β Errore durante la lettura o l'invio dell'output della screen: {e}")
- self.bot.send_message(message.chat.id, f"β Errore durante la lettura o l'invio dell'output della screen: {e}")
+ print(
+ f"β Errore durante la lettura o l'invio dell'output della screen: {e}"
+ )
+ self.bot.send_message(
+ message.chat.id,
+ f"β Errore durante la lettura o l'invio dell'output della screen: {e}",
+ )
# Cancella il file temporaneo
os.remove(temp_file)
@@ -486,7 +520,7 @@ class TelegramBot:
"""Suddivide e invia un messaggio troppo lungo in piΓΉ parti."""
for i in range(0, len(text), chunk_size):
print(f"{text[i:i+chunk_size]}")
- self.bot.send_message(chat_id, text[i:i+chunk_size])
+ self.bot.send_message(chat_id, text[i : i + chunk_size])
def ask(self, type, prompt_message, choices, timeout=60):
self.request_manager.create_request(type)
@@ -518,30 +552,10 @@ class TelegramBot:
def run(self):
print("π Avvio del bot...")
- # svuoto il file ../../scripts.json
with open("../../scripts.json", "w") as f:
json.dump([], f)
self.bot.infinity_polling()
+
def get_bot_instance():
- return TelegramBot.get_instance()
-
-# Esempio di utilizzo
-if __name__ == "__main__":
-
- # Usa le variabili
- token = os.getenv("TOKEN_TELEGRAM")
- authorized_user_id = os.getenv("AUTHORIZED_USER_ID")
-
- TOKEN = token # Inserisci il token del tuo bot Telegram sul file .env
- AUTHORIZED_USER_ID = int(authorized_user_id) # Inserisci il tuo ID utente Telegram sul file .env
-
- # Inizializza il bot
- bot = TelegramBot.init_bot(TOKEN, AUTHORIZED_USER_ID)
- bot.run()
-
-"""
-start - Avvia lo script
-list - Lista script attivi
-get - Mostra ID utente Telegram
-"""
+ return TelegramBot.get_instance()
\ No newline at end of file
diff --git a/StreamingCommunity/Upload/update.py b/StreamingCommunity/Upload/update.py
index 7dd7f22..d6617a9 100644
--- a/StreamingCommunity/Upload/update.py
+++ b/StreamingCommunity/Upload/update.py
@@ -25,8 +25,7 @@ def update():
"""
Check for updates on GitHub and display relevant information.
"""
-
- console.print("[green]Checking GitHub version [white]...")
+ console.print("\n[cyan]β [green]Checking GitHub version ...")
# Make the GitHub API requests and handle potential errors
try:
@@ -59,7 +58,7 @@ def update():
if str(__version__).replace('v', '') != str(last_version).replace('v', '') :
console.print(f"[red]New version available: [yellow]{last_version} \n")
else:
- console.print(f" [yellow]Everything is up to date \n")
+ console.print(f" [red]Everything is up to date \n")
console.print(f"[red]{__title__} has been downloaded [yellow]{total_download_count} [red]times, but only [yellow]{percentual_stars}% [red]of users have starred it.\n\
[cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
diff --git a/StreamingCommunity/Upload/version.py b/StreamingCommunity/Upload/version.py
index 6e81304..36ad52c 100644
--- a/StreamingCommunity/Upload/version.py
+++ b/StreamingCommunity/Upload/version.py
@@ -1,5 +1,5 @@
__title__ = 'StreamingCommunity'
__version__ = '2.5.0'
-__author__ = 'Lovi-0'
+__author__ = 'Abc-dc'
__description__ = 'A command-line program to download film'
__copyright__ = 'Copyright 2024'
diff --git a/StreamingCommunity/Util/_jsonConfig.py b/StreamingCommunity/Util/_jsonConfig.py
index 653380b..79716c8 100644
--- a/StreamingCommunity/Util/_jsonConfig.py
+++ b/StreamingCommunity/Util/_jsonConfig.py
@@ -34,7 +34,7 @@ class ConfigManager:
else:
logging.info("Configuration file does not exist. Downloading...")
self.download_requirements(
- 'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/config.json',
+ 'https://raw.githubusercontent.com/Abc-dc/StreamingCommunity/refs/heads/main/config.json',
self.file_path
)
diff --git a/StreamingCommunity/Util/message.py b/StreamingCommunity/Util/message.py
index ad541ad..804541c 100644
--- a/StreamingCommunity/Util/message.py
+++ b/StreamingCommunity/Util/message.py
@@ -7,58 +7,30 @@ import platform
from StreamingCommunity.Util.console import console
from StreamingCommunity.Util._jsonConfig import config_manager
+
# Variable
CLEAN = config_manager.get_bool('DEFAULT', 'clean_console')
SHOW = config_manager.get_bool('DEFAULT', 'show_message')
-def create_italian_flag_colored_text(text: str) -> str:
- """Create text divided into three sections with Italian flag colors, splitting each line at two spaces."""
-
- # Split the text into lines
- lines = text.splitlines()
-
- colored_lines = []
-
- for line in lines:
- # Split each line into parts using two spaces as a delimiter
- parts = line.split(" ")
-
- # Ensure there are exactly 3 parts (add empty strings if necessary)
- parts += [''] * (4 - len(parts))
-
- # Apply flag colors to the parts
- green_part = f"[green]{parts[0]}[/]"
- white_part = f"[white]{parts[1]}[/]"
- red_part = f"[red]{parts[2]}[/]"
-
- # Reassemble the colored line
- colored_line = green_part + white_part + red_part
- colored_lines.append(colored_line)
-
- # Join all colored lines back into a single string
- return "\n".join(colored_lines)
-
def start_message():
"""Display a stylized start message in the console."""
msg = r'''
- βββ βββββββ βββ ββββββ βββ βββ ββββββββββββββββββββββββ ββββββββ ββββββ ββββ βββββββββββ βββ βββββββ
- βββ ββββββββββββ ββββββ ββββββββ ββββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββ βββββββββββ
- βββ βββ ββββββ ββββββ ββββββ ββββββββ βββ ββββββββββββββ ββββββββββββββββββββββββββββ ββββββ ββββ
- βββ βββ βββββββ βββββββ ββββββ ββββββββ βββ ββββββββββββββ βββββββββββββββββββββββββββββββββββ βββ
- βββββββββββββββββ βββββββ βββ ββββ βββ ββββββββ βββ βββ ββββββββββββββ ββββββ βββ βββββββββ βββββββββββββββ
- ββββββββ βββββββ βββββ βββ βββ βββ ββββββββ βββ βββ ββββββββββββββ ββββββ βββββββββ βββββ βββββββ
+ __ __ _ _____ __ _
+ / //_/(_)________ ____ _ __ / ___// /_________ ____ _____ ___ (_)___ ____ _
+ / ,< / / ___/ __ \/ __ \ | |/_/ \__ \/ __/ ___/ _ \/ __ `/ __ `__ \/ / __ \/ __ `/
+ / /| |/ / / / /_/ / / / / _> < ___/ / /_/ / / __/ /_/ / / / / / / / / / / /_/ /
+/_/ |_/_/_/ \____/_/ /_/ /_/|_| /____/\__/_/ \___/\__,_/_/ /_/ /_/_/_/ /_/\__, /
+ /____/
'''.rstrip()
- colored_msg = create_italian_flag_colored_text(msg)
-
if CLEAN:
os.system("cls" if platform.system() == 'Windows' else "clear")
if SHOW:
- console.print(colored_msg)
+ console.print(f"[purple]{msg}")
# Print a decorative separator line using asterisks
separator = "_" * (console.width - 2) # Ridotto di 2 per il padding
- console.print(f"[yellow]{separator}[/yellow]\n")
+ console.print(f"[cyan]{separator}[/cyan]\n")
diff --git a/StreamingCommunity/Util/os.py b/StreamingCommunity/Util/os.py
index b8166d2..c849fbf 100644
--- a/StreamingCommunity/Util/os.py
+++ b/StreamingCommunity/Util/os.py
@@ -411,7 +411,6 @@ class OsSummary:
def get_system_summary(self):
self.check_python_version()
InternManager().check_internet()
- console.print("[bold blue]System Summary[/bold blue][white]:")
# Python info
python_version = sys.version.split()[0]
@@ -477,7 +476,7 @@ class OsSummary:
if not os.path.exists(requirements_file):
self.download_requirements(
- 'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/requirements.txt',
+ 'https://raw.githubusercontent.com/Abc-dc/StreamingCommunity/refs/heads/main/requirements.txt',
requirements_file
)
@@ -492,7 +491,7 @@ class OsSummary:
else:
logging.info(f"Library: {installed_version}")
- console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
+ #console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
diff --git a/StreamingCommunity/Util/table.py b/StreamingCommunity/Util/table.py
index 67acb5c..a7ed84b 100644
--- a/StreamingCommunity/Util/table.py
+++ b/StreamingCommunity/Util/table.py
@@ -19,7 +19,7 @@ from .message import start_message
from .call_stack import get_call_stack
# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
from StreamingCommunity.Util._jsonConfig import config_manager
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
diff --git a/StreamingCommunity/run.py b/StreamingCommunity/run.py
index 4339452..ee7e71d 100644
--- a/StreamingCommunity/run.py
+++ b/StreamingCommunity/run.py
@@ -20,13 +20,16 @@ from StreamingCommunity.Upload.update import update as git_update
from StreamingCommunity.Util.os import os_summary
from StreamingCommunity.Util.logger import Logger
-# Telegram bot instance
-from StreamingCommunity.HelpTg.telegram_bot import get_bot_instance
+
+# Telegram util
+from StreamingCommunity.TelegramHelp.session import get_session, deleteScriptId
+from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
+
# Config
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-from StreamingCommunity.HelpTg.session import get_session, deleteScriptId
+
def run_function(func: Callable[..., None], close_console: bool = False) -> None:
@@ -136,11 +139,13 @@ def initialize():
except:
console.log("[red]Error with loading github.")
+
def restart_script():
"""Riavvia lo script con gli stessi argomenti della riga di comando."""
print("\nπ Riavvio dello script...\n")
python = sys.executable
- os.execv(python, [python] + sys.argv) # Riavvia lo stesso script con gli stessi argomenti
+ os.execv(python, [python] + sys.argv)
+
def force_exit():
"""Forza la chiusura dello script in qualsiasi contesto."""
@@ -160,23 +165,24 @@ def force_exit():
print("β‘ Arresto del loop asyncio...")
loop.stop()
except RuntimeError:
- pass # Se non c'Γ¨ un loop asyncio attivo, ignora l'errore
+ pass
# 3οΈβ£ Esce con sys.exit(), se fallisce usa os._exit()
try:
print("β
Uscita con sys.exit(0)")
sys.exit(0)
except SystemExit:
- pass # Se viene intercettato da un try/except, ignora
+ pass
print("π¨ Uscita forzata con os._exit(0)")
- os._exit(0) # Se sys.exit() non funziona, esce forzatamente
+ os._exit(0)
+
def main(script_id):
if TELEGRAM_BOT:
- bot = get_bot_instance()
- bot.send_message(f"π Avviato script {script_id}", None)
+ bot = get_bot_instance()
+ bot.send_message(f"π Avviato script {script_id}", None)
start = time.time()
@@ -291,41 +297,36 @@ def main(script_id):
if TELEGRAM_BOT:
- # Mappa delle emoji per i colori
- emoji_map = {
- "yellow": "π‘", # Giallo
- "red": "π΄", # Rosso
- "blue": "π΅", # Blu
- "green": "π’" # Verde
- }
+ # Mappa delle emoji per i colori
+ emoji_map = {
+ "yellow": "π‘", # Giallo
+ "red": "π΄", # Rosso
+ "blue": "π΅", # Blu
+ "green": "π’" # Verde
+ }
- # Display the category legend in a single line
- category_legend_str = "Categorie: \n" + " | ".join([
- f"{emoji_map.get(color, 'βͺ')} {category.capitalize()}"
- for category, color in color_map.items()
- ])
+ # Display the category legend in a single line
+ category_legend_str = "Categorie: \n" + " | ".join([
+ f"{emoji_map.get(color, 'βͺ')} {category.capitalize()}"
+ for category, color in color_map.items()
+ ])
- # Costruisci il messaggio con le emoji al posto dei colori
- prompt_message = "Inserisci il sito:\n" + "\n".join(
- [f"{key}: {emoji_map[color_map[label[1]]]} {label[0]}" for key, label in choice_labels.items()]
- )
+ # Costruisci il messaggio con le emoji al posto dei colori
+ prompt_message = "Inserisci il sito:\n" + "\n".join(
+ [f"{key}: {emoji_map[color_map[label[1]]]} {label[0]}" for key, label in choice_labels.items()]
+ )
- console.print(f"\n{prompt_message}")
+ console.print(f"\n{prompt_message}")
- # Chiedi la scelta all'utente con il bot Telegram
- category = bot.ask(
- "select_provider",
- f"{category_legend_str}\n\n{prompt_message}",
- None # Passiamo la lista delle chiavi come scelte
- )
+ # Chiedi la scelta all'utente con il bot Telegram
+ category = bot.ask(
+ "select_provider",
+ f"{category_legend_str}\n\n{prompt_message}",
+ None # Passiamo la lista delle chiavi come scelte
+ )
else:
-
- #console.print(f"\n{prompt_message}")
-
- # Ask the user for input
- category = msg.ask(prompt_message, choices=list(choice_labels.keys()), default="0", show_choices=False, show_default=False)
-
+ category = msg.ask(prompt_message, choices=list(choice_labels.keys()), default="0", show_choices=False, show_default=False)
# Run the corresponding function based on user input
if category in input_to_function:
@@ -338,18 +339,14 @@ def main(script_id):
console.print("[red]Invalid category.")
if CLOSE_CONSOLE:
- restart_script() # Riavvia lo script invece di uscire
- # Riavvia lo script
- # Chiude il processo attuale e avvia una nuova istanza dello script
- """ subprocess.Popen([sys.executable] + sys.argv)
- sys.exit() """
+ restart_script() # Riavvia lo script invece di uscire
else:
- force_exit() # Usa la funzione per chiudere sempre
+ force_exit() # Usa la funzione per chiudere sempre
- if TELEGRAM_BOT:
- bot.send_message(f"Chiusura in corso", None)
- # Delete script_id
- script_id = get_session()
- if script_id != "unknown":
- deleteScriptId(script_id)
-
\ No newline at end of file
+ if TELEGRAM_BOT:
+ bot.send_message(f"Chiusura in corso", None)
+
+ # Delete script_id
+ script_id = get_session()
+ if script_id != "unknown":
+ deleteScriptId(script_id)
diff --git a/Test/Download/HLS.py b/Test/Download/HLS.py
index 2fc1598..ec35166 100644
--- a/Test/Download/HLS.py
+++ b/Test/Download/HLS.py
@@ -20,6 +20,6 @@ from StreamingCommunity.Lib.Downloader import HLS_Downloader
start_message()
logger = Logger()
print("Return: ", HLS_Downloader(
- output_filename="test.mp4",
- m3u8_playlist="https://acdn.ak-stream-videoplatform.sky.it/hls/2024/11/21/968275/master.m3u8"
+ output_path="test.mp4",
+ m3u8_url="https://acdn.ak-stream-videoplatform.sky.it/hls/2024/11/21/968275/master.m3u8"
).start())
\ No newline at end of file
diff --git a/Test/Util/ffmpegVersion.py b/Test/Util/ffmpegVersion.py
new file mode 100644
index 0000000..1fce934
--- /dev/null
+++ b/Test/Util/ffmpegVersion.py
@@ -0,0 +1,29 @@
+# 05.02.25
+
+# Fix import
+import sys
+import os
+src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+sys.path.append(src_path)
+
+
+
+from StreamingCommunity.Util.ffmpeg_installer import FFMPEGDownloader
+
+
+def test_ffmpeg_downloader():
+
+ # Create an instance of the downloader
+ downloader = FFMPEGDownloader()
+
+ # Check if the download method works and fetches the executables
+ ffmpeg, ffprobe, ffplay = downloader.download()
+
+ # Output the destination paths
+ print(f"FFmpeg path: {ffmpeg}")
+ print(f"FFprobe path: {ffprobe}")
+ print(f"FFplay path: {ffplay}")
+
+
+if __name__ == "__main__":
+ test_ffmpeg_downloader()
\ No newline at end of file
diff --git a/Test/Util/oss.py b/Test/Util/osPath.py
similarity index 100%
rename from Test/Util/oss.py
rename to Test/Util/osPath.py
diff --git a/config.json b/config.json
index e547b1f..5b555ba 100644
--- a/config.json
+++ b/config.json
@@ -3,15 +3,15 @@
"debug": false,
"log_file": "app.log",
"log_to_file": false,
- "show_message": false,
+ "show_message": true,
"clean_console": true,
- "root_path": "/home/giuseppepiccolo/Develop/docker/jellyfin/media/",
+ "root_path": "Video",
"movie_folder_name": "Movie",
"serie_folder_name": "Serie",
"anime_folder_name": "Anime",
- "map_episode_name": "%(tv_name)_S%(season)E%(episode)_%(episode_name)",
+ "map_episode_name": "S%(season)E%(episode)_%(episode_name)",
"config_qbit_tor": {
- "host": "192.168.5.172",
+ "host": "192.168.5.99",
"port": "8080",
"user": "admin",
"pass": "adminadmin"
@@ -19,10 +19,10 @@
"add_siteName": false,
"disable_searchDomain": false,
"not_close": false,
- "telegram_bot": true
+ "telegram_bot": false
},
"REQUESTS": {
- "timeout": 30,
+ "timeout": 20,
"max_retry": 8,
"proxy_start_min": 0.1,
"proxy_start_max": 0.5
@@ -35,7 +35,7 @@
"specific_list_audio": [
"ita"
],
- "merge_subs": false,
+ "merge_subs": true,
"specific_list_subtitles": [
"eng",
"spa"
@@ -79,7 +79,7 @@
"domain": "so"
},
"cb01new": {
- "domain": "mobi"
+ "domain": "media"
},
"1337xx": {
"domain": "to"
diff --git a/requirements.txt b/requirements.txt
index 21212e1..fbf4453 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,6 +11,6 @@ pycryptodomex
googlesearch-python
fake-useragent
qbittorrent-api
-python-qbittorrent
-pyTelegramBotAPI
-Pillow
\ No newline at end of file
+python-qbittorrent
+Pillow
+pyTelegramBotAPI
\ No newline at end of file
diff --git a/test_run.py b/test_run.py
index 0ca8a16..abe28dc 100644
--- a/test_run.py
+++ b/test_run.py
@@ -2,14 +2,20 @@
import sys
from StreamingCommunity.run import main
-from StreamingCommunity.HelpTg.request_manager import RequestManager
-from StreamingCommunity.HelpTg.session import set_session
+from StreamingCommunity.Util._jsonConfig import config_manager
+from StreamingCommunity.TelegramHelp.request_manager import RequestManager
+from StreamingCommunity.TelegramHelp.session import set_session
# Svuoto il file
-request_manager = RequestManager()
-request_manager.clear_file()
-script_id = sys.argv[1] if len(sys.argv) > 1 else "unknown"
+TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
-set_session(script_id)
+if TELEGRAM_BOT:
+ request_manager = RequestManager()
+ request_manager.clear_file()
+ script_id = sys.argv[1] if len(sys.argv) > 1 else "unknown"
-main(script_id)
\ No newline at end of file
+ set_session(script_id)
+ main(script_id)
+
+else:
+ main(0)
\ No newline at end of file