Files
profilarr/exportarr.py
santiagosayshey e82f2cdca9 v0.2.3
- changed from json to yaml for config
- added export_path setting to set where exported files go : fixes https://github.com/santiagosayshey/Profilarr/issues/5
- exports now save to {export_path}/{app_type}/{app_name}
- can choose which instance to manually import to
- sync automatically chooses correct custom formats / quality profiles for the app type
- updated readme with usage examples
- added install requirements

Closes #4, #5
2024-01-27 15:14:04 +10:30

183 lines
6.8 KiB
Python

import requests
import os
import re
import yaml
import json
# 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.yml', 'r') as config_file:
config = yaml.safe_load(config_file)
master_config = config['instances']['master']
export_base_path = config['settings']['export_path']
def get_user_choice():
sources = []
print(Colors.HEADER + "Available sources to export from:" + Colors.ENDC)
# Add master installations
for app in master_config:
sources.append((app, f"{app.capitalize()} [Master]", "master"))
# Add extra installations
if "extras" in config['instances']:
for app, instances in config['instances']['extras'].items():
for install in instances:
sources.append((app, f"{app.capitalize()} [{install['name']}]", install['name']))
# Display sources with numbers
for idx, (app, name, _) in enumerate(sources, start=1):
print(f"{idx}. {name}")
# User selection
choice = input("Enter the number of the app to export from: ").strip()
while not choice.isdigit() or int(choice) < 1 or int(choice) > len(sources):
print(Colors.FAIL + "Invalid input. Please enter a valid number." + Colors.ENDC)
choice = input("Enter the number of the app to export from: ").strip()
selected_app, instance_name = sources[int(choice) - 1][0], sources[int(choice) - 1][2]
print()
return selected_app, instance_name
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 = master_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, instance_name, save_path=None):
if save_path is None:
save_path = os.path.join(export_base_path, source, instance_name, 'custom_formats')
ensure_directory_exists(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 = os.path.join(save_path, f'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, instance_name, save_path=None):
if save_path is None:
save_path = os.path.join(export_base_path, source, instance_name, 'profiles')
ensure_directory_exists(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)
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)
with open(profile_filepath, 'w') as file:
json.dump([profile], file, indent=4)
saved_profiles.append(profile_name) # Add the profile name to the list
print_saved_items(saved_profiles, "Quality Profiles")
print(Colors.OKGREEN + f"Saved to '{os.path.normpath(save_path)}'" + Colors.ENDC) # Normalize the path
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, instance_name = get_user_choice()
export_choice = get_export_choice()
if export_choice in ["1", "3"]:
export_cf(user_choice, instance_name)
if export_choice in ["2", "3"]:
export_qf(user_choice, instance_name)