├── .gitignore ├── README.md ├── auto_upload.py ├── config.env.sample ├── description_template.jinja2 ├── images └── upload_screenshots.py ├── requirements.txt ├── rtorrent_on_complete_reupload.sh.sample ├── search_for_dupes.py └── site_templates ├── aither.json ├── asiancinema.json ├── beyond-hd.json ├── blutopia.json ├── default_template.json ├── desitorrents.json ├── ntelogo.json ├── racing4everyone.json ├── telly.json └── uncutflixhd.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /venv/ 3 | /config.env 4 | /upload_script.log 5 | /.github/ 6 | /temp_upload/* 7 | /images/screenshots/* 8 | rtorrent_on_complete_reupload.sh 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReadMe!! 2 | `While I still occasionally update this project, you should really consider using` [GG-BOT](https://gitlab.com/NoobMaster669/gg-bot-upload-assistant) `instead, its a fork of this project but is way more active & does pretty much everything better :)` 3 | 4 | 5 | *** 6 | # UNIT3D auto upload 7 | Automatically parse, rename, and upload torrents to trackers using the UNIT3D codebase 8 | ### Supported sites: 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
ACMAsianCinema
ATHAither
BHDBeyond-HD
BLUBlutopia
R4ERacing4Everyone
TellyTelly.wtf
NtelogoNtelogo
UFHDUncutflixhd
DSTDesiTorrents
49 | 50 | 51 | 52 | # Basic setup: 53 | 1. Clone / download this repository 54 | 2. Install necessary packages ```pip3 install -r requirements.txt``` 55 | 3. Rename `config.env.sample` to `config.env` 56 | 4. Fill out the required values in `config.env` 57 | 5. Ensure you have [mediainfo](https://mediaarea.net/en/MediaInfo/Download/Ubuntu) & [ffmpeg](https://ffmpeg.org/download.html) installed on your system 58 | 6. Run the script using [Python3](https://www.python.org/downloads/) (If you're having issues or torf isn't installing, try python3.9) 59 | 60 |
61 | 62 | **Things to note:** 63 | 1. We use TMDB API for all things media related (Title, Year, External IDs, etc) 64 | 2. If you provide the IMDB ID via ```-imdb```, you must include the 'tt' that precedes the numerical ID 65 | 3. If you're trying to pass in a file as an arg, you may find autocomplete isn't working. Do this to fix it 66 | * (What I mean by autocomplete is when you double hit *Tab*, and the filename/folder gets automatically filled in) 67 | * ```chmod u+x auto_upload.py``` 68 | * run script using ```./auto_upload.py -t etc -p /path/to/file/autocompletes.now``` 69 | 4. A folder called ``temp_upload`` will be created which will store the files: 70 | * ```description.txt``` ```mediainfo.txt``` ```*.torrent``` 71 | 72 | 73 | # Wiki 74 | ### [Video usage examples](https://github.com/ryelogheat/xpbot/wiki/Video-examples) 75 | ### [Available args & user input](https://github.com/ryelogheat/xpbot/wiki/Args-and-User-Input) 76 | ### [config.env breakdown](https://github.com/ryelogheat/xpbot/wiki/config.env) 77 | ### [/site_templates/*.json guide](https://github.com/ryelogheat/xpbot/wiki/Tracker-Templates) 78 | ### [Automatic re-uploading (autodl)](https://github.com/ryelogheat/xpbot/wiki/autodl-irssi-automatic-re-uploading) 79 | 80 | -------------------------------------------------------------------------------- /config.env.sample: -------------------------------------------------------------------------------- 1 | ######################################################### 2 | # _____ __ _ # 3 | # / ____| / _(_) # 4 | # | | ___ _ __ | |_ _ __ _ ___ _ ____ __ # 5 | # | | / _ \| '_ \| _| |/ _` | / _ \ '_ \ \ / / # 6 | # | |___| (_) | | | | | | | (_| || __/ | | \ V / # 7 | # \_____\___/|_| |_|_| |_|\__, (_)___|_| |_|\_/ # 8 | # __/ | # 9 | # |___/ # 10 | # # 11 | ######################################################## 12 | 13 | 14 | # Private tracker API keys & Announce URLs 15 | # ------------------------------------------------------------ # 16 | ACM_API_KEY= 17 | ACM_ANNOUNCE_URL=https://asiancinema.me/announce/ 18 | 19 | BHD_API_KEY= 20 | BHD_ANNOUNCE_URL=https://beyond-hd.me/announce/ 21 | 22 | BLU_API_KEY= 23 | BLU_ANNOUNCE_URL=https://blutopia.xyz/announce/ 24 | 25 | R4E_API_KEY= 26 | R4E_ANNOUNCE_URL=https://racing4everyone.eu/announce/ 27 | 28 | ATH_API_KEY= 29 | ATH_ANNOUNCE_URL=https://aither.cc/announce/ 30 | 31 | TELLY_API_KEY= 32 | TELLY_ANNOUNCE_URL=https://telly.wtf/announce/ 33 | 34 | NTELOGO_API_KEY= 35 | NTELOGO_ANNOUNCE_URL= https://ntelogo.org/announce/ 36 | 37 | UFHD_API_KEY= 38 | UFHD_ANNOUNCE_URL=https://uncutflixhd.com/announce/ 39 | 40 | DST_API_KEY= 41 | DST_ANNOUNCE_URL=https://desitorrents.tv/announce/ 42 | 43 | 44 | # TMDB API key (Required) 45 | TMDB_API_KEY= 46 | 47 | # Discord Webhook URL (https://help.dashe.io/en/articles/2521940-how-to-create-a-discord-webhook-url) (Optional) 48 | DISCORD_WEBHOOK= 49 | 50 | 51 | 52 | 53 | # Image Hosts 54 | # ------------------------------------------------------------ # 55 | # 4 options are available --> imgbox | imgbb | freeimage | ptpimg (requires additional pip package) 56 | # set their order below (spelling matters) (to remove a host just delete the value and leave it blank) 57 | img_host_1=imgbox 58 | img_host_2=freeimage 59 | img_host_3=imgbb 60 | img_host_4= 61 | # set the image hosts API keys below 62 | imgbox_api_key=leave_blank # No API key needed here, don't touch this line https://pypi.org/project/pyimgbox/ 63 | imgbb_api_key= # https://api.imgbb.com/ 64 | freeimage_api_key= # https://freeimage.host/page/api 65 | ptpimg_api_key= # (pip install ptpimg_uploader) & follow https://github.com/theirix/ptpimg-uploader#api-key guide to get api key 66 | 67 | # pretty self explanatory, this will take number of screenshots you want, all evenly spaced depending on how long the video is 68 | # Set this to 0 if you want to upload without taking any screenshots 69 | num_of_screenshots=6 70 | 71 | 72 | 73 | # Post Processing 74 | # ------------------------------------------------------------ # 75 | # you can specify a "Watch Directory" in some torrent clients that will automatically add new .torrent files to its download queue 76 | # this could be used to automatically start seeding an upload using AutoTools (rtorrent) 77 | # -- Leave the location blank to disable any sort of movement -- *(Do not delete the keys)* 78 | dot_torrent_move_location= 79 | media_move_location= 80 | 81 | 82 | 83 | # Check for dupes (true | false) 84 | # ------------------------------------------------------------ # 85 | check_dupes=true 86 | # 100% matches will always be rejected so you'll need to set 'check_dupes' to 'false' if you insist on uploading a dupe (??) 87 | # ------------------------------------------------------------ # 88 | # ! Lower == Better ! (this is a percentage of similarity between your file and what's already on the tracker) 89 | # Typically 80 filters out most obvious dupes (Don't include the percentage symbol below), if you want to be more cautious you should choose a lower number 90 | 91 | # SEE EXAMPLES BELOW 92 | # --------------------------- # 93 | # You're uploading: Atomic Blonde 2017 1080p UHD Bluray DD+ 7.1 HDR x265-NCmt 94 | # Already on site: Atomic Blonde 2017 1080p UHD BluRay DD+7.1 HDR x265 - HQMUX 95 | # similarity percentage = 84% 96 | # --------------------------- # 97 | # You're uploading: Rogue 2020 1080p Bluray DD 5.1 x264-BHDStudio 98 | # Already on site: Rogue 2020 1080p BluRay DTS 5.1 x264-iFT 99 | # similarity percentage = 79% 100 | # --------------------------- # 101 | # You're uploading: Get Him to the Greek 2010 1080p Bluray DTS-HD MA 5.1 AVC Remux-EPSiLON 102 | # Already on site: Get Him to the Greek 2010 Unrated BluRay 1080p DTS-HD MA 5.1 AVC REMUX-FraMeSToR 103 | # similarity percentage = 88% 104 | # --------------------------- # 105 | acceptable_similarity_percentage=75 106 | 107 | 108 | 109 | # auto mode 110 | # ------------------------------------------------------------ # 111 | # Set this to 'False' if you want an interactive & hands on experience 112 | # You'll be prompted to select the correct TMDB ID, Prompted to fix any small issues/discrepancies found during the upload process 113 | # !! It's recommended to keep this set to 'False' !! 114 | auto_mode=False 115 | # 116 | # 117 | # !! THIS ONLY APPLIES IF ^^ "auto_mode=true" ^^ !! 118 | # Set this to 'true' if you want an upload to to be forced through unless a critical issue is found 119 | # e.g. If we can't auto detect 'audio_channels', we can still upload but we'll just omit that part from the torrent title 120 | # But if something like 'resolution' can't be detected then the upload will be cancelled since 'resolution' is a required API/Upload Key 121 | # !! It's recommended to keep this set to 'False' !! (Remember, you are responsible for following all tracker rules!) 122 | force_auto_upload=False 123 | 124 | 125 | 126 | # Live / Draft (BHD) 127 | # ------------------------------------------------------------ # 128 | # This only applies to BHD since they are the only site that have a 'draft'/'live on site' option 129 | # Its great for testing and verifying the script submits the correct info before posting it live 130 | # True == Live on site, everyone can see it 131 | # False == Goes to your private drafts https://beyond-hd.me/drafts 132 | live=False 133 | 134 | 135 | 136 | # This is required to upload raw bluray discs (.iso not supported) 137 | # download & install docker then clone https://github.com/zoffline/BDInfoCLI-ng 138 | # Inside the project files for BDInfoCLI-ng you'll find a folder called 'scripts' and in it you'll find the file 'bdinfo' 139 | # You want to paste the full system path to that 'bdinfo' file here 140 | bdinfo_script= 141 | 142 | 143 | 144 | # Automated re-uploading (Docker) 145 | # ------------------------------------------------------------ # 146 | # If you're running r(u)torrent in a docker container you will need to map the containers download path to its system path 147 | # (If you are using Sonarr/Radarr then you've already probably done this under "Remote Path Mappings" in the "Download Clients" section) 148 | # e.g. 149 | # OS System path: /mnt/local/downloads/torrents/rutorrent/completed/file.mkv 150 | # Docker container path: /downloads/torrents/rutorrent/completed/file.mkv 151 | # 152 | # ------- Using those paths ^^ you would set the mappings like this 153 | # host_path=/mnt/local/downloads/ 154 | # remote_path=/downloads/ 155 | # This allows us to translate the "d.data_path" rtorrent returns to something that this project can access/upload 156 | # 157 | # ------ If you aren't using docker containers for anything and your rtorrent path is the same as your system path, set 'translation_needed' equal to False ----- # 158 | translation_needed=False 159 | host_path= 160 | remote_path= 161 | -------------------------------------------------------------------------------- /description_template.jinja2: -------------------------------------------------------------------------------- 1 | {% if desc_note %}[quote]{{ desc_note }}[/quote]{% endif %} 2 | {% if nfo_text %}[code]{{ nfo_text }}[/code]{% endif %} 3 | [center] 4 | ---------------------- [size=22]Screenshots[/size] ---------------------- 5 | {{ img_bbcode }} 6 | 7 | Uploaded with [color=red]❤[/color] using [url=https://github.com/ryelogheat/xpbot]XpBot[/url] 8 | [/center] 9 | -------------------------------------------------------------------------------- /images/upload_screenshots.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import base64 4 | import asyncio 5 | import logging 6 | import pyimgbox 7 | import requests 8 | from ffmpy import FFmpeg 9 | from datetime import datetime 10 | from dotenv import load_dotenv 11 | from rich.progress import track 12 | from rich.console import Console 13 | 14 | # For more control over rich terminal content, import and construct a Console object. 15 | console = Console() 16 | 17 | 18 | def get_ss_range(duration, num_of_screenshots): 19 | list_of_ss_timestamps = [] 20 | # Now start a loop that will run for the num_of_screenshots & create evenly split timestamps at which to take screenshots 21 | first_time_stamp = int(duration) / int(int(num_of_screenshots) + 1) 22 | for num_screen in range(1, int(num_of_screenshots) + 1): 23 | millis = round(first_time_stamp) * num_screen 24 | list_of_ss_timestamps.append(str(datetime.strptime("%d:%d:%d" % (int((millis / (1000 * 60 * 60)) % 24), int((millis / (1000 * 60)) % 60), int((millis / 1000) % 60)), '%H:%M:%S').time())) 25 | # Return the list that contains timestamps at which we'll take screenshots next 26 | return list_of_ss_timestamps 27 | 28 | 29 | def upload_screens(img_host, img_host_api, image_path, torrent_title): 30 | # ptpimg does all for us to upload multiple images at the same time but to simplify things & allow for simple "backup hosts"/upload failures we instead upload 1 image at a time 31 | # 32 | # Both imgbb & freeimage are based on Chevereto which the API has us upload 1 image at a time while imgbox uses something custom and we upload a list of images at the same time 33 | # 34 | # Annoyingly pyimgbox requires every upload be apart of a "gallery", This is fine if you're uploading a list of multiple images at the same time 35 | # but because of the way we deal with "backup" image hosts/upload failures its not realistic to pass a list of all the images to imgbox at the same time. 36 | # so instead we just upload 1 image at a time to imgbox (also creates 1 gallery per image) 37 | 38 | if img_host == 'ptpimg': 39 | try: 40 | import ptpimg_uploader 41 | ptp_img_upload = ptpimg_uploader.upload(api_key=os.getenv('ptpimg_api_key'), files_or_urls=[image_path], timeout=5) 42 | # Make sure the response we get from ptpimg is a list 43 | assert type(ptp_img_upload) == list 44 | # assuming it is, we can then get the img url, format it into bbcode & return it 45 | 46 | # Pretty sure ptpimg doesn't compress/host multiple 'versions' of the same image so we use the direct image link for both parts of the bbcode (url & img) 47 | return True, f'[url={ptp_img_upload[0]}][img=350x350]{ptp_img_upload[0]}[/img][/url]' 48 | 49 | except ImportError: 50 | logging.error(msg='cant upload to ptpimg without this pip package: https://pypi.org/project/ptpimg-uploader/') 51 | console.print(f"\nInstall required pip package: [bold]ptpimg_uploader[/bold] to enable ptpimg uploads\n", style='Red', highlight=False) 52 | return False 53 | except AssertionError: 54 | logging.error(msg='ptpimg uploaded an image but returned something unexpected (should be a list)') 55 | console.print(f"\nUnexpected response from ptpimg upload (should be a list). No image link found\n", style='Red', highlight=False) 56 | return False 57 | except Exception: 58 | logging.error(msg='ptpimg upload failed, double check the ptpimg API Key & try again.') 59 | console.print(f"\nptpimg upload failed. double check the [bold]ptpimg_api_key[/bold] in [bold]config.env[/bold]\n", style='Red', highlight=False) 60 | return False 61 | 62 | if img_host in ('imgbb', 'freeimage'): 63 | # Get the correct image host url/json key 64 | available_image_host_urls = {'imgbb': 'https://api.imgbb.com/1/upload', 'freeimage': 'https://freeimage.host/api/1/upload'} 65 | parent_key = 'data' if img_host == 'imgbb' else 'image' 66 | 67 | # Load the img_host_url, api key & img encoded in base64 into a dict called 'data' & post it 68 | image_host_url = available_image_host_urls[img_host] 69 | data = {'key': img_host_api, 'image': base64.b64encode(open(image_path, "rb").read())} 70 | try: 71 | img_upload_request = requests.post(url=image_host_url, data=data) 72 | if img_upload_request.ok: 73 | img_upload_response = img_upload_request.json() 74 | # When you upload an image you get a few links back, you get 'medium', 'thumbnail', 'url', 'url_viewer' and we only need max 2 so we set the order/list to try and get the ones we want 75 | possible_image_types = ['medium', 'thumb'] 76 | try: 77 | for img_type in possible_image_types: 78 | if img_type in img_upload_response[parent_key]: 79 | if 'delete_url' in img_upload_response: 80 | logging.info(f'{img_host} delete url for {image_path}: {img_upload_response["delete_url"]}') 81 | return True, f'[url={img_upload_response[parent_key]["url_viewer"]}][img=350x350]{img_upload_response[parent_key][img_type]["url"]}[/img][/url]' 82 | else: 83 | return True, f'[url={img_upload_response[parent_key]["url_viewer"]}][img=350x350]{img_upload_response[parent_key]["url"]}[/img][/url]' 84 | 85 | except KeyError as key_error: 86 | logging.error(f'{img_host} json KeyError: {key_error}') 87 | return False 88 | else: 89 | logging.error(f'{img_host} upload failed. JSON Response: {img_upload_request.json()}') 90 | console.print(f"{img_host} upload failed. Status code: [bold]{img_upload_request.status_code}[/bold]", style='red3', highlight=False) 91 | return False 92 | except requests.exceptions.RequestException: 93 | logging.error(f"Failed to upload {image_path} to {img_host}") 94 | console.print(f"upload to [bold]{img_host}[/bold] has failed!", style="Red") 95 | return False 96 | 97 | # Instead of coding our own solution we'll use the awesome project https://github.com/plotski/pyimgbox to upload to imgbox 98 | if img_host == "imgbox": 99 | async def imgbox_upload(filepaths): 100 | async with pyimgbox.Gallery(title=torrent_title, thumb_width=350) as gallery: 101 | async for submission in gallery.add(filepaths): 102 | if not submission['success']: 103 | logging.error(f"{submission['filename']}: {submission['error']}") 104 | return False 105 | else: 106 | logging.info(f'imgbox edit url for {image_path}: {submission["edit_url"]}') 107 | return True, f'[url={submission["web_url"]}][img=350x350]{submission["thumbnail_url"]}[/img][/url]' 108 | 109 | if os.path.getsize(image_path) >= 10485760: # Bytes 110 | logging.error('Screenshot size is over imgbox limit of 10MB, Trying another host (if available)') 111 | return False 112 | 113 | if sys.version_info < (3, 7): 114 | logging.critical(f'Required Python version to use pyimgbox is: 3.7+ You currently are on {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}') 115 | return False 116 | 117 | imgbox_asyncio_upload = asyncio.run(imgbox_upload(filepaths=[image_path])) 118 | if imgbox_asyncio_upload: 119 | return True, imgbox_asyncio_upload[1] 120 | 121 | # # Python 3.7+ version 122 | # asyncio.run(imgbox_upload(filepaths=[image_path])) # call the function that uploads images to imgbox 123 | # 124 | # # Python <= 3.6 friendly alternative 125 | # loop = asyncio.get_event_loop() 126 | # loop.run_until_complete(imgbox_upload(list_of_images)) 127 | 128 | 129 | def take_upload_screens(duration, upload_media_import, torrent_title_import, base_path, discord_url): 130 | logging.basicConfig(filename=f'{base_path}/upload_script.log', level=logging.INFO, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s') 131 | 132 | # Open the config file 133 | load_dotenv(f"{base_path}config.env") 134 | num_of_screenshots = os.getenv("num_of_screenshots") 135 | 136 | logging.info(f"Using {upload_media_import} to generate screenshots") 137 | console.print(f'\nTaking [chartreuse1]{str(num_of_screenshots)}[/chartreuse1] screenshots', style="Bold Blue") 138 | 139 | enabled_img_hosts_list = [] 140 | # ---------------------- check if 'num_of_screenshots=0' or not set ---------------------- # 141 | if num_of_screenshots == "0" or not bool(num_of_screenshots): 142 | logging.error(f'num_of_screenshots is {"not set" if not bool(num_of_screenshots) else f"set to {num_of_screenshots}"}, continuing without screenshots.') 143 | console.print(f'\nnum_of_screenshots is {"not set" if not bool(num_of_screenshots) else f"set to {num_of_screenshots}"}\n', style='bold red') 144 | else: 145 | # ---------------------- verify at least 1 image-host is set/enabled ---------------------- # 146 | enabled_img_host_num_loop = 0 147 | while bool(os.getenv(f'img_host_{enabled_img_host_num_loop + 1}')): 148 | enabled_img_hosts_list.append(os.getenv(f'img_host_{enabled_img_host_num_loop + 1}')) 149 | enabled_img_host_num_loop += 1 150 | # now check if the loop ^^ found any enabled image hosts 151 | if not bool(enabled_img_host_num_loop): 152 | logging.error('All image-hosts are disabled/not set (try setting "img_host_1=imgbox" in config.env)') 153 | console.print(f'\nNo image-hosts are enabled, maybe try setting [dodger_blue2][bold]img_host_1=imgbox[/bold][/dodger_blue2] in [dodger_blue2]config.env[/dodger_blue2]\n', style='bold red') 154 | 155 | # -------------------- verify an API key is set for 'enabled_img_hosts' -------------------- # 156 | for img_host_api_check in enabled_img_hosts_list: 157 | # Check if an API key is set for the image host 158 | if not bool(os.getenv(f'{img_host_api_check}_api_key')): 159 | logging.error(f"Can't upload to {img_host_api_check} without an API key") 160 | console.print(f"\nCan't upload to [bold]{img_host_api_check}[/bold] without an API key\n", style='red3', highlight=False) 161 | # If the api key is missing then remove the img_host from the 'enabled_img_hosts_list' list 162 | enabled_img_hosts_list.remove(img_host_api_check) 163 | # log the leftover enabled image hosts 164 | logging.info(f"Image host order we will try & upload to: {enabled_img_hosts_list}") 165 | 166 | # -------------------------- Check if any img_hosts are still in the 'enabled_img_hosts_list' list -------------------------- # 167 | # if no image_hosts are left then we show the user an error that we will continue the upload with screenshots & return back to auto_upload.py 168 | if not bool(enabled_img_hosts_list): 169 | with open(f"{base_path}/temp_upload/bbcode_images.txt", "w") as no_images: 170 | no_images.write("[b][color=#FF0000][size=22]None[/size][/color][/b]") 171 | no_images.close() 172 | logging.error(f"Continuing upload without screenshots") 173 | console.print(f'Continuing without screenshots\n', style='chartreuse1') 174 | return 175 | 176 | # ##### Now that we've verified that at least 1 imghost is available & has an api key etc we can try & upload the screenshots ##### # 177 | 178 | # Figure out where exactly to take screenshots by evenly dividing up the length of the video 179 | ss_timestamps_list = [] 180 | screenshots_to_upload_list = [] 181 | for ss_timestamp in track(get_ss_range(duration=duration, num_of_screenshots=num_of_screenshots), description="Taking screenshots..."): 182 | # Save the ss_ts to the 'ss_timestamps_list' list 183 | ss_timestamps_list.append(ss_timestamp) 184 | screenshots_to_upload_list.append(f'{base_path}/images/screenshots/{torrent_title_import} - ({ss_timestamp.replace(":", ".")}).png') 185 | # Now with each of those timestamps we can take a screenshot and update the progress bar 186 | FFmpeg(inputs={upload_media_import: f'-loglevel panic -ss {ss_timestamp}'}, outputs={f'{base_path}/images/screenshots/{torrent_title_import} - ({ss_timestamp.replace(":", ".")}).png': '-frames:v 1 -q:v 10'}).run() 187 | console.print('Finished taking screenshots!\n', style='sea_green3') 188 | # log the list of screenshot timestamps 189 | logging.info(f'Taking screenshots at the following timestamps {ss_timestamps_list}') 190 | 191 | # ---------------------------------------------------------------------------------------- # 192 | 193 | console.print(f"Image host order: [chartreuse1]{' [bold blue]:arrow_right:[/bold blue] '.join(enabled_img_hosts_list)}[/chartreuse1]", style="Bold Blue") 194 | successfully_uploaded_image_count = 0 195 | for ss_to_upload in track(screenshots_to_upload_list, description="Uploading screenshots..."): 196 | # This is how we fall back to a second host if the first fails 197 | for img_host in enabled_img_hosts_list: 198 | 199 | # call the function that uploads the screenshot 200 | upload_image = upload_screens(img_host=img_host, img_host_api=os.getenv(f'{img_host}_api_key'), image_path=ss_to_upload, torrent_title=torrent_title_import) 201 | 202 | # If the upload function returns True, we add it to bbcode_images.txt 203 | if upload_image: 204 | with open(f"{base_path}/temp_upload/bbcode_images.txt", "a") as append_bbcode_txt: 205 | append_bbcode_txt.write(f"{upload_image[1]} ") 206 | successfully_uploaded_image_count += 1 207 | # Since the image uploaded successfully, we need to break now so we don't reupload to the backup image host (if exists) 208 | break 209 | 210 | # Depending on the image upload outcome we print a success or fail message showing the user what & how many images failed/succeeded 211 | if len(screenshots_to_upload_list) == successfully_uploaded_image_count: 212 | console.print(f'Uploaded {successfully_uploaded_image_count}/{len(screenshots_to_upload_list)} screenshots', style='sea_green3', highlight=False) 213 | logging.info(f'Successfully uploaded {successfully_uploaded_image_count}/{len(screenshots_to_upload_list)} screenshots') 214 | else: 215 | console.print(f'{len(screenshots_to_upload_list) - successfully_uploaded_image_count}/{len(screenshots_to_upload_list)} screenshots failed to upload', style='bold red', highlight=False) 216 | logging.error(f'{len(screenshots_to_upload_list) - successfully_uploaded_image_count}/{len(screenshots_to_upload_list)} screenshots failed to upload') 217 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pymediainfo==5.1.0 2 | python-dotenv==0.19.2 3 | requests==2.27.1 4 | ffmpy==0.3.0 5 | guessit==3.4.3 6 | rich==11.2.0 7 | fuzzywuzzy~=0.18.0 8 | pyimgbox~=1.0.3 9 | torf~=3.1.3 10 | python-Levenshtein~=0.12.2 11 | jinja2~=3.0.3 -------------------------------------------------------------------------------- /rtorrent_on_complete_reupload.sh.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | ##### This requires you to be using r(u)torrent & have autodl-irssi configured ##### 5 | # (More complete documentation coming soon) 6 | 7 | 8 | 9 | # -------- Following lines need to be appended to rtorrent.rc -------- # 10 | # method.insert = d.data_path, simple, "if=(d.is_multi_file), (cat,(d.directory),/), (cat,(d.directory),/,(d.name))" 11 | # method.set_key = event.download.finished,complete,"execute=/mnt/local/torrents/scripts/rtorrent_on_complete_reupload.sh,$d.name=,$d.data_path=,$d.custom1=,$d.custom=upload_to_tracker" 12 | 13 | 14 | 15 | # ------ Update the following info to be accurate ------ # 16 | log_location_for_autodl_matches='/path/to/where/you/want/this/logfile/xpbot-autodl.log' 17 | location_of_auto_upload_py='/path/to/xpbot/auto_upload.py' 18 | 19 | 20 | 21 | # You don't need to rename or do anything with these variables 22 | title=$1 23 | path=$2 24 | imdb_id=$3 25 | upload_to=$4 26 | time_stamp=$(date -u +"%Y-%m-%d %H:%M:%SZ") 27 | 28 | # If the 'upload_to' variable is not empty we trigger auto_upload.py with all required info & the -reupload flag which sets some custom settings for this type of upload 29 | # (set the 'upload_to' arg in your autodl filter settings) 30 | if [ -n "$upload_to" ]; then 31 | 32 | # Log what we are uploading for future reference 33 | echo '{"time_stamp":"'"$time_stamp"'","title":"'"$title"'","path":"'"$path"'","imdb_id":"'"$imdb_id"'","upload_to":"'"$upload_to"'"}' &>>$log_location_for_autodl_matches 34 | /usr/bin/python3 $location_of_auto_upload_py -t "$upload_to" -p "$path" -imdb "$imdb_id" -reupload "$title" 35 | 36 | fi 37 | 38 | -------------------------------------------------------------------------------- /search_for_dupes.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import json 4 | import logging 5 | import requests 6 | from distutils import util 7 | from guessit import guessit 8 | from fuzzywuzzy import fuzz 9 | from rich.table import Table 10 | from rich.prompt import Confirm 11 | from rich.console import Console 12 | 13 | 14 | 15 | console = Console() 16 | 17 | working_folder = os.path.dirname(os.path.realpath(__file__)) 18 | 19 | logging.basicConfig(filename=f'{working_folder}/upload_script.log', 20 | level=logging.INFO, 21 | format='%(asctime)s | %(name)s | %(levelname)s | %(message)s') 22 | 23 | 24 | def search_for_dupes_api(search_site, imdb, torrent_info, tracker_api): 25 | with open(f'{working_folder}/site_templates/{search_site}.json', "r", encoding="utf-8") as config_file: 26 | # with open(working_folder + "/site_templates/{}.json".format(search_site), "r", encoding="utf-8") as config_file: 27 | config = json.load(config_file) 28 | 29 | if str(config["dupes"]["request"]) == "POST": 30 | # POST request (BHD) 31 | url_dupe_search = str(config["torrents_search"]).format(api_key=tracker_api) 32 | url_dupe_payload = {'action': 'search', config["translation"]["imdb"]: imdb} 33 | dupe_check_response = requests.post(url=url_dupe_search, data=url_dupe_payload) 34 | else: 35 | # GET request (BLU & ACM) 36 | url_dupe_search = str(config["dupes"]["url_format"]).format(search_url=str(config["torrents_search"]).format(api_key=tracker_api), imdb=imdb[2:]) 37 | url_dupe_payload = None # this is here just for the log, its not technically needed 38 | dupe_check_response = requests.get(url=url_dupe_search) 39 | 40 | logging.info(msg=f'Dupe search request | Method: {str(config["dupes"]["request"])} | URL: {url_dupe_search} | Payload: {url_dupe_payload}') 41 | 42 | 43 | if dupe_check_response.status_code != 200: 44 | logging.error(f"{search_site} returned the status code: {dupe_check_response.status_code}") 45 | logging.error(f"Dupe check for {search_site} failed, assuming no dupes and continuing upload") 46 | return False 47 | 48 | # Now that we have the response from tracker(X) we can parse the json and try to identify dupes 49 | 50 | existing_release_types = {} # We first break down the results into very basic categories like "remux", "encode", "web" etc and store the title + results here 51 | existing_releases_count = {'bluray_encode': 0, 'bluray_remux': 0, 'webdl': 0, 'webrip': 0, 'hdtv': 0} # We also log the num each type shows up on site 52 | # to handle torrents with HDR and DV, we keep a separate dictionary to keep tracker of hdr. non-hdr and dv releases 53 | # the reason to go for a separate map is because in `existing_release_types` the keys are torrent titles and that is not possible for hdr based filtering 54 | # note that for hdr filtering we are not bothered about the different formats (PQ10, HDR, HLG etc), Since its rare to see a show release in multiple formats. 55 | # although not impossible. (moonknight had PQ10 and HDR versions) 56 | hdr_format_types = { 'hdr': [], 'dv_hdr': [], 'dv': [], 'normal': []} 57 | 58 | for item in dupe_check_response.json()[str(config["dupes"]["parse_json"]["top_lvl"])]: 59 | 60 | if "torrent_details" in config["dupes"]["parse_json"]: 61 | # BLU & ACM have us go 2 "levels" down to get torrent info --> [data][attributes][name] = torrent title 62 | torrent_details = item[str(config["dupes"]["parse_json"]["torrent_details"])] 63 | else: 64 | # BHD only has us go down 1 "level" to get torrent info --> [data][name] = torrent title 65 | torrent_details = item 66 | 67 | torrent_title = str(torrent_details["name"]) 68 | torrent_title_split = torrent_title.replace("-", " ").lower().split(' ') 69 | 70 | 71 | # Bluray Encode 72 | if all(x in torrent_title_split for x in ['bluray']) and any(x in torrent_title_split for x in ['720p', '1080i', '1080p', '2160p']) and any(x in torrent_title_split for x in ['x264', 'x265']): 73 | existing_release_types[torrent_title] = 'bluray_encode' 74 | 75 | # Bluray Remux 76 | if all(x in torrent_title_split for x in ['bluray', 'remux']) and any(x in torrent_title_split for x in ['720p', '1080i', '1080p', '2160p']): 77 | existing_release_types[torrent_title] = 'bluray_remux' 78 | 79 | # WEB-DL 80 | if all(x in torrent_title_split for x in ['web', 'dl']) and any(x in torrent_title_split for x in ['h.264', 'h264', 'h.265', 'h265', 'hevc']): 81 | existing_release_types[torrent_title] = "webdl" 82 | 83 | # WEBRip 84 | if all(x in torrent_title_split for x in ['webrip']) and any(x in torrent_title_split for x in ['h.264', 'h264', 'h.265', 'h265', 'hevc', 'x264', 'x265']): 85 | existing_release_types[torrent_title] = "webrip" 86 | 87 | # HDTV 88 | if all(x in torrent_title_split for x in ['hdtv']): 89 | existing_release_types[torrent_title] = "hdtv" 90 | 91 | # DVD 92 | if all(x in torrent_title_split for x in ['dvd']): 93 | existing_release_types[torrent_title] = "dvd" 94 | 95 | # HDR 96 | if any(x in torrent_title_split for x in ['hdr', 'hdr10', 'hdr10+', 'hdr10plus', 'pq10', 'hlg', 'wcg']): 97 | hdr_format_types['hdr'].append(torrent_title) 98 | 99 | # DV 100 | if any(x in torrent_title_split for x in ['dv', 'dovi', 'dolbyvision']): 101 | hdr_format_types['dv'].append(torrent_title) 102 | 103 | # Non-HDR 104 | if all(x not in torrent_title_split for x in ['dv', 'dovi', 'dolbyvision', 'hdr', 'hdr10', 'hdr10+', 'hdr10plus', 'pq10', 'hlg', 'wcg']): 105 | hdr_format_types['normal'].append(torrent_title) 106 | 107 | # DV HDR 108 | if any(x in torrent_title_split for x in ['dv', 'dovi', 'dolbyvision']) and any(x in torrent_title_split for x in ['hdr', 'hdr10', 'hdr10+', 'hdr10plus', 'pq10', 'hlg', 'wcg']): 109 | hdr_format_types['dv_hdr'].append(torrent_title) 110 | 111 | logging.info(f'[DupeCheck] Existing release types based on hdr formats identified from tracker {search_site} are {hdr_format_types}') 112 | 113 | # This just updates a dict with the number of a particular "type" of release exists on site (e.g. "2 bluray_encodes" or "1 bluray_remux" etc) 114 | for onsite_quality_type in existing_release_types.values(): 115 | existing_releases_count[onsite_quality_type] += 1 116 | for hdr_format in hdr_format_types.keys(): 117 | existing_releases_count[hdr_format] = len(hdr_format_types[hdr_format]) 118 | logging.info(msg=f'Results from initial dupe query (all resolution): {existing_releases_count}') 119 | 120 | 121 | 122 | # If we get no matches when searching via IMDB ID that means this content hasn't been upload in any format, no possibility for dupes 123 | if len(existing_release_types.keys()) == 0: 124 | logging.info(msg='Dupe query did not return any releases that we could parse, assuming no dupes exist.') 125 | return False 126 | 127 | our_format = "normal" 128 | if "dv" in torrent_info: 129 | our_format = "dv_hdr" if "hdr" in torrent_info else "dv" 130 | elif "hdr" in torrent_info: 131 | our_format = "hdr" 132 | 133 | logging.info(f'[DupeCheck] Eliminating releases based on HDR Format. We are tring to upload: "{our_format}". All other formats will be ignored.') 134 | for item in hdr_format_types.keys(): 135 | if item != our_format: 136 | for their_title in hdr_format_types[item]: 137 | if their_title in existing_release_types and their_title not in hdr_format_types[our_format]: 138 | their_title_type = existing_release_types[their_title] 139 | existing_releases_count[their_title_type] -= 1 140 | existing_release_types.pop(their_title) 141 | existing_releases_count[item] = 0 142 | hdr_format_types[item] = [] 143 | logging.info(msg=f'[DupeCheck] After applying "HDR Format" filter: {existing_releases_count}') 144 | 145 | # --------------- Filter the existing_release_types dict to only include correct res & source_type --------------- # 146 | for their_title in list(existing_release_types.keys()): # we wrap the dict keys in a "list()" so we can modify (pop) keys from it while the loop is running below 147 | # use guessit to get details about the release 148 | their_title_guessit = guessit(their_title) 149 | their_title_type = existing_release_types[their_title] 150 | 151 | # This next if statement does 2 things: 152 | # 1. If the torrent title from the API request doesn't have the same resolution as the file being uploaded we pop (remove) it from the dict "existing_release_types" 153 | # 2. If the API torrent title source type (e.g. bluray_encode) is not the same as the local file then we again pop it from the "existing_release_types" dict 154 | if ("screen_size" not in their_title_guessit or their_title_guessit["screen_size"] != torrent_info["screen_size"]) or their_title_type != torrent_info["source_type"]: 155 | existing_releases_count[their_title_type] -= 1 156 | existing_release_types.pop(their_title) 157 | 158 | logging.info(msg=f'After applying resolution & "source_type" filter: {existing_releases_count}') 159 | 160 | 161 | 162 | # Movies (mostly blurays) are usually a bit more flexible with dupe/trump rules due to editions, regions, etc 163 | # TV Shows (mostly web) are usually only allowed 1 "version" onsite & we also need to consider individual episode uploads when a season pack exists etc 164 | # for those reasons ^^ we place this dict here that we will use to generate the Table we show the user of possible dupes 165 | possible_dupe_with_percentage_dict = {} # By keeping it out of the fuzzy_similarity() func/loop we are able to directly insert/modify data into it when dealing with tv show dupes/trumps below 166 | 167 | # If we are uploading a tv show we should only add the correct season to the existing_release_types dict 168 | if "s00e00" in torrent_info: 169 | # First check if what the user is uploading is a full season or not 170 | is_full_season = bool(len(torrent_info["s00e00"]) == 3) 171 | 172 | # We just want the season of whatever we are uploading so we can filter the results later (Most API requests include all the seasons/episodes of a tv show in the response, we don't need all of them) 173 | season_num = torrent_info["s00e00"] if is_full_season else str(torrent_info["s00e00"])[:-3] 174 | episode_num = str(torrent_info["s00e00"])[3:] 175 | logging.info(msg=f'Filtering out results that are not from the same season being uploaded ({season_num})') 176 | 177 | # Loop through the results & discard everything that is not from the correct season 178 | number_of_discarded_seasons = 0 179 | for existing_release_types_key in list(existing_release_types.keys()): 180 | 181 | if season_num not in existing_release_types_key: # filter our wrong seasons 182 | existing_release_types.pop(existing_release_types_key) 183 | number_of_discarded_seasons += 1 184 | continue 185 | 186 | 187 | # at this point we've filtered out all the different resolutions/types/seasons 188 | # so now we check each remaining title to see if its a season pack or individual episode 189 | extracted_season_episode_from_title = list(filter(lambda x: x.startswith(season_num), existing_release_types_key.split(" ")))[0] 190 | if len(extracted_season_episode_from_title) == 3: 191 | logging.info(msg=f'Found a season pack for {season_num} on {search_site}') 192 | # TODO maybe mark the season pack as a 100% dupe or consider expanding dupe Table to allow for error messages to inform the user 193 | 194 | 195 | # If a full season pack is onsite then in almost all cases individual episodes from that season are not allowed to be uploaded anymore 196 | # check to see if that's ^^ happening, if it is then we will log it and if 'auto_mode' is enabled we also cancel the upload 197 | # if 'auto_mode=false' then we prompt the user & let them decide 198 | if not is_full_season: 199 | 200 | 201 | if bool(util.strtobool(os.getenv('auto_mode'))): 202 | # possible_dupe_with_percentage_dict[existing_release_types_key] = 100 203 | logging.critical(msg=f'Canceling upload to {search_site} because uploading a full season pack is already available: {existing_release_types_key}') 204 | return True 205 | 206 | 207 | # if this is an interactive upload then we can prompt the user & let them choose if they want to cancel or continue the upload 208 | logging.error(msg="Almost all trackers don't allow individual episodes to be uploaded after season pack is released") 209 | console.print(f"\n[bold red on white] :warning: Need user input! :warning: [/bold red on white]") 210 | console.print(f"You're trying to upload an [bold red]Individual Episode[/bold red] [bold]({torrent_info['title']} {torrent_info['s00e00']})[/bold] to {search_site}", highlight=False) 211 | console.print(f"A [bold red]Season Pack[/bold red] is already available: {existing_release_types_key}", highlight=False) 212 | console.print("Most sites [bold red]don't allow[/bold red] individual episode uploads when the season pack is available") 213 | console.print('---------------------------------------------------------') 214 | if not bool(Confirm.ask("Ignore and continue upload?", default=False)): 215 | return True 216 | 217 | 218 | # now we just need to make sure the episode we're trying to upload is not already on site 219 | number_of_discarded_episodes = 0 220 | if extracted_season_episode_from_title != torrent_info['s00e00']: 221 | number_of_discarded_episodes += 1 222 | existing_release_types.pop(existing_release_types_key) 223 | 224 | logging.info(msg=f'Filtered out: {number_of_discarded_episodes} results for having different episode numbers (looking for {episode_num})') 225 | 226 | 227 | logging.info(msg=f'Filtered out: {number_of_discarded_seasons} results for not being the right season ({season_num})') 228 | 229 | 230 | def fuzzy_similarity(our_title, check_against_title): 231 | check_against_title_original = check_against_title 232 | # We will remove things like the title & year from the comparison stings since we know they will be exact matches anyways 233 | 234 | # replace DD+ with DDP from both our title and tracker results title to make the dupe check a bit more accurate since some sites like to use DD+ and others DDP but they refer to the same thing 235 | our_title = re.sub(r'dd\+', 'ddp', str(our_title).lower()) 236 | check_against_title = re.sub(r'dd\+', 'ddp', str(check_against_title).lower()) 237 | content_title = re.sub('[^0-9a-zA-Z]+', ' ', str(torrent_info["title"]).lower()) 238 | 239 | if "year" in torrent_info: 240 | # Also remove the year because that *should* be an exact match, that's not relevant to detecting changes 241 | if str(int(torrent_info["year"]) + 1) in check_against_title: 242 | check_against_title_year = str(int(torrent_info["year"]) + 1) # some releases are occasionally off by 1 year, it's still the same media so it can be used for dupe check 243 | elif str(int(torrent_info["year"]) - 1) in check_against_title: 244 | check_against_title_year = str(int(torrent_info["year"]) - 1) 245 | else: 246 | check_against_title_year = str(torrent_info["year"]) 247 | else: 248 | check_against_title_year = "" 249 | 250 | # our_title = str(our_title).replace(torrent_info["resolution"], "").replace(check_against_title_year, "").replace(content_title, "") 251 | our_title = re.sub(r'[^A-Za-z0-9 ]+', ' ', str(our_title)).lower().replace(torrent_info["screen_size"], "").replace(check_against_title_year, "") 252 | our_title = " ".join(our_title.split()) 253 | 254 | check_against_title = re.sub(r'[^A-Za-z0-9 ]+', ' ', str(check_against_title)).lower().replace(torrent_info["screen_size"], "").replace(check_against_title_year, "") 255 | check_against_title = " ".join(check_against_title.split()) 256 | 257 | token_set_ratio = fuzz.token_set_ratio(our_title.replace(content_title, ''), check_against_title.replace(content_title, '')) 258 | logging.info(f"'{check_against_title_original}' was flagged with a {str(token_set_ratio)}% dupe probability") 259 | 260 | 261 | # Instead of wasting time trying to create a 'low, medium, high' risk system we just have the user enter in a percentage they are comfortable with 262 | # if a torrent titles vs local title similarity percentage exceeds a limit the user set we immediately quit trying to upload to that site 263 | # since what the user considers (via token_set_ratio percentage) to be a dupe exists 264 | return token_set_ratio 265 | 266 | 267 | possible_dupes_table = Table(show_header=True, header_style="bold cyan") 268 | possible_dupes_table.add_column(f"Exceeds Max % ({os.getenv('acceptable_similarity_percentage')}%)", justify="left") 269 | possible_dupes_table.add_column(f"Possible Dupes ({str(config['source']).upper()})", justify="left") 270 | possible_dupes_table.add_column("Similarity %", justify="center") 271 | 272 | 273 | max_dupe_percentage_exceeded = False 274 | 275 | for possible_dupe_title in existing_release_types.keys(): 276 | # If we get a match then run further checks 277 | possible_dupe_with_percentage_dict[possible_dupe_title] = fuzzy_similarity(our_title=torrent_info["torrent_title"], check_against_title=possible_dupe_title) 278 | 279 | 280 | for possible_dupe in sorted(possible_dupe_with_percentage_dict, key=possible_dupe_with_percentage_dict.get, reverse=True): 281 | mark_as_dupe = bool(possible_dupe_with_percentage_dict[possible_dupe] >= int(os.getenv('acceptable_similarity_percentage'))) 282 | mark_as_dupe_color = "bright_red" if mark_as_dupe else "dodger_blue1" 283 | mark_as_dupe_percentage_difference_raw_num = possible_dupe_with_percentage_dict[possible_dupe] - int(os.getenv('acceptable_similarity_percentage')) 284 | mark_as_dupe_percentage_difference = f'{"+" if mark_as_dupe_percentage_difference_raw_num >= 0 else "-"}{abs(mark_as_dupe_percentage_difference_raw_num)}%' 285 | 286 | possible_dupes_table.add_row(f'[{mark_as_dupe_color}]{mark_as_dupe}[/{mark_as_dupe_color}] ({mark_as_dupe_percentage_difference})', possible_dupe, f'{str(possible_dupe_with_percentage_dict[possible_dupe])}%') 287 | 288 | 289 | # because we want to show the user every possible dupe (not just the ones that exceed the max percentage) we just mark an outside var True & finish the for loop that adds the table rows 290 | if not max_dupe_percentage_exceeded: 291 | max_dupe_percentage_exceeded = mark_as_dupe 292 | 293 | 294 | if max_dupe_percentage_exceeded: 295 | console.print(f"\n\n[bold red on white] :warning: Detected possible dupe! :warning: [/bold red on white]") 296 | console.print(possible_dupes_table) 297 | return True if bool(util.strtobool(os.getenv('auto_mode'))) else not bool(Confirm.ask("\nContinue upload even with possible dupe?")) 298 | else: 299 | console.print(f":heavy_check_mark: Yay! No dupes found on [bold]{str(config['source']).upper()}[/bold], continuing the upload process now\n") 300 | return False 301 | -------------------------------------------------------------------------------- /site_templates/aither.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Aither", 3 | "url": "https://aither.cc", 4 | "upload_form": "https://aither.cc/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://aither.cc/api/torrents/filter?api_token={api_key}", 6 | "source": "Aither", 7 | "bbcode_line_break": "\n", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "tvdb": "tvdb", 21 | "mal": "mal", 22 | "igdb": "igdb", 23 | "anon": "anonymous", 24 | "live": "live", 25 | "sd": "sd", 26 | "optimized": "stream", 27 | "nfo_file": "nfo", 28 | 29 | "internal": "internal", 30 | "featured": "featured", 31 | "doubleup": "doubleup", 32 | "sticky": "sticky", 33 | "freeleech": "free" 34 | 35 | }, 36 | 37 | "Required": { 38 | "torrent": "file", 39 | "name": "string", 40 | "description": "string", 41 | "mediainfo": "string", 42 | "anonymous": "string", 43 | "sd": "string", 44 | "stream": "string", 45 | "tvdb": "string", 46 | "tmdb": "string", 47 | "imdb": "string", 48 | "mal": "string", 49 | "igdb": "string", 50 | 51 | 52 | "internal": "string", 53 | "free": "string", 54 | "featured": "string", 55 | "doubleup": "string", 56 | "sticky": "string", 57 | 58 | 59 | "category_id": { 60 | "1": "movie", 61 | "2": "episode" 62 | }, 63 | 64 | "type_id": { 65 | "1": { 66 | "disc": 1, 67 | "bluray_disc": 2, 68 | "dvd_disc": 2 69 | }, 70 | "2": { 71 | "remux": 1, 72 | "bluray_remux": 2, 73 | "dvd_remux": 2 74 | }, 75 | "3": { 76 | "bluray_encode": 1 77 | }, 78 | "4": { 79 | "webdl": 1 80 | }, 81 | "5": { 82 | "webrip": 1 83 | }, 84 | "6": { 85 | "hdtv": 1 86 | } 87 | }, 88 | 89 | 90 | "resolution_id": { 91 | "1": { 92 | "4360p": 1 93 | }, 94 | 95 | "2": { 96 | "2160p": 1 97 | }, 98 | 99 | "3": { 100 | "1080p" : 1 101 | }, 102 | 103 | "4": { 104 | "1080i" : 1 105 | }, 106 | 107 | "5": { 108 | "720p" : 1 109 | }, 110 | 111 | "6": { 112 | "576p" : 1 113 | }, 114 | 115 | "7": { 116 | "576i": 1 117 | }, 118 | 119 | "8": { 120 | "480p": 1 121 | }, 122 | 123 | "9": { 124 | "480i": 1 125 | }, 126 | 127 | "10": { 128 | "other": 1 129 | } 130 | 131 | } 132 | }, 133 | 134 | "Optional": { 135 | "nfo_file": "file", 136 | "season_number": "string", 137 | "episode_number": "string" 138 | }, 139 | 140 | "dupes": { 141 | "request": "GET", 142 | "url_format": "{search_url}&imdbId={imdb}", 143 | 144 | "parse_json": { 145 | "top_lvl": "data", 146 | "torrent_details": "attributes" 147 | }, 148 | 149 | "different_cuts": "1", 150 | "2160p_remux": 1, 151 | "2160p_encode": 1, 152 | "1080p_remux": 1, 153 | "1080p_encode": 100, 154 | "720p_encode": 100, 155 | "else": 1 156 | }, 157 | 158 | "torrent_title_format": { 159 | "episode": { 160 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 161 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 162 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 163 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 164 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 165 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 166 | }, 167 | 168 | "movie": { 169 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 170 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 171 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 172 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 173 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 174 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 175 | } 176 | }, 177 | 178 | 179 | 180 | "banned_groups": [ 181 | "GalaxyTV", 182 | "TGx", 183 | "YIFY", 184 | "YTS", 185 | "RARBG", 186 | "Will1896" 187 | ] 188 | 189 | } 190 | -------------------------------------------------------------------------------- /site_templates/asiancinema.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AsianCinema", 3 | "url": "https://asiancinema.me", 4 | "upload_form": "https://asiancinema.me/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://asiancinema.me/api/torrents/filter?api_token={api_key}", 6 | "source": "acm", 7 | "bbcode_line_break": "
", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title": "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "anon": "anonymous", 21 | "live": "live", 22 | 23 | "sd": "sd", 24 | "tvdb": "tvdb", 25 | "mal": "mal", 26 | "igdb": "igdb", 27 | "optimized": "stream", 28 | "nfo_file": "nfo_file" 29 | 30 | 31 | }, 32 | "Required": { 33 | 34 | "torrent": "file", 35 | "name": "string", 36 | "description": "string", 37 | "mediainfo": "string", 38 | "sd": "string", 39 | "anonymous": "string", 40 | "stream": "string", 41 | "igdb": "string", 42 | "mal": "string", 43 | "tvdb": "string", 44 | 45 | 46 | 47 | "category_id": { 48 | "1": "movie", 49 | "2": "episode" 50 | }, 51 | 52 | "type_id": { 53 | 54 | "1": { 55 | "bluray_disc": 1, 56 | "2160p": 1, 57 | "uhd_100": 1 58 | }, 59 | 60 | "2": { 61 | "bluray_disc": 1, 62 | "2160p": 1, 63 | "uhd_66": 1 64 | }, 65 | 66 | "3": { 67 | "bluray_disc": 1, 68 | "2160p": 1, 69 | "uhd_50": 1 70 | }, 71 | 72 | "4": { 73 | "bluray_disc": 1, 74 | "bd_50": 1, 75 | "1080p": 2, 76 | "1080i": 2 77 | 78 | }, 79 | 80 | "5": { 81 | "bluray_disc": 1, 82 | "bd_25": 1, 83 | "1080p": 2, 84 | "1080i": 2 85 | }, 86 | 87 | "12": { 88 | "bluray_remux": 1, 89 | "2160p": 1 90 | }, 91 | 92 | "7": { 93 | "bluray_remux": 1, 94 | "1080p": 2, 95 | "1080i": 2 96 | }, 97 | 98 | "8": { 99 | "bluray_disc": 1, 100 | "2160p": 1 101 | }, 102 | 103 | "9": { 104 | "webdl": 1 105 | }, 106 | 107 | "10": { 108 | "bluray_disc": 1, 109 | "1080p": 2, 110 | "1080i": 2 111 | }, 112 | 113 | "11": { 114 | "720p": 1 115 | }, 116 | 117 | "13": { 118 | "bluray_disc": 1, 119 | "576p": 2, 120 | "576i": 2, 121 | "480p": 2, 122 | "480i": 2 123 | }, 124 | 125 | "17": { 126 | "hdtv": 1 127 | } 128 | }, 129 | 130 | 131 | "resolution_id": { 132 | "1": { 133 | "2160p": 1 134 | }, 135 | "2": { 136 | "1080": 1, 137 | "1080p": 2, 138 | "1080i": 2 139 | }, 140 | "3": { 141 | "720p": 1 142 | }, 143 | "4": { 144 | "576": 1, 145 | "576p": 2, 146 | "576i": 2 147 | }, 148 | "5": { 149 | "480": 1, 150 | "480p": 2, 151 | "480i": 2 152 | } 153 | }, 154 | 155 | 156 | 157 | "tmdb": "string", 158 | "imdb": "string" 159 | }, 160 | "Optional": { 161 | "nfo_file": "file", 162 | "season_number": "string", 163 | "episode_number": "string" 164 | }, 165 | "dupes": { 166 | 167 | "request": "GET", 168 | "url_format": "{search_url}&imdb={imdb}", 169 | 170 | "parse_json": { 171 | "top_lvl": "data", 172 | "torrent_details": "attributes" 173 | }, 174 | 175 | 176 | "different_cuts": "1", 177 | "2160p_remux": 1, 178 | "2160p_encode": 1, 179 | "1080p_remux": 1, 180 | "1080p_encode": 100, 181 | "720p_encode": 100, 182 | "else": 1 183 | }, 184 | 185 | "torrent_title_format": { 186 | "episode": { 187 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 188 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 189 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 190 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 191 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 192 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 193 | }, 194 | 195 | "movie": { 196 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 197 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 198 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 199 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 200 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 201 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 202 | } 203 | } 204 | } 205 | 206 | -------------------------------------------------------------------------------- /site_templates/beyond-hd.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Beyond-HD", 3 | "url": "https://beyond-hd.me", 4 | "upload_form": "https://beyond-hd.me/api/upload/{api_key}", 5 | "torrents_search": "https://beyond-hd.me/api/torrents/{api_key}", 6 | "source": "bhd", 7 | "bbcode_line_break": "[br][/br]", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "file", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "source", 17 | "resolution": "type", 18 | "tmdb": "tmdb_id", 19 | "imdb": "imdb_id", 20 | "anon": "anon", 21 | 22 | "live": "live", 23 | "sd": "sd", 24 | "tvdb": "tvdb", 25 | "mal": "mal", 26 | "igdb": "igdb", 27 | "optimized": "stream", 28 | "nfo_file": "nfo_file", 29 | "pack": "pack" 30 | }, 31 | 32 | 33 | 34 | "Required": { 35 | "file": "file", 36 | "name": "string", 37 | "description": "string", 38 | "mediainfo": "file", 39 | "live": "string", 40 | "pack": "string", 41 | "anon": "string", 42 | 43 | "category_id": { 44 | "1": "movie", 45 | "2": "episode" 46 | }, 47 | 48 | 49 | "type": { 50 | 51 | "UHD 100": { 52 | "bluray_disc": 1, 53 | "2160p": 1, 54 | "uhd_100": 1 55 | }, 56 | 57 | "UHD 66": { 58 | "bluray_disc": 1, 59 | "2160p": 1, 60 | "uhd_66": 1 61 | }, 62 | 63 | 64 | "UHD 50": { 65 | "bluray_disc": 1, 66 | "2160p": 1, 67 | "uhd_50": 1 68 | }, 69 | 70 | 71 | "BD 50": { 72 | "bluray_disc": 1, 73 | "bd_50": 1, 74 | "1080p": 2, 75 | "1080i": 2 76 | 77 | }, 78 | 79 | 80 | "BD 25": { 81 | "bluray_disc": 1, 82 | "bd_25": 1, 83 | "1080p": 2, 84 | "1080i": 2 85 | }, 86 | 87 | 88 | 89 | "UHD Remux": { 90 | "bluray_remux": 1, 91 | "2160p": 1 92 | }, 93 | 94 | "BD Remux": { 95 | "bluray_remux": 1, 96 | "1080p": 2, 97 | "1080i": 2 98 | }, 99 | 100 | "2160p": { 101 | "2160p": 1, 102 | "bluray_encode": 2, 103 | "webdl": 2, 104 | "webrip" : 2 105 | }, 106 | 107 | "1080p": { 108 | "1080p": 1, 109 | "bluray_encode": 2, 110 | "webdl": 2, 111 | "webrip" : 2 112 | }, 113 | 114 | "1080i": { 115 | "1080i": 1, 116 | "bluray_encode": 2, 117 | "webdl": 2, 118 | "webrip" : 2 119 | }, 120 | 121 | "720p": { 122 | "720p": 1, 123 | "bluray_encode": 2, 124 | "webdl": 2, 125 | "webrip" : 2 126 | }, 127 | 128 | "576p": { 129 | "576p": 1, 130 | "webdl": 1 131 | }, 132 | 133 | "540p": { 134 | "540p": 1, 135 | "webdl": 1 136 | }, 137 | 138 | "DVD Remux": { 139 | "dvd": 1, 140 | "576p": 2, 141 | "540p" : 2, 142 | "480p": 2 143 | }, 144 | 145 | "480p": { 146 | "480p": 1, 147 | "webdl": 1 148 | }, 149 | 150 | "Other": { 151 | "other": 0 152 | } 153 | }, 154 | 155 | 156 | "source": { 157 | "Blu-ray": { 158 | "bluray": 1, 159 | "disc": 2, 160 | "remux": 2, 161 | "encode": 2 162 | }, 163 | 164 | "HD-DVD": { 165 | "?": 1 166 | }, 167 | 168 | "WEB": { 169 | "web": 1, 170 | "webrip": 2, 171 | "webdl": 2 172 | }, 173 | 174 | "HDTV": { 175 | "hdtv": 1 176 | }, 177 | 178 | "DVD": { 179 | "dvd": 1, 180 | "dvd_disc": 2, 181 | "dvd_remux": 2 182 | } 183 | 184 | }, 185 | 186 | "imdb_id": "string", 187 | "tmdb_id": "string" 188 | }, 189 | 190 | "Optional": { 191 | "edition": [ 192 | "Collector", 193 | "Director", 194 | "Extended", 195 | "Limited", 196 | "Special", 197 | "Theatrical", 198 | "Uncut", 199 | "Unrated" 200 | ], 201 | "custom_edition": "string", 202 | "region": [ 203 | "AUS", 204 | "CAN", 205 | "CEE", 206 | "CHN", 207 | "ESP", 208 | "EUR", 209 | "FRA", 210 | "GBR", 211 | "GER", 212 | "HKG", 213 | "ITA", 214 | "JPN", 215 | "KOR", 216 | "NOR", 217 | "NLD", 218 | "RUS", 219 | "TWN", 220 | "USA" 221 | ], 222 | "tags": [ 223 | "Commentary", 224 | "2in1", 225 | "Hybrid", 226 | "OpenMatte", 227 | "2D3D", 228 | "WEBRip", 229 | "WEBDL", 230 | "3D", 231 | "4kRemaster", 232 | "DualAudio", 233 | "EnglishDub", 234 | "Personal", 235 | "Scene", 236 | "DigitalExtras", 237 | "Extras" 238 | ], 239 | "nfo": "string", 240 | "nfo_file": "file", 241 | "pack": "int", 242 | "special": "int", 243 | "sd": "string" 244 | }, 245 | 246 | "dupes": { 247 | "request": "POST", 248 | 249 | "keys": { 250 | "action": "search", 251 | "imdb_id": "string" 252 | }, 253 | 254 | "parse_json": { 255 | "top_lvl": "results" 256 | }, 257 | "different_cuts": "100", 258 | "2160p_remux": 1, 259 | "2160p_encode": 1, 260 | "1080p_remux": 1, 261 | "1080p_encode": 2, 262 | "720p_encode": 1, 263 | "else": 1 264 | }, 265 | 266 | "torrent_title_format": { 267 | "episode": { 268 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 269 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 270 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 271 | "web": "{title} {year} {s00e00} {repack} {episode_title} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 272 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {episode_title} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 273 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 274 | }, 275 | 276 | "movie": { 277 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 278 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 279 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 280 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 281 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 282 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 283 | } 284 | }, 285 | 286 | 287 | "banned_groups": [ 288 | "x0r", 289 | "nikt0", 290 | "FGT", 291 | "d3g", 292 | "MeGusta", 293 | "d3g", 294 | "YIFY", 295 | "tigole", 296 | "TEKNO3D", 297 | "C4K" 298 | ] 299 | } 300 | 301 | 302 | -------------------------------------------------------------------------------- /site_templates/blutopia.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Blutopia", 3 | "url": "https://blutopia.xyz", 4 | "upload_form": "https://blutopia.xyz/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://blutopia.xyz/api/torrents/filter?api_token={api_key}", 6 | "source": "blu", 7 | "bbcode_line_break": "\n", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "anon": "anonymous", 21 | "live": "live", 22 | "sd": "sd", 23 | "tvdb": "tvdb", 24 | "mal": "mal", 25 | "igdb": "igdb", 26 | "optimized": "stream", 27 | "nfo_file": "nfo", 28 | 29 | "internal": "internal", 30 | "featured": "featured", 31 | "doubleup": "doubleup", 32 | "sticky": "sticky", 33 | "freeleech": "free" 34 | }, 35 | 36 | 37 | "Required": { 38 | "torrent": "file", 39 | "name": "string", 40 | "description": "string", 41 | "mediainfo": "string", 42 | "anonymous": "string", 43 | "sd": "string", 44 | "stream": "string", 45 | 46 | "tvdb": "string", 47 | "tmdb": "string", 48 | "imdb": "string", 49 | "mal": "string", 50 | "igdb": "string", 51 | 52 | "internal": "string", 53 | "free": "string", 54 | "featured": "string", 55 | "doubleup": "string", 56 | "sticky": "string", 57 | 58 | "category_id": { 59 | "1": "movie", 60 | "2": "episode" 61 | }, 62 | 63 | "type_id": { 64 | "1": { 65 | "disc": 1, 66 | "bluray_disc": 2, 67 | "dvd_disc": 2 68 | }, 69 | "3": { 70 | "remux": 1, 71 | "bluray_remux": 2, 72 | "dvd_remux": 2 73 | }, 74 | "12": { 75 | "bluray_encode": 1 76 | }, 77 | "4": { 78 | "webdl": 1 79 | }, 80 | "5": { 81 | "webrip": 1 82 | }, 83 | "6": { 84 | "hdtv": 1 85 | } 86 | }, 87 | 88 | 89 | "resolution_id": { 90 | "11": { 91 | "4360p": 1 92 | }, 93 | 94 | "1": { 95 | "2160p": 1 96 | }, 97 | 98 | "2": { 99 | "1080p" : 1 100 | }, 101 | 102 | "3": { 103 | "1080i" : 1 104 | }, 105 | 106 | "5": { 107 | "720p" : 1 108 | }, 109 | 110 | "6": { 111 | "576p" : 1 112 | }, 113 | 114 | "7": { 115 | "576i": 1 116 | }, 117 | 118 | "8": { 119 | "480p": 1 120 | }, 121 | 122 | "9": { 123 | "480i": 1 124 | }, 125 | 126 | "10": { 127 | "other": 1 128 | } 129 | 130 | } 131 | }, 132 | 133 | "Optional": { 134 | "nfo_file": "file", 135 | "season_number": "string", 136 | "episode_number": "string" 137 | }, 138 | 139 | "dupes": { 140 | "request": "GET", 141 | "url_format": "{search_url}&imdbId={imdb}", 142 | 143 | "parse_json": { 144 | "top_lvl": "data", 145 | "torrent_details": "attributes" 146 | }, 147 | 148 | "different_cuts": "1", 149 | "2160p_remux": 1, 150 | "2160p_encode": 1, 151 | "1080p_remux": 1, 152 | "1080p_encode": 100, 153 | "720p_encode": 100, 154 | "else": 1 155 | }, 156 | 157 | "torrent_title_format": { 158 | "episode": { 159 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 160 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 161 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 162 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 163 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 164 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 165 | }, 166 | 167 | "movie": { 168 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 169 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 170 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 171 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 172 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 173 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 174 | } 175 | }, 176 | 177 | 178 | "banned_groups": [ 179 | "AROMA", 180 | "aXXo", 181 | "BRrip", 182 | "CM8", 183 | "CrEwSaDe", 184 | "d3g", 185 | "DNL", 186 | "FGT", 187 | "FaNGDiNG0", 188 | "FRDS", 189 | "HD2DVD", 190 | "HDTime", 191 | "iPlanet", 192 | "KiNGDOM", 193 | "Leffe", 194 | "MeGusta", 195 | "mHD", 196 | "mSD", 197 | "nHD", 198 | "nikt0", 199 | "nSD", 200 | "NhaNc3", 201 | "PRODJi", 202 | "RDN", 203 | "SANTi", 204 | "STUTTERSHIT", 205 | "ViSION", 206 | "WAF", 207 | "x0r", 208 | "YIFY", 209 | "Leffe", 210 | "MeGusta", 211 | "mHD", 212 | "mSD", 213 | "nHD", 214 | "nikt0", 215 | "nSD", 216 | "NhaNc3", 217 | "PRODJi", 218 | "RDN" 219 | ] 220 | 221 | } 222 | 223 | 224 | -------------------------------------------------------------------------------- /site_templates/default_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "url": "", 4 | "upload_form": "{api_key}", 5 | "torrents_search": "{api_key}", 6 | "source": "", 7 | "bbcode_line_break": "| [br][/br] | OR |
| (Check which one works) ", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "", 12 | "torrent_title" : "", 13 | "description": "", 14 | "mediainfo": "", 15 | "type": "", 16 | "source": "", 17 | "resolution": "", 18 | "tmdb": "", 19 | "imdb": "", 20 | "anon": "", 21 | "live": "", 22 | "tvdb": "", 23 | "mal": "", 24 | "igdb": "", 25 | 26 | "sd": "", 27 | "optimized": "", 28 | "nfo_file": "", 29 | 30 | "internal": "internal", 31 | "featured": "featured", 32 | "doubleup": "doubleup", 33 | "sticky": "sticky", 34 | "freeleech": "free" 35 | 36 | }, 37 | 38 | 39 | 40 | "Required": { 41 | "required_key_file": "file", 42 | "required_key_name": "string", 43 | "required_key_description": "string", 44 | "required_key_mediainfo": "file", 45 | "required_key_live": "string", 46 | "required_key_anon": "string", 47 | 48 | "required_key_category_id": { 49 | "1": "movie", 50 | "2": "episode" 51 | }, 52 | 53 | 54 | "required_key_resolution": { 55 | "tracker_required_key_2160p_remux": { 56 | "bluray_remux": 1, 57 | "2160p": 1 58 | }, 59 | 60 | "tracker_required_key_1080p_remux": { 61 | "bluray_remux": 1, 62 | "1080p": 2, 63 | "1080i": 2 64 | }, 65 | 66 | "tracker_required_key_2160p": { 67 | "2160p": 1, 68 | "bluray_encode": 2, 69 | "webdl": 2, 70 | "webrip" : 2 71 | }, 72 | 73 | "tracker_required_key_1080p": { 74 | "1080p": 1, 75 | "bluray_encode": 2, 76 | "webdl": 2, 77 | "webrip" : 2 78 | }, 79 | 80 | "tracker_required_key_1080i": { 81 | "1080i": 1, 82 | "bluray_encode": 2, 83 | "webdl": 2, 84 | "webrip" : 2 85 | }, 86 | 87 | "tracker_required_key_720p": { 88 | "720p": 1, 89 | "bluray_encode": 2, 90 | "webdl": 2, 91 | "webrip" : 2 92 | }, 93 | 94 | "tracker_required_key_576p": { 95 | "576p": 1, 96 | "webdl": 1 97 | }, 98 | 99 | "tracker_required_key_540p": { 100 | "540p": 1, 101 | "webdl": 1 102 | }, 103 | 104 | "tracker_required_key_dvd_remux": { 105 | "dvd": 1, 106 | "576p": 2, 107 | "540p" : 2, 108 | "480p": 2 109 | }, 110 | 111 | "tracker_required_key_480p": { 112 | "480p": 1, 113 | "webdl": 1 114 | }, 115 | 116 | "tracker_required_key_Other": { 117 | "other": 0 118 | } 119 | }, 120 | 121 | 122 | "required_key_type_id": { 123 | "Blu-ray": { 124 | "bluray": 1, 125 | "disc": 2, 126 | "remux": 2, 127 | "encode": 2 128 | }, 129 | 130 | "HD-DVD": { 131 | "?": 1 132 | }, 133 | 134 | "WEB": { 135 | "web": 1, 136 | "webrip": 2, 137 | "webdl": 2 138 | }, 139 | 140 | "HDTV": { 141 | "hdtv": 1 142 | }, 143 | 144 | "DVD": { 145 | "dvd": 1 146 | } 147 | }, 148 | 149 | "required_key_imdb": "string", 150 | "required_key_tmdb": "string" 151 | }, 152 | 153 | "Optional": { 154 | "edition": [ 155 | "Collector", 156 | "Director", 157 | "Extended", 158 | "Limited", 159 | "Special", 160 | "Theatrical", 161 | "Uncut", 162 | "Unrated" 163 | ], 164 | "custom_edition": "string", 165 | "region": [ 166 | "AUS", 167 | "CAN", 168 | "CEE", 169 | "CHN", 170 | "ESP", 171 | "EUR", 172 | "FRA", 173 | "GBR", 174 | "GER", 175 | "HKG", 176 | "ITA", 177 | "JPN", 178 | "KOR", 179 | "NOR", 180 | "NLD", 181 | "RUS", 182 | "TWN", 183 | "USA" 184 | ], 185 | "tags": [ 186 | "Commentary", 187 | "2in1", 188 | "Hybrid", 189 | "OpenMatte", 190 | "2D3D", 191 | "WEBRip", 192 | "WEBDL", 193 | "3D", 194 | "4kRemaster", 195 | "DualAudio", 196 | "EnglishDub", 197 | "Personal", 198 | "Scene", 199 | "DigitalExtras", 200 | "Extras" 201 | ], 202 | "nfo": "string", 203 | "nfo_file": "file", 204 | "pack": "int", 205 | "special": "int", 206 | "sd": "int", 207 | "season_number": "string", 208 | "episode_number": "string" 209 | }, 210 | 211 | "dupes": { 212 | 213 | "request": "GET", 214 | "url_format": "{search_url}&imdbId={imdb}", 215 | 216 | "parse_json": { 217 | "top_lvl": "data", 218 | "torrent_details": "attributes" 219 | }, 220 | 221 | 222 | "different_cuts": "100", 223 | "2160p_remux": 1, 224 | "2160p_encode": 1, 225 | "1080p_remux": 1, 226 | "1080p_encode": 2, 227 | "720p_encode": 1, 228 | "else": 1 229 | }, 230 | 231 | "torrent_title_format": { 232 | "episode": { 233 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 234 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 235 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 236 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 237 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 238 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 239 | }, 240 | 241 | "movie": { 242 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 243 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 244 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 245 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 246 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 247 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 248 | } 249 | }, 250 | 251 | 252 | "banned_groups": [ 253 | "group_1_optional", 254 | "group_2_optional" 255 | ] 256 | } 257 | 258 | 259 | -------------------------------------------------------------------------------- /site_templates/desitorrents.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DesiTorrents", 3 | "url": "https://desitorrents.tv", 4 | "upload_form": "https://desitorrents.tv/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://desitorrents.tv/api/torrents/filter?api_token={api_key}", 6 | "source": "DesiTorrents", 7 | "bbcode_line_break": "\n", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "anon": "anonymous", 21 | "live": "live", 22 | "sd": "sd", 23 | "tvdb": "tvdb", 24 | "mal": "mal", 25 | "igdb": "igdb", 26 | "optimized": "stream", 27 | "nfo_file": "nfo", 28 | 29 | "internal": "internal", 30 | "featured": "featured", 31 | "doubleup": "doubleup", 32 | "sticky": "sticky", 33 | "freeleech": "free" 34 | }, 35 | 36 | 37 | "Required": { 38 | "torrent": "file", 39 | "name": "string", 40 | "description": "string", 41 | "mediainfo": "string", 42 | "anonymous": "string", 43 | "sd": "string", 44 | "stream": "string", 45 | 46 | "tvdb": "string", 47 | "tmdb": "string", 48 | "imdb": "string", 49 | "mal": "string", 50 | "igdb": "string", 51 | 52 | "internal": "string", 53 | "free": "string", 54 | "featured": "string", 55 | "doubleup": "string", 56 | "sticky": "string", 57 | 58 | "category_id": { 59 | "1": "movie", 60 | "2": "episode" 61 | }, 62 | 63 | "type_id": { 64 | "2": { 65 | "bluray_remux": 1, 66 | "2160p": 1 67 | }, 68 | "1": { 69 | "2160p": 1 70 | }, 71 | "3": { 72 | "bluray_disc": 1, 73 | "bd_50": 1 74 | }, 75 | "4": { 76 | "bluray_disc": 1, 77 | "bd_25": 1 78 | }, 79 | "5": { 80 | "bluray_remux": 1, 81 | "1080p": 2, 82 | "1080i": 2, 83 | "720p": 2 84 | }, 85 | "8": { 86 | "dvd_remux": 1, 87 | "576p": 2, 88 | "540p" : 2, 89 | "480p": 2 90 | }, 91 | "11": { 92 | "webdl": 2, 93 | "webrip" : 2 94 | }, 95 | "12": { 96 | "bluray_encode": 1 97 | }, 98 | "13": { 99 | "other": 0 100 | } 101 | }, 102 | 103 | 104 | "resolution_id": { 105 | "9": { 106 | "4360p": 1 107 | }, 108 | 109 | "8": { 110 | "2160p": 1 111 | }, 112 | 113 | "11": { 114 | "1080p" : 1 115 | }, 116 | 117 | "7": { 118 | "1080i" : 1 119 | }, 120 | 121 | "6": { 122 | "720p" : 1 123 | }, 124 | 125 | "4": { 126 | "576p" : 1 127 | }, 128 | 129 | "3": { 130 | "576i": 1 131 | }, 132 | 133 | "2": { 134 | "480p": 1 135 | }, 136 | 137 | "1": { 138 | "480i": 1 139 | }, 140 | 141 | "12": { 142 | "540p": 1 143 | }, 144 | 145 | "5": { 146 | "720i": 1 147 | }, 148 | 149 | "10": { 150 | "other": 1 151 | } 152 | 153 | } 154 | }, 155 | 156 | "Optional": { 157 | "season_number": "string", 158 | "episode_number": "string" 159 | }, 160 | 161 | "dupes": { 162 | "request": "GET", 163 | "url_format": "{search_url}&imdbId={imdb}", 164 | 165 | "parse_json": { 166 | "top_lvl": "data", 167 | "torrent_details": "attributes" 168 | }, 169 | 170 | "different_cuts": "100", 171 | "2160p_remux": 100, 172 | "2160p_encode": 100, 173 | "1080p_remux": 100, 174 | "1080p_encode": 100, 175 | "720p_encode": 100, 176 | "else": 100 177 | }, 178 | 179 | "torrent_title_format": { 180 | "episode": { 181 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 182 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 183 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 184 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 185 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 186 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 187 | }, 188 | 189 | "movie": { 190 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 191 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 192 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 193 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 194 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 195 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 196 | } 197 | }, 198 | 199 | 200 | "banned_groups": [ 201 | "DusIcTv", 202 | "PHDM" 203 | ] 204 | 205 | } 206 | 207 | 208 | -------------------------------------------------------------------------------- /site_templates/ntelogo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ntelogo", 3 | "url": "https://ntelogo.org", 4 | "upload_form": "https://ntelogo.org/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://ntelogo.org/api/torrents/filter?api_token={api_key}", 6 | "source": "Ntelogo", 7 | "bbcode_line_break": "
", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title": "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "anon": "anonymous", 21 | "live": "live", 22 | "sd": "sd", 23 | "tvdb": "tvdb", 24 | "mal": "mal", 25 | "igdb": "igdb", 26 | "optimized": "stream", 27 | "nfo_file": "nfo", 28 | 29 | 30 | "internal": "internal", 31 | "featured": "featured", 32 | "doubleup": "doubleup", 33 | "sticky": "sticky", 34 | "freeleech": "free" 35 | 36 | }, 37 | 38 | 39 | "Required": { 40 | 41 | "torrent": "file", 42 | "name": "string", 43 | "description": "string", 44 | "mediainfo": "string", 45 | "anonymous": "string", 46 | "sd": "string", 47 | "stream": "string", 48 | 49 | "tvdb": "string", 50 | "tmdb": "string", 51 | "imdb": "string", 52 | "mal": "string", 53 | "igdb": "string", 54 | 55 | 56 | "internal": "string", 57 | "free": "string", 58 | "featured": "string", 59 | "doubleup": "string", 60 | "sticky": "string", 61 | 62 | 63 | 64 | "category_id": { 65 | "1": "movie", 66 | "2": "episode" 67 | }, 68 | 69 | 70 | "type_id": { 71 | "1": { 72 | "bluray_encode": 2, 73 | "bluray_disc": 2, 74 | "bluray_remux": 2 75 | }, 76 | "2": { 77 | "webrip": 1 78 | }, 79 | "3": { 80 | "webdl": 1 81 | }, 82 | "23": { 83 | "hdtv": 1 84 | }, 85 | "10": { 86 | "dvd_remux": 2, 87 | "dvd_disc": 2 88 | } 89 | }, 90 | 91 | 92 | "resolution_id": { 93 | "1": { 94 | "4360p": 1 95 | }, 96 | "2": { 97 | "2160p": 1 98 | }, 99 | "3": { 100 | "1080p": 1 101 | }, 102 | "4": { 103 | "1080i": 1 104 | }, 105 | "5": { 106 | "720p": 1 107 | }, 108 | "6": { 109 | "576p": 1 110 | }, 111 | "7": { 112 | "576i": 1 113 | }, 114 | "8": { 115 | "480p": 1 116 | }, 117 | "9": { 118 | "480i": 1 119 | }, 120 | "10": { 121 | "other": 1 122 | } 123 | } 124 | }, 125 | 126 | 127 | "Optional": { 128 | "nfo": "file", 129 | "season_number": "string", 130 | "episode_number": "string" 131 | }, 132 | 133 | 134 | "dupes": { 135 | 136 | "request": "GET", 137 | "url_format": "{search_url}&imdbId={imdb}", 138 | 139 | "parse_json": { 140 | "top_lvl": "data", 141 | "torrent_details": "attributes" 142 | }, 143 | 144 | "different_cuts": "100", 145 | "2160p_remux": 1, 146 | "2160p_encode": 1, 147 | "1080p_remux": 1, 148 | "1080p_encode": 2, 149 | "720p_encode": 1, 150 | "else": 1 151 | }, 152 | 153 | 154 | "torrent_title_format": { 155 | "episode": { 156 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 157 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 158 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 159 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 160 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 161 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 162 | }, 163 | "movie": { 164 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 165 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 166 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 167 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 168 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 169 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 170 | } 171 | }, 172 | 173 | 174 | "banned_groups": [ 175 | "group_1_optional", 176 | "group_2_optional" 177 | ] 178 | } 179 | 180 | 181 | -------------------------------------------------------------------------------- /site_templates/racing4everyone.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Racing4Everyone", 3 | "url": "https://racing4everyone.eu", 4 | "upload_form": "https://racing4everyone.eu/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://racing4everyone.eu/api/torrents/filter?api_token={api_key}", 6 | "source": "R4E", 7 | "bbcode_line_break": "\n", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | 14 | "type": "category_id", 15 | "resolution": "type_id", 16 | "resolution_id": "resolution_id", 17 | "description": "description", 18 | "mediainfo": "mediainfo", 19 | 20 | "tmdb": "tmdb", 21 | "imdb": "imdb", 22 | "tvdb": "tvdb", 23 | "mal": "mal", 24 | "igdb": "igdb", 25 | 26 | "anon": "anonymous", 27 | "sd": "sd", 28 | "optimized": "stream", 29 | 30 | "internal": "internal", 31 | "featured": "featured", 32 | "doubleup": "doubleup", 33 | "sticky": "sticky", 34 | "freeleech": "free" 35 | }, 36 | 37 | 38 | 39 | "Required": { 40 | "torrent": "file", 41 | "name": "string", 42 | "description": "string", 43 | "mediainfo": "string", 44 | 45 | "tvdb": "string", 46 | "tmdb": "string", 47 | "imdb": "string", 48 | "mal": "string", 49 | "igdb": "string", 50 | 51 | "anonymous": "string", 52 | "sd": "string", 53 | "stream": "string", 54 | 55 | "category_id": { 56 | "70": "Movie", 57 | "79": "TV Show", 58 | "72": "AMA Pro Motocross", 59 | "73": "AMA Supercross", 60 | "76": "ARCA", 61 | "77": "Blancpain GT", 62 | "1": "BTCC", 63 | "60": "Drag Racing", 64 | "3": "DTM", 65 | "61": "ERC", 66 | "81": "Extreme E", 67 | "67": "Formula 1", 68 | "22": "Formula 2", 69 | "68": "Formula 3", 70 | "23": "Formula E", 71 | "62": "Indycar Series", 72 | "26": "MotoGP-2-3-E", 73 | "31": "Nascar", 74 | "25": "Other Bikes", 75 | "69": "Other Cars", 76 | "78": "Porsche Supercar", 77 | "32": "Stock Car Brazil", 78 | "63": "Supercars", 79 | "80": "Touring Cars", 80 | "57": "WEC", 81 | "50": "WRC", 82 | "56": "WRX", 83 | "71": "WSBK", 84 | "74": "WTCR", 85 | "58": "W Series", 86 | "55": "Season Reviews", 87 | "66": "Documentaries", 88 | "2": "TV Documentaries", 89 | "24": "Misc" 90 | }, 91 | 92 | "internal": "string", 93 | "free": "string", 94 | "featured": "string", 95 | "doubleup": "string", 96 | "sticky": "string", 97 | 98 | "type_id": { 99 | "17": { 100 | "2160p": 1 101 | }, 102 | 103 | "14": { 104 | "1080i": 1 105 | }, 106 | 107 | "9": { 108 | "1080p" : 1 109 | }, 110 | 111 | "11": { 112 | "720p" : 1 113 | }, 114 | 115 | "13": { 116 | "576p": 2, 117 | "576i": 2, 118 | "480p": 2, 119 | "480i": 2 120 | } 121 | }, 122 | 123 | 124 | "resolution_id": { 125 | "1": { 126 | "4360p": 1 127 | }, 128 | 129 | "2": { 130 | "2160p": 1 131 | }, 132 | 133 | "3": { 134 | "1080p" : 1 135 | }, 136 | 137 | "4": { 138 | "1080i" : 1 139 | }, 140 | 141 | "5": { 142 | "720p" : 1 143 | }, 144 | 145 | "6": { 146 | "576p" : 1 147 | }, 148 | 149 | "7": { 150 | "576i": 1 151 | }, 152 | 153 | "8": { 154 | "480p": 1 155 | }, 156 | 157 | "9": { 158 | "480i": 1 159 | }, 160 | 161 | "10": { 162 | "other": 2, 163 | "sd": 2 164 | } 165 | 166 | } 167 | 168 | 169 | }, 170 | 171 | 172 | "Optional": { 173 | "none": "none", 174 | "season_number": "string", 175 | "episode_number": "string" 176 | }, 177 | 178 | 179 | 180 | "dupes": { 181 | 182 | "request": "GET", 183 | "url_format": "{search_url}&imdb={imdb}", 184 | 185 | "parse_json": { 186 | "top_lvl": "data", 187 | "torrent_details": "attributes" 188 | } 189 | }, 190 | 191 | "torrent_title_format": { 192 | "episode": { 193 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 194 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 195 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 196 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 197 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 198 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 199 | }, 200 | 201 | "movie": { 202 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 203 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 204 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 205 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 206 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 207 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 208 | } 209 | } 210 | } 211 | 212 | 213 | -------------------------------------------------------------------------------- /site_templates/telly.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Telly", 3 | "url": "https://telly.wtf", 4 | "upload_form": "https://telly.wtf/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://telly.wtf/api/torrents/filter?api_token={api_key}", 6 | "source": "Telly", 7 | "bbcode_line_break": "
", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | 17 | "source": "type_id", 18 | "resolution": "resolution_id", 19 | "tmdb": "tmdb", 20 | "imdb": "imdb", 21 | "tvdb": "tvdb", 22 | "mal": "mal", 23 | "igdb": "igdb", 24 | "anon": "anonymous", 25 | "live": "live", 26 | "sd": "sd", 27 | "optimized": "stream", 28 | "nfo_file": "nfo", 29 | 30 | "internal": "internal", 31 | "featured": "featured", 32 | "doubleup": "doubleup", 33 | "sticky": "sticky", 34 | "freeleech": "free" 35 | 36 | }, 37 | 38 | "Required": { 39 | "torrent": "file", 40 | "name": "string", 41 | "description": "string", 42 | "mediainfo": "string", 43 | "anonymous": "string", 44 | "sd": "string", 45 | "stream": "string", 46 | 47 | "tvdb": "string", 48 | "tmdb": "string", 49 | "imdb": "string", 50 | "mal": "string", 51 | "igdb": "string", 52 | 53 | "internal": "string", 54 | "free": "string", 55 | "featured": "string", 56 | "doubleup": "string", 57 | "sticky": "string", 58 | 59 | "category_id": { 60 | "1": "movie", 61 | "2": "episode" 62 | }, 63 | 64 | "type_id": { 65 | "1": { 66 | "disc": 1, 67 | "bluray_disc": 2, 68 | "dvd_disc": 2 69 | }, 70 | "2": { 71 | "remux": 1, 72 | "bluray_remux": 2, 73 | "dvd_remux": 2 74 | }, 75 | "3": { 76 | "bluray_encode": 1 77 | }, 78 | "4": { 79 | "webdl": 1 80 | }, 81 | "5": { 82 | "webrip": 1 83 | }, 84 | "6": { 85 | "hdtv": 1 86 | } 87 | }, 88 | 89 | 90 | "resolution_id": { 91 | "1": { 92 | "4360p": 1 93 | }, 94 | 95 | "2": { 96 | "2160p": 1 97 | }, 98 | 99 | "3": { 100 | "1080p" : 1 101 | }, 102 | 103 | "4": { 104 | "1080i" : 1 105 | }, 106 | 107 | "5": { 108 | "720p" : 1 109 | }, 110 | 111 | "6": { 112 | "576p" : 1 113 | }, 114 | 115 | "7": { 116 | "576i": 1 117 | }, 118 | 119 | "8": { 120 | "480p": 1 121 | }, 122 | 123 | "9": { 124 | "480i": 1 125 | }, 126 | 127 | "10": { 128 | "other": 1 129 | } 130 | 131 | } 132 | }, 133 | 134 | "Optional": { 135 | "nfo_file": "file", 136 | "season_number": "string", 137 | "episode_number": "string" 138 | }, 139 | 140 | "dupes": { 141 | "request": "GET", 142 | "url_format": "{search_url}&imdbId={imdb}", 143 | 144 | "parse_json": { 145 | "top_lvl": "data", 146 | "torrent_details": "attributes" 147 | }, 148 | 149 | "different_cuts": "1", 150 | "2160p_remux": 1, 151 | "2160p_encode": 1, 152 | "1080p_remux": 1, 153 | "1080p_encode": 100, 154 | "720p_encode": 100, 155 | "else": 1 156 | }, 157 | 158 | "torrent_title_format": { 159 | "episode": { 160 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 161 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 162 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 163 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 164 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 165 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 166 | }, 167 | 168 | "movie": { 169 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 170 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 171 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 172 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 173 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 174 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 175 | } 176 | }, 177 | 178 | 179 | 180 | "banned_groups": [ 181 | "YIFY" 182 | ] 183 | 184 | } 185 | -------------------------------------------------------------------------------- /site_templates/uncutflixhd.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UNCUTFLIXHD", 3 | "url": "https://uncutflixhd.com", 4 | "upload_form": "https://uncutflixhd.com/api/torrents/upload?api_token={api_key}", 5 | "torrents_search": "https://uncutflixhd.com/api/torrents/filter?api_token={api_key}", 6 | "source": "UNCUTFLIXHD", 7 | "bbcode_line_break": "\n", 8 | 9 | 10 | "translation": { 11 | "dot_torrent": "torrent", 12 | "torrent_title" : "name", 13 | "description": "description", 14 | "mediainfo": "mediainfo", 15 | "type": "category_id", 16 | "source": "type_id", 17 | "resolution": "resolution_id", 18 | "tmdb": "tmdb", 19 | "imdb": "imdb", 20 | "anon": "anonymous", 21 | "live": "live", 22 | "sd": "sd", 23 | "tvdb": "tvdb", 24 | "mal": "mal", 25 | "igdb": "igdb", 26 | "optimized": "stream", 27 | "nfo_file": "nfo", 28 | 29 | "internal": "internal", 30 | "featured": "featured", 31 | "doubleup": "doubleup", 32 | "sticky": "sticky", 33 | "freeleech": "free" 34 | 35 | }, 36 | 37 | 38 | "Required": { 39 | "torrent": "file", 40 | "name": "string", 41 | "description": "string", 42 | "mediainfo": "string", 43 | "anonymous": "string", 44 | "sd": "string", 45 | "stream": "string", 46 | 47 | "tvdb": "string", 48 | "tmdb": "string", 49 | "imdb": "string", 50 | "mal": "string", 51 | "igdb": "string", 52 | 53 | "internal": "string", 54 | "free": "string", 55 | "featured": "string", 56 | "doubleup": "string", 57 | "sticky": "string", 58 | 59 | "category_id": { 60 | "1": "movie", 61 | "2": "episode" 62 | }, 63 | 64 | "type_id": { 65 | "1": { 66 | "disc": 1, 67 | "bluray_disc": 2, 68 | "dvd_disc": 2 69 | }, 70 | "2": { 71 | "remux": 1, 72 | "bluray_remux": 2, 73 | "dvd_remux": 2 74 | }, 75 | "3": { 76 | "bluray_encode": 1 77 | }, 78 | "4": { 79 | "webdl": 1 80 | }, 81 | "5": { 82 | "webrip": 1 83 | }, 84 | "6": { 85 | "hdtv": 1 86 | } 87 | }, 88 | 89 | 90 | "resolution_id": { 91 | "1": { 92 | "4360p": 1 93 | }, 94 | 95 | "2": { 96 | "2160p": 1 97 | }, 98 | 99 | "3": { 100 | "1080p" : 1 101 | }, 102 | 103 | "4": { 104 | "1080i" : 1 105 | }, 106 | 107 | "5": { 108 | "720p" : 1 109 | }, 110 | 111 | "6": { 112 | "576p" : 1 113 | }, 114 | 115 | "7": { 116 | "576i": 1 117 | }, 118 | 119 | "8": { 120 | "480p": 1 121 | }, 122 | 123 | "9": { 124 | "480i": 1 125 | }, 126 | 127 | "10": { 128 | "other": 1 129 | } 130 | 131 | } 132 | }, 133 | 134 | "Optional": { 135 | "nfo_file": "file", 136 | "season_number": "string", 137 | "episode_number": "string" 138 | }, 139 | 140 | "dupes": { 141 | "request": "GET", 142 | "url_format": "{search_url}&imdbId={imdb}", 143 | 144 | "parse_json": { 145 | "top_lvl": "data", 146 | "torrent_details": "attributes" 147 | }, 148 | 149 | "different_cuts": "1", 150 | "2160p_remux": 1, 151 | "2160p_encode": 1, 152 | "1080p_remux": 1, 153 | "1080p_encode": 100, 154 | "720p_encode": 100, 155 | "else": 1 156 | }, 157 | 158 | "torrent_title_format": { 159 | "episode": { 160 | "bluray_disc": "{title} {year} {s00e00} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 161 | "bluray_remux": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {audio_channels} {atmos} {release_group}", 162 | "bluray_encode": "{title} {year} {s00e00} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 163 | "web": "{title} {year} {s00e00} {repack} {screen_size} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 164 | "hdtv": "{title} {year} {s00e00} {repack} {screen_size} {hybrid} {source} {audio_codec} {audio_channels} {video_codec} {release_group}", 165 | "dvd": "{title} {year} {s00e00} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 166 | }, 167 | 168 | "movie": { 169 | "bluray_disc": "{title} {year} {edition} {repack} {screen_size} {region} {source} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 170 | "bluray_remux": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {remux} {hdr} {dv} {video_codec} {audio_codec} {atmos} {audio_channels} {release_group}", 171 | "bluray_encode": "{title} {year} {edition} {repack} {screen_size} {uhd} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 172 | "web": "{title} {year} {edition} {repack} {screen_size} {hybrid} {web_source} {web_type} {audio_codec} {atmos} {audio_channels} {hdr} {dv} {video_codec} {release_group}", 173 | "hdtv": "{title} {year} {edition} {repack} {screen_size} {hybrid} {source} {audio_codec} {atmos} {audio_channels} {video_codec} {release_group}", 174 | "dvd": "{title} {year} {edition} {repack} {screen_size} {region} {hybrid} {source} {video_codec} {remux} {audio_codec} {atmos} {audio_channels} {release_group}" 175 | } 176 | }, 177 | 178 | "banned_groups": [] 179 | 180 | } 181 | 182 | 183 | --------------------------------------------------------------------------------