fix duplicated email

This commit is contained in:
2025-08-13 21:13:25 +02:00
parent 70c95f7976
commit 839fafdb33
3 changed files with 94 additions and 45 deletions

View File

@@ -2,6 +2,7 @@ from django.core.mail import send_mail
from django.conf import settings from django.conf import settings
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.db import transaction
from settingspanel.models import AppSettings from settingspanel.models import AppSettings
# from accounts.utils import JellyfinClient # not needed for availability; use Sonarr/Radarr instead # from accounts.utils import JellyfinClient # not needed for availability; use Sonarr/Radarr instead
import requests import requests
@@ -254,22 +255,32 @@ def check_and_notify_users():
if season is None or number is None: if season is None or number is None:
continue continue
# duplicate guard (per series per day per user)
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
already_notified = SentNotification.objects.filter(
media_id=sub.series_id,
media_type='series',
air_date=today,
user=sub.user
).exists()
if already_notified:
continue
# check availability via Sonarr hasFile # check availability via Sonarr hasFile
if sonarr_episode_has_file(sub.series_id, season, number): if sonarr_episode_has_file(sub.series_id, season, number):
if not sub.user.email: if not sub.user.email:
continue continue
send_notification_email( # After confirming availability, reserve once per user/series/day
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
try:
with transaction.atomic():
obj, created = SentNotification.objects.get_or_create(
user=sub.user,
media_id=sub.series_id,
media_type='series',
air_date=today,
defaults={
'media_title': sub.series_title,
}
)
if not created:
# already reserved/sent
continue
except Exception:
# if DB error (race), skip to avoid duplicates
continue
try:
send_notification_email(
user=sub.user, user=sub.user,
media_title=sub.series_title, media_title=sub.series_title,
media_type='series', media_type='series',
@@ -279,16 +290,21 @@ def check_and_notify_users():
season=season, season=season,
episode=number, episode=number,
air_date=ep.get('airDateUtc'), air_date=ep.get('airDateUtc'),
)
# mark as sent unless duplicates are allowed
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
SentNotification.objects.create(
user=sub.user,
media_id=sub.series_id,
media_type='series',
media_title=sub.series_title,
air_date=today
) )
except Exception:
# roll back reservation so we can retry next run
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
try:
SentNotification.objects.filter(
user=sub.user,
media_id=sub.series_id,
media_type='series',
air_date=today,
).delete()
except Exception:
pass
continue
# no-op: already reserved via get_or_create above
# Film-Abos # Film-Abos
for sub in MovieSubscription.objects.select_related('user').all(): for sub in MovieSubscription.objects.select_related('user').all():
@@ -296,16 +312,6 @@ def check_and_notify_users():
if not it: if not it:
continue continue
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
already_notified = SentNotification.objects.filter(
media_id=sub.movie_id,
media_type='movie',
air_date=today,
user=sub.user
).exists()
if already_notified:
continue
if radarr_movie_has_file(sub.movie_id): if radarr_movie_has_file(sub.movie_id):
if not sub.user.email: if not sub.user.email:
continue continue
@@ -323,23 +329,47 @@ def check_and_notify_users():
except Exception: except Exception:
pass pass
send_notification_email( # After confirming availability, reserve once per user/movie/day
user=sub.user,
media_title=sub.title,
media_type='movie',
overview=sub.overview,
poster_url=it.get('posterUrl'),
year=it.get('year'),
release_type=rel,
)
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False): if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
SentNotification.objects.create( try:
with transaction.atomic():
obj, created = SentNotification.objects.get_or_create(
user=sub.user,
media_id=sub.movie_id,
media_type='movie',
air_date=today,
defaults={
'media_title': sub.title,
}
)
if not created:
continue
except Exception:
continue
try:
send_notification_email(
user=sub.user, user=sub.user,
media_id=sub.movie_id,
media_type='movie',
media_title=sub.title, media_title=sub.title,
air_date=today media_type='movie',
overview=sub.overview,
poster_url=it.get('posterUrl'),
year=it.get('year'),
release_type=rel,
) )
except Exception:
if not getattr(settings, 'NOTIFICATIONS_ALLOW_DUPLICATES', False):
try:
SentNotification.objects.filter(
user=sub.user,
media_id=sub.movie_id,
media_type='movie',
air_date=today,
).delete()
except Exception:
pass
continue
# no-op: already reserved via get_or_create above
def has_new_episode_today(series_id): def has_new_episode_today(series_id):

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.5 on 2025-08-13 19:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('settingspanel', '0003_alter_appsettings_mail_secure'),
]
operations = [
migrations.AlterField(
model_name='appsettings',
name='mail_secure',
field=models.CharField(blank=True, choices=[('', 'No TLS/SSL'), ('starttls', 'STARTTLS (Port 587)'), ('ssl', 'SSL/TLS (Port 465)'), ('tls', 'TLS (alias STARTTLS)')], max_length=10, null=True),
),
]

View File

@@ -183,4 +183,5 @@ DEFAULT_FROM_EMAIL = None # Will be set from AppSettings
# Notifications / Debug # Notifications / Debug
# If True, duplicate suppression is disabled and emails can be resent on every run. # If True, duplicate suppression is disabled and emails can be resent on every run.
NOTIFICATIONS_ALLOW_DUPLICATES = os.getenv('NOTIFICATIONS_ALLOW_DUPLICATES', 'True').lower() == 'true' # Default is False to avoid accidental duplicate mails in local/dev runs.
NOTIFICATIONS_ALLOW_DUPLICATES = os.getenv('NOTIFICATIONS_ALLOW_DUPLICATES', 'False').lower() == 'true'