diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index f33827acd..84e6adfe1 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -5,7 +5,6 @@ on: branches: - master - develop - - unit3d-searching env: REGISTRY: ghcr.io diff --git a/README.md b/README.md index 86f2a571f..1a1b9b00f 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,15 @@ A simple tool to take the work out of uploading. - Generates and Parses MediaInfo/BDInfo. - Generates and Uploads screenshots. - Uses srrdb to fix scene filenames - - Can grab descriptions from PTP (automatically on filename match or arg) / BLU (arg) + - Can grab descriptions from PTP/BLU (automatically on filename match or arg) / Aither/LST/OE (with arg) + - Can strip existing screenshots from descriptions to skip screenshot generation and uploading - Obtains TMDb/IMDb/MAL identifiers. - Converts absolute to season episode numbering for Anime - Generates custom .torrents without useless top level folders/nfos. - Can re-use existing torrents instead of hashing new - Generates proper name for your upload using Mediainfo/BDInfo and TMDb/IMDb conforming to site rules - Checks for existing releases already on site - - Uploads to PTP/BLU/BHD/Aither/THR/STC/R4E(limited)/STT/HP/ACM/LCD/LST/NBL/ANT/FL/HUNO/RF/SN/RTF/OTW/FNP/CBR/UTP/HDB/AL/SHRI + - Uploads to PTP/BLU/BHD/Aither/THR/STC/R4E(limited)/STT/HP/ACM/LCD/LST/NBL/ANT/FL/HUNO/RF/SN/RTF/OTW/FNP/CBR/UTP/HDB/AL/SHRI/OE/TL/BHDTV/HDT/JPTV/LT/MTV/PTER/TDC/TTG/UTP - Adds to your client with fast resume, seeding instantly (rtorrent/qbittorrent/deluge/watch folder) - ALL WITH MINIMAL INPUT! - Currently works with .mkv/.mp4/Blu-ray/DVD/HD-DVDs @@ -33,7 +34,7 @@ Built with updated BDInfoCLI from https://github.com/rokibhasansagar/BDInfoCLI-n - Also needs MediaInfo and ffmpeg installed on your system - On Windows systems, ffmpeg must be added to PATH (https://windowsloop.com/install-ffmpeg-windows-10/) - On linux systems, get it from your favorite package manager - - Clone the repo to your system `git clone https://github.com/Audionut/Upload-Assistant.git` + - Clone the repo to your system `git clone https://github.com/Audionut/Upload-Assistant.git` - or download a zip of the source - Copy and Rename `data/example-config.py` to `data/config.py` - Edit `config.py` to use your information (more detailed information in the [wiki](https://github.com/Audionut/Upload-Assistant/wiki)) - tmdb_api (v3) key can be obtained from https://developers.themoviedb.org/3/getting-started/introduction @@ -50,6 +51,7 @@ Built with updated BDInfoCLI from https://github.com/rokibhasansagar/BDInfoCLI-n - To update first navigate into the Upload-Assistant directory: `cd Upload-Assistant` - Run a `git pull` to grab latest updates - Run `python3 -m pip install --user -U -r requirements.txt` to ensure dependencies are up to date + - Or download a fresh zip and overwrite existing files ## **CLI Usage:** `python3 upload.py /downloads/path/to/content --args` diff --git a/data/example-config.py b/data/example-config.py index da8cf8463..0043aad12 100644 --- a/data/example-config.py +++ b/data/example-config.py @@ -37,14 +37,15 @@ "TRACKERS": { # Which trackers do you want to upload to? # Available tracker: BLU, BHD, AITHER, STC, STT, SN, THR, R4E, HP, ACM, PTP, LCD, LST, PTER, NBL, ANT, MTV, CBR, RTF, HUNO, BHDTV, LT, PTER, TL, TDC, HDT, OE, RF, OTW, FNP, UTP, AL, HDB - # Remove the ones not used to save being asked everytime + # Remove the trackers from the default_trackers list that are not used, to save being asked everytime "default_trackers": "BLU, BHD, AITHER, STC, STT, SN, THR, R4E, HP, ACM, PTP, LCD, LST, PTER, NBL, ANT, MTV, CBR, RTF, HUNO, BHDTV, LT, PTER, TL, TDC, HDT, OE, RF, OTW, FNP, UTP, AL, HDB", "BLU": { "useAPI": False, # Set to True if using BLU "api_key": "BLU api key", "announce_url": "https://blutopia.cc/announce/customannounceurl", - # "anon" : False + # "anon" : False, + # "modq" : False ## Not working yet }, "BHD": { "api_key": "BHD api key", @@ -71,7 +72,8 @@ "AITHER": { "api_key": "AITHER api key", "announce_url": "https://aither.cc/announce/customannounceurl", - # "anon" : False + # "anon" : False, + # "modq" : False ## Not working yet }, "R4E": { "api_key": "R4E api key", @@ -239,9 +241,9 @@ }, }, - # enable_search to true will automatically try and find a suitable hash to save having to rehash when creating torrents + # enable_search to True will automatically try and find a suitable hash to save having to rehash when creating torrents # Should use the qbit API, but will also use the torrent_storage_dir to find suitable hashes - # If you find issue, use the "--debug" command option to print out some related details + # If you find issue, use the "--debug" argument to print out some related details "TORRENT_CLIENTS": { # Name your torrent clients here, for example, this example is named "Client1" and is set as default_torrent_client above # All options relate to the webui, make sure you have the webui secured if it has WAN access @@ -253,7 +255,7 @@ "qbit_port": "8080", "qbit_user": "username", "qbit_pass": "password", - # "torrent_storage_dir": "path/to/BT_backup folder" + # "torrent_storage_dir": "path/to/BT_backup folder" ## use double-backslash on windows eg: "C:\\client\\backup" # Remote path mapping (docker/etc.) CASE SENSITIVE # "local_path": "/LocalPath", diff --git a/src/args.py b/src/args.py index 58bf929c4..988c22715 100644 --- a/src/args.py +++ b/src/args.py @@ -46,6 +46,7 @@ def parse(self, args, meta): parser.add_argument('-blu', '--blu', nargs='*', required=False, help="BLU torrent id/link", type=str) parser.add_argument('-aither', '--aither', nargs='*', required=False, help="Aither torrent id/link", type=str) parser.add_argument('-lst', '--lst', nargs='*', required=False, help="LST torrent id/link", type=str) + parser.add_argument('-oe', '--oe', nargs='*', required=False, help="OE torrent id/link", type=str) parser.add_argument('-hdb', '--hdb', nargs='*', required=False, help="HDB torrent id/link", type=str) parser.add_argument('-d', '--desc', nargs='*', required=False, help="Custom Description (string)") parser.add_argument('-pb', '--desclink', nargs='*', required=False, help="Custom Description (link to hastebin/pastebin)") @@ -165,6 +166,19 @@ def parse(self, args, meta): console.print('[red]Continuing without --lst') else: meta['lst'] = value2 + elif key == 'oe': + if value2.startswith('http'): + parsed = urllib.parse.urlparse(value2) + try: + oepath = parsed.path + if oepath.endswith('/'): + oepath = oepath[:-1] + meta['oe'] = oepath.split('/')[-1] + except Exception: + console.print('[red]Unable to parse id from url') + console.print('[red]Continuing without --oe') + else: + meta['oe'] = value2 elif key == 'hdb': if value2.startswith('http'): parsed = urllib.parse.urlparse(value2) diff --git a/src/bbcode.py b/src/bbcode.py index 6509d594e..983469c4e 100644 --- a/src/bbcode.py +++ b/src/bbcode.py @@ -123,7 +123,7 @@ def clean_ptp_description(self, desc, is_disc): desc = re.sub(r"\[img=[\s\S]*?\]", "", desc, flags=re.IGNORECASE) # Extract loose images and add to imagelist as dictionaries - loose_images = re.findall(r"(https?:\/\/.*\.(?:png|jpg))", nocomp, flags=re.IGNORECASE) + loose_images = re.findall(r"(https?:\/\/[^\s\[\]]+\.(?:png|jpg))", nocomp, flags=re.IGNORECASE) if loose_images: for img_url in loose_images: image_dict = { @@ -198,6 +198,12 @@ def clean_unit3d_description(self, desc, site): # Remove the [img] tag and its contents from the description desc = re.sub(rf"\[img[^\]]*\]{re.escape(img_url)}\[/img\]", '', desc, flags=re.IGNORECASE) + # Now, remove matching URLs from [URL] tags + for img in imagelist: + img_url = re.escape(img['img_url']) + desc = re.sub(rf"\[URL={img_url}\]\[/URL\]", '', desc, flags=re.IGNORECASE) + desc = re.sub(rf"\[URL={img_url}\]\[img[^\]]*\]{img_url}\[/img\]\[/URL\]", '', desc, flags=re.IGNORECASE) + # Filter out bot images from imagelist bot_image_urls = [ "https://blutopia.xyz/favicon.ico", # Example bot image URL @@ -236,10 +242,10 @@ def clean_unit3d_description(self, desc, site): desc = re.sub(bot_signature_regex, "", desc, flags=re.IGNORECASE | re.VERBOSE) desc = re.sub(r"\[center\].*Created by L4G's Upload Assistant.*\[\/center\]", "", desc, flags=re.IGNORECASE) - # Ensure no dangling tags and remove extra blank lines - desc = re.sub(r'\n\s*\n', '\n', desc) # Remove multiple consecutive blank lines - desc = re.sub(r'\n\n+', '\n\n', desc) # Ensure no excessive blank lines - desc = desc.strip() # Final cleanup of trailing newlines and spaces + # Remove leftover [img] or [URL] tags in the description + desc = re.sub(r"\[img\][\s\S]*?\[\/img\]", "", desc, flags=re.IGNORECASE) + desc = re.sub(r"\[img=[\s\S]*?\]", "", desc, flags=re.IGNORECASE) + desc = re.sub(r"\[URL=[\s\S]*?\]\[\/URL\]", "", desc, flags=re.IGNORECASE) # Strip trailing whitespace and newlines: desc = desc.rstrip() diff --git a/src/prep.py b/src/prep.py index c860718c8..bc0dabd6c 100644 --- a/src/prep.py +++ b/src/prep.py @@ -6,6 +6,7 @@ from src.trackers.BLU import BLU from src.trackers.AITHER import AITHER from src.trackers.LST import LST +from src.trackers.OE import OE from src.trackers.HDB import HDB from src.trackers.COMMON import COMMON @@ -47,6 +48,7 @@ import aiohttp from PIL import Image import io + import sys except ModuleNotFoundError: console.print(traceback.print_exc()) console.print('[bold red]Missing Module Found. Please reinstall required dependancies.') @@ -77,8 +79,7 @@ async def prompt_user_for_confirmation(self, message: str) -> bool: return True return False except EOFError: - console.print("[bold red]Input was interrupted.") - return False + sys.exit(1) async def check_images_concurrently(self, imagelist): async def check_and_collect(image_dict): @@ -155,7 +156,7 @@ async def update_metadata_from_tracker(self, tracker_name, tracker_instance, met manual_key = f"{tracker_key}_manual" found_match = False - if tracker_name in ["BLU", "AITHER", "LST"]: # Example for UNIT3D trackers + if tracker_name in ["BLU", "AITHER", "LST", "OE"]: if meta.get(tracker_key) is not None: console.print(f"[cyan]{tracker_name} ID found in meta, reusing existing ID: {meta[tracker_key]}[/cyan]") tracker_data = await COMMON(self.config).unit3d_torrent_info( @@ -294,10 +295,13 @@ async def handle_image_list(self, meta, tracker_name): approved_image_hosts = ['ptpimg', 'imgbox'] # Check if the images are already hosted on an approved image host - if all(any(host in img for host in approved_image_hosts) for img in meta['image_list']): + if all(any(host in image['raw_url'] for host in approved_image_hosts) for image in meta['image_list']): image_list = meta['image_list'] # noqa #F841 else: - console.print("[red]Warning: Some images are not hosted on an MTV approved image host. MTV will fail if you keep these images.") + default_trackers = self.config['TRACKERS'].get('default_trackers', '') + trackers_list = [tracker.strip() for tracker in default_trackers.split(',')] + if 'MTV' in trackers_list or 'MTV' in meta.get('trackers', ''): + console.print("[red]Warning: Some images are not hosted on an MTV approved image host. MTV will fail if you keep these images.") keep_images = await self.prompt_user_for_confirmation(f"Do you want to keep the images found on {tracker_name}?") if not keep_images: @@ -446,6 +450,8 @@ async def gather_prep(self, meta, mode): specific_tracker = 'AITHER' elif meta.get('lst'): specific_tracker = 'LST' + elif meta.get('oe'): + specific_tracker = 'OE' # If a specific tracker is found, only process that one if specific_tracker: @@ -475,6 +481,12 @@ async def gather_prep(self, meta, mode): if match: found_match = True + elif specific_tracker == 'OE' and str(self.config['TRACKERS'].get('OE', {}).get('useAPI')).lower() == "true": + oe = OE(config=self.config) + meta, match = await self.update_metadata_from_tracker('OE', oe, meta, search_term, search_file_folder) + if match: + found_match = True + elif specific_tracker == 'HDB' and str(self.config['TRACKERS'].get('HDB', {}).get('useAPI')).lower() == "true": hdb = HDB(config=self.config) meta, match = await self.update_metadata_from_tracker('HDB', hdb, meta, search_term, search_file_folder) @@ -1324,8 +1336,8 @@ def screenshots(self, path, filename, folder_id, base_dir, meta, num_screens=Non .global_args('-loglevel', loglevel) .run(quiet=debug) ) - except Exception: - console.print(traceback.format_exc()) + except (KeyboardInterrupt, Exception): + sys.exit(1) self.optimize_images(image_path) if os.path.getsize(Path(image_path)) <= 75000: @@ -1394,8 +1406,8 @@ def optimize_images(self, image): oxipng.optimize(image, level=6) else: oxipng.optimize(image, level=3) - except Exception: - pass + except (KeyboardInterrupt, Exception): + sys.exit(1) return """ diff --git a/src/trackers/AITHER.py b/src/trackers/AITHER.py index 7436862a7..7da76fe81 100644 --- a/src/trackers/AITHER.py +++ b/src/trackers/AITHER.py @@ -24,6 +24,7 @@ def __init__(self, config): self.source_flag = 'Aither' self.search_url = 'https://aither.cc/api/torrents/filter' self.upload_url = 'https://aither.cc/api/torrents/upload' + self.torrent_url = 'https://aither.cc/api/torrents/' self.signature = "\n[center][url=https://aither.cc/forums/topics/1349/posts/24958]Created by L4G's Upload Assistant[/url][/center]" self.banned_groups = ['4K4U', 'AROMA', 'd3g', 'edge2020', 'EMBER', 'EVO', 'FGT', 'FreetheFish', 'Hi10', 'HiQVE', 'ION10', 'iVy', 'Judas', 'LAMA', 'MeGusta', 'nikt0', 'OEPlus', 'OFT', 'OsC', 'PYC', 'QxR', 'Ralphy', 'RARBG', 'RetroPeeps', 'SAMPA', 'Sicario', 'Silence', 'SkipTT', 'SPDVD', 'STUTTERSHIT', 'SWTYBLZ', 'TAoE', 'TGx', 'Tigole', 'TSP', 'TSPxL', 'VXT', 'Weasley[HONE]', @@ -37,6 +38,7 @@ async def upload(self, meta): cat_id = await self.get_cat_id(meta['category']) type_id = await self.get_type_id(meta['type']) resolution_id = await self.get_res_id(meta['resolution']) + modq = await self.get_flag(meta, 'modq') name = await self.edit_name(meta) if meta['anon'] == 0 and bool(str2bool(str(self.config['TRACKERS'][self.tracker].get('anon', "False")))) is False: anon = 0 @@ -74,6 +76,7 @@ async def upload(self, meta): 'free': 0, 'doubleup': 0, 'sticky': 0, + 'mod_queue_opt_in': modq, } headers = { 'User-Agent': f'Upload Assistant/2.1 ({platform.system()} {platform.release()})' @@ -102,31 +105,59 @@ async def upload(self, meta): console.print(data) open_torrent.close() + async def get_flag(self, meta, flag_name): + config_flag = self.config['TRACKERS'][self.tracker].get(flag_name) + if config_flag is not None: + return 1 if config_flag else 0 + + return 1 if meta.get(flag_name, False) else 0 + async def edit_name(self, meta): aither_name = meta['name'] - has_eng_audio = False + + # Helper function to check if English audio is present + def has_english_audio(tracks, is_bdmv=False): + for track in tracks: + if is_bdmv and track.get('language') == 'English': + return True + if not is_bdmv and track['@type'] == "Audio": + # Ensure Language is not None and is a string before checking startswith + if isinstance(track.get('Language'), str) and track.get('Language').startswith('en'): + return True + return False + + # Helper function to get audio language + def get_audio_lang(tracks, is_bdmv=False): + if is_bdmv: + return tracks[0].get('language', '').upper() if tracks else "" + return tracks[2].get('Language_String', '').upper() if len(tracks) > 2 else "" + if meta['is_disc'] != "BDMV": - with open(f"{meta.get('base_dir')}/tmp/{meta.get('uuid')}/MediaInfo.json", 'r', encoding='utf-8') as f: - mi = json.load(f) + try: + with open(f"{meta.get('base_dir')}/tmp/{meta.get('uuid')}/MediaInfo.json", 'r', encoding='utf-8') as f: + mi = json.load(f) + + audio_tracks = mi['media']['track'] + has_eng_audio = has_english_audio(audio_tracks) + if not has_eng_audio: + audio_lang = get_audio_lang(audio_tracks) + if audio_lang: + aither_name = aither_name.replace(meta['resolution'], f"{audio_lang} {meta['resolution']}", 1) + except (FileNotFoundError, KeyError, IndexError) as e: + print(f"Error processing MediaInfo: {e}") - for track in mi['media']['track']: - if track['@type'] == "Audio": - if track.get('Language', 'None').startswith('en'): - has_eng_audio = True - if not has_eng_audio: - audio_lang = mi['media']['track'][2].get('Language_String', "").upper() - if audio_lang != "": - aither_name = aither_name.replace(meta['resolution'], f"{audio_lang} {meta['resolution']}", 1) else: - for audio in meta['bdinfo']['audio']: - if audio['language'] == 'English': - has_eng_audio = True + bdinfo_audio = meta.get('bdinfo', {}).get('audio', []) + has_eng_audio = has_english_audio(bdinfo_audio, is_bdmv=True) if not has_eng_audio: - audio_lang = meta['bdinfo']['audio'][0]['language'].upper() - if audio_lang != "": + audio_lang = get_audio_lang(bdinfo_audio, is_bdmv=True) + if audio_lang: aither_name = aither_name.replace(meta['resolution'], f"{audio_lang} {meta['resolution']}", 1) - if meta['category'] == "TV" and meta.get('tv_pack', 0) == 0 and meta.get('episode_title_storage', '').strip() != '' and meta['episode'].strip() != '': + + # Handle TV show episode title inclusion + if meta['category'] == "TV" and meta.get('tv_pack', 0) == 0 and meta.get('episode_title_storage', '').strip() and meta['episode'].strip(): aither_name = aither_name.replace(meta['episode'], f"{meta['episode']} {meta['episode_title_storage']}", 1) + return aither_name async def get_cat_id(self, category_name): diff --git a/src/trackers/BLU.py b/src/trackers/BLU.py index 54509c9b7..9af559cf5 100644 --- a/src/trackers/BLU.py +++ b/src/trackers/BLU.py @@ -46,6 +46,7 @@ async def upload(self, meta): cat_id = await self.get_cat_id(meta['category'], meta.get('edition', '')) type_id = await self.get_type_id(meta['type']) resolution_id = await self.get_res_id(meta['resolution']) + modq = await self.get_flag(meta, 'modq') region_id = await common.unit3d_region_ids(meta.get('region')) distributor_id = await common.unit3d_distributor_ids(meta.get('distributor')) if meta['anon'] == 0 and bool(str2bool(str(self.config['TRACKERS'][self.tracker].get('anon', "False")))) is False: @@ -85,6 +86,7 @@ async def upload(self, meta): 'free': 0, 'doubleup': 0, 'sticky': 0, + 'mod_queue_opt_in': modq, } # Internal if self.config['TRACKERS'][self.tracker].get('internal', False) is True: @@ -118,6 +120,13 @@ async def upload(self, meta): console.print(data) open_torrent.close() + async def get_flag(self, meta, flag_name): + config_flag = self.config['TRACKERS'][self.tracker].get(flag_name) + if config_flag is not None: + return 1 if config_flag else 0 + + return 1 if meta.get(flag_name, False) else 0 + async def get_cat_id(self, category_name, edition): category_id = { 'MOVIE': '1', diff --git a/src/trackers/COMMON.py b/src/trackers/COMMON.py index a9fb5f536..19cc873ce 100644 --- a/src/trackers/COMMON.py +++ b/src/trackers/COMMON.py @@ -4,6 +4,7 @@ import re import json import click +import sys from src.bbcode import BBCODE from src.console import console @@ -163,10 +164,13 @@ async def prompt_user_for_id_selection(self, tmdb=None, imdb=None, tvdb=None, fi console.print(f"Filename: {filename}") # Ensure filename is printed if available selection = input(f"Do you want to use these IDs from {tracker_name}? (Y/n): ").strip().lower() - if selection == '' or selection == 'y' or selection == 'yes': - return True - else: - return False + try: + if selection == '' or selection == 'y' or selection == 'yes': + return True + else: + return False + except (KeyboardInterrupt, EOFError): + sys.exit(1) async def prompt_user_for_confirmation(self, message): response = input(f"{message} (Y/n): ").strip().lower() @@ -193,11 +197,12 @@ async def unit3d_torrent_info(self, tracker, torrent_url, search_url, id=None, f return None, None, None, None, None, None, None, None, None response = requests.get(url=url, params=params) + # console.print(f"[blue]Raw API Response: {response}[/blue]") try: json_response = response.json() - # console.print(f"[blue]Raw API Response: {json_response}[/blue]") + # console.print(f"[blue]Raw API Response: {json_response}[/blue]", markup=False) except ValueError: return None, None, None, None, None, None, None, None, None @@ -246,15 +251,18 @@ async def unit3d_torrent_info(self, tracker, torrent_url, search_url, id=None, f if tmdb or imdb or tvdb: if not id: # Only prompt the user for ID selection if not searching by ID - if not await self.prompt_user_for_id_selection(tmdb, imdb, tvdb, file_name): - console.print("[yellow]User chose to skip based on IDs.[/yellow]") - return None, None, None, None, None, None, None, None, None + try: + if not await self.prompt_user_for_id_selection(tmdb, imdb, tvdb, file_name): + console.print("[yellow]User chose to skip based on IDs.[/yellow]") + return None, None, None, None, None, None, None, None, None + except (KeyboardInterrupt, EOFError): + sys.exit(1) if description: bbcode = BBCODE() description, imagelist = bbcode.clean_unit3d_description(description, torrent_url) console.print(f"[green]Successfully grabbed description from {tracker}") - console.print(f"[blue]Extracted description: [yellow]{description}") + console.print(f"[blue]Extracted description: [yellow]{description}", markup=False) # Allow user to edit or discard the description console.print("[cyan]Do you want to edit, discard or keep the description?[/cyan]") @@ -264,7 +272,7 @@ async def unit3d_torrent_info(self, tracker, torrent_url, search_url, id=None, f edited_description = click.edit(description) if edited_description: description = edited_description.strip() - console.print(f"[green]Final description after editing:[/green] {description}") + console.print(f"[green]Final description after editing:[/green] {description}", markup=False) elif edit_choice.lower() == 'd': description = None console.print("[yellow]Description discarded.[/yellow]") diff --git a/src/trackers/LST.py b/src/trackers/LST.py index 553caadd7..7278f7ed4 100644 --- a/src/trackers/LST.py +++ b/src/trackers/LST.py @@ -24,6 +24,7 @@ def __init__(self, config): self.source_flag = 'LST.GG' self.upload_url = 'https://lst.gg/api/torrents/upload' self.search_url = 'https://lst.gg/api/torrents/filter' + self.torrent_url = 'https://lst.gg/api/torrents/' self.signature = "\n[center][url=https://github.com/Audionut/Upload-Assistant]Created by L4G's Upload Assistant[/url][/center]" self.banned_groups = ['aXXo', 'BRrip', 'CM8', 'CrEwSaDe', 'CTFOH', 'DNL', 'FaNGDiNG0', 'HD2DVD', 'HDTime', 'ION10', 'iPlanet', 'KiNGDOM', 'mHD', 'mSD', 'nHD', 'nikt0', 'nSD', 'NhaNc3', 'OFT', 'PRODJi', 'SANTi', 'STUTTERSHIT', 'ViSION', 'VXT', 'WAF', @@ -158,8 +159,9 @@ async def upload(self, meta): async def get_flag(self, meta, flag_name): config_flag = self.config['TRACKERS'][self.tracker].get(flag_name) - if config_flag: - return 1 + if config_flag is not None: + return 1 if config_flag else 0 + return 1 if meta.get(flag_name, False) else 0 async def search_existing(self, meta): diff --git a/src/trackers/OE.py b/src/trackers/OE.py index a1b4d3156..8cb819518 100644 --- a/src/trackers/OE.py +++ b/src/trackers/OE.py @@ -23,6 +23,7 @@ def __init__(self, config): self.source_flag = 'OE' self.search_url = 'https://onlyencodes.cc/api/torrents/filter' self.upload_url = 'https://onlyencodes.cc/api/torrents/upload' + self.torrent_url = 'https://onlyencodes.cc/api/torrents/' self.signature = "\n[center][url=https://github.com/Audionut/Upload-Assistant]Created by L4G's Upload Assistant[/url][/center]" self.banned_groups = ['0neshot', '3LT0N', '4K4U', '4yEo', '$andra', '[Oj]', 'AFG', 'AkihitoSubs', 'AniHLS', 'Anime Time', 'AnimeRG', 'AniURL', 'AR', 'AROMA', 'ASW', 'aXXo', 'BakedFish', 'BiTOR', 'BHDStudio', 'BRrip', 'bonkai', 'Cleo', 'CM8', 'C4K', 'CrEwSaDe', 'core', 'd3g', 'DDR', 'DeadFish', 'DeeJayAhmed', 'DNL', 'ELiTE', 'EMBER', 'eSc', 'EVO', 'EZTV', 'FaNGDiNG0', 'FGT', 'fenix', 'FUM', 'FRDS', 'FROZEN', 'GalaxyTV', 'GalaxyRG', 'GERMini', 'Grym', 'GrymLegacy', 'HAiKU', 'HD2DVD', 'HDTime', 'Hi10', 'ION10', 'iPlanet', 'JacobSwaggedUp', 'JIVE', 'Judas', 'KiNGDOM', 'LAMA', 'Leffe', 'LiGaS', 'LOAD', 'LycanHD', 'MeGusta,' 'MezRips,' 'mHD,' 'Mr.Deadpool', 'mSD', 'NemDiggers', 'neoHEVC', 'NeXus', 'NhaNc3', 'nHD', 'nikt0', 'nSD', 'NhaNc3', 'NOIVTC', 'pahe.in', 'PlaySD', 'playXD', 'PRODJi', 'ProRes', 'project-gxs', 'PSA', 'QaS', 'Ranger', 'RAPiDCOWS', 'RARBG', 'Raze', 'RCDiVX', 'RDN', 'Reaktor', 'REsuRRecTioN', 'RMTeam', 'ROBOTS', 'rubix', 'SANTi', 'SHUTTERSHIT', 'SpaceFish', 'SPASM', 'SSA', 'TBS', 'Telly,' 'Tenrai-Sensei,' 'TERMiNAL,' 'TM', 'topaz', 'TSP', 'TSPxL', 'Trix', 'URANiME', 'UTR', 'VipapkSudios', 'ViSION', 'WAF', 'Wardevil', 'x0r', 'xRed', 'XS', 'YakuboEncodes', 'YIFY', 'YTS', 'YuiSubs', 'ZKBL', 'ZmN', 'ZMNT'] pass diff --git a/upload.py b/upload.py index 3abab7f25..249e257b7 100644 --- a/upload.py +++ b/upload.py @@ -247,17 +247,46 @@ async def do_the_thing(base_dir): ####### Upload to Trackers ####### # noqa #F266 #################################### common = COMMON(config=config) - api_trackers = ['BLU', 'AITHER', 'STC', 'R4E', 'STT', 'RF', 'ACM', 'LCD', 'HUNO', 'SN', 'LT', 'NBL', 'ANT', 'JPTV', 'TDC', 'OE', 'BHDTV', 'RTF', 'OTW', 'FNP', 'CBR', 'UTP', 'AL', 'HDB', 'SHRI'] - http_trackers = ['TTG', 'FL', 'PTER', 'HDT', 'MTV'] + api_trackers = ['BLU', 'AITHER', 'STC', 'R4E', 'STT', 'RF', 'ACM', 'LCD', 'HUNO', 'SN', 'LT', 'NBL', 'ANT', 'JPTV', 'TDC', 'OE', 'BHDTV', 'RTF', 'OTW', 'FNP', 'CBR', 'UTP', 'AL', 'SHRI', 'LST', 'BHD', 'TL'] + http_trackers = ['HDB', 'TTG', 'FL', 'PTER', 'HDT', 'MTV'] tracker_class_map = { 'BLU': BLU, 'BHD': BHD, 'AITHER': AITHER, 'STC': STC, 'R4E': R4E, 'THR': THR, 'STT': STT, 'HP': HP, 'PTP': PTP, 'RF': RF, 'SN': SN, 'ACM': ACM, 'HDB': HDB, 'LCD': LCD, 'TTG': TTG, 'LST': LST, 'HUNO': HUNO, 'FL': FL, 'LT': LT, 'NBL': NBL, 'ANT': ANT, 'PTER': PTER, 'JPTV': JPTV, 'TL': TL, 'TDC': TDC, 'HDT': HDT, 'MTV': MTV, 'OE': OE, 'BHDTV': BHDTV, 'RTF': RTF, 'OTW': OTW, 'FNP': FNP, 'CBR': CBR, 'UTP': UTP, 'AL': AL, 'SHRI': SHRI} + tracker_capabilities = { + 'LST': {'mod_q': True, 'draft': True}, + 'BLU': {'mod_q': True, 'draft': False}, + 'AITHER': {'mod_q': True, 'draft': False}, + 'BHD': {'draft_live': True}, + } + + async def check_mod_q_and_draft(tracker_class, meta, debug): + modq, draft = None, None + + tracker_caps = tracker_capabilities.get(tracker_class.tracker, {}) + + # Handle BHD specific draft/live logic + if tracker_class.tracker == 'BHD' and tracker_caps.get('draft_live'): + draft_int = await tracker_class.get_live(meta) + draft = "Draft" if draft_int == 0 else "Live" + + # Handle mod_q and draft for other trackers + else: + if tracker_caps.get('mod_q'): + modq = await tracker_class.get_flag(meta, 'modq') + modq = 'Yes' if modq else 'No' + if tracker_caps.get('draft'): + draft = await tracker_class.get_flag(meta, 'draft') + draft = 'Yes' if draft else 'No' + + return modq, draft + for tracker in trackers: + tracker = tracker.replace(" ", "").upper().strip() if meta['name'].endswith('DUPE?'): meta['name'] = meta['name'].replace(' DUPE?', '') - tracker = tracker.replace(" ", "").upper().strip() + if meta['debug']: debug = "(DEBUG)" else: @@ -265,21 +294,45 @@ async def do_the_thing(base_dir): if tracker in api_trackers: tracker_class = tracker_class_map[tracker](config=config) + if meta['unattended']: upload_to_tracker = True else: - upload_to_tracker = cli_ui.ask_yes_no(f"Upload to {tracker_class.tracker}? {debug}", default=meta['unattended']) + try: + upload_to_tracker = cli_ui.ask_yes_no( + f"Upload to {tracker_class.tracker}? {debug}", + default=meta['unattended'] + ) + except (KeyboardInterrupt, EOFError): + sys.exit(1) # Exit immediately + if upload_to_tracker: + # Get mod_q, draft, or draft/live depending on the tracker + modq, draft = await check_mod_q_and_draft(tracker_class, meta, debug) + + # Print mod_q and draft info if relevant + if modq is not None: + console.print(f"(modq: {modq})") + if draft is not None: + console.print(f"(draft: {draft})") + console.print(f"Uploading to {tracker_class.tracker}") + + # Check if the group is banned for the tracker if check_banned_group(tracker_class.tracker, tracker_class.banned_groups, meta): continue - if tracker == "RTF": - await tracker_class.api_test(meta) - dupes = await tracker_class.search_existing(meta) - dupes = await common.filter_dupes(dupes, meta) - # note BHDTV does not have search implemented. - meta = dupe_check(dupes, meta) - if meta['upload'] is True: + + # Perform the existing checks for dupes except TL + if tracker != "TL": + if tracker == "RTF": + await tracker_class.api_test(meta) + + dupes = await tracker_class.search_existing(meta) + dupes = await common.filter_dupes(dupes, meta) + meta = dupe_check(dupes, meta) + + # Proceed with upload if the meta is set to upload + if tracker == "TL" or meta.get('upload', False): await tracker_class.upload(meta) if tracker == 'SN': await asyncio.sleep(16) @@ -287,10 +340,18 @@ async def do_the_thing(base_dir): if tracker in http_trackers: tracker_class = tracker_class_map[tracker](config=config) + if meta['unattended']: upload_to_tracker = True else: - upload_to_tracker = cli_ui.ask_yes_no(f"Upload to {tracker_class.tracker}? {debug}", default=meta['unattended']) + try: + upload_to_tracker = cli_ui.ask_yes_no( + f"Upload to {tracker_class.tracker}? {debug}", + default=meta['unattended'] + ) + except (KeyboardInterrupt, EOFError): + sys.exit(1) # Exit immediately + if upload_to_tracker: console.print(f"Uploading to {tracker}") if check_banned_group(tracker_class.tracker, tracker_class.banned_groups, meta): @@ -324,56 +385,17 @@ async def do_the_thing(base_dir): console.print(f"[green]{meta['name']}") console.print(f"[green]Files can be found at: [yellow]{url}[/yellow]") - if tracker == "BHD": - bhd = BHD(config=config) - draft_int = await bhd.get_live(meta) - if draft_int == 0: - draft = "Draft" - else: - draft = "Live" - if meta['unattended']: - upload_to_bhd = True - else: - upload_to_bhd = cli_ui.ask_yes_no(f"Upload to BHD? ({draft}) {debug}", default=meta['unattended']) - if upload_to_bhd: - console.print("Uploading to BHD") - if check_banned_group("BHD", bhd.banned_groups, meta): - continue - dupes = await bhd.search_existing(meta) - dupes = await common.filter_dupes(dupes, meta) - meta = dupe_check(dupes, meta) - if meta['upload'] is True: - await bhd.upload(meta) - await client.add_to_client(meta, "BHD") - - if tracker == "LST": - lst = LST(config=config) - modq, draft = await asyncio.gather(lst.get_flag(meta, 'modq'), lst.get_flag(meta, 'draft')) - - modq = 'Yes' if modq else 'No' - draft = 'Yes' if draft else 'No' - - upload_to_lst = meta['unattended'] or cli_ui.ask_yes_no(f"Upload to LST? (draft: {draft}) (modq: {modq}) {debug}", default=meta['unattended']) - if not upload_to_lst: - continue - - console.print("Uploading to LST") - - if check_banned_group('LST', lst.banned_groups, meta): - continue - - dupes = await lst.search_existing(meta) - dupes = await common.filter_dupes(dupes, meta) - meta = dupe_check(dupes, meta) - if meta['upload']: - await lst.upload(meta) - await client.add_to_client(meta, lst.tracker) - if tracker == "THR": if meta['unattended']: upload_to_thr = True else: - upload_to_thr = cli_ui.ask_yes_no(f"Upload to THR? {debug}", default=meta['unattended']) + try: + upload_to_ptp = cli_ui.ask_yes_no( + f"Upload to THR? {debug}", + default=meta['unattended'] + ) + except (KeyboardInterrupt, EOFError): + sys.exit(1) # Exit immediately if upload_to_thr: console.print("Uploading to THR") # nable to get IMDB id/Youtube Link @@ -402,8 +424,15 @@ async def do_the_thing(base_dir): if meta['unattended']: upload_to_ptp = True else: - upload_to_ptp = cli_ui.ask_yes_no(f"Upload to {tracker}? {debug}", default=meta['unattended']) - if upload_to_ptp: + try: + upload_to_ptp = cli_ui.ask_yes_no( + f"Upload to {tracker}? {debug}", + default=meta['unattended'] + ) + except (KeyboardInterrupt, EOFError): + sys.exit(1) # Exit immediately + + if upload_to_ptp: # Ensure the variable is defined before this check console.print(f"Uploading to {tracker}") if meta.get('imdb_id', '0') == '0': imdb_id = cli_ui.ask_string("Unable to find IMDB id, please enter e.g.(tt1234567)") @@ -435,19 +464,6 @@ async def do_the_thing(base_dir): except Exception: console.print(traceback.print_exc()) - if tracker == "TL": - tracker_class = tracker_class_map[tracker](config=config) - if meta['unattended']: - upload_to_tracker = True - else: - upload_to_tracker = cli_ui.ask_yes_no(f"Upload to {tracker_class.tracker}? {debug}", default=meta['unattended']) - if upload_to_tracker: - console.print(f"Uploading to {tracker_class.tracker}") - if check_banned_group(tracker_class.tracker, tracker_class.banned_groups, meta): - continue - await tracker_class.upload(meta) - await client.add_to_client(meta, tracker_class.tracker) - def get_confirmation(meta): if meta['debug'] is True: @@ -504,7 +520,11 @@ def get_confirmation(meta): cli_ui.info_section(cli_ui.yellow, "Is this correct?") cli_ui.info(f"Name: {meta['name']}") - confirm = cli_ui.ask_yes_no("Correct?", default=False) + try: + confirm = cli_ui.ask_yes_no("Correct?", default=False) + except (KeyboardInterrupt, EOFError): + sys.exit(1) # Exit immediately + else: cli_ui.info(f"Name: {meta['name']}") confirm = True