fix duplicated email
This commit is contained in:
@@ -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):
|
||||||
|
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
@@ -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'
|
||||||
|
Reference in New Issue
Block a user