Skip to content

Commit 7a5ec58

Browse files
authored
Merge pull request #150 from Drazzilb08/dev
Merge to master
2 parents 235e5ad + 5242b30 commit 7a5ec58

14 files changed

+111
-756
lines changed

Pipfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ requests = "*"
88
tqdm = "*"
99
pyyaml = "*"
1010
unidecode = "*"
11-
qbittorrent-api = "*"
1211
plexapi = "*"
1312
pillow = "*"
1413
prettytable = "*"
1514
croniter = "*"
1615
python-dateutil = "*"
16+
pathvalidate = "*"
1717

1818
[dev-packages]
1919

Pipfile.lock

+6-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.5
1+
1.1.2

config/config.sample.yml

+2-36
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ schedule:
2828
sync_gdrive:
2929
poster_cleanarr:
3030
poster_renamerr:
31-
queinatorr:
3231
renameinatorr:
3332
unmatched_assets:
3433
upgradinatorr:
@@ -69,16 +68,6 @@ instances:
6968
# API key can be found here: https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/
7069
url: http://localhost:32400
7170
api: abcdefghijklmnopqrstuvwxyz1234567890
72-
qbittorrent:
73-
# Instance name can be whatever you want, it just needs to match the name used in other sections
74-
qbittorrent_1:
75-
url: http://localhost:8080
76-
username: admin
77-
password: adminadmin
78-
qbittorrent_2:
79-
url:
80-
username:
81-
password:
8271

8372
discord:
8473
# Discord notifications:
@@ -110,10 +99,6 @@ discord:
11099
# Will notify if non-hardlinked files are found/searched for
111100
discord_webhook:
112101
channel_id:
113-
queinatorr:
114-
# Will notify if a torrent/queue item has been handled
115-
discord_webhook:
116-
channel_id:
117102
labelarr:
118103
# Will notify if a label has been added/removed to a movie or series
119104
discord_webhook:
@@ -396,27 +381,6 @@ nohl:
396381
- Marvel's Spider-Man
397382
- M*A*S*H
398383

399-
queinatorr:
400-
# This script will move torrents from one category to another in qBittorrent based on
401-
# the title of the torrent. This is useful for moving torrents from a category that are stuck
402-
# in a queue due to a missing file or not being an upgrade for existing episode file(s).
403-
log_level: info
404-
dry_run: true
405-
days_to_keep: 14 # The number of days to keep in the pre_import_category prior to moving to the post_import_category
406-
instances:
407-
radarr_1:
408-
qbit_instance: qbittorrent_1
409-
pre_import_category: movies
410-
post_import_category: completed-movies
411-
sonarr_1:
412-
qbit_instance: qbittorrent_2
413-
pre_import_category: series
414-
post_import_category: completed-series
415-
sonarr_anime:
416-
qbit_instance: qbittorrent_2
417-
pre_import_category: anime
418-
post_import_category: completed-anime
419-
420384
labelarr:
421385
# A script to sync labels between Plex and Radarr/Sonarr
422386
# Warning: Due to the way that Plex API works, this script can take a while to run with a large library
@@ -467,6 +431,8 @@ bash_scripts:
467431
-
468432
jduparr:
469433
data_dir:
434+
# Silences notifications if no duplicates are found
435+
silent: false
470436

471437
main:
472438
log_level: info

main.py

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
"nohl",
2424
"poster_cleanarr",
2525
"poster_renamerr",
26-
"queinatorr",
2726
"renameinatorr",
2827
"sync_gdrive",
2928
"upgradinatorr",

modules/bash_scripts.py

+4-57
Original file line numberDiff line numberDiff line change
@@ -29,57 +29,28 @@ def set_cmd_args(settings, bash_script_file, logger, script_name):
2929
if discord_check(script_name):
3030
webhook_url, channel = get_discord_data(script_name, logger)
3131
if settings:
32-
script_debug = str(settings.get('debug')) if 'debug' in settings else None
3332

3433
source = str(settings.get('source')) if 'source' in settings else None
35-
destination = str(settings.get('destination')) if 'destination' in settings else None
36-
keep_backups = str(settings.get('keep_backups')) if 'keep_backups' in settings else None
37-
compress = str(settings.get('compress')) if 'compress' in settings else None
3834
data_dir = str(settings.get('data_dir')) if 'data_dir' in settings else None
3935
include = list(settings.get('include')) if 'include' in settings else None
4036
exclude = list(settings.get('exclude')) if 'exclude' in settings else None
37+
silent = settings.get('silent') if 'silent' in settings else None
4138

42-
keep_essential = str(settings.get('keep_essential')) if 'keep_essential' in settings else None
43-
keep_full = str(settings.get('keep_full')) if 'keep_full' in settings else None
44-
force_full_Backup = str(settings.get('force_full_backup')) if 'force_full_backup' in settings else None
45-
script_dry_run = str(settings.get('dry_run')) if 'dry_run' in settings else None
46-
shutdown_plex = str(settings.get('shutdown_plex')) if 'shutdown_plex' in settings else None
47-
full_backup = str(settings.get('full_backup')) if 'full_backup' in settings else None
48-
4939
logger.debug(f"channel: {channel}")
5040
logger.debug(f"webhook_url: {webhook_url}")
5141
logger.debug(f"source: {source}")
52-
logger.debug(f"destination: {destination}")
53-
logger.debug(f"keep_backups: {keep_backups}")
54-
logger.debug(f"compress: {compress}")
55-
logger.debug(f"keep_essential: {keep_essential}")
56-
logger.debug(f"keep_full: {keep_full}")
57-
logger.debug(f"force_full_Backup: {force_full_Backup}")
58-
logger.debug(f"script_dry_run: {script_dry_run}")
59-
logger.debug(f"shutdown_plex: {shutdown_plex}")
60-
logger.debug(f"script_debug: {script_debug}")
61-
logger.debug(f"full_backup: {full_backup}")
6242
logger.debug(f"webhook_url: {webhook_url}")
6343
logger.debug(f"channel: {channel}")
6444
logger.debug(f"script_name: {script_name}")
6545
logger.debug(f"settings: {settings}")
6646
logger.debug(f"bash_script_file: {bash_script_file}")
6747
logger.debug(f"include: {include}")
6848
logger.debug(f"exclude: {exclude}")
49+
logger.debug(f"Silent: {silent}")
6950

7051
if source:
7152
cmd.append('-s')
7253
cmd.append(shlex.quote(str(source)))
73-
if destination:
74-
cmd.append('-d')
75-
cmd.append(shlex.quote(str(destination)))
76-
if keep_backups:
77-
cmd.append('-k')
78-
cmd.append(shlex.quote(str(keep_backups)))
79-
80-
if compress:
81-
cmd.append('-c')
82-
cmd.append(shlex.quote(str(compress)))
8354

8455
if webhook_url:
8556
cmd.append('-w')
@@ -89,34 +60,10 @@ def set_cmd_args(settings, bash_script_file, logger, script_name):
8960
cmd.append('-C')
9061
cmd.append(shlex.quote(str(channel)))
9162

92-
if keep_essential:
93-
cmd.append('-k')
94-
cmd.append(shlex.quote(str(keep_essential)))
95-
96-
if keep_full:
97-
cmd.append('-K')
98-
cmd.append(shlex.quote(str(keep_full)))
99-
100-
if force_full_Backup:
101-
cmd.append('-F')
102-
cmd.append(shlex.quote(str(force_full_Backup)))
103-
104-
if full_backup:
105-
cmd.append('-f')
106-
cmd.append(shlex.quote(str(full_backup)))
107-
108-
if script_dry_run:
109-
cmd.append('-r')
110-
cmd.append(shlex.quote(str(script_dry_run)))
111-
112-
if shutdown_plex:
63+
if silent is not None:
11364
cmd.append('-S')
114-
cmd.append(shlex.quote(str(shutdown_plex)))
65+
cmd.append(shlex.quote(str(silent)))
11566

116-
if script_debug:
117-
cmd.append('-D')
118-
cmd.append(shlex.quote(str(script_debug)))
119-
12067
if data_dir:
12168
cmd.append('-D')
12269
cmd.append(shlex.quote(str(data_dir)))

modules/poster_renamerr.py

+50-33
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
try:
3131
from plexapi.server import PlexServer
3232
from tqdm import tqdm
33+
from pathvalidate import sanitize_filename, is_valid_filename
3334
except ImportError as e:
3435
print(f"ImportError: {e}")
3536
print("Please install the required modules with 'pip install -r requirements.txt'")
@@ -94,6 +95,28 @@ def get_assets_files(source_dirs, logger):
9495

9596
return final_assets
9697

98+
def handle_series_match(asset, media_seasons_numbers, asset_season_numbers):
99+
# Iterate through each file in the asset
100+
files_to_remove = []
101+
seasons_to_remove = []
102+
for file in asset['files']:
103+
# Check for season-related file naming
104+
if re.search(r' - Season| - Specials', file):
105+
if re.search(r"Season (\d+)", file):
106+
season_number = int(re.search(r"Season (\d+)", file).group(1))
107+
elif "Specials" in file:
108+
season_number = 0
109+
if season_number not in media_seasons_numbers:
110+
files_to_remove.append(file)
111+
continue
112+
for file in files_to_remove:
113+
asset['files'].remove(file)
114+
for season in asset_season_numbers:
115+
if season not in media_seasons_numbers:
116+
seasons_to_remove.append(season)
117+
for season in seasons_to_remove:
118+
asset_season_numbers.remove(season)
119+
97120
def match_data(media_dict, asset_files):
98121
"""
99122
Matches media data to asset files
@@ -124,7 +147,7 @@ def match_data(media_dict, asset_files):
124147
asset_data = asset_files[asset_type]
125148
media_data = media_dict[asset_type]
126149
# Iterate through each media entry of the current asset type
127-
with tqdm(total=len(media_data), desc=f"Matching {asset_type}", unit="media", leave=True, disable=None) as pbar_inner:
150+
with tqdm(total=len(media_data), desc=f"Matching {asset_type}", leave=True, disable=None) as pbar_inner:
128151
for media in media_data:
129152
matched = False
130153
if asset_type == 'series':
@@ -136,36 +159,25 @@ def match_data(media_dict, asset_files):
136159
matched = True # Set flag to indicate a match
137160
asset_season_numbers = asset.get('season_numbers', None)
138161
if asset_type == "series":
139-
# Iterate through each file in the asset
140-
files_to_remove = []
141-
seasons_to_remove = []
142-
for file in asset['files']:
143-
# Check for season-related file naming
144-
if re.search(r' - Season| - Specials', file):
145-
if re.search(r"Season (\d+)", file):
146-
season_number = int(re.search(r"Season (\d+)", file).group(1))
147-
elif "Specials" in file:
148-
season_number = 0
149-
if season_number not in media_seasons_numbers:
150-
files_to_remove.append(file)
151-
continue
152-
for file in files_to_remove:
153-
asset['files'].remove(file)
154-
for season in asset_season_numbers:
155-
if season not in media_seasons_numbers:
156-
seasons_to_remove.append(season)
157-
for season in seasons_to_remove:
158-
asset_season_numbers.remove(season)
159-
160-
# Store matched data in the matched dictionary
161-
matched_dict.append({
162-
'title': media['title'],
163-
'year': media['year'],
164-
'folder': media['folder'],
165-
'files': asset['files'],
166-
'seasons_numbers': asset_season_numbers,
167-
})
168-
break # Break loop after finding a match
162+
handle_series_match(asset, media_seasons_numbers, asset_season_numbers)
163+
break
164+
if not matched:
165+
for asset in asset_data:
166+
if is_match_alternate(asset, media):
167+
matched = True
168+
asset_season_numbers = asset.get('season_numbers', None)
169+
if asset_type == "series":
170+
handle_series_match(asset, media_seasons_numbers, asset_season_numbers)
171+
break
172+
173+
if matched:
174+
matched_dict.append({
175+
'title': media['title'],
176+
'year': media['year'],
177+
'folder': media['folder'],
178+
'files': asset['files'],
179+
'seasons_numbers': asset_season_numbers,
180+
})
169181

170182
if not matched:
171183
# If no match is found, add to unmatched dictionary
@@ -264,7 +276,8 @@ def rename_files(matched_assets, script_config, logger):
264276

265277
# Remove any OS illegal characters from the file name
266278
if asset_type == "collections":
267-
folder = re.sub(r'[<>:"/\\|?*]', '', folder.replace('/', ''))
279+
if not is_valid_filename(folder):
280+
folder = sanitize_filename(folder)
268281

269282
# Handle asset_folders configuration
270283
if asset_folders:
@@ -282,7 +295,11 @@ def rename_files(matched_assets, script_config, logger):
282295

283296
# Check for season-related file naming
284297
if re.search(r' - Season| - Specials', file_name):
285-
season_number = (re.search(r"Season (\d+)", file_name).group(1) if "Season" in file_name else "00").zfill(2)
298+
try:
299+
season_number = (re.search(r"Season (\d+)", file_name).group(1) if "Season" in file_name else "00").zfill(2)
300+
except AttributeError:
301+
logger.debug(f"Error extracting season number from {file_name}")
302+
continue
286303
if asset_folders:
287304
new_file_name = f"Season{season_number}{file_extension}"
288305
else:

0 commit comments

Comments
 (0)