AntennaPod-Import und automatischer Download pro Podcast
- Neuer Befehl `manage.py import_antennapod <backup.db> --user <name> [--dry-run]
[--global-auto-download on|off] [--backup-date ...]`: übernimmt aus einer
AntennaPod-Datenbanksicherung die Abos (Abgleich über normalisierte URL,
fehlende werden angelegt, lokale übersprungen), die Podcast-Einstellungen
(Tempo, Intro/Outro, Lautstärke, Filter mit Phrasen in Anführungszeichen,
Mindestlänge, neue Folgen, „nicht aktualisieren“ -> ignorieren, automatischer
Download), den Zustand jeder Folge mit Zustand (gehört, neu, Position,
Favorit, Zeitpunkt), die Warteschlange (AntennaPods Reihenfolge zuerst) und
nimmt bekannte, dort nicht neue Folgen aus „Neu“. Neuere Änderungen in diora
bleiben, Favoriten werden vereinigt. Alles in einer Transaktion; ein zweiter
Lauf ändert nichts mehr.
- PodcastFeed.auto_download (Einstellung pro Podcast, in feeds/, state/ und
feeds/<id>/settings/) und PodcastSettings.auto_download_default (in settings/
und state/), mit dem neue Abos (Hinzufügen, OPML, gpodder, Import) starten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:41:04 +02:00
|
|
|
import os
|
|
|
|
|
from datetime import datetime, timezone as dt_timezone
|
|
|
|
|
|
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
|
|
|
from django.utils.dateparse import parse_datetime
|
|
|
|
|
|
|
|
|
|
from podcasts.antennapod import AntennaPodImport, AntennaPodImportError, summary_lines
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
|
help = (
|
|
|
|
|
"Import an AntennaPod database backup (Settings > Database > Export) into one user's podcasts: "
|
2026-10-02 12:01:42 +02:00
|
|
|
'subscriptions with their settings, played/new/started episodes, favourites and the queue; '
|
|
|
|
|
'the episodes downloaded in AntennaPod are offered to the app for download. '
|
AntennaPod-Import und automatischer Download pro Podcast
- Neuer Befehl `manage.py import_antennapod <backup.db> --user <name> [--dry-run]
[--global-auto-download on|off] [--backup-date ...]`: übernimmt aus einer
AntennaPod-Datenbanksicherung die Abos (Abgleich über normalisierte URL,
fehlende werden angelegt, lokale übersprungen), die Podcast-Einstellungen
(Tempo, Intro/Outro, Lautstärke, Filter mit Phrasen in Anführungszeichen,
Mindestlänge, neue Folgen, „nicht aktualisieren“ -> ignorieren, automatischer
Download), den Zustand jeder Folge mit Zustand (gehört, neu, Position,
Favorit, Zeitpunkt), die Warteschlange (AntennaPods Reihenfolge zuerst) und
nimmt bekannte, dort nicht neue Folgen aus „Neu“. Neuere Änderungen in diora
bleiben, Favoriten werden vereinigt. Alles in einer Transaktion; ein zweiter
Lauf ändert nichts mehr.
- PodcastFeed.auto_download (Einstellung pro Podcast, in feeds/, state/ und
feeds/<id>/settings/) und PodcastSettings.auto_download_default (in settings/
und state/), mit dem neue Abos (Hinzufügen, OPML, gpodder, Import) starten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:41:04 +02:00
|
|
|
'Feeds it creates are fetched by the next refresh; running the import once more after that '
|
|
|
|
|
'also takes their older episodes that were not new in AntennaPod out of the inbox. '
|
|
|
|
|
'Running it twice changes nothing more.'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
|
|
|
|
parser.add_argument('path', help='AntennaPod backup (.db)')
|
|
|
|
|
parser.add_argument('--user', required=True, help='diora username')
|
|
|
|
|
parser.add_argument('--dry-run', action='store_true',
|
|
|
|
|
help='Do everything inside a transaction that is rolled back, print the summary')
|
|
|
|
|
parser.add_argument('--global-auto-download', choices=('on', 'off'), default='off',
|
|
|
|
|
help="Auto-download for feeds that use AntennaPod's global setting (default off)")
|
2026-10-02 12:44:49 +02:00
|
|
|
parser.add_argument('--replace', action='store_true',
|
|
|
|
|
help="A clean slate: unsubscribe podcasts the backup doesn't have, keep only "
|
|
|
|
|
"AntennaPod's queue, AntennaPod wins over later changes in diora")
|
2026-10-02 12:38:41 +02:00
|
|
|
parser.add_argument('--quiet-diora-only', action='store_true',
|
|
|
|
|
help='Podcasts only diora has (not in the backup): their episodes out before the backup '
|
|
|
|
|
'leave the inbox (the subscriptions stay)')
|
2026-10-02 12:14:03 +02:00
|
|
|
parser.add_argument('--no-downloads', action='store_true',
|
|
|
|
|
help="Don't offer AntennaPod's downloaded episodes to the app (they stay in AntennaPod)")
|
AntennaPod-Import und automatischer Download pro Podcast
- Neuer Befehl `manage.py import_antennapod <backup.db> --user <name> [--dry-run]
[--global-auto-download on|off] [--backup-date ...]`: übernimmt aus einer
AntennaPod-Datenbanksicherung die Abos (Abgleich über normalisierte URL,
fehlende werden angelegt, lokale übersprungen), die Podcast-Einstellungen
(Tempo, Intro/Outro, Lautstärke, Filter mit Phrasen in Anführungszeichen,
Mindestlänge, neue Folgen, „nicht aktualisieren“ -> ignorieren, automatischer
Download), den Zustand jeder Folge mit Zustand (gehört, neu, Position,
Favorit, Zeitpunkt), die Warteschlange (AntennaPods Reihenfolge zuerst) und
nimmt bekannte, dort nicht neue Folgen aus „Neu“. Neuere Änderungen in diora
bleiben, Favoriten werden vereinigt. Alles in einer Transaktion; ein zweiter
Lauf ändert nichts mehr.
- PodcastFeed.auto_download (Einstellung pro Podcast, in feeds/, state/ und
feeds/<id>/settings/) und PodcastSettings.auto_download_default (in settings/
und state/), mit dem neue Abos (Hinzufügen, OPML, gpodder, Import) starten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:41:04 +02:00
|
|
|
parser.add_argument('--backup-date',
|
|
|
|
|
help='When the backup was made (ISO date/time, UTC if no zone); default: the file date. '
|
|
|
|
|
'diora changes after it win over the backup.')
|
|
|
|
|
|
|
|
|
|
def handle(self, *args, **options):
|
|
|
|
|
path = options['path']
|
|
|
|
|
if not os.path.isfile(path):
|
|
|
|
|
raise CommandError(f'No such file: {path}')
|
|
|
|
|
try:
|
|
|
|
|
user = User.objects.get(username=options['user'])
|
|
|
|
|
except User.DoesNotExist:
|
|
|
|
|
raise CommandError(f"No such user: {options['user']}")
|
|
|
|
|
|
|
|
|
|
if options['backup_date']:
|
|
|
|
|
raw = options['backup_date']
|
|
|
|
|
backup_date = parse_datetime(raw) or parse_datetime(f'{raw}T00:00')
|
|
|
|
|
if backup_date is None:
|
|
|
|
|
raise CommandError(f'Invalid --backup-date: {raw}')
|
|
|
|
|
if backup_date.tzinfo is None:
|
|
|
|
|
backup_date = backup_date.replace(tzinfo=dt_timezone.utc)
|
|
|
|
|
else:
|
|
|
|
|
backup_date = datetime.fromtimestamp(os.path.getmtime(path), tz=dt_timezone.utc)
|
|
|
|
|
|
|
|
|
|
global_auto_download = options['global_auto_download'] == 'on'
|
|
|
|
|
try:
|
2026-10-02 12:14:03 +02:00
|
|
|
stats = AntennaPodImport(user, path, backup_date, global_auto_download,
|
2026-10-02 12:38:41 +02:00
|
|
|
offer_downloads=not options['no_downloads'],
|
2026-10-02 12:44:49 +02:00
|
|
|
quiet_diora_only=options['quiet_diora_only'],
|
|
|
|
|
replace=options['replace']).run(dry_run=options['dry_run'])
|
AntennaPod-Import und automatischer Download pro Podcast
- Neuer Befehl `manage.py import_antennapod <backup.db> --user <name> [--dry-run]
[--global-auto-download on|off] [--backup-date ...]`: übernimmt aus einer
AntennaPod-Datenbanksicherung die Abos (Abgleich über normalisierte URL,
fehlende werden angelegt, lokale übersprungen), die Podcast-Einstellungen
(Tempo, Intro/Outro, Lautstärke, Filter mit Phrasen in Anführungszeichen,
Mindestlänge, neue Folgen, „nicht aktualisieren“ -> ignorieren, automatischer
Download), den Zustand jeder Folge mit Zustand (gehört, neu, Position,
Favorit, Zeitpunkt), die Warteschlange (AntennaPods Reihenfolge zuerst) und
nimmt bekannte, dort nicht neue Folgen aus „Neu“. Neuere Änderungen in diora
bleiben, Favoriten werden vereinigt. Alles in einer Transaktion; ein zweiter
Lauf ändert nichts mehr.
- PodcastFeed.auto_download (Einstellung pro Podcast, in feeds/, state/ und
feeds/<id>/settings/) und PodcastSettings.auto_download_default (in settings/
und state/), mit dem neue Abos (Hinzufügen, OPML, gpodder, Import) starten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:41:04 +02:00
|
|
|
except AntennaPodImportError as e:
|
|
|
|
|
raise CommandError(str(e))
|
|
|
|
|
for line in summary_lines(stats, user.username, path, options['dry_run'], global_auto_download):
|
|
|
|
|
self.stdout.write(line)
|