Add genres!
This commit is contained in:
25
vrobbler/apps/books/migrations/0011_book_genre.py
Normal file
25
vrobbler/apps/books/migrations/0011_book_genre.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("books", "0010_rename_run_time_book_run_time_seconds"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="book",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
25
vrobbler/apps/music/migrations/0017_track_genre.py
Normal file
25
vrobbler/apps/music/migrations/0017_track_genre.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("music", "0016_rename_run_time_track_run_time_seconds"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="track",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
25
vrobbler/apps/podcasts/migrations/0008_episode_genre.py
Normal file
25
vrobbler/apps/podcasts/migrations/0008_episode_genre.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("podcasts", "0007_rename_run_time_episode_run_time_seconds"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="episode",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -6,6 +6,7 @@ from scrobbles.models import (
|
||||
LastFmImport,
|
||||
Scrobble,
|
||||
)
|
||||
from scrobbles.mixins import Genre
|
||||
|
||||
|
||||
class ScrobbleInline(admin.TabularInline):
|
||||
@ -49,6 +50,14 @@ class KoReaderImportAdmin(ImportBaseAdmin):
|
||||
""""""
|
||||
|
||||
|
||||
@admin.register(Genre)
|
||||
class GenreAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"name",
|
||||
"source",
|
||||
)
|
||||
|
||||
|
||||
@admin.register(ChartRecord)
|
||||
class ChartRecordAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = "created"
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
(
|
||||
"scrobbles",
|
||||
"0032_rename_playback_position_scrobble_playback_position_seconds",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Genre",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
max_length=100, unique=True, verbose_name="name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"slug",
|
||||
models.SlugField(
|
||||
max_length=100, unique=True, verbose_name="slug"
|
||||
),
|
||||
),
|
||||
(
|
||||
"source",
|
||||
models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Genre",
|
||||
"verbose_name_plural": "Genres",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ObjectWithGenres",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"object_id",
|
||||
models.IntegerField(
|
||||
db_index=True, verbose_name="object ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"content_type",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(app_label)s_%(class)s_tagged_items",
|
||||
to="contenttypes.contenttype",
|
||||
verbose_name="content type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tag",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(app_label)s_%(class)s_items",
|
||||
to="scrobbles.genre",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -5,13 +5,31 @@ from uuid import uuid4
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
from taggit.managers import TaggableManager
|
||||
from scrobbles.utils import get_scrobbles_for_media
|
||||
from taggit.models import TagBase, GenericTaggedItemBase
|
||||
|
||||
BNULL = {"blank": True, "null": True}
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Genre(TagBase):
|
||||
source = models.CharField(max_length=255, **BNULL)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Genre"
|
||||
verbose_name_plural = "Genres"
|
||||
|
||||
|
||||
class ObjectWithGenres(GenericTaggedItemBase):
|
||||
tag = models.ForeignKey(
|
||||
Genre,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="%(app_label)s_%(class)s_items",
|
||||
)
|
||||
|
||||
|
||||
class ScrobblableMixin(TimeStampedModel):
|
||||
SECONDS_TO_STALE = 1600
|
||||
|
||||
@ -20,6 +38,8 @@ class ScrobblableMixin(TimeStampedModel):
|
||||
run_time_seconds = models.IntegerField(**BNULL)
|
||||
run_time_ticks = models.PositiveBigIntegerField(**BNULL)
|
||||
|
||||
genre = TaggableManager(through=ObjectWithGenres)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ from typing import Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from books.models import Book
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
||||
25
vrobbler/apps/sports/migrations/0013_sportevent_genre.py
Normal file
25
vrobbler/apps/sports/migrations/0013_sportevent_genre.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("sports", "0012_sport_default_event_run_time_seconds"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="sportevent",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
25
vrobbler/apps/videogames/migrations/0009_videogame_genre.py
Normal file
25
vrobbler/apps/videogames/migrations/0009_videogame_genre.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("videogames", "0008_rename_run_time_videogame_run_time_seconds"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="videogame",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 19:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"videos",
|
||||
"0009_rename_overview_series_plot_remove_series_tagline_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="series",
|
||||
old_name="genres",
|
||||
new_name="tags",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="video",
|
||||
name="cover_image",
|
||||
field=models.ImageField(
|
||||
blank=True, null=True, upload_to="videos/video/"
|
||||
),
|
||||
),
|
||||
]
|
||||
25
vrobbler/apps/videos/migrations/0011_video_tags.py
Normal file
25
vrobbler/apps/videos/migrations/0011_video_tags.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 19:42
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"),
|
||||
("videos", "0010_rename_genres_series_tags_alter_video_cover_image"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="video",
|
||||
name="tags",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="taggit.TaggedItem",
|
||||
to="taggit.Tag",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,43 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-14 22:27
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0033_genre_objectwithgenres"),
|
||||
("videos", "0011_video_tags"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="series",
|
||||
name="tags",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="video",
|
||||
name="tags",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="series",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="video",
|
||||
name="genre",
|
||||
field=taggit.managers.TaggableManager(
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -9,8 +9,7 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
from scrobbles.mixins import ScrobblableMixin
|
||||
from scrobbles.utils import convert_to_seconds
|
||||
from scrobbles.mixins import ObjectWithGenres, ScrobblableMixin
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from videos.imdb import lookup_video_from_imdb
|
||||
@ -27,7 +26,7 @@ class Series(TimeStampedModel):
|
||||
imdb_rating = models.FloatField(**BNULL)
|
||||
cover_image = models.ImageField(upload_to="videos/series/", **BNULL)
|
||||
|
||||
genres = TaggableManager()
|
||||
genre = TaggableManager(through=ObjectWithGenres)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "series"
|
||||
@ -49,7 +48,10 @@ class Series(TimeStampedModel):
|
||||
).order_by("-timestamp")
|
||||
|
||||
def fix_metadata(self, force_update=False):
|
||||
imdb_dict = lookup_video_from_imdb(self.name, kind="tv series")
|
||||
name_or_id = self.name
|
||||
if self.imdb_id:
|
||||
name_or_id = self.imdb_id
|
||||
imdb_dict = lookup_video_from_imdb(name_or_id)
|
||||
if not imdb_dict:
|
||||
logger.warn(f"No imdb data for {self}")
|
||||
return
|
||||
@ -67,6 +69,9 @@ class Series(TimeStampedModel):
|
||||
fname = f"{self.name}_{self.uuid}.jpg"
|
||||
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
||||
|
||||
if genres := imdb_dict.data.get("genres"):
|
||||
self.genre.add(*genres)
|
||||
|
||||
|
||||
class Video(ScrobblableMixin):
|
||||
COMPLETION_PERCENT = getattr(settings, "VIDEO_COMPLETION_PERCENT", 90)
|
||||
@ -149,6 +154,9 @@ class Video(ScrobblableMixin):
|
||||
fname = f"{self.title}_{self.uuid}.jpg"
|
||||
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
||||
|
||||
if genres := imdb_dict.data.get("genres"):
|
||||
self.genre.add(*genres)
|
||||
|
||||
@classmethod
|
||||
def find_or_create(cls, data_dict: Dict) -> "Video":
|
||||
"""Given a data dict from Jellyfin, does the heavy lifting of looking up
|
||||
|
||||
Reference in New Issue
Block a user