Skip to content

Commit

Permalink
handle "stuck" torrents by blacklisting after failing to complete aft…
Browse files Browse the repository at this point in the history
…er X days
  • Loading branch information
lardbit committed Jan 14, 2024
1 parent ac99127 commit aa69013
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 2 deletions.
28 changes: 28 additions & 0 deletions src/frontend/src/app/settings/settings.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,34 @@
</div>
</div>
</div>
<!-- handle "stuck" media -->
<div class="card">
<div class="card-header">Handle Stuck Downloads</div>
<div class="card-body">
<div class="card-title">
Do you want to automatically blacklist torrents that get stuck and never complete? Enable this setting and define how many days
to decide when a torrent is "stuck" and to blacklist and retry.
</div>
<div class="my-2">
<div class="form-check">
<input class="form-check-input" type="radio" [value]="true" formControlName="stuck_download_handling_enabled" id="stuck-downloads-true">
<label class="form-check-label" for="stuck-downloads-true">
Enabled
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" [value]="false" formControlName="stuck_download_handling_enabled" id="stuck-downloads-false">
<label class="form-check-label" for="stuck-downloads-false">
Disabled
</label>
</div>
<div class="my-2" [hidden]="!form.get('stuck_download_handling_enabled').value">
<label>How many days to wait before blacklisting?</label>
<input type="number" [min]="1" [step]="1" class="form-control" placeholder="localhost" formControlName="stuck_download_handling_days" required>
</div>
</div>
</div>
</div>
<!-- logs -->
<div class="card">
<div class="card-header">Logs</div>
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/src/app/settings/settings.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
'users': new UntypedFormArray([]),
'apprise_notification_url': [settings['apprise_notification_url']],
'preferred_media_category': [settings['preferred_media_category'], Validators.required],
'stuck_download_handling_enabled': [settings['stuck_download_handling_enabled'], Validators.required],
'stuck_download_handling_days': [settings['stuck_download_handling_days'], Validators.required],
});

this.isLoadingUsers = true;
Expand Down
23 changes: 23 additions & 0 deletions src/nefarious/migrations/0075_auto_20240114_1715.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.0.2 on 2024-01-14 17:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('nefarious', '0074_add_preferred_media_category'),
]

operations = [
migrations.AddField(
model_name='nefarioussettings',
name='stuck_download_handling_days',
field=models.IntegerField(default=3, help_text='How many days to wait before blacklisting stuck downloads'),
),
migrations.AddField(
model_name='nefarioussettings',
name='stuck_download_handling_enabled',
field=models.BooleanField(default=False, help_text='Whether to enable stuck download handling by blacklisting stuck torrents'),
),
]
7 changes: 6 additions & 1 deletion src/nefarious/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
class NefariousSettings(models.Model):
JACKETT_TOKEN_DEFAULT = 'COPY_YOUR_JACKETT_TOKEN_HERE'

language = models.CharField(max_length=2, default='en') # chosen language
# chosen language
language = models.CharField(max_length=2, default='en')

# jackett
jackett_host = models.CharField(max_length=500, default='jackett')
Expand Down Expand Up @@ -68,6 +69,10 @@ class NefariousSettings(models.Model):
choices=media_category.MEDIA_CATEGORIES,
)

# handling of "stuck" downloads and how many days to blacklist a torrent if it's been stuck
stuck_download_handling_enabled = models.BooleanField(default=False, help_text='Whether to enable stuck download handling by blacklisting stuck torrents')
stuck_download_handling_days = models.IntegerField(default=3, help_text='How many days to wait before blacklisting stuck downloads')

@classmethod
def get(cls):
if cls.objects.all().count() > 1:
Expand Down
33 changes: 32 additions & 1 deletion src/nefarious/tasks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import os

import pytz
from celery import chain
from celery.signals import task_failure
from datetime import datetime
from datetime import datetime, timedelta
from celery_once import QueueOnce
from django.conf import settings
from django.contrib.auth.models import User
Expand Down Expand Up @@ -52,6 +53,10 @@
'task': 'nefarious.tasks.populate_release_dates_task',
'schedule': 60 * 60 * 24 * 1,
},
'Stuck Download Handling': {
'task': 'nefarious.tasks.handle_stuck_downloads',
'schedule': 60 * 60 * 24 * 1,
},
}


Expand Down Expand Up @@ -513,3 +518,29 @@ def queue_download_subtitles_for_season_task(watch_season_id: int):
watch_season = get_object_or_404(WatchTVSeason, id=watch_season_id) # type: WatchTVSeason
for watch_episode in watch_season.watch_tv_show.watchtvepisode_set.filter(season_number=watch_season.season_number):
download_subtitles_task.delay(MEDIA_TYPE_TV_EPISODE, watch_episode.id)


@app.task
def handle_stuck_downloads_task():
# find media that's been "stuck" downloading for X days and blacklist (if setting is enabled)
nefarious_settings = NefariousSettings.get()
if nefarious_settings.stuck_download_handling_enabled:
stuck_media_type_queries = [
WatchMovie.objects.all(),
WatchTVSeason.objects.all(),
WatchTVEpisode.objects.all(),
]
for query in stuck_media_type_queries:
# torrent found, not collected, and older than X days
exclude_kwargs = dict(transmission_torrent_hash__isnull=True)
filter_kwargs = dict(
collected=False,
last_attempt_date__lt=datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(days=nefarious_settings.stuck_download_handling_days),
)
for media in query.exclude(**exclude_kwargs).filter(**filter_kwargs):
msg = 'blacklisting stuck media "{media}" since it has been trying to download for longer than {stuck_download_handling_days} days'.format(
media=media, stuck_download_handling_days=nefarious_settings.stuck_download_handling_days,
)
logger_background.info(msg)
notification.send_message(msg)
blacklist_media_and_retry(media)

0 comments on commit aa69013

Please sign in to comment.