models.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import logging
  2. from typing import Dict, Optional
  3. from uuid import uuid4
  4. from django.apps.config import cached_property
  5. from django.db import models
  6. from django.utils.translation import gettext_lazy as _
  7. from django_extensions.db.models import TimeStampedModel
  8. logger = logging.getLogger(__name__)
  9. BNULL = {"blank": True, "null": True}
  10. class Album(TimeStampedModel):
  11. uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
  12. name = models.CharField(max_length=255)
  13. year = models.IntegerField(**BNULL)
  14. musicbrainz_id = models.CharField(max_length=255, **BNULL)
  15. musicbrainz_releasegroup_id = models.CharField(max_length=255, **BNULL)
  16. musicbrainz_albumartist_id = models.CharField(max_length=255, **BNULL)
  17. def __str__(self):
  18. return self.name
  19. @property
  20. def mb_link(self):
  21. return f"https://musicbrainz.org/release/{self.musicbrainz_id}"
  22. class Artist(TimeStampedModel):
  23. uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
  24. name = models.CharField(max_length=255)
  25. musicbrainz_id = models.CharField(max_length=255, **BNULL)
  26. def __str__(self):
  27. return self.name
  28. @property
  29. def mb_link(self):
  30. return f"https://musicbrainz.org/artist/{self.musicbrainz_id}"
  31. class Track(TimeStampedModel):
  32. uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
  33. title = models.CharField(max_length=255, **BNULL)
  34. artist = models.ForeignKey(Artist, on_delete=models.DO_NOTHING)
  35. album = models.ForeignKey(Album, on_delete=models.DO_NOTHING, **BNULL)
  36. musicbrainz_id = models.CharField(max_length=255, **BNULL)
  37. run_time = models.CharField(max_length=8, **BNULL)
  38. run_time_ticks = models.PositiveBigIntegerField(**BNULL)
  39. def __str__(self):
  40. return f"{self.title} by {self.artist}"
  41. @property
  42. def mb_link(self):
  43. return f"https://musicbrainz.org/recording/{self.musicbrainz_id}"
  44. @cached_property
  45. def scrobble_count(self):
  46. return self.scrobble_set.filter(in_progress=False).count()
  47. @classmethod
  48. def find_or_create(
  49. cls, artist_dict: Dict, album_dict: Dict, track_dict: Dict
  50. ) -> Optional["Track"]:
  51. """Given a data dict from Jellyfin, does the heavy lifting of looking up
  52. the video and, if need, TV Series, creating both if they don't yet
  53. exist.
  54. """
  55. if not artist_dict.get('name') or not artist_dict.get(
  56. 'musicbrainz_id'
  57. ):
  58. logger.warning(
  59. f"No artist or artist musicbrainz ID found in message from Jellyfin, not scrobbling"
  60. )
  61. return
  62. artist, artist_created = Artist.objects.get_or_create(**artist_dict)
  63. if artist_created:
  64. logger.debug(f"Created new album {artist}")
  65. else:
  66. logger.debug(f"Found album {artist}")
  67. album, album_created = Album.objects.get_or_create(**album_dict)
  68. if album_created:
  69. logger.debug(f"Created new album {album}")
  70. else:
  71. logger.debug(f"Found album {album}")
  72. track_dict['album_id'] = getattr(album, "id", None)
  73. track_dict['artist_id'] = artist.id
  74. track, created = cls.objects.get_or_create(**track_dict)
  75. if created:
  76. logger.debug(f"Created new track: {track}")
  77. else:
  78. logger.debug(f"Found track {track}")
  79. return track