├── .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 |
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 |
--------------------------------------------------------------------------------