mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-23 19:21:12 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68c76872ae | ||
|
|
bae4d0c45c | ||
|
|
da2d76fc2c | ||
|
|
f6698516af | ||
|
|
a015fd1ccb | ||
|
|
6b09e4e297 | ||
|
|
47cf31ab10 | ||
|
|
827efd6633 | ||
|
|
aba057a8c9 | ||
|
|
eacb063164 | ||
|
|
d928c1f769 | ||
|
|
520af41f5f | ||
|
|
5615f6251a | ||
|
|
018e570199 |
68
README.md
68
README.md
@@ -1,41 +1,63 @@
|
||||
# Profilarr
|
||||
|
||||
Profilarr is a Python-based tool that enables seamless synchronization of custom formats and quality profiles in Radarr / Sonarr. It's designed to aid users in sharing / importing custom formats & quality profiles seamlessly.
|
||||
Profilarr is a Python-based tool designed to add import/export functionality to the \*arr suite. It offers a user-friendly way to export and import custom formats and quality profiles between Radarr and Sonarr installations.
|
||||
|
||||
## ⚠️ Before Continuing
|
||||
|
||||
- **This tool will overwrite any custom formats in your Radarr installation that have the same name.**
|
||||
- **This tool will overwrite any custom formats in your \*arr installation that have the same name.**
|
||||
- **Custom Formats MUST be imported before syncing any premade profile.**
|
||||
- **Always back up your Radarr and Sonarr configurations before using Profilarr to avoid unintended data loss.** (Seriously, do it. Even I've lost data to this tool because I forgot to back up my configs.)
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.x installed. You can download it from [python.org](https://www.python.org/downloads/).
|
||||
- Radarr / Sonarr
|
||||
|
||||
### Steps
|
||||
|
||||
1. Download the Profilarr zip file from the release section.
|
||||
2. Extract its contents into a folder.
|
||||
3. Open `import.py` in a text editor of your choice.
|
||||
- Add your Radarr API key to the designated section.
|
||||
- Modify the Base URL if needed
|
||||
4. Save the changes and close the text editor.
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
1. Open a terminal or command prompt.
|
||||
2. Navigate to the directory where you extracted Profilarr.
|
||||
3. Run the command `python import_cf.py` to import the necessary custom formats.
|
||||
4. Run the command `python import_qf.py` and follow the prompts to choose and import your desired profile.
|
||||
|
||||
## 📦 Dependencies
|
||||
### 📦 Dependencies
|
||||
|
||||
- `requests` (Install using `pip install requests`)
|
||||
|
||||
## ⚙️ Configuration
|
||||
### Initial Setup
|
||||
|
||||
### Radarr API Key and Base URL
|
||||
1. Download the latest Profilarr package from the release section.
|
||||
2. Extract its contents into a folder.
|
||||
3. Open the `config.json` file in a text editor.
|
||||
- Add your Radarr / Sonarr API key and modify the base URL as necessary.
|
||||
- If importing / exporting, only change the master installation's API key and base URL.
|
||||
- If syncing, add the API keys and base URLs of all instances you want to sync.
|
||||
- The master install will be the one that all other instances sync to.
|
||||
4. Save the changes.
|
||||
|
||||
- Your Radarr API Key and Base URL can be configured in the `import.py` file.
|
||||
- The Base URL should be in the format `http://localhost:7878` unless you have a different host or port.
|
||||
## 🚀 Usage
|
||||
|
||||
### Exporting
|
||||
|
||||
1. Run `python exportarr.py` in your command line interface.
|
||||
2. Follow the on-screen prompts to select the app (Radarr or Sonarr) and the data (Custom Formats or Quality Profiles) you want to export.
|
||||
3. Exported data will be saved in respective directories within the tool's folder.
|
||||
|
||||
### Importing
|
||||
|
||||
1. Run `python importarr.py` in your command line interface.
|
||||
2. Follow the on-screen prompts to select the app and the data you want to import.
|
||||
3. Choose the specific file for Custom Formats or select a profile for Quality Profiles.
|
||||
4. The data will be imported to your selected Radarr or Sonarr installation.
|
||||
|
||||
### Syncing
|
||||
|
||||
1. Run `python syncarr.py` in your command line interface.
|
||||
2. The script will automatically export data from the master instance and import it to all other instances specified in `config.json`.
|
||||
3. This feature is designed to manage multiple Radarr/Sonarr instances, syncing profiles and formats seamlessly.
|
||||
|
||||
### Radarr and Sonarr Compatibility
|
||||
|
||||
- Custom formats _can_ be imported and exported between Radarr and Sonarr (but might not work as expected).
|
||||
- Quality profiles are not directly interchangeable between Radarr and Sonarr due to differences in quality source names. If you want to use the same profile in both apps, you will need to manually edit the profile's quality source names before importing it.
|
||||
|
||||
## 🌟 Upcoming Features
|
||||
|
||||
- **Lidarr Support:** Expand functionality to include Lidarr, allowing users to manage music quality profiles and custom formats.
|
||||
- **User Interface (UI):** Development of a graphical user interface (GUI) for easier and more intuitive interaction with Profilarr. This UI will cater to users who prefer graphical over command-line interactions.
|
||||
- **Automatic Updates:** Implement an auto-update mechanism for Profilarr, ensuring users always have access to the latest features, improvements, and bug fixes without manual intervention.
|
||||
|
||||
38
config.json
Normal file
38
config.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"master": {
|
||||
"sonarr": {
|
||||
"base_url": "http://localhost:8989",
|
||||
"api_key": "API_KEY_HERE"
|
||||
},
|
||||
"radarr": {
|
||||
"base_url": "http://localhost:7878",
|
||||
"api_key": "API_KEY_HERE"
|
||||
}
|
||||
},
|
||||
"extra_installations": {
|
||||
"radarr": [
|
||||
{
|
||||
"name": "extra_radarr1",
|
||||
"base_url": "http://localhost:7788",
|
||||
"api_key": "API_KEY_HERE"
|
||||
},
|
||||
{
|
||||
"name": "extra_radarr2",
|
||||
"base_url": "http://localhost:7789",
|
||||
"api_key": "API_KEY_HERE"
|
||||
}
|
||||
],
|
||||
"sonarr": [
|
||||
{
|
||||
"name": "extra_sonarr1",
|
||||
"base_url": "http://localhost:8988",
|
||||
"api_key": "API_KEY_HERE"
|
||||
},
|
||||
{
|
||||
"name": "extra_sonarr2",
|
||||
"base_url": "http://localhost:8987",
|
||||
"api_key": "API_KEY_HERE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
11958
custom_formats/Custom Formats (Radarr).json
Normal file
11958
custom_formats/Custom Formats (Radarr).json
Normal file
File diff suppressed because it is too large
Load Diff
10390
custom_formats/Custom Formats (Sonarr).json
Normal file
10390
custom_formats/Custom Formats (Sonarr).json
Normal file
File diff suppressed because it is too large
Load Diff
11879
custom_formats/cf.json
11879
custom_formats/cf.json
File diff suppressed because it is too large
Load Diff
85
export.py
85
export.py
@@ -1,85 +0,0 @@
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
import re
|
||||
|
||||
# Define constants
|
||||
base_url = "http://localhost:7878"
|
||||
api_key = "API_GOES_HERE"
|
||||
|
||||
# Define parameters and headers
|
||||
params = {"apikey": api_key}
|
||||
headers = {"X-Api-Key": api_key}
|
||||
files = {'file': ('', '')} # Empty file to force multipart/form-data
|
||||
|
||||
# Login
|
||||
login_url = f"{base_url}/login"
|
||||
response = requests.get(login_url, params=params, headers=headers, files=files)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"Login Failed! (HTTP {response.status_code})")
|
||||
print("Response Content: ", response.content)
|
||||
exit()
|
||||
|
||||
def export_cf():
|
||||
custom_format_url = f"{base_url}/api/v3/customformat"
|
||||
response = requests.get(custom_format_url, params=params, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
# Remove 'id' from each custom format
|
||||
for custom_format in data:
|
||||
custom_format.pop('id', None)
|
||||
|
||||
# Ensure the ./custom_formats directory exists
|
||||
if not os.path.exists('./custom_formats'):
|
||||
os.makedirs('./custom_formats')
|
||||
|
||||
# Save to JSON file
|
||||
with open('./custom_formats/cf.json', 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
print("Custom Formats have been saved to './custom_formats/cf.json'")
|
||||
else:
|
||||
print(f"Failed to retrieve custom formats! (HTTP {response.status_code})")
|
||||
print("Response Content: ", response.content)
|
||||
|
||||
def sanitize_filename(filename):
|
||||
# Replace any characters not allowed in filenames with _
|
||||
sanitized_filename = re.sub(r'[\\/*?:"<>|]', '_', filename)
|
||||
return sanitized_filename
|
||||
|
||||
def export_qf():
|
||||
response = requests.get(f"{base_url}/api/v3/qualityprofile", params=params, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
quality_profiles = response.json()
|
||||
|
||||
# Ensure the ./profiles directory exists
|
||||
if not os.path.exists('./profiles'):
|
||||
os.makedirs('./profiles')
|
||||
|
||||
# Process each profile separately
|
||||
for profile in quality_profiles:
|
||||
profile.pop('id', None) # Remove the 'id' field
|
||||
|
||||
# Use the name of the profile to create a filename
|
||||
profile_name = profile.get('name', 'unnamed_profile') # Use a default name if the profile has no name
|
||||
profile_name = sanitize_filename(profile_name) # Sanitize the filename
|
||||
profile_filename = f"{profile_name}.json"
|
||||
profile_filepath = os.path.join('./profiles', profile_filename)
|
||||
|
||||
# Save the individual profile to a file as a single-element array
|
||||
with open(profile_filepath, 'w') as file:
|
||||
json.dump([profile], file, indent=4) # Note the [profile], it will make it an array with a single element
|
||||
|
||||
print("Quality profiles have been successfully saved to the ./profiles directory")
|
||||
else:
|
||||
print("Failed to retrieve quality profiles!")
|
||||
print("Response Content: ", response.content.decode('utf-8'))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
export_cf()
|
||||
export_qf()
|
||||
158
exportarr.py
Normal file
158
exportarr.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
import re
|
||||
|
||||
# ANSI escape sequences for colors
|
||||
class Colors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
# Load configuration for main app
|
||||
with open('config.json', 'r') as config_file:
|
||||
config = json.load(config_file)['master']
|
||||
|
||||
def get_user_choice():
|
||||
choice = input("Enter an app to export from (radarr/sonarr): ").lower()
|
||||
while choice not in ["radarr", "sonarr"]:
|
||||
print(Colors.FAIL + "Invalid input. Please enter either 'radarr' or 'sonarr'." + Colors.ENDC)
|
||||
choice = input("Enter the source (radarr/sonarr): ").lower()
|
||||
print()
|
||||
return choice
|
||||
|
||||
def get_export_choice():
|
||||
print(Colors.HEADER + "Choose what to export:" + Colors.ENDC)
|
||||
print("1. Custom Formats")
|
||||
print("2. Quality Profiles")
|
||||
print("3. Both")
|
||||
choice = input("Enter your choice (1/2/3): ").strip()
|
||||
while choice not in ["1", "2", "3"]:
|
||||
print(Colors.FAIL + "Invalid input. Please enter 1, 2, or 3." + Colors.ENDC)
|
||||
choice = input("Enter your choice (1/2/3): ").strip()
|
||||
print()
|
||||
return choice
|
||||
|
||||
def get_app_config(source):
|
||||
app_config = config[source]
|
||||
return app_config['base_url'], app_config['api_key']
|
||||
|
||||
def sanitize_filename(filename):
|
||||
sanitized_filename = re.sub(r'[\\/*?:"<>|]', '_', filename)
|
||||
return sanitized_filename
|
||||
|
||||
def handle_response_errors(response):
|
||||
if response.status_code == 401:
|
||||
print(Colors.FAIL + "Authentication error: Invalid API key." + Colors.ENDC)
|
||||
elif response.status_code == 403:
|
||||
print(Colors.FAIL + "Forbidden: Access is denied." + Colors.ENDC)
|
||||
else:
|
||||
print(Colors.FAIL + f"An error occurred! (HTTP {response.status_code})" + Colors.ENDC)
|
||||
print("Response Content: ", response.content.decode('utf-8'))
|
||||
|
||||
def print_saved_items(items, item_type):
|
||||
if len(items) > 10:
|
||||
items_to_display = items[:10]
|
||||
for item in items_to_display:
|
||||
print(f" - {item}")
|
||||
print(f"... and {len(items) - 10} more.")
|
||||
else:
|
||||
for item in items:
|
||||
print(f" - {item}")
|
||||
|
||||
def ensure_directory_exists(directory):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
print(Colors.OKBLUE + f"Created directory: {directory}" + Colors.ENDC)
|
||||
|
||||
def export_cf(source, save_path='./custom_formats'):
|
||||
ensure_directory_exists(save_path) # Ensure the directory exists with the given save_path
|
||||
|
||||
base_url, api_key = get_app_config(source)
|
||||
headers = {"X-Api-Key": api_key}
|
||||
params = {"apikey": api_key}
|
||||
|
||||
print(Colors.OKBLUE + f"Attempting to access {source.capitalize()} at {base_url}" + Colors.ENDC)
|
||||
|
||||
custom_format_url = f"{base_url}/api/v3/customformat"
|
||||
|
||||
try:
|
||||
response = requests.get(custom_format_url, params=params, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(Colors.OKGREEN + f"Found {len(data)} custom formats." + Colors.ENDC)
|
||||
|
||||
saved_formats = []
|
||||
for custom_format in data:
|
||||
custom_format.pop('id', None)
|
||||
saved_formats.append(custom_format['name'])
|
||||
|
||||
file_path = f'{save_path}/Custom Formats ({source.capitalize()}).json'
|
||||
with open(file_path, 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
print_saved_items(saved_formats, "Custom Formats")
|
||||
print(Colors.OKGREEN + f"Saved to '{file_path}'" + Colors.ENDC)
|
||||
print()
|
||||
else:
|
||||
handle_response_errors(response)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(Colors.FAIL + f"Failed to connect to {source.capitalize()}! Please check if it's running and accessible." + Colors.ENDC)
|
||||
|
||||
|
||||
|
||||
def export_qf(source, save_path='./profiles'):
|
||||
ensure_directory_exists(save_path) # Ensure the directory exists with the given save_path
|
||||
|
||||
base_url, api_key = get_app_config(source)
|
||||
headers = {"X-Api-Key": api_key}
|
||||
params = {"apikey": api_key}
|
||||
|
||||
print(Colors.OKBLUE + f"Attempting to access {source.capitalize()} at {base_url}" + Colors.ENDC)
|
||||
|
||||
try:
|
||||
response = requests.get(f"{base_url}/api/v3/qualityprofile", params=params, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
quality_profiles = response.json()
|
||||
print(Colors.OKGREEN + f"Found {len(quality_profiles)} quality profiles." + Colors.ENDC)
|
||||
|
||||
if not os.path.exists('./profiles'):
|
||||
os.makedirs('./profiles')
|
||||
|
||||
saved_profiles = []
|
||||
for profile in quality_profiles:
|
||||
profile.pop('id', None)
|
||||
profile_name = profile.get('name', 'unnamed_profile')
|
||||
profile_name = sanitize_filename(profile_name)
|
||||
profile_filename = f"{profile_name} ({source.capitalize()}).json"
|
||||
profile_filepath = os.path.join(save_path, profile_filename)
|
||||
saved_profiles.append(profile_name)
|
||||
|
||||
with open(profile_filepath, 'w') as file:
|
||||
json.dump([profile], file, indent=4)
|
||||
print_saved_items(saved_profiles, "Quality Profiles")
|
||||
print(Colors.OKGREEN + f"Saved to '{profile_filepath}'" + Colors.ENDC)
|
||||
print()
|
||||
else:
|
||||
handle_response_errors(response)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(Colors.FAIL + f"Failed to connect to {source.capitalize()}! Please check if it's running and accessible." + Colors.ENDC)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
user_choice = get_user_choice()
|
||||
export_choice = get_export_choice()
|
||||
|
||||
if export_choice in ["1", "3"]:
|
||||
export_cf(user_choice)
|
||||
if export_choice in ["2", "3"]:
|
||||
export_qf(user_choice)
|
||||
60
import_cf.py
60
import_cf.py
@@ -1,60 +0,0 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
# Define constants
|
||||
base_url = "http://localhost:7878" # Update to your Radarr URL
|
||||
api_key = "API_GOES_HERE" # Update to your Radarr API Key
|
||||
|
||||
# Define headers
|
||||
headers = {"X-Api-Key": api_key}
|
||||
|
||||
def get_existing_formats():
|
||||
get_url = f"{base_url}/api/v3/customformat"
|
||||
print(f"Getting existing formats from {get_url}")
|
||||
response = requests.get(get_url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
with open('temp_cf.json', 'w') as temp_file:
|
||||
json.dump(response.json(), temp_file)
|
||||
else:
|
||||
print(f"Failed to retrieve existing custom formats from {get_url}! (HTTP {response.status_code})")
|
||||
print("Response Content: \n", response.content.decode())
|
||||
exit(1)
|
||||
|
||||
def import_custom_formats():
|
||||
with open('temp_cf.json', 'r') as temp_file:
|
||||
existing_formats = json.load(temp_file)
|
||||
existing_names_to_id = {format['name']: format['id'] for format in existing_formats}
|
||||
|
||||
with open('custom_formats/cf.json', 'r') as import_file:
|
||||
import_formats = json.load(import_file)
|
||||
|
||||
for format in import_formats:
|
||||
format_name = format['name']
|
||||
if format_name in existing_names_to_id:
|
||||
format_id = existing_names_to_id[format_name]
|
||||
put_url = f"{base_url}/api/v3/customformat/{format_id}"
|
||||
print(f"Updating existing format {format_name} using PUT at {put_url}")
|
||||
format['id'] = format_id # Include the id in the request body
|
||||
response = requests.put(put_url, json=format, headers=headers)
|
||||
|
||||
if response.status_code in [200, 201, 202]:
|
||||
print(f"Successfully updated custom format {format_name}! (HTTP {response.status_code})")
|
||||
else:
|
||||
print(f"Failed to update custom format {format_name} at {put_url}! (HTTP {response.status_code})")
|
||||
print("Response Content: \n", response.content.decode())
|
||||
|
||||
else:
|
||||
post_url = f"{base_url}/api/v3/customformat"
|
||||
print(f"Creating new format {format_name} using POST at {post_url}")
|
||||
response = requests.post(post_url, json=format, headers=headers)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
print(f"Successfully created custom format {format_name}! (HTTP {response.status_code})")
|
||||
else:
|
||||
print(f"Failed to create custom format {format_name} at {post_url}! (HTTP {response.status_code})")
|
||||
print("Response Content: \n", response.content.decode())
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_existing_formats()
|
||||
import_custom_formats()
|
||||
115
import_qf.py
115
import_qf.py
@@ -1,115 +0,0 @@
|
||||
import json
|
||||
import requests
|
||||
import os # For deleting the temporary file
|
||||
|
||||
# Define constants
|
||||
base_url = "http://localhost:7878" # Update to your Radarr URL
|
||||
api_key = "API_GOES_HERE" # Update to your Radarr API Key
|
||||
|
||||
# Define headers
|
||||
params = {"apikey": api_key}
|
||||
headers = {"X-Api-Key": api_key}
|
||||
|
||||
def cf_import_sync():
|
||||
custom_format_url = f"{base_url}/api/v3/customformat"
|
||||
response = requests.get(custom_format_url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
with open('custom_formats.json', 'w') as file:
|
||||
json.dump(data, file, indent=4)
|
||||
print("Custom Formats have been saved to 'custom_formats.json'")
|
||||
return True
|
||||
else:
|
||||
print(f"Failed to retrieve custom formats! (HTTP {response.status_code})")
|
||||
print("Response Content: ", response.content.decode('utf-8'))
|
||||
return False
|
||||
|
||||
|
||||
def import_qf():
|
||||
# Call cf_import_sync first
|
||||
cf_import_sync()
|
||||
|
||||
profile_dir = './profiles'
|
||||
profiles = [f for f in os.listdir(profile_dir) if f.endswith('.json')]
|
||||
|
||||
# Prompt user to select a profile
|
||||
print("Available Profiles:")
|
||||
for i, profile in enumerate(profiles, 1):
|
||||
print(f"{i}. {profile}")
|
||||
|
||||
selection = input("Please enter the number of the profile you want to import: ")
|
||||
|
||||
try:
|
||||
selected_file = profiles[int(selection) - 1]
|
||||
except (ValueError, IndexError):
|
||||
print("Invalid selection, please enter a valid number.")
|
||||
return
|
||||
|
||||
# Load the selected profile
|
||||
with open(os.path.join(profile_dir, selected_file), 'r') as file:
|
||||
try:
|
||||
quality_profiles = json.load(file)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error loading selected profile: {e}")
|
||||
return
|
||||
|
||||
# Load custom formats
|
||||
try:
|
||||
with open('custom_formats.json', 'r') as file:
|
||||
custom_formats_data = json.load(file)
|
||||
custom_formats = {format['name']: format['id'] for format in custom_formats_data}
|
||||
except Exception as e:
|
||||
print(f"Failed to load custom formats! Error: {e}")
|
||||
return
|
||||
|
||||
# Process each profile and send requests
|
||||
for profile in quality_profiles:
|
||||
existing_format_names = set()
|
||||
if 'formatItems' in profile:
|
||||
for format_item in profile['formatItems']:
|
||||
format_name = format_item.get('name')
|
||||
if format_name:
|
||||
existing_format_names.add(format_name)
|
||||
if format_name in custom_formats:
|
||||
format_item['format'] = custom_formats[format_name]
|
||||
|
||||
for format_name, format_id in custom_formats.items():
|
||||
if format_name not in existing_format_names:
|
||||
profile.setdefault('formatItems', []).append({
|
||||
"format": format_id,
|
||||
"name": format_name,
|
||||
"score": 0
|
||||
})
|
||||
|
||||
post_url = f"{base_url}/api/v3/qualityprofile"
|
||||
response = requests.post(post_url, json=profile, params=params, headers=headers)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
print(f"Successfully added Quality Profile {profile['name']}! (HTTP {response.status_code})")
|
||||
else:
|
||||
try:
|
||||
# Assuming the response is JSON, parse it
|
||||
errors = response.json()
|
||||
|
||||
# Extract relevant information from the error message
|
||||
message = errors.get("message", "No Message Provided")
|
||||
description = errors.get("description", "No Description Provided")
|
||||
|
||||
# Format and print the error message
|
||||
print(f"Failed to add Quality Profile {profile['name']}! (HTTP {response.status_code})")
|
||||
print(f"Error Message: {message}")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# If response is not JSON, print the whole response
|
||||
print("Failed to parse error message:")
|
||||
print(response.text)
|
||||
try:
|
||||
os.remove('custom_formats.json')
|
||||
except FileNotFoundError:
|
||||
pass # File already deleted or does not exist
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import_qf()
|
||||
219
importarr.py
Normal file
219
importarr.py
Normal file
@@ -0,0 +1,219 @@
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
|
||||
# ANSI escape sequences for colors
|
||||
class Colors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
# Load configuration for main app
|
||||
with open('config.json', 'r') as config_file:
|
||||
config = json.load(config_file)
|
||||
|
||||
def print_success(message):
|
||||
print(Colors.OKGREEN + message + Colors.ENDC)
|
||||
|
||||
def print_error(message):
|
||||
print(Colors.FAIL + message + Colors.ENDC)
|
||||
|
||||
def print_connection_error():
|
||||
print(Colors.FAIL + "Failed to connect to the service! Please check if it's running and accessible." + Colors.ENDC)
|
||||
|
||||
def get_user_choice():
|
||||
choice = input("Enter the app you want to import to (radarr/sonarr): ").lower()
|
||||
while choice not in ["radarr", "sonarr"]:
|
||||
print_error("Invalid input. Please enter either 'radarr' or 'sonarr'.")
|
||||
choice = input("Enter the source (radarr/sonarr): ").lower()
|
||||
return choice
|
||||
|
||||
def get_import_choice():
|
||||
print()
|
||||
print(Colors.HEADER + "Choose what to import:" + Colors.ENDC)
|
||||
print("1. Custom Formats")
|
||||
print("2. Quality Profiles")
|
||||
choice = input("Enter your choice (1/2): ").strip()
|
||||
while choice not in ["1", "2"]:
|
||||
print_error("Invalid input. Please enter 1 or 2.")
|
||||
choice = input("Enter your choice (1/2): ").strip()
|
||||
return choice
|
||||
|
||||
def get_app_config(source):
|
||||
return config['master'][source]
|
||||
|
||||
def select_file(directory):
|
||||
files = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
|
||||
print()
|
||||
print(Colors.OKBLUE + "Available files:" + Colors.ENDC)
|
||||
for i, file in enumerate(files, 1):
|
||||
print(f"{i}. {file}")
|
||||
choice = int(input("Select a file to import: "))
|
||||
return files[choice - 1]
|
||||
|
||||
def import_custom_formats(source_config, import_path='./custom_formats', auto_select_file=False):
|
||||
headers = {"X-Api-Key": source_config['api_key']}
|
||||
get_url = f"{source_config['base_url']}/api/v3/customformat"
|
||||
try:
|
||||
response = requests.get(get_url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
existing_formats = response.json()
|
||||
existing_names_to_id = {format['name']: format['id'] for format in existing_formats}
|
||||
|
||||
files = os.listdir(import_path)
|
||||
if auto_select_file and len(files) == 1:
|
||||
selected_file = files[0]
|
||||
else:
|
||||
selected_file = select_file(import_path)
|
||||
|
||||
added_count, updated_count = 0, 0
|
||||
|
||||
with open(os.path.join(import_path, selected_file), 'r') as import_file:
|
||||
import_formats = json.load(import_file)
|
||||
|
||||
print()
|
||||
|
||||
for format in import_formats:
|
||||
format_name = format['name']
|
||||
if format_name in existing_names_to_id:
|
||||
format_id = existing_names_to_id[format_name]
|
||||
put_url = f"{source_config['base_url']}/api/v3/customformat/{format_id}"
|
||||
response = requests.put(put_url, json=format, headers=headers)
|
||||
if response.status_code in [200, 201, 202]:
|
||||
print(Colors.WARNING + f"Updating custom format '{format_name}': " + Colors.ENDC, end='')
|
||||
print_success("SUCCESS")
|
||||
updated_count += 1
|
||||
else:
|
||||
print_error(f"Updating custom format '{format_name}': FAIL")
|
||||
print(response.content.decode())
|
||||
|
||||
else:
|
||||
post_url = f"{source_config['base_url']}/api/v3/customformat"
|
||||
response = requests.post(post_url, json=format, headers=headers)
|
||||
if response.status_code in [200, 201]:
|
||||
print(Colors.OKBLUE + f"Adding custom format '{format_name}': " + Colors.ENDC, end='')
|
||||
print_success("SUCCESS")
|
||||
added_count += 1
|
||||
else:
|
||||
print_error(f"Adding custom format '{format_name}': FAIL")
|
||||
print(response.content.decode())
|
||||
|
||||
print()
|
||||
print_success(f"Successfully added {added_count} custom formats, updated {updated_count} custom formats.")
|
||||
|
||||
else:
|
||||
print_error(f"Failed to retrieve existing custom formats from {get_url}! (HTTP {response.status_code})")
|
||||
print(response.content.decode())
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print_connection_error()
|
||||
|
||||
def import_quality_profiles(source_config, import_path='./profiles'):
|
||||
headers = {"X-Api-Key": source_config['api_key']}
|
||||
try:
|
||||
cf_import_sync(source_config)
|
||||
|
||||
profile_dir = import_path
|
||||
profiles = [f for f in os.listdir(profile_dir) if f.endswith('.json')]
|
||||
|
||||
print()
|
||||
print(Colors.HEADER + "Available Profiles:" + Colors.ENDC)
|
||||
for i, profile in enumerate(profiles, 1):
|
||||
print(f"{i}. {profile}")
|
||||
print(f"{len(profiles) + 1}. Import all profiles")
|
||||
|
||||
print()
|
||||
selection = input("Please enter the number of the profile you want to import (or enter " + str(len(profiles) + 1) + " to import all): ")
|
||||
selected_files = []
|
||||
try:
|
||||
selection = int(selection)
|
||||
if selection == len(profiles) + 1:
|
||||
selected_files = profiles
|
||||
else:
|
||||
selected_files = [profiles[selection - 1]]
|
||||
except (ValueError, IndexError):
|
||||
print_error("Invalid selection, please enter a valid number.")
|
||||
return
|
||||
|
||||
for selected_file in selected_files:
|
||||
with open(os.path.join(profile_dir, selected_file), 'r') as file:
|
||||
try:
|
||||
quality_profiles = json.load(file)
|
||||
except json.JSONDecodeError as e:
|
||||
print_error(f"Error loading selected profile: {e}")
|
||||
continue
|
||||
|
||||
for profile in quality_profiles:
|
||||
existing_format_names = set()
|
||||
if 'formatItems' in profile:
|
||||
for format_item in profile['formatItems']:
|
||||
format_name = format_item.get('name')
|
||||
if format_name:
|
||||
existing_format_names.add(format_name)
|
||||
if format_name in source_config['custom_formats']:
|
||||
format_item['format'] = source_config['custom_formats'][format_name]
|
||||
|
||||
for format_name, format_id in source_config['custom_formats'].items():
|
||||
if format_name not in existing_format_names:
|
||||
profile.setdefault('formatItems', []).append({
|
||||
"format": format_id,
|
||||
"name": format_name,
|
||||
"score": 0
|
||||
})
|
||||
|
||||
post_url = f"{source_config['base_url']}/api/v3/qualityprofile"
|
||||
response = requests.post(post_url, json=profile, headers=headers)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
print_success(f"Successfully added Quality Profile {profile['name']}")
|
||||
elif response.status_code == 409:
|
||||
print_error(f"Failed to add Quality Profile {profile['name']} due to a naming conflict. Quality profile names must be unique. (HTTP {response.status_code})")
|
||||
else:
|
||||
try:
|
||||
errors = response.json()
|
||||
message = errors.get("message", "No Message Provided")
|
||||
print_error(f"Failed to add Quality Profile {profile['name']}! (HTTP {response.status_code})")
|
||||
print(message)
|
||||
except json.JSONDecodeError:
|
||||
print_error("Failed to parse error message:")
|
||||
print(response.text)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print_connection_error()
|
||||
|
||||
|
||||
def cf_import_sync(source_config):
|
||||
headers = {"X-Api-Key": source_config['api_key']}
|
||||
custom_format_url = f"{source_config['base_url']}/api/v3/customformat"
|
||||
try:
|
||||
response = requests.get(custom_format_url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
source_config['custom_formats'] = {format['name']: format['id'] for format in data}
|
||||
elif response.status_code == 401:
|
||||
print_error("Authentication error: Invalid API key. Terminating program.")
|
||||
exit(1)
|
||||
else:
|
||||
print_error(f"Failed to retrieve custom formats! (HTTP {response.status_code})")
|
||||
print(response.content.decode())
|
||||
exit(1)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print_connection_error()
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
user_choice = get_user_choice()
|
||||
source_config = get_app_config(user_choice)
|
||||
import_choice = get_import_choice()
|
||||
|
||||
if import_choice == "1":
|
||||
import_custom_formats(source_config)
|
||||
elif import_choice == "2":
|
||||
import_quality_profiles(source_config)
|
||||
@@ -357,8 +357,13 @@
|
||||
}
|
||||
],
|
||||
"minFormatScore": 0,
|
||||
"cutoffFormatScore": 0,
|
||||
"cutoffFormatScore": 320,
|
||||
"formatItems": [
|
||||
{
|
||||
"format": 333,
|
||||
"name": "h265",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 331,
|
||||
"name": "Golden Popcorn 720p",
|
||||
909
profiles/Optimal (Sonarr).json
Normal file
909
profiles/Optimal (Sonarr).json
Normal file
@@ -0,0 +1,909 @@
|
||||
[
|
||||
{
|
||||
"name": "Optimal",
|
||||
"upgradeAllowed": true,
|
||||
"cutoff": 21,
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 0,
|
||||
"name": "Unknown",
|
||||
"source": "unknown",
|
||||
"resolution": 0
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 13,
|
||||
"name": "Bluray-480p",
|
||||
"source": "bluray",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 4,
|
||||
"name": "HDTV-720p",
|
||||
"source": "television",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 14,
|
||||
"name": "WEBRip-720p",
|
||||
"source": "webRip",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 5,
|
||||
"name": "WEBDL-720p",
|
||||
"source": "web",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 6,
|
||||
"name": "Bluray-720p",
|
||||
"source": "bluray",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"name": "Fallback SD",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 1,
|
||||
"name": "SDTV",
|
||||
"source": "television",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 12,
|
||||
"name": "WEBRip-480p",
|
||||
"source": "webRip",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 8,
|
||||
"name": "WEBDL-480p",
|
||||
"source": "web",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 2,
|
||||
"name": "DVD",
|
||||
"source": "dvd",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
}
|
||||
],
|
||||
"allowed": false,
|
||||
"id": 1003
|
||||
},
|
||||
{
|
||||
"name": "Fallback 1080p",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 10,
|
||||
"name": "Raw-HD",
|
||||
"source": "televisionRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 9,
|
||||
"name": "HDTV-1080p",
|
||||
"source": "television",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
}
|
||||
],
|
||||
"allowed": false,
|
||||
"id": 1002
|
||||
},
|
||||
{
|
||||
"name": "Transparent Capable",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 3,
|
||||
"name": "WEBDL-1080p",
|
||||
"source": "web",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 15,
|
||||
"name": "WEBRip-1080p",
|
||||
"source": "webRip",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 7,
|
||||
"name": "Bluray-1080p",
|
||||
"source": "bluray",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
}
|
||||
],
|
||||
"allowed": false,
|
||||
"id": 1001
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 20,
|
||||
"name": "Bluray-1080p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 16,
|
||||
"name": "HDTV-2160p",
|
||||
"source": "television",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 17,
|
||||
"name": "WEBRip-2160p",
|
||||
"source": "webRip",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 19,
|
||||
"name": "Bluray-2160p",
|
||||
"source": "bluray",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 18,
|
||||
"name": "WEBDL-2160p",
|
||||
"source": "web",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 21,
|
||||
"name": "Bluray-2160p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
}
|
||||
],
|
||||
"minFormatScore": 0,
|
||||
"cutoffFormatScore": 320,
|
||||
"formatItems": [
|
||||
{
|
||||
"format": 224,
|
||||
"name": "h265",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 223,
|
||||
"name": "LiNG",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 214,
|
||||
"name": "LQ",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 213,
|
||||
"name": "EPSiLON",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 212,
|
||||
"name": "playBD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 211,
|
||||
"name": "BLURANiUM",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 210,
|
||||
"name": "PmP",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 209,
|
||||
"name": "WiLDCAT",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 208,
|
||||
"name": "TRiToN",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 207,
|
||||
"name": "3L",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 206,
|
||||
"name": "Dolby Vision w/out Fallback",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 205,
|
||||
"name": "UHDBits",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 204,
|
||||
"name": "ATMOS (Missing)",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 203,
|
||||
"name": "TrueHD (Missing)",
|
||||
"score": 50
|
||||
},
|
||||
{
|
||||
"format": 202,
|
||||
"name": "HDR10 (Missing)",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 201,
|
||||
"name": "HDR10",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 200,
|
||||
"name": "Dolby Vision",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 199,
|
||||
"name": "HDR10+",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 198,
|
||||
"name": "Stream Optimised",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 197,
|
||||
"name": "LoRD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 196,
|
||||
"name": "Scene",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 195,
|
||||
"name": "mHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 194,
|
||||
"name": "AV-1",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 193,
|
||||
"name": "VVC",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 192,
|
||||
"name": "Extras",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 191,
|
||||
"name": "480p",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 190,
|
||||
"name": "Dariush ",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 189,
|
||||
"name": "TBB SD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 188,
|
||||
"name": "Xvid",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 187,
|
||||
"name": "DVD",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 186,
|
||||
"name": "DVD REMUX ",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 185,
|
||||
"name": "HANDJOB SD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 184,
|
||||
"name": "iTunes (Missing)",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 183,
|
||||
"name": "ROKU",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 182,
|
||||
"name": "BeyondHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 181,
|
||||
"name": "Disc ",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 180,
|
||||
"name": "3D",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 179,
|
||||
"name": "Unwanted",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 178,
|
||||
"name": "RightSIZE",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 177,
|
||||
"name": "Black and White",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 176,
|
||||
"name": "TrueHD",
|
||||
"score": 50
|
||||
},
|
||||
{
|
||||
"format": 175,
|
||||
"name": "DTS-X",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 174,
|
||||
"name": "FLAC",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 173,
|
||||
"name": "DTS-HD MA",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 172,
|
||||
"name": "x265",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 171,
|
||||
"name": "IMAX",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 170,
|
||||
"name": "ATMOS",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 169,
|
||||
"name": "DD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 168,
|
||||
"name": "DTS",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 167,
|
||||
"name": "DD+",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 166,
|
||||
"name": "iTunes",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 165,
|
||||
"name": "Paramount+",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 164,
|
||||
"name": "Peacock TV",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 163,
|
||||
"name": "Hulu",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 162,
|
||||
"name": "Netflix",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 161,
|
||||
"name": "HBO Max",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 160,
|
||||
"name": "Disney+",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 159,
|
||||
"name": "Apple TV+",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 158,
|
||||
"name": "Movies Anywhere",
|
||||
"score": 50
|
||||
},
|
||||
{
|
||||
"format": 157,
|
||||
"name": "Amazon Prime",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 156,
|
||||
"name": "REMUX",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 155,
|
||||
"name": "x264",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 154,
|
||||
"name": "WEBRip",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 153,
|
||||
"name": "Blu-Ray",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 152,
|
||||
"name": "2160p",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 151,
|
||||
"name": "720p",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 150,
|
||||
"name": "1080p",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 149,
|
||||
"name": "BHDStudio",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 148,
|
||||
"name": "LEGi0N",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 147,
|
||||
"name": "FraMeSToR",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 146,
|
||||
"name": "iFT",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 145,
|
||||
"name": "MTeam",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 144,
|
||||
"name": "EDPH",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 143,
|
||||
"name": "HANDJOB",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 142,
|
||||
"name": "c0ke",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 141,
|
||||
"name": "KASHMiR",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 140,
|
||||
"name": "WMING",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 139,
|
||||
"name": "playHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 138,
|
||||
"name": "E.N.D",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 137,
|
||||
"name": "GutS",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 136,
|
||||
"name": "BV",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 135,
|
||||
"name": "SiMPLE",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 134,
|
||||
"name": "W4NK3R",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 133,
|
||||
"name": "GS88",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 132,
|
||||
"name": "HiP",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 131,
|
||||
"name": "GALAXY",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 130,
|
||||
"name": "luvBB",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 129,
|
||||
"name": "NyHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 128,
|
||||
"name": "ZIMBO",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 127,
|
||||
"name": "ThD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 126,
|
||||
"name": "SaNcTi",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 125,
|
||||
"name": "ORiGEN",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 124,
|
||||
"name": "Positive",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 123,
|
||||
"name": "ESiR",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 122,
|
||||
"name": "Chotab",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 121,
|
||||
"name": "xander",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 120,
|
||||
"name": "FTW-HD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 119,
|
||||
"name": "Penumbra",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 118,
|
||||
"name": "TDD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 117,
|
||||
"name": "Dariush SD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 116,
|
||||
"name": "PTer",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 115,
|
||||
"name": "SbR",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 114,
|
||||
"name": "nmd",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 113,
|
||||
"name": "TBB",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 112,
|
||||
"name": "EA",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 111,
|
||||
"name": "BMF",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 110,
|
||||
"name": "LolHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 109,
|
||||
"name": "HDMaNiAcS",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 108,
|
||||
"name": "IDE",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 107,
|
||||
"name": "de[42]",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 106,
|
||||
"name": "NCmt",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 105,
|
||||
"name": "decibeL",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 104,
|
||||
"name": "NTb",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 103,
|
||||
"name": "CRiSC",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 102,
|
||||
"name": "SA89",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 101,
|
||||
"name": "HiDt",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 100,
|
||||
"name": "FoRM",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 99,
|
||||
"name": "HiFi",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 98,
|
||||
"name": "CtrlHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 97,
|
||||
"name": "VietHD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 96,
|
||||
"name": "ZQ",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 95,
|
||||
"name": "TayTo",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 94,
|
||||
"name": "Geek",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 93,
|
||||
"name": "EbP",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 92,
|
||||
"name": "DON",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 91,
|
||||
"name": "D-Z0N3",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 215,
|
||||
"name": "CJ",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 216,
|
||||
"name": "pcroland",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 217,
|
||||
"name": "BTN",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 218,
|
||||
"name": "iON",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 219,
|
||||
"name": "AJP69",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 220,
|
||||
"name": "VLAD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 221,
|
||||
"name": "hdalx",
|
||||
"score": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -366,6 +366,11 @@
|
||||
"minFormatScore": 0,
|
||||
"cutoffFormatScore": 500,
|
||||
"formatItems": [
|
||||
{
|
||||
"format": 333,
|
||||
"name": "h265",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 331,
|
||||
"name": "Golden Popcorn 720p",
|
||||
909
profiles/Transparent (Sonarr).json
Normal file
909
profiles/Transparent (Sonarr).json
Normal file
@@ -0,0 +1,909 @@
|
||||
[
|
||||
{
|
||||
"name": "Transparent",
|
||||
"upgradeAllowed": true,
|
||||
"cutoff": 1001,
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 0,
|
||||
"name": "Unknown",
|
||||
"source": "unknown",
|
||||
"resolution": 0
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 13,
|
||||
"name": "Bluray-480p",
|
||||
"source": "bluray",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 4,
|
||||
"name": "HDTV-720p",
|
||||
"source": "television",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 14,
|
||||
"name": "WEBRip-720p",
|
||||
"source": "webRip",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 5,
|
||||
"name": "WEBDL-720p",
|
||||
"source": "web",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 6,
|
||||
"name": "Bluray-720p",
|
||||
"source": "bluray",
|
||||
"resolution": 720
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"name": "Fallback SD",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 1,
|
||||
"name": "SDTV",
|
||||
"source": "television",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 12,
|
||||
"name": "WEBRip-480p",
|
||||
"source": "webRip",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 8,
|
||||
"name": "WEBDL-480p",
|
||||
"source": "web",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 2,
|
||||
"name": "DVD",
|
||||
"source": "dvd",
|
||||
"resolution": 480
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
}
|
||||
],
|
||||
"allowed": true,
|
||||
"id": 1003
|
||||
},
|
||||
{
|
||||
"name": "Fallback 1080p",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 10,
|
||||
"name": "Raw-HD",
|
||||
"source": "televisionRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 9,
|
||||
"name": "HDTV-1080p",
|
||||
"source": "television",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
}
|
||||
],
|
||||
"allowed": true,
|
||||
"id": 1002
|
||||
},
|
||||
{
|
||||
"name": "Transparent Capable",
|
||||
"items": [
|
||||
{
|
||||
"quality": {
|
||||
"id": 3,
|
||||
"name": "WEBDL-1080p",
|
||||
"source": "web",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 15,
|
||||
"name": "WEBRip-1080p",
|
||||
"source": "webRip",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 7,
|
||||
"name": "Bluray-1080p",
|
||||
"source": "bluray",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": true
|
||||
}
|
||||
],
|
||||
"allowed": true,
|
||||
"id": 1001
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 20,
|
||||
"name": "Bluray-1080p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 16,
|
||||
"name": "HDTV-2160p",
|
||||
"source": "television",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 17,
|
||||
"name": "WEBRip-2160p",
|
||||
"source": "webRip",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 18,
|
||||
"name": "WEBDL-2160p",
|
||||
"source": "web",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 19,
|
||||
"name": "Bluray-2160p",
|
||||
"source": "bluray",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 21,
|
||||
"name": "Bluray-2160p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 2160
|
||||
},
|
||||
"items": [],
|
||||
"allowed": false
|
||||
}
|
||||
],
|
||||
"minFormatScore": 0,
|
||||
"cutoffFormatScore": 500,
|
||||
"formatItems": [
|
||||
{
|
||||
"format": 224,
|
||||
"name": "h265",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 223,
|
||||
"name": "LiNG",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 221,
|
||||
"name": "hdalx",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 220,
|
||||
"name": "VLAD",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 219,
|
||||
"name": "AJP69",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 218,
|
||||
"name": "iON",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 217,
|
||||
"name": "BTN",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 216,
|
||||
"name": "pcroland",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 215,
|
||||
"name": "CJ",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 214,
|
||||
"name": "LQ",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 213,
|
||||
"name": "EPSiLON",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 212,
|
||||
"name": "playBD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 211,
|
||||
"name": "BLURANiUM",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 210,
|
||||
"name": "PmP",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 209,
|
||||
"name": "WiLDCAT",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 208,
|
||||
"name": "TRiToN",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 207,
|
||||
"name": "3L",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 206,
|
||||
"name": "Dolby Vision w/out Fallback",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 205,
|
||||
"name": "UHDBits",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 204,
|
||||
"name": "ATMOS (Missing)",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 203,
|
||||
"name": "TrueHD (Missing)",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 202,
|
||||
"name": "HDR10 (Missing)",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 201,
|
||||
"name": "HDR10",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 200,
|
||||
"name": "Dolby Vision",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 199,
|
||||
"name": "HDR10+",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 198,
|
||||
"name": "Stream Optimised",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 197,
|
||||
"name": "LoRD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 196,
|
||||
"name": "Scene",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 195,
|
||||
"name": "mHD",
|
||||
"score": -999999
|
||||
},
|
||||
{
|
||||
"format": 194,
|
||||
"name": "AV-1",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 193,
|
||||
"name": "VVC",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 192,
|
||||
"name": "Extras",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 191,
|
||||
"name": "480p",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 190,
|
||||
"name": "Dariush ",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 189,
|
||||
"name": "TBB SD",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 188,
|
||||
"name": "Xvid",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 187,
|
||||
"name": "DVD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 186,
|
||||
"name": "DVD REMUX ",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 185,
|
||||
"name": "HANDJOB SD",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 184,
|
||||
"name": "iTunes (Missing)",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 183,
|
||||
"name": "ROKU",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 182,
|
||||
"name": "BeyondHD",
|
||||
"score": -10000
|
||||
},
|
||||
{
|
||||
"format": 181,
|
||||
"name": "Disc ",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 180,
|
||||
"name": "3D",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 179,
|
||||
"name": "Unwanted",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 178,
|
||||
"name": "RightSIZE",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 177,
|
||||
"name": "Black and White",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 176,
|
||||
"name": "TrueHD",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 175,
|
||||
"name": "DTS-X",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 174,
|
||||
"name": "FLAC",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 173,
|
||||
"name": "DTS-HD MA",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 172,
|
||||
"name": "x265",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 171,
|
||||
"name": "IMAX",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 170,
|
||||
"name": "ATMOS",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"format": 169,
|
||||
"name": "DD",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 168,
|
||||
"name": "DTS",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 167,
|
||||
"name": "DD+",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 166,
|
||||
"name": "iTunes",
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"format": 165,
|
||||
"name": "Paramount+",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 164,
|
||||
"name": "Peacock TV",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 163,
|
||||
"name": "Hulu",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 162,
|
||||
"name": "Netflix",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 161,
|
||||
"name": "HBO Max",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 160,
|
||||
"name": "Disney+",
|
||||
"score": 40
|
||||
},
|
||||
{
|
||||
"format": 159,
|
||||
"name": "Apple TV+",
|
||||
"score": 50
|
||||
},
|
||||
{
|
||||
"format": 158,
|
||||
"name": "Movies Anywhere",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 157,
|
||||
"name": "Amazon Prime",
|
||||
"score": 50
|
||||
},
|
||||
{
|
||||
"format": 156,
|
||||
"name": "REMUX",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 155,
|
||||
"name": "x264",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"format": 154,
|
||||
"name": "WEBRip",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 153,
|
||||
"name": "Blu-Ray",
|
||||
"score": 0
|
||||
},
|
||||
{
|
||||
"format": 152,
|
||||
"name": "2160p",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 151,
|
||||
"name": "720p",
|
||||
"score": -10000
|
||||
},
|
||||
{
|
||||
"format": 150,
|
||||
"name": "1080p",
|
||||
"score": 60
|
||||
},
|
||||
{
|
||||
"format": 149,
|
||||
"name": "BHDStudio",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 148,
|
||||
"name": "LEGi0N",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 147,
|
||||
"name": "FraMeSToR",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 146,
|
||||
"name": "iFT",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 145,
|
||||
"name": "MTeam",
|
||||
"score": -9999
|
||||
},
|
||||
{
|
||||
"format": 144,
|
||||
"name": "EDPH",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 143,
|
||||
"name": "HANDJOB",
|
||||
"score": 30
|
||||
},
|
||||
{
|
||||
"format": 142,
|
||||
"name": "c0ke",
|
||||
"score": 110
|
||||
},
|
||||
{
|
||||
"format": 141,
|
||||
"name": "KASHMiR",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 140,
|
||||
"name": "WMING",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 139,
|
||||
"name": "playHD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 138,
|
||||
"name": "E.N.D",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 137,
|
||||
"name": "GutS",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 136,
|
||||
"name": "BV",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 135,
|
||||
"name": "SiMPLE",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 134,
|
||||
"name": "W4NK3R",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 133,
|
||||
"name": "GS88",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 132,
|
||||
"name": "HiP",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 131,
|
||||
"name": "GALAXY",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 130,
|
||||
"name": "luvBB",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 129,
|
||||
"name": "NyHD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 128,
|
||||
"name": "ZIMBO",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 127,
|
||||
"name": "ThD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 126,
|
||||
"name": "SaNcTi",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 125,
|
||||
"name": "ORiGEN",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 124,
|
||||
"name": "Positive",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 123,
|
||||
"name": "ESiR",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 122,
|
||||
"name": "Chotab",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 121,
|
||||
"name": "xander",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 120,
|
||||
"name": "FTW-HD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 119,
|
||||
"name": "Penumbra",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 118,
|
||||
"name": "TDD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 117,
|
||||
"name": "Dariush SD",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 116,
|
||||
"name": "PTer",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 115,
|
||||
"name": "SbR",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 114,
|
||||
"name": "nmd",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 113,
|
||||
"name": "TBB",
|
||||
"score": 70
|
||||
},
|
||||
{
|
||||
"format": 112,
|
||||
"name": "EA",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 111,
|
||||
"name": "BMF",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 110,
|
||||
"name": "LolHD",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 109,
|
||||
"name": "HDMaNiAcS",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 108,
|
||||
"name": "IDE",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 107,
|
||||
"name": "de[42]",
|
||||
"score": 90
|
||||
},
|
||||
{
|
||||
"format": 106,
|
||||
"name": "NCmt",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 105,
|
||||
"name": "decibeL",
|
||||
"score": 90
|
||||
},
|
||||
{
|
||||
"format": 104,
|
||||
"name": "NTb",
|
||||
"score": 80
|
||||
},
|
||||
{
|
||||
"format": 103,
|
||||
"name": "CRiSC",
|
||||
"score": 90
|
||||
},
|
||||
{
|
||||
"format": 102,
|
||||
"name": "SA89",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 101,
|
||||
"name": "HiDt",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 100,
|
||||
"name": "FoRM",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 99,
|
||||
"name": "HiFi",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 98,
|
||||
"name": "CtrlHD",
|
||||
"score": 100
|
||||
},
|
||||
{
|
||||
"format": 97,
|
||||
"name": "VietHD",
|
||||
"score": 110
|
||||
},
|
||||
{
|
||||
"format": 96,
|
||||
"name": "ZQ",
|
||||
"score": 110
|
||||
},
|
||||
{
|
||||
"format": 95,
|
||||
"name": "TayTo",
|
||||
"score": 110
|
||||
},
|
||||
{
|
||||
"format": 94,
|
||||
"name": "Geek",
|
||||
"score": 110
|
||||
},
|
||||
{
|
||||
"format": 93,
|
||||
"name": "EbP",
|
||||
"score": 120
|
||||
},
|
||||
{
|
||||
"format": 92,
|
||||
"name": "DON",
|
||||
"score": 120
|
||||
},
|
||||
{
|
||||
"format": 91,
|
||||
"name": "D-Z0N3",
|
||||
"score": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
1020
profiles/Transparent - HD Fallback (Radarr).json
Normal file
1020
profiles/Transparent - HD Fallback (Radarr).json
Normal file
File diff suppressed because it is too large
Load Diff
1020
profiles/Transparent - Remux Fallback (Radarr).json
Normal file
1020
profiles/Transparent - Remux Fallback (Radarr).json
Normal file
File diff suppressed because it is too large
Load Diff
39
syncarr.py
Normal file
39
syncarr.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import exportarr
|
||||
import importarr
|
||||
import json
|
||||
import shutil
|
||||
import os
|
||||
|
||||
def sync_data():
|
||||
# Load configuration for main app
|
||||
with open('config.json', 'r') as config_file:
|
||||
config = json.load(config_file)
|
||||
|
||||
# Specify the temporary path where files were saved
|
||||
temp_cf_path = './temp_directory/custom_formats'
|
||||
temp_qf_path = './temp_directory/quality_profiles'
|
||||
|
||||
# Get user choice for app (radarr/sonarr)
|
||||
app_choice = importarr.get_user_choice()
|
||||
|
||||
# Export data for the chosen app
|
||||
exportarr.export_cf(app_choice, save_path=temp_cf_path)
|
||||
exportarr.export_qf(app_choice, save_path=temp_qf_path)
|
||||
|
||||
# Sync with each extra installation of the chosen app
|
||||
for extra_instance in config['extra_installations'].get(app_choice, []):
|
||||
source_config = extra_instance
|
||||
print(f"Importing to instance: {extra_instance['name']}")
|
||||
|
||||
# Import custom formats and quality profiles to each extra instance
|
||||
importarr.import_custom_formats(source_config, import_path=temp_cf_path, auto_select_file=True)
|
||||
importarr.import_quality_profiles(source_config, import_path=temp_qf_path)
|
||||
|
||||
# Delete the temporary directories after the sync is complete
|
||||
temp_directory = './temp_directory'
|
||||
if os.path.exists(temp_directory):
|
||||
shutil.rmtree(temp_directory)
|
||||
print(f"Deleted temporary directory: {temp_directory}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
sync_data()
|
||||
Reference in New Issue
Block a user