Files
XCDownloader/downloader.py
2025-04-25 12:12:02 +00:00

194 lines
6.3 KiB
Python

from movies import *
from series import *
import os
import re
import requests
import subprocess
import signal
import sys
import pyfiglet
from urllib.parse import urlparse
from collections import defaultdict
from colorama import Fore, Style
USER_AGENT = "VLC/3.0.9 LibVLC/3.0.9"
HEADERS = {'User-Agent': USER_AGENT}
SUPPORTED_EXTENSIONS = ('.mkv', '.mp4', '.avi')
max_connections = 1
def handle_exit(sig, frame):
print("\nScript exit with CTRL+C by user")
sys.exit(0)
signal.signal(signal.SIGINT, handle_exit)
def sanitize_filename(name):
return re.sub(r'[\\/*?:\"<>|]', "", name).strip()
def aria2c_exists():
return os.path.exists("./aria2c") or os.path.exists("./aria2c.exe")
def get_remote_file_size(url):
try:
with requests.head(url, headers=HEADERS, timeout=10) as head:
head.raise_for_status()
content_length = head.headers.get('content-length')
return int(content_length) if content_length else 0
except Exception:
return 0
def is_series(title):
match = re.search(r'(.*?)\s+[Ss](\d{2})[Ee](\d{2})', title)
if match:
series_name = match.group(1).strip()
season = f"S{match.group(2)}"
episode = f"E{match.group(3)}"
return True, series_name, season, episode
return False, None, None, None
def parse_m3u(m3u_content):
entries = []
lines = m3u_content.splitlines()
i = 0
while i < len(lines):
line = lines[i]
if line.startswith("#EXTINF"):
info_line = line
i += 1
if i < len(lines):
url_line = lines[i]
entries.append((info_line, url_line))
i += 1
return entries
def extract_metadata(info_line):
tvg_name_match = re.search(r'tvg-name="([^"]+)"', info_line)
tvg_logo_match = re.search(r'tvg-logo="([^"]+)"', info_line)
group_title_match = re.search(r'group-title="([^"]+)"', info_line)
title = tvg_name_match.group(1) if tvg_name_match else "Unknown Title"
logo_url = tvg_logo_match.group(1) if tvg_logo_match else None
group_title = group_title_match.group(1) if group_title_match else "Unknown Group"
return title, logo_url, group_title
def input_range(prompt, max_val):
inp = input(prompt)
selected = set()
for part in inp.split(','):
if '-' in part:
start, end = map(int, part.split('-'))
selected.update(range(start, end + 1))
else:
selected.add(int(part))
return [i for i in selected if 1 <= i <= max_val]
def show_banner():
print("=" * 72)
banner = pyfiglet.figlet_format("XC Downloader", font="slant")
print(banner)
print(" by redhat@woi ")
print("=" * 72 + "\n\n")
def get_credentials():
print("Enter your server credentials below")
print("-----------------------------------\n")
server_url = input("Server URL & Port (e.g., http://serverip_or_url:port): ").strip().rstrip('/')
username = input("Username: ").strip()
password = input("Password: ").strip()
try:
conn_limit = int(input("Server connection limit (max concurrent downloads): ").strip())
if conn_limit < 1:
conn_limit = 1
except ValueError:
conn_limit = 1
return server_url, username, password, conn_limit
def main():
show_banner()
global max_connections
# Loop until valid credentials are provided
while True:
server_url, username, password, max_connections = get_credentials()
m3u_url = f"{server_url}/get.php?username={username}&password={password}&type=m3u_plus&output=mpegts"
try:
response = requests.get(m3u_url, headers=HEADERS, timeout=30)
response.raise_for_status()
m3u_content = response.text
break # ✅ Exit loop if successful
except Exception:
print(f"{Fore.RED}Error: Check your credentials, connection could not be established{Style.RESET_ALL}\n")
entries = parse_m3u(m3u_content)
movies = defaultdict(list)
series = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
for info_line, media_url in entries:
if not media_url.lower().endswith(SUPPORTED_EXTENSIONS):
continue
title, logo_url, group_title = extract_metadata(info_line)
is_series_flag, series_name, season, episode = is_series(title)
if is_series_flag:
series[group_title][series_name][season].append({
'title': title,
'logo_url': logo_url,
'media_url': media_url,
'season': season,
'episode': episode
})
else:
movies[group_title].append({
'title': title,
'logo_url': logo_url,
'media_url': media_url
})
while True:
print("\n" + "*" * 21)
print("* * Main Menu * *")
print("*" * 21 + "\n")
print("Results:")
print("-----------\n")
print(f"Movies found: {sum(len(v) for v in movies.values())}")
print(f"Movies Categories found: {len(movies)}")
print("-----------\n")
print("-----------")
print(f"Series found: {sum(len(series[g][s][se]) for g in series for s in series[g] for se in series[g][s])}")
print(f"Series Categories found: {len(series)}")
print("-----------\n")
print("1. Show Movies Categories")
print("2. Show Series Categories")
print("3. Download all Movies")
print("4. Download all Series")
print("5. Exit\n")
choice = input("Select: ").strip()
if choice == '1':
show_movie_categories(movies)
elif choice == '2':
show_series_categories(series)
elif choice == '3':
for category in movies:
for movie in movies[category]:
download_movie(category, movie)
elif choice == '4':
for category in series:
for sname in series[category]:
for season in series[category][sname]:
for ep in series[category][sname][season]:
download_series_episode(category, sname, season, ep)
elif choice == '5':
break
else:
print("Invalid choice.")
if __name__ == "__main__":
main()