[videos] More dedup scripts
This commit is contained in:
@ -0,0 +1,84 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Find TV Series with duplicate imdb_id values and merge videos to the newest series"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be done without making changes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--auto",
|
||||
action="store_true",
|
||||
help="Automatically fix duplicates without prompting",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from videos.models import Series
|
||||
|
||||
dry_run = options["dry_run"]
|
||||
auto = options["auto"]
|
||||
|
||||
duplicate_imdb_ids = (
|
||||
Series.objects.filter(imdb_id__isnull=False)
|
||||
.exclude(imdb_id="")
|
||||
.values("imdb_id")
|
||||
.annotate(count=models.Count("id"))
|
||||
.filter(count__gt=1)
|
||||
)
|
||||
|
||||
if not duplicate_imdb_ids.exists():
|
||||
self.stdout.write("No duplicate imdb_id values found in Series")
|
||||
return
|
||||
|
||||
self.stdout.write(
|
||||
f"Found {duplicate_imdb_ids.count()} imdb_ids with duplicates in Series"
|
||||
)
|
||||
|
||||
for dup in duplicate_imdb_ids:
|
||||
imdb_id = dup["imdb_id"]
|
||||
series_list = list(
|
||||
Series.objects.filter(imdb_id=imdb_id).order_by("created")
|
||||
)
|
||||
|
||||
self.stdout.write(f"\nimdb_id: {imdb_id}")
|
||||
self.stdout.write(f" Found {len(series_list)} series:")
|
||||
|
||||
for s in series_list:
|
||||
video_count = s.video_set.count()
|
||||
self.stdout.write(
|
||||
f" id={s.id}, name='{s.name}', created={s.created}, videos={video_count}"
|
||||
)
|
||||
|
||||
keeper = series_list[-1]
|
||||
duplicates = series_list[:-1]
|
||||
|
||||
self.stdout.write(f" Keeping: id={keeper.id} ('{keeper.name}')")
|
||||
self.stdout.write(f" Merging from: {[d.id for d in duplicates]}")
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING(" [DRY RUN] Would move videos"))
|
||||
continue
|
||||
|
||||
if not auto:
|
||||
confirm = input(" Move videos from duplicates to keeper? [y/N] ")
|
||||
if confirm.lower() != "y":
|
||||
self.stdout.write(" Skipped")
|
||||
continue
|
||||
|
||||
for dup_series in duplicates:
|
||||
video_count = dup_series.video_set.count()
|
||||
if video_count > 0:
|
||||
dup_series.video_set.update(tv_series=keeper)
|
||||
self.stdout.write(
|
||||
f" Moved {video_count} videos from id={dup_series.id} to id={keeper.id}"
|
||||
)
|
||||
|
||||
dup_series.delete()
|
||||
self.stdout.write(f" Deleted series id={dup_series.id}")
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("\nDone!"))
|
||||
@ -0,0 +1,98 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Find TV Series with duplicate names, merge Videos into the one with 'tt' IMDB id"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be done without making changes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--auto",
|
||||
action="store_true",
|
||||
help="Automatically fix duplicates without prompting",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from videos.models import Series
|
||||
|
||||
dry_run = options["dry_run"]
|
||||
auto = options["auto"]
|
||||
|
||||
duplicate_names = (
|
||||
Series.objects.values("name")
|
||||
.annotate(count=models.Count("id"))
|
||||
.filter(count__gt=1)
|
||||
)
|
||||
|
||||
if not duplicate_names.exists():
|
||||
self.stdout.write("No duplicate Series names found")
|
||||
return
|
||||
|
||||
self.stdout.write(f"Found {duplicate_names.count()} names with duplicates")
|
||||
|
||||
for dup in duplicate_names:
|
||||
name = dup["name"]
|
||||
series_list = list(Series.objects.filter(name=name).order_by("created"))
|
||||
|
||||
self.stdout.write(f"\nSeries name: {name}")
|
||||
self.stdout.write(f" Found {len(series_list)} series:")
|
||||
|
||||
for s in series_list:
|
||||
video_count = s.video_set.count()
|
||||
self.stdout.write(
|
||||
f" id={s.id}, imdb_id='{s.imdb_id}', created={s.created}, videos={video_count}"
|
||||
)
|
||||
|
||||
tt_series = None
|
||||
for s in series_list:
|
||||
if s.imdb_id and s.imdb_id.startswith("tt"):
|
||||
tt_series = s
|
||||
break
|
||||
|
||||
if not tt_series:
|
||||
self.stdout.write(
|
||||
self.style.ERROR(
|
||||
f" ERROR: No series with 'tt' IMDB id found, skipping"
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
duplicates = [s for s in series_list if s.id != tt_series.id]
|
||||
self.stdout.write(
|
||||
f" Keeper (tt): id={tt_series.id} ('{tt_series.imdb_id}')"
|
||||
)
|
||||
self.stdout.write(f" Merging from: {[d.id for d in duplicates]}")
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING(" [DRY RUN] Would move videos"))
|
||||
continue
|
||||
|
||||
if not auto:
|
||||
confirm = input(" Move videos from duplicates to keeper? [y/N] ")
|
||||
if confirm.lower() != "y":
|
||||
self.stdout.write(" Skipped")
|
||||
continue
|
||||
|
||||
total_moved = 0
|
||||
for dup_series in duplicates:
|
||||
video_count = dup_series.video_set.count()
|
||||
if video_count > 0:
|
||||
dup_series.video_set.update(tv_series=tt_series)
|
||||
self.stdout.write(
|
||||
f" Moved {video_count} videos from id={dup_series.id} to id={tt_series.id}"
|
||||
)
|
||||
total_moved += video_count
|
||||
|
||||
dup_series.delete()
|
||||
self.stdout.write(f" Deleted series id={dup_series.id}")
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f" Merged {total_moved} videos total")
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("\nDone!"))
|
||||
Reference in New Issue
Block a user