Skip to content
This repository was archived by the owner on Sep 22, 2023. It is now read-only.

Commit 8cdf9d3

Browse files
authored
Merge pull request #1 from Gabisonfire/0.2
feat: Fix directory/mutliple files torrents
2 parents cb1aa08 + af99ee7 commit 8cdf9d3

File tree

5 files changed

+79
-23
lines changed

5 files changed

+79
-23
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
### 0.2
3+
- Fixed error not macthing torrents with multiple files
4+
- Changed default behaviour to kill threads on SIGINT
5+
- Moved a lot of logging to "debug"
6+
- Added a lot of debug logging
7+
8+
### 0.1
9+
- Initial release

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ It will monitor your Radarr downloads. Once downloaded and copied to your librar
2323
| torrent_category[^1] | The category assigned to torrents by Radarr. | ex: radarr |
2424
| radarr_host | The Radarr url | ex: http://radarr.example.com |
2525
| radar_api_key[^2] | Your Radarr api key | 1234456789asd123456789asd |
26-
| torrent_dowload_directory | The directory where your torrent client stores the downloads | ex: /mnt/downloads |
26+
| torrent_download_directory | The directory where your torrent client stores the downloads | ex: /mnt/downloads |
2727
| torrent_library_directory | The directory where you store your movies relative to your torrent client | ex: /mnt/movies |
2828
| radarr_library_directory[^3] | Your Radarr library folder | ex: /etc/radarr/movies |
2929
| missing_status_scan_interval | The interval in secs when Seedr looks for "missing" movies | 30 |

config.json.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"torrent_category": "radarr",
88
"radarr_host": "http://radarr.example.com",
99
"radar_api_key": "1234456789asd123456789asd",
10-
"torrent_dowload_directory": "/mnt/downloads",
10+
"torrent_download_directory": "/mnt/downloads",
1111
"torrent_library_directory": "/mnt/movies",
1212
"radarr_library_directory": "/data/movies",
1313
"missing_status_scan_interval": 30,

configs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ def read_config(cfg):
1717
if k == cfg:
1818
return cfg_file[k]
1919

20+
# not found should throw an error
21+
2022

seedr.py

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import argparse
99
import signal
1010
import json
11+
import shutil
1112
import settings
1213

1314
from pathlib import Path
@@ -19,6 +20,7 @@
1920
parser.add_argument("--add-id", help="Add a TMDBid to watch.", type=int)
2021
parser.add_argument("--skip-hash", help="Skip the file hash check.", action="store_true")
2122
parser.add_argument("--no-delete", help="Don't delete the original file.", action="store_true")
23+
parser.add_argument("--no-save", help="Don't save.", action="store_true")
2224
parser.add_argument("--force-state-change", help="Skip the file hash check.", type=int)
2325
parser.add_argument("-c", "--config", help="Path to the config file.", type=str)
2426
args = parser.parse_args()
@@ -82,9 +84,9 @@ def update_state():
8284
found = False
8385
unwatch = []
8486
if len(settings.watch) == 0:
85-
logger.info("Watch queue empty.")
87+
logger.debug("Watch queue empty.")
8688
return
87-
logger.info("Looking for state changes...")
89+
logger.debug("Looking for state changes...")
8890
for id in settings.watch:
8991
movie = settings.radarr.get_movie(id)
9092
if len(movie) == 1:
@@ -99,7 +101,7 @@ def update_state():
99101
for i in unwatch:
100102
settings.watch.remove(i)
101103
if not found:
102-
logger.info("No state changes found.")
104+
logger.debug("No state changes found.")
103105

104106
def blake(file):
105107
with open(file, "rb") as f:
@@ -108,7 +110,7 @@ def blake(file):
108110
file_hash.update(chunk)
109111
return file_hash.hexdigest()
110112

111-
def move_torrent(t, movie):
113+
def move_torrent(t, movie, rename):
112114
# Removing before hash check to prevent threads from analyzing the same file
113115
if movie['tmdbId'] in settings.changed:
114116
settings.changed.remove(movie['tmdbId'])
@@ -126,15 +128,46 @@ def move_torrent(t, movie):
126128
logger.info(f"{lib_file} and {download_file} hashes are identical.")
127129
else:
128130
logger.info("'--skip-hash' set, skipping hash check.")
129-
logger.info(f"Moving torrent({t['hash']}) from {t['content_path']} to {new_path}")
131+
if rename:
132+
logger.debug("This torrent is in a folder and requires a rename before moving.")
133+
rename_path = os.path.dirname(new_path)
134+
logger.debug(f"Renaming {ntpath.basename(os.path.dirname(t['content_path']))} to {ntpath.basename(rename_path)}")
135+
try:
136+
settings.client.torrents_rename_folder(t['hash'], ntpath.basename(os.path.dirname(t['content_path'])), ntpath.basename(rename_path))
137+
# Let the client rename the folder
138+
time.sleep(5)
139+
logger.info(f"Torrent moved, deleting old directory({os.path.dirname(t['content_path'])})")
140+
# Failsafe
141+
if os.path.dirname(t['content_path']) != cfg.read_config("torrent_library_directory") and os.path.dirname(t['content_path']) != cfg.read_config("radarr_library_directory") and os.path.dirname(t['content_path']) != cfg.read_config("torrent_download_directory"):
142+
try:
143+
shutil.rmtree(os.path.dirname(t['content_path']))
144+
except Exception as ex:
145+
logger.error(f"Error deleting old directory: {ex}")
146+
logger.debug(f"Error deleting old directory({os.path.dirname(t['content_path'])}): {ex}")
147+
else:
148+
logger.error(f"Hitting failsafe to prevent library deletion -> {os.path.dirname(t['content_path'])}")
149+
t['content_path'] = os.path.join(cfg.read_config("torrent_download_directory"), ntpath.basename(rename_path))
150+
except Exception as ee:
151+
logger.error(f"Error renaming torrent: {ee}")
152+
logger.debug(f"Error renaming torrent | {t['content_path']}, {rename_path}, {new_path}: {ee}")
130153
try:
131154
og_path = t['content_path']
132-
t.set_location(location=os.path.dirname(new_path))
155+
if rename:
156+
logger.info(f"Moving torrent({t['hash']}) from {t['content_path']} to {os.path.dirname(new_path)}")
157+
logger.debug("Renamed -> set_location")
158+
t.set_location(location=cfg.read_config("torrent_library_directory"))
159+
else:
160+
logger.info(f"Moving torrent({t['hash']}) from {t['content_path']} to {new_path}")
161+
t.set_location(location=os.path.dirname(new_path))
133162
logger.info(f"{movie['title']} moved. Removing from watch queue.")
134163
if movie['tmdbId'] in settings.changed:
135164
settings.changed.remove(movie['tmdbId'])
136165
logger.info(f"Queueing {t['name']} for deletion")
166+
if rename:
167+
# Renamed files only have a folder, adding a fake file to prevent dirname going up too much.
168+
og_path = os.path.join(og_path, "fakefile.mkv")
137169
if {"torrent": t, "original_path": og_path} not in settings.to_delete:
170+
logger.debug({"torrent": t, "original_path": og_path})
138171
settings.to_delete.append({"torrent": t, "original_path": og_path})
139172
except Exception as e:
140173
logger.error(f"Could not move torrent: {t['name']}. {e}")
@@ -146,20 +179,26 @@ def match_and_move_torrents():
146179
found = False
147180
torrents = settings.client.torrents_info(category=cfg.read_config("torrent_category"))
148181
if len(settings.changed) == 0:
149-
logger.info("Change queue empty.")
182+
logger.debug("Change queue empty.")
150183
return
151184
if len(torrents) > 0:
152-
logger.info("Looking for torrent file match...")
185+
logger.debug("Looking for torrent file match...")
153186
for id in settings.changed:
154187
movie = settings.radarr.get_movie(id)
155188
if len(movie) == 1:
156189
movie = movie[0]
157-
logger.info(f"Looking for movie: {movie['title']} ({movie['movieFile']['relativePath']})")
190+
logger.debug(f"Looking for movie: {movie['title']} ({movie['movieFile']['relativePath']})")
158191
for t in torrents:
159-
if ntpath.basename(t['content_path']) == movie['movieFile']['relativePath']:
160-
found = True
161-
logger.info(f"Found a match with torrent: {t['name']}.")
162-
move_torrent(t, movie)
192+
for f in settings.client.torrents_files(torrent_hash=t['hash']):
193+
if movie['movieFile']['relativePath'] == ntpath.basename(f['name']):
194+
found = True
195+
rename_needed = False
196+
logger.info(f"Found a match for {movie['title']} with torrent: {t['name']}.")
197+
t['content_path'] = os.path.join(t['content_path'],ntpath.basename(f['name']))
198+
if ntpath.basename(f['name']) != f['name']:
199+
rename_needed = True
200+
move_torrent(t, movie, rename_needed)
201+
break
163202
if not found and len(torrents) > 0:
164203
logger.warning(f"{len(settings.changed)} changes in Radarr but no match found in your torrent client.")
165204

@@ -168,28 +207,34 @@ def check_and_delete():
168207
for torrent in settings.to_delete:
169208
status = settings.client.torrents_info(torrent_hashes=torrent["torrent"]['hash'])[0]
170209
if status['state'] not in ["error", "checkingUP", "moving", "unknown"]:
171-
logger.info(f"{status['name']}'s state is {status['state']}, deleting: {torrent['original_path']}")
210+
logger.info(f"{status['name']}'s state is '{status['state']}', deleting: {os.path.dirname(torrent['original_path'])}")
172211
if not args.no_delete:
173212
try:
174-
os.remove(torrent['original_path'])
213+
# Failsafe
214+
if os.path.dirname(torrent['original_path']) != cfg.read_config("torrent_library_directory") and os.path.dirname(torrent['original_path']) != cfg.read_config("radarr_library_directory") and os.path.dirname(torrent['original_path']) != cfg.read_config("torrent_download_directory"):
215+
shutil.rmtree(os.path.dirname(torrent['original_path']))
216+
else:
217+
logger.error(f"Hitting failsafe to prevent library deletion -> {os.path.dirname(torrent['original_path'])}")
175218
if torrent not in deleted:
176219
deleted.append(torrent)
177-
logger.info(f"{torrent['original_path']} deleted.")
220+
logger.info(f"{os.path.dirname(torrent['original_path'])} deleted.")
178221
except Exception as e:
179-
logger.error(f"Error deleting {torrent['original_path']}, {e}")
222+
logger.error(f"Error deleting {os.path.dirname(torrent['original_path'])}, {e}")
180223
else:
181224
logger.info("'--no-delete' set, skipping deletion.")
182225
if torrent not in deleted:
183226
deleted.append(torrent)
184227
else:
185-
logger.info(f"{status['name']}'s state is {status['state']}, not ready for deletion.")
228+
logger.debug(f"{status['name']}'s state is '{status['state']}', not ready for deletion.")
186229
for i in deleted:
187230
settings.to_delete.remove(i)
188231

189232
def save():
233+
if args.no_save:
234+
return
190235
savepath = os.path.dirname(settings.config_file)
191236
try:
192-
logger.info("Saving...")
237+
logger.debug("Saving...")
193238
watch = open(os.path.join(savepath, "watch.json"), "w")
194239
changed = open(os.path.join(savepath, "changed.json"), "w")
195240
to_delete = open(os.path.join(savepath, "to_delete.json"), "w")
@@ -199,7 +244,7 @@ def save():
199244
watch.close()
200245
changed.close()
201246
to_delete.close()
202-
logger.info("Saved.")
247+
logger.debug("Saved.")
203248
except Exception as e:
204249
logger.error(f"Error saving data. {e}")
205250

@@ -227,7 +272,7 @@ def load():
227272
except Exception as e:
228273
logger.error(f"Error loading data. {e}")
229274

230-
def clean_shutdown(force=False):
275+
def clean_shutdown(force=True):
231276
save()
232277
sched.shutdown(wait=not force)
233278
logger.info("Goodbye.")

0 commit comments

Comments
 (0)