## Features
- Implemented #12 
- Enhanced message formatting and user prompts for deletions.

## Additions
- ETHEL SCENE release group added.
- FLiGHTS group to missing HDR10.
- Overhauled h265 custom formats and added new h265 4k format.
- MAX WEB source added to custom formats.

## Improvements
- Script to generate initial config file.
- Removed tracking of config.yml for security.
- Updated README for install process and new delete feature.
This commit is contained in:
santiagosayshey
2024-02-03 11:32:04 +10:30
committed by GitHub
parent a17bb1cd77
commit c22385335e
7 changed files with 1329 additions and 35 deletions

4
.gitignore vendored
View File

@@ -158,3 +158,7 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
config.yml
exports/

View File

@@ -23,11 +23,13 @@ Profilarr is a Python-based tool designed to add import/export/sync functionalit
1. Download the latest Profilarr package from the release section.
2. Extract its contents into a folder.
3. Open the `config.yml` file in a text editor.
3. Run `python setup.py` in your command line interface to generate a config file.
- This will create a `config.yml` file in the same directory as `setup.py`.
4. Open the `config.yml` file in a text editor.
- Add the URL and API key to the master instances of Radarr / Sonarr.
- If syncing, add the URL, API key and a name to each extra instance of Radarr / Sonarr.
- If exporting, adjust the `export_path` to your desired export location.
4. Save the changes.
5. Save the changes.
## 🚀 Usage
@@ -250,6 +252,66 @@ Deleted temporary directory: ./temp_directory
PS Z:\Profilarr>
```
### Deleting
1. Run `python deletarr.py` in your command line interface.
2. Select the instance from which you wish to delete data.
3. Choose between deleting Custom Formats or Quality Profiles.
4. Select specific items by typing their numbers separated by commas, or type 'all' to delete everything.
#### Example: Deleting Custom Formats
```plaintext
PS Z:\Profilarr> python deletarr.py
Available instances to delete from:
1. Sonarr [Master]
2. Radarr [Master]
Enter the number of the instance to delete from: 2
Choose what to delete:
1. Custom Formats
2. Quality Profiles
Enter your choice (1/2): 1
Deleting selected custom formats...
Available items:
1. UHDBits
2. Dolby Vision w/out Fallback
...
132. h265 (4k)
133. MAX
Your choice: all
Deleting custom format 'UHDBits': SUCCESS
...
Deleting custom format 'MAX': SUCCESS
```
#### Example: Deleting Quality Profiles
```plaintext
PS Z:\Profilarr> python deletarr.py
Choose what to delete:
1. Custom Formats
2. Quality Profiles
Enter your choice (1/2): 2
Deleting selected quality profiles...
Available items:
1. 1080p Balanced
...
11. 2160p Optimal
Your choice: all
Deleting quality profile '1080p Balanced': SUCCESS
...
Deleting quality profile '2160p Optimal': SUCCESS
```
### Radarr and Sonarr Compatibility
- You are only able to import / sync files to the app that is included in the file name (e.g. `Radarr` or `Sonarr`).
@@ -264,6 +326,10 @@ PS Z:\Profilarr>
- **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.
## Contributing
- I've added a docker compose file for testing custom formats / quality profiles. Run `docker-compose up -d` to start the Radarr/ Sonarr test containers. Add your API keys to the `config.yml` file and begin testing!
# TRaSH Guides
Some custom formats found here have been interated on from the trash guides. Credit for these goes entirely to trash, and can be found on their site here. It is not my intention to steal their work, but rather to build on it and make it more accessible to the average user through my quality profiles. Please check out their site for more information on their work.

View File

@@ -8928,6 +8928,27 @@
"isFloat": false
}
]
},
{
"name": "ETHEL",
"implementation": "ReleaseGroupSpecification",
"implementationName": "Release Group",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?<=^|[\\s.-])ETHEL\\b",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
}
]
},
@@ -9272,7 +9293,7 @@
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "\\b(FraMeSToR|HQMUX|SiCFoI|playBD|RYU|ElNeekster|CiNEPHiLES|3L|EDV|Kenobi|TRiToN|HDH|NTb)\\b",
"value": "\\b(FraMeSToR|HQMUX|SiCFoI|playBD|RYU|ElNeekster|CiNEPHiLES|3L|EDV|Kenobi|TRiToN|HDH|NTb|Flights)\\b",
"type": "textbox",
"advanced": false,
"privacy": "normal",
@@ -11896,7 +11917,7 @@
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "^(?!.*(?i:remux)).*([hH]\\s*\\.?\\s*265)",
"value": "(?i)h\\s*\\.?\\s*265",
"type": "textbox",
"advanced": false,
"privacy": "normal",
@@ -11938,7 +11959,7 @@
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?i)(REMUX|DVDRip)",
"value": "Remux",
"type": "textbox",
"advanced": false,
"privacy": "normal",
@@ -11948,8 +11969,8 @@
},
{
"name": "WEB",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": true,
@@ -11957,11 +11978,142 @@
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?<!dts[ .-]?hd[ .-]?)ma\\b(?=.*\\bweb[ ._-]?(dl|rip)\\b)|\\b(amzn|amazon)\\b|\\b(atvp|aptv|Apple TV\\+)\\b|\\b(dsnp|dsny|disney|Disney\\+)\\b|\\b(nf|netflix)\\b|\\b(hmax|hbom|hbo[ ._-]max)\\b(?=[ ._-]web[ ._-]?(dl|rip)\\b)|\\b(hulu)\\b|\\b(pcok|peacock)\\b|\\b(pmtp|Paramount Plus)\\b|\\b(it|itunes)\\b(?=[ ._-]web[ ._-]?(dl|rip)\\b)",
"type": "textbox",
"label": "Source",
"value": 7,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "UNKNOWN",
"order": 0,
"dividerAfter": false
},
{
"value": 1,
"name": "CAM",
"order": 1,
"dividerAfter": false
},
{
"value": 2,
"name": "TELESYNC",
"order": 2,
"dividerAfter": false
},
{
"value": 3,
"name": "TELECINE",
"order": 3,
"dividerAfter": false
},
{
"value": 4,
"name": "WORKPRINT",
"order": 4,
"dividerAfter": false
},
{
"value": 5,
"name": "DVD",
"order": 5,
"dividerAfter": false
},
{
"value": 6,
"name": "TV",
"order": 6,
"dividerAfter": false
},
{
"value": 7,
"name": "WEBDL",
"order": 7,
"dividerAfter": false
},
{
"value": 8,
"name": "WEBRIP",
"order": 8,
"dividerAfter": false
},
{
"value": 9,
"name": "BLURAY",
"order": 9,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "4k",
"implementation": "ResolutionSpecification",
"implementationName": "Resolution",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Resolution",
"value": 2160,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0,
"dividerAfter": false
},
{
"value": 360,
"name": "R360p",
"order": 360,
"dividerAfter": false
},
{
"value": 480,
"name": "R480p",
"order": 480,
"dividerAfter": false
},
{
"value": 540,
"name": "R540p",
"order": 540,
"dividerAfter": false
},
{
"value": 576,
"name": "R576p",
"order": 576,
"dividerAfter": false
},
{
"value": 720,
"name": "R720p",
"order": 720,
"dividerAfter": false
},
{
"value": 1080,
"name": "R1080p",
"order": 1080,
"dividerAfter": false
},
{
"value": 2160,
"name": "R2160p",
"order": 2160,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
@@ -12230,5 +12382,417 @@
]
}
]
},
{
"name": "MAX",
"includeCustomFormatWhenRenaming": true,
"specifications": [
{
"name": "Max",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "\\b((?<!hbo[ ._-])max)\\b(?=[ ._-]web[ ._-]?(dl|rip)\\b)",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEBDL",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 7,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "UNKNOWN",
"order": 0,
"dividerAfter": false
},
{
"value": 1,
"name": "CAM",
"order": 1,
"dividerAfter": false
},
{
"value": 2,
"name": "TELESYNC",
"order": 2,
"dividerAfter": false
},
{
"value": 3,
"name": "TELECINE",
"order": 3,
"dividerAfter": false
},
{
"value": 4,
"name": "WORKPRINT",
"order": 4,
"dividerAfter": false
},
{
"value": 5,
"name": "DVD",
"order": 5,
"dividerAfter": false
},
{
"value": 6,
"name": "TV",
"order": 6,
"dividerAfter": false
},
{
"value": 7,
"name": "WEBDL",
"order": 7,
"dividerAfter": false
},
{
"value": 8,
"name": "WEBRIP",
"order": 8,
"dividerAfter": false
},
{
"value": 9,
"name": "BLURAY",
"order": 9,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEBRIP",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 8,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "UNKNOWN",
"order": 0,
"dividerAfter": false
},
{
"value": 1,
"name": "CAM",
"order": 1,
"dividerAfter": false
},
{
"value": 2,
"name": "TELESYNC",
"order": 2,
"dividerAfter": false
},
{
"value": 3,
"name": "TELECINE",
"order": 3,
"dividerAfter": false
},
{
"value": 4,
"name": "WORKPRINT",
"order": 4,
"dividerAfter": false
},
{
"value": 5,
"name": "DVD",
"order": 5,
"dividerAfter": false
},
{
"value": 6,
"name": "TV",
"order": 6,
"dividerAfter": false
},
{
"value": 7,
"name": "WEBDL",
"order": 7,
"dividerAfter": false
},
{
"value": 8,
"name": "WEBRIP",
"order": 8,
"dividerAfter": false
},
{
"value": 9,
"name": "BLURAY",
"order": 9,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
]
}
]
},
{
"name": "h265 (4k)",
"includeCustomFormatWhenRenaming": false,
"specifications": [
{
"name": "h265",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?i)h\\s*\\.?\\s*265",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "Disc",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "^(?!.*\\b((?<!HD[._ -]|HD)DVD|BDRip|MKV|XviD|WMV|d3g|BDREMUX|REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*DL|((?<=\\d{4}).*German.*(DL)?)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\\b))\\b)(((?=.*\\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\\b)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\\b))|^((?=.*\\b(^((?=.*\\b((.*_)?COMPLETE.*|Dis[ck])\\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*|(?i)(DVD9|DVD5|NTSC|PAL|VOB IFO|VC-1|AVC|MPEG-2|\\bCOMPLETE[-.\\s]?(?:UHD[-.\\s])?BLU[-.\\s]?RAY\\b|\\bCOMPLETE BLURAY\\b|\\bBR-Disk\\b)",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "Remux",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Regular Expression",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "Remux",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEB",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 7,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "UNKNOWN",
"order": 0,
"dividerAfter": false
},
{
"value": 1,
"name": "CAM",
"order": 1,
"dividerAfter": false
},
{
"value": 2,
"name": "TELESYNC",
"order": 2,
"dividerAfter": false
},
{
"value": 3,
"name": "TELECINE",
"order": 3,
"dividerAfter": false
},
{
"value": 4,
"name": "WORKPRINT",
"order": 4,
"dividerAfter": false
},
{
"value": 5,
"name": "DVD",
"order": 5,
"dividerAfter": false
},
{
"value": 6,
"name": "TV",
"order": 6,
"dividerAfter": false
},
{
"value": 7,
"name": "WEBDL",
"order": 7,
"dividerAfter": false
},
{
"value": 8,
"name": "WEBRIP",
"order": 8,
"dividerAfter": false
},
{
"value": 9,
"name": "BLURAY",
"order": 9,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "4k",
"implementation": "ResolutionSpecification",
"implementationName": "Resolution",
"infoLink": "https://wiki.servarr.com/radarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Resolution",
"value": 2160,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0,
"dividerAfter": false
},
{
"value": 360,
"name": "R360p",
"order": 360,
"dividerAfter": false
},
{
"value": 480,
"name": "R480p",
"order": 480,
"dividerAfter": false
},
{
"value": 540,
"name": "R540p",
"order": 540,
"dividerAfter": false
},
{
"value": 576,
"name": "R576p",
"order": 576,
"dividerAfter": false
},
{
"value": 720,
"name": "R720p",
"order": 720,
"dividerAfter": false
},
{
"value": 1080,
"name": "R1080p",
"order": 1080,
"dividerAfter": false
},
{
"value": 2160,
"name": "R2160p",
"order": 2160,
"dividerAfter": false
}
],
"privacy": "normal",
"isFloat": false
}
]
}
]
}
]

View File

@@ -6916,6 +6916,27 @@
"isFloat": false
}
]
},
{
"name": "ETHEL",
"implementation": "ReleaseGroupSpecification",
"implementationName": "Release Group",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?<=^|[\\s.-])ETHEL\\b",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
}
]
},
@@ -7260,7 +7281,7 @@
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "\\b(FraMeSToR|HQMUX|SiCFoI|playBD|RYU|ElNeekster|CiNEPHiLES|3L|EDV|Kenobi|TRiToN|HDH|TekMUX|NTb)\\b",
"value": "\\b(FraMeSToR|HQMUX|SiCFoI|playBD|RYU|ElNeekster|CiNEPHiLES|3L|EDV|Kenobi|TRiToN|HDH|NTb|Flights)\\b",
"type": "textbox",
"advanced": false,
"privacy": "normal",
@@ -10378,28 +10399,7 @@
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "^(?!.*(?i:remux)).*([hH]\\\\s*\\\\.?\\\\s*265)",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "Disc",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "^(?!.*\\b((?<!HD[._ -]|HD)DVD|BDRip|MKV|XviD|WMV|d3g|BDREMUX|REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*DL|((?<=\\d{4}).*German.*(DL)?)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\\b))\\b)(((?=.*\\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\\b)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\\b))|^((?=.*\\b(^((?=.*\\b((.*_)?COMPLETE.*|Dis[ck])\\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*|(?i)(DVD9|DVD5|NTSC|PAL|VOB IFO|VC-1|AVC|MPEG-2|\\bCOMPLETE[-.\\s]?(?:UHD[-.\\s])?BLU[-.\\s]?RAY\\b|\\bCOMPLETE BLURAY\\b|\\bBR-Disk\\b)",
"value": "(?i)h\\s*\\.?\\s*265",
"type": "textbox",
"advanced": false,
"privacy": "normal",
@@ -10420,13 +10420,137 @@
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?i)(REMUX|DVDRip)",
"value": "Remux",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEB",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 3,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 1,
"name": "Television",
"order": 1
},
{
"value": 2,
"name": "TelevisionRaw",
"order": 2
},
{
"value": 3,
"name": "Web",
"order": 3
},
{
"value": 4,
"name": "WebRip",
"order": 4
},
{
"value": 5,
"name": "DVD",
"order": 5
},
{
"value": 6,
"name": "Bluray",
"order": 6
},
{
"value": 7,
"name": "BlurayRaw",
"order": 7
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "4k",
"implementation": "ResolutionSpecification",
"implementationName": "Resolution",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Resolution",
"value": 2160,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 360,
"name": "R360P",
"order": 360
},
{
"value": 480,
"name": "R480P",
"order": 480
},
{
"value": 540,
"name": "R540p",
"order": 540
},
{
"value": 576,
"name": "R576p",
"order": 576
},
{
"value": 720,
"name": "R720p",
"order": 720
},
{
"value": 1080,
"name": "R1080p",
"order": 1080
},
{
"value": 2160,
"name": "R2160p",
"order": 2160
}
],
"privacy": "normal",
"isFloat": false
}
]
}
]
},
@@ -10671,5 +10795,349 @@
]
}
]
},
{
"name": "h265 (4k)",
"includeCustomFormatWhenRenaming": false,
"specifications": [
{
"name": "h265",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "(?i)h\\s*\\.?\\s*265",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "Disc",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "^(?!.*\\b((?<!HD[._ -]|HD)DVD|BDRip|MKV|XviD|WMV|d3g|BDREMUX|REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*DL|((?<=\\d{4}).*German.*(DL)?)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\\b))\\b)(((?=.*\\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\\b)(?=.*\\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\\b))|^((?=.*\\b(^((?=.*\\b((.*_)?COMPLETE.*|Dis[ck])\\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*|(?i)(DVD9|DVD5|NTSC|PAL|VOB IFO|VC-1|AVC|MPEG-2|\\bCOMPLETE[-.\\s]?(?:UHD[-.\\s])?BLU[-.\\s]?RAY\\b|\\bCOMPLETE BLURAY\\b|\\bBR-Disk\\b)",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "Remux",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": true,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "Remux",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEB",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 7,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 1,
"name": "Television",
"order": 1
},
{
"value": 2,
"name": "TelevisionRaw",
"order": 2
},
{
"value": 3,
"name": "Web",
"order": 3
},
{
"value": 4,
"name": "WebRip",
"order": 4
},
{
"value": 5,
"name": "DVD",
"order": 5
},
{
"value": 6,
"name": "Bluray",
"order": 6
},
{
"value": 7,
"name": "BlurayRaw",
"order": 7
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "4k",
"implementation": "ResolutionSpecification",
"implementationName": "Resolution",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Resolution",
"value": 2160,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 360,
"name": "R360P",
"order": 360
},
{
"value": 480,
"name": "R480P",
"order": 480
},
{
"value": 540,
"name": "R540p",
"order": 540
},
{
"value": 576,
"name": "R576p",
"order": 576
},
{
"value": 720,
"name": "R720p",
"order": 720
},
{
"value": 1080,
"name": "R1080p",
"order": 1080
},
{
"value": 2160,
"name": "R2160p",
"order": 2160
}
],
"privacy": "normal",
"isFloat": false
}
]
}
]
},
{
"name": "MAX",
"includeCustomFormatWhenRenaming": true,
"specifications": [
{
"name": "Max",
"implementation": "ReleaseTitleSpecification",
"implementationName": "Release Title",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": true,
"fields": [
{
"order": 0,
"name": "value",
"label": "Language",
"helpText": "Custom Format RegEx is Case Insensitive",
"value": "\\b((?<!hbo[ ._-])max)\\b(?=[ ._-]web[ ._-]?(dl|rip)\\b)",
"type": "textbox",
"advanced": false,
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEBDL",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 7,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 1,
"name": "Television",
"order": 1
},
{
"value": 2,
"name": "TelevisionRaw",
"order": 2
},
{
"value": 3,
"name": "Web",
"order": 3
},
{
"value": 4,
"name": "WebRip",
"order": 4
},
{
"value": 5,
"name": "DVD",
"order": 5
},
{
"value": 6,
"name": "Bluray",
"order": 6
},
{
"value": 7,
"name": "BlurayRaw",
"order": 7
}
],
"privacy": "normal",
"isFloat": false
}
]
},
{
"name": "WEBRIP",
"implementation": "SourceSpecification",
"implementationName": "Source",
"infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2",
"negate": false,
"required": false,
"fields": [
{
"order": 0,
"name": "value",
"label": "Source",
"value": 8,
"type": "select",
"advanced": false,
"selectOptions": [
{
"value": 0,
"name": "Unknown",
"order": 0
},
{
"value": 1,
"name": "Television",
"order": 1
},
{
"value": 2,
"name": "TelevisionRaw",
"order": 2
},
{
"value": 3,
"name": "Web",
"order": 3
},
{
"value": 4,
"name": "WebRip",
"order": 4
},
{
"value": 5,
"name": "DVD",
"order": 5
},
{
"value": 6,
"name": "Bluray",
"order": 6
},
{
"value": 7,
"name": "BlurayRaw",
"order": 7
}
],
"privacy": "normal",
"isFloat": false
}
]
}
]
}
]

164
deletarr.py Normal file
View File

@@ -0,0 +1,164 @@
import requests
import os
import yaml
import json
# ANSI escape sequences for colors
class Colors:
HEADER = '\033[95m' # Purple for questions and headers
OKBLUE = '\033[94m' # Blue for actions
OKGREEN = '\033[92m' # Green for success messages
FAIL = '\033[91m' # Red for error messages
ENDC = '\033[0m' # Reset to default
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']
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():
print(Colors.HEADER + "\nAvailable instances to delete from:" + Colors.ENDC)
sources = []
# Add master installations
for app in master_config:
sources.append((app, f"{app.capitalize()} [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']}]"))
# Display sources with numbers
for idx, (app, name) in enumerate(sources, start=1):
print(f"{idx}. {name}")
# User selection
choice = input(Colors.HEADER + "Enter the number of the instance to delete from: " + Colors.ENDC).strip()
while not choice.isdigit() or int(choice) < 1 or int(choice) > len(sources):
print_error("Invalid input. Please enter a valid number.")
choice = input(Colors.HEADER + "Enter the number of the instance to delete from: " + Colors.ENDC).strip()
selected_app, selected_name = sources[int(choice) - 1]
print()
return selected_app, selected_name
def user_select_items_to_delete(items):
print(Colors.HEADER + "\nAvailable items:" + Colors.ENDC)
for idx, item in enumerate(items, start=1):
print(f"{idx}. {item['name']}")
print(Colors.HEADER + "Type the number(s) of the items you wish to delete separated by commas, or type 'all' to delete everything." + Colors.ENDC)
selection = input(Colors.HEADER + "Your choice: " + Colors.ENDC).strip().lower()
if selection == 'all':
return [item['id'] for item in items] # Return all IDs if "all" is selected
else:
selected_ids = []
try:
selected_indices = [int(i) - 1 for i in selection.split(',') if i.isdigit()]
for idx in selected_indices:
if idx < len(items):
selected_ids.append(items[idx]['id'])
return selected_ids
except ValueError:
print_error("Invalid input. Please enter a valid number or 'all'.")
return []
def delete_custom_formats(source_config):
print(Colors.OKBLUE + "\nDeleting selected custom formats..." + Colors.ENDC)
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:
formats_to_delete = response.json()
selected_ids = user_select_items_to_delete(formats_to_delete)
for format_id in selected_ids:
delete_url = f"{get_url}/{format_id}"
del_response = requests.delete(delete_url, headers=headers)
format_name = next((item['name'] for item in formats_to_delete if item['id'] == format_id), "Unknown")
if del_response.status_code in [200, 202, 204]:
print(Colors.OKBLUE + f"Deleting custom format '{format_name}': " + Colors.ENDC + Colors.OKGREEN + "SUCCESS" + Colors.ENDC)
else:
print(Colors.OKBLUE + f"Deleting custom format '{format_name}': " + Colors.ENDC + Colors.FAIL + "FAIL" + Colors.ENDC)
else:
print_error("Failed to retrieve custom formats for deletion!")
except requests.exceptions.ConnectionError:
print_connection_error()
def delete_quality_profiles(source_config):
print(Colors.OKBLUE + "\nDeleting selected quality profiles..." + Colors.ENDC)
headers = {"X-Api-Key": source_config['api_key']}
get_url = f"{source_config['base_url']}/api/v3/qualityprofile"
try:
response = requests.get(get_url, headers=headers)
if response.status_code == 200:
profiles_to_delete = response.json()
selected_ids = user_select_items_to_delete(profiles_to_delete)
for profile_id in selected_ids:
delete_url = f"{get_url}/{profile_id}"
del_response = requests.delete(delete_url, headers=headers)
profile_name = next((item['name'] for item in profiles_to_delete if item['id'] == profile_id), "Unknown")
if del_response.status_code in [200, 202, 204]:
print(Colors.OKBLUE + f"Deleting quality profile '{profile_name}': " + Colors.ENDC + Colors.OKGREEN + "SUCCESS" + Colors.ENDC)
else:
# Handle failure due to the profile being in use or other errors
error_message = "Failed to delete due to an unknown error."
try:
# Attempt to parse JSON error message from response
error_details = del_response.json()
if 'message' in error_details:
error_message = error_details['message']
elif 'error' in error_details:
error_message = error_details['error']
except json.JSONDecodeError:
# If response is not JSON or doesn't have expected fields
error_message = del_response.text or "Failed to delete with no detailed error message."
print(Colors.OKBLUE + f"Deleting quality profile '{profile_name}': " + Colors.ENDC + Colors.FAIL + f"FAIL - {error_message}" + Colors.ENDC)
else:
print_error("Failed to retrieve quality profiles for deletion!")
except requests.exceptions.ConnectionError:
print_connection_error()
def get_app_config(app_name, instance_name):
if instance_name.endswith("[Master]"):
return master_config[app_name]
else:
instance_name = instance_name.replace(f"{app_name.capitalize()} [", "").replace("]", "")
extras = config['instances']['extras'].get(app_name, [])
for instance in extras:
if instance['name'] == instance_name:
return instance
raise ValueError(f"Configuration for {app_name} - {instance_name} not found.")
if __name__ == "__main__":
selected_app, selected_instance = get_user_choice()
source_config = get_app_config(selected_app, selected_instance)
source_config['app_name'] = selected_app
print(Colors.HEADER + "\nChoose what to delete:" + Colors.ENDC)
print("1. Custom Formats")
print("2. Quality Profiles")
choice = input(Colors.HEADER + "Enter your choice (1/2): " + Colors.ENDC).strip()
if choice == "1":
delete_custom_formats(source_config)
elif choice == "2":
delete_quality_profiles(source_config)

View File

@@ -0,0 +1,23 @@
version: "3.3"
services:
radarr:
image: linuxserver/radarr
container_name: radarr
environment:
- PUID=1000 # user id, change as necessary
- PGID=1000 # group id, change as necessary
- TZ=Europe/London # timezone, change as necessary
ports:
- "7887:7878" # change the left value to the desired host port for Radarr
restart: unless-stopped
sonarr:
image: linuxserver/sonarr
container_name: sonarr
environment:
- PUID=1000 # user id, change as necessary
- PGID=1000 # group id, change as necessary
- TZ=Europe/London # timezone, change as necessary
ports:
- "8998:8989" # change the left value to the desired host port for Sonarr
restart: unless-stopped

View File

@@ -1,3 +1,4 @@
config_content = """
instances:
master:
sonarr:
@@ -18,3 +19,7 @@ instances:
settings:
export_path: "./exports"
"""
with open('config.yml', 'w') as file:
file.write(config_content)