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,
|
LastFmImport,
|
||||||
Scrobble,
|
Scrobble,
|
||||||
)
|
)
|
||||||
|
from scrobbles.mixins import Genre
|
||||||
|
|
||||||
|
|
||||||
class ScrobbleInline(admin.TabularInline):
|
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)
|
@admin.register(ChartRecord)
|
||||||
class ChartRecordAdmin(admin.ModelAdmin):
|
class ChartRecordAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created"
|
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.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_extensions.db.models import TimeStampedModel
|
from django_extensions.db.models import TimeStampedModel
|
||||||
|
from taggit.managers import TaggableManager
|
||||||
from scrobbles.utils import get_scrobbles_for_media
|
from scrobbles.utils import get_scrobbles_for_media
|
||||||
|
from taggit.models import TagBase, GenericTaggedItemBase
|
||||||
|
|
||||||
BNULL = {"blank": True, "null": True}
|
BNULL = {"blank": True, "null": True}
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
class ScrobblableMixin(TimeStampedModel):
|
||||||
SECONDS_TO_STALE = 1600
|
SECONDS_TO_STALE = 1600
|
||||||
|
|
||||||
@ -20,6 +38,8 @@ class ScrobblableMixin(TimeStampedModel):
|
|||||||
run_time_seconds = models.IntegerField(**BNULL)
|
run_time_seconds = models.IntegerField(**BNULL)
|
||||||
run_time_ticks = models.PositiveBigIntegerField(**BNULL)
|
run_time_ticks = models.PositiveBigIntegerField(**BNULL)
|
||||||
|
|
||||||
|
genre = TaggableManager(through=ObjectWithGenres)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from typing import Optional
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from books.models import Book
|
from books.models import Book
|
||||||
from django.apps import apps
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
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.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_extensions.db.models import TimeStampedModel
|
from django_extensions.db.models import TimeStampedModel
|
||||||
from scrobbles.mixins import ScrobblableMixin
|
from scrobbles.mixins import ObjectWithGenres, ScrobblableMixin
|
||||||
from scrobbles.utils import convert_to_seconds
|
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from videos.imdb import lookup_video_from_imdb
|
from videos.imdb import lookup_video_from_imdb
|
||||||
@ -27,7 +26,7 @@ class Series(TimeStampedModel):
|
|||||||
imdb_rating = models.FloatField(**BNULL)
|
imdb_rating = models.FloatField(**BNULL)
|
||||||
cover_image = models.ImageField(upload_to="videos/series/", **BNULL)
|
cover_image = models.ImageField(upload_to="videos/series/", **BNULL)
|
||||||
|
|
||||||
genres = TaggableManager()
|
genre = TaggableManager(through=ObjectWithGenres)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "series"
|
verbose_name_plural = "series"
|
||||||
@ -49,7 +48,10 @@ class Series(TimeStampedModel):
|
|||||||
).order_by("-timestamp")
|
).order_by("-timestamp")
|
||||||
|
|
||||||
def fix_metadata(self, force_update=False):
|
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:
|
if not imdb_dict:
|
||||||
logger.warn(f"No imdb data for {self}")
|
logger.warn(f"No imdb data for {self}")
|
||||||
return
|
return
|
||||||
@ -67,6 +69,9 @@ class Series(TimeStampedModel):
|
|||||||
fname = f"{self.name}_{self.uuid}.jpg"
|
fname = f"{self.name}_{self.uuid}.jpg"
|
||||||
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
||||||
|
|
||||||
|
if genres := imdb_dict.data.get("genres"):
|
||||||
|
self.genre.add(*genres)
|
||||||
|
|
||||||
|
|
||||||
class Video(ScrobblableMixin):
|
class Video(ScrobblableMixin):
|
||||||
COMPLETION_PERCENT = getattr(settings, "VIDEO_COMPLETION_PERCENT", 90)
|
COMPLETION_PERCENT = getattr(settings, "VIDEO_COMPLETION_PERCENT", 90)
|
||||||
@ -149,6 +154,9 @@ class Video(ScrobblableMixin):
|
|||||||
fname = f"{self.title}_{self.uuid}.jpg"
|
fname = f"{self.title}_{self.uuid}.jpg"
|
||||||
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
self.cover_image.save(fname, ContentFile(r.content), save=True)
|
||||||
|
|
||||||
|
if genres := imdb_dict.data.get("genres"):
|
||||||
|
self.genre.add(*genres)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_or_create(cls, data_dict: Dict) -> "Video":
|
def find_or_create(cls, data_dict: Dict) -> "Video":
|
||||||
"""Given a data dict from Jellyfin, does the heavy lifting of looking up
|
"""Given a data dict from Jellyfin, does the heavy lifting of looking up
|
||||||
|
|||||||
Reference in New Issue
Block a user