Compare commits

...

10 Commits

Author SHA1 Message Date
dependabot[bot]
bbe08d0f43
Bump werkzeug from 2.3.8 to 3.0.1
Bumps [werkzeug](https://github.com/pallets/werkzeug) from 2.3.8 to 3.0.1.
- [Release notes](https://github.com/pallets/werkzeug/releases)
- [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/werkzeug/compare/2.3.8...3.0.1)

---
updated-dependencies:
- dependency-name: werkzeug
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-23 16:56:41 +00:00
f2f5446c18 fixed poetry lock 2023-12-23 19:52:24 +03:00
dependabot[bot]
f39e49ab39
Bump django-colorfield from 0.8.0 to 0.11.0 (#218)
Bumps [django-colorfield](https://github.com/fabiocaccamo/django-colorfield) from 0.8.0 to 0.11.0.
- [Release notes](https://github.com/fabiocaccamo/django-colorfield/releases)
- [Changelog](https://github.com/fabiocaccamo/django-colorfield/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fabiocaccamo/django-colorfield/compare/0.8.0...0.11.0)

---
updated-dependencies:
- dependency-name: django-colorfield
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexander Karpov <51019345+Alexander-D-Karpov@users.noreply.github.com>
2023-12-23 19:47:05 +03:00
dependabot[bot]
0f465e40de
Bump rawpy from 0.18.1 to 0.19.0 (#217)
Bumps [rawpy](https://github.com/letmaik/rawpy) from 0.18.1 to 0.19.0.
- [Release notes](https://github.com/letmaik/rawpy/releases)
- [Commits](https://github.com/letmaik/rawpy/compare/v0.18.1...v0.19.0)

---
updated-dependencies:
- dependency-name: rawpy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-23 19:46:32 +03:00
8f19d51410 updated files 2023-12-19 12:51:59 +03:00
07640962fa fixed music 2023-12-18 17:30:47 +03:00
fbddd3c1dc updated compose 2023-12-18 14:54:12 +03:00
79e865bf37 updated author creation 2023-12-18 14:51:24 +03:00
75d787f460 fixed music processing 2023-12-18 03:50:51 +03:00
51f3a6bb57 updated music file processing, major info retrieving update 2023-12-18 03:33:36 +03:00
14 changed files with 1189 additions and 983 deletions

View File

@ -13,6 +13,11 @@
from ..documents import FileDocument from ..documents import FileDocument
from .lema import lemmatize_and_remove_stopwords from .lema import lemmatize_and_remove_stopwords
"""
Calculus on types of searches:
https://new.akarpov.ru/files/FZUTFBIyfbdlDHVzxUNU
"""
class BaseSearch: class BaseSearch:
def __init__(self, queryset: QuerySet | None = None): def __init__(self, queryset: QuerySet | None = None):

View File

@ -92,9 +92,7 @@ def get_queryset(self):
): ):
return self.filter(BaseFileItem.objects.none()) return self.filter(BaseFileItem.objects.none())
return self.filter( return self.filter(
BaseFileItem.objects.cache().filter( BaseFileItem.objects.filter(user=self.request.user, parent__isnull=True)
user=self.request.user, parent__isnull=True
)
) )

View File

@ -7,10 +7,8 @@ class MusicConfig(AppConfig):
name = "akarpov.music" name = "akarpov.music"
def ready(self): def ready(self):
try:
import akarpov.music.signals # noqa F401 import akarpov.music.signals # noqa F401
except ImportError:
pass
try: try:
from akarpov.music.tasks import start_next_song from akarpov.music.tasks import start_next_song

View File

@ -0,0 +1,27 @@
# Generated by Django 4.2.8 on 2023-12-17 22:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("music", "0011_alter_playlist_private_userlistenhistory"),
]
operations = [
migrations.AddField(
model_name="album",
name="meta",
field=models.JSONField(blank=True, null=True),
),
migrations.AddField(
model_name="author",
name="albums",
field=models.ManyToManyField(related_name="authors", to="music.album"),
),
migrations.AddField(
model_name="author",
name="meta",
field=models.JSONField(blank=True, null=True),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 4.2.8 on 2023-12-18 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("music", "0012_album_meta_author_albums_author_meta"),
]
operations = [
migrations.RemoveField(
model_name="author",
name="albums",
),
migrations.AddField(
model_name="album",
name="authors",
field=models.ManyToManyField(related_name="albums", to="music.album"),
),
migrations.AlterField(
model_name="album",
name="name",
field=models.CharField(max_length=200, unique=True),
),
migrations.AlterField(
model_name="author",
name="name",
field=models.CharField(max_length=200, unique=True),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.8 on 2023-12-18 14:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("music", "0013_remove_author_albums_album_authors_alter_album_name_and_more"),
]
operations = [
migrations.AlterField(
model_name="album",
name="authors",
field=models.ManyToManyField(related_name="albums", to="music.author"),
),
]

View File

@ -8,8 +8,9 @@
class Author(BaseImageModel, ShortLinkModel): class Author(BaseImageModel, ShortLinkModel):
name = models.CharField(max_length=200) name = models.CharField(max_length=200, unique=True)
link = models.URLField(blank=True) link = models.URLField(blank=True)
meta = models.JSONField(blank=True, null=True)
def get_absolute_url(self): def get_absolute_url(self):
return reverse("music:author", kwargs={"slug": self.slug}) return reverse("music:author", kwargs={"slug": self.slug})
@ -19,8 +20,10 @@ def __str__(self):
class Album(BaseImageModel, ShortLinkModel): class Album(BaseImageModel, ShortLinkModel):
name = models.CharField(max_length=200) name = models.CharField(max_length=200, unique=True)
link = models.URLField(blank=True) link = models.URLField(blank=True)
meta = models.JSONField(blank=True, null=True)
authors = models.ManyToManyField("Author", related_name="albums")
def get_absolute_url(self): def get_absolute_url(self):
return reverse("music:album", kwargs={"slug": self.slug}) return reverse("music:album", kwargs={"slug": self.slug})

View File

@ -27,16 +27,28 @@ def load_track(
if album and type(album) is str and album.startswith("['"): if album and type(album) is str and album.startswith("['"):
album = album.replace("['", "").replace("']", "") album = album.replace("['", "").replace("']", "")
re_authors = []
if authors: if authors:
authors = [Author.objects.get_or_create(name=x)[0] for x in authors if authors] for x in authors:
else: try:
authors = [] re_authors.append(Author.objects.get(name=x))
except Author.DoesNotExist:
re_authors.append(Author.objects.create(name=x))
authors = re_authors
album_name = None
if album: if album:
if type(album) is str: if type(album) is str:
album = Album.objects.get_or_create(name=album)[0] album_name = album
elif type(album) is list: elif type(album) is list:
album = Album.objects.get_or_create(name=album[0])[0] album_name = album[0]
else: else:
album_name = None
if album_name:
try:
album = Album.objects.get(name=album_name)
except Album.DoesNotExist:
album = Album.objects.create(name=album_name)
if not album_name:
album = None album = None
if sng := Song.objects.filter( if sng := Song.objects.filter(
@ -78,7 +90,7 @@ def load_track(
str( str(
slugify( slugify(
GoogleTranslator(source="auto", target="en").translate( GoogleTranslator(source="auto", target="en").translate(
f"{song.name} {' '.join([x.name for x in song.authors])}", f"{song.name} {' '.join([x.name for x in authors])}",
target_language="en", target_language="en",
) )
) )
@ -101,7 +113,7 @@ def load_track(
album.save() album.save()
if authors: if authors:
song.authors.set(authors) song.authors.set([x.id for x in authors])
# set music meta # set music meta
tag = MutagenFile(song.file.path) tag = MutagenFile(song.file.path)
@ -124,9 +136,9 @@ def load_track(
data=f.read(), data=f.read(),
) )
) )
if "release" in kwargs: if "release" in kwargs and kwargs["release"]:
tag.tags.add(TORY(text=kwargs["release"])) tag.tags.add(TORY(text=kwargs["release"]))
if "genre" in kwargs: if "genre" in kwargs and kwargs["genre"]:
tag.tags.add(TCON(text=kwargs["genre"])) tag.tags.add(TCON(text=kwargs["genre"]))
tag.save() tag.save()

View File

@ -2,8 +2,6 @@
from django.conf import settings from django.conf import settings
from spotipy.oauth2 import SpotifyClientCredentials from spotipy.oauth2 import SpotifyClientCredentials
from akarpov.music.services.yandex import search_ym
def login() -> spotipy.Spotify: def login() -> spotipy.Spotify:
if not settings.MUSIC_SPOTIFY_ID or not settings.MUSIC_SPOTIFY_SECRET: if not settings.MUSIC_SPOTIFY_ID or not settings.MUSIC_SPOTIFY_SECRET:
@ -47,15 +45,7 @@ def get_track_info(name: str) -> dict:
# try to get genre # try to get genre
sp = login() sp = login()
genres = sp.album(res["album"]["external_urls"]["spotify"])["genres"] genres = sp.album(res["album"]["external_urls"]["spotify"])["genres"]
if not genres:
ym_info = search_ym(info["artist"] + " " + info["title"])
if ym_info and "genre" in ym_info:
info["genre"] = ym_info["genre"]
else:
genres = sp.artist(res["artists"][0]["external_urls"]["spotify"])["genres"]
if genres: if genres:
info["genre"] = genres[0] info["genre"] = genres[0]
else:
info["genre"] = genres[0]
return info return info

View File

@ -2,11 +2,13 @@
from random import randint from random import randint
from django.conf import settings from django.conf import settings
from django.utils.text import slugify from django.core.files import File
from yandex_music import Client, Playlist, Search, Track from yandex_music import Client, Playlist, Search, Track
from yandex_music.exceptions import NotFoundError
from akarpov.music import tasks from akarpov.music import tasks
from akarpov.music.models import Song, SongInQue from akarpov.music.models import Album as AlbumModel
from akarpov.music.models import Author, Song, SongInQue
from akarpov.music.services.db import load_track from akarpov.music.services.db import load_track
@ -43,7 +45,7 @@ def search_ym(name: str):
return info return info
def load_file_meta(track: int, user_id: int): def load_file_meta(track: int, user_id: int) -> str:
que = SongInQue.objects.create() que = SongInQue.objects.create()
client = login() client = login()
track = client.tracks(track)[0] # type: Track track = client.tracks(track)[0] # type: Track
@ -55,19 +57,27 @@ def load_file_meta(track: int, user_id: int):
name=track.title, album__name=track.albums[0].title name=track.title, album__name=track.albums[0].title
): ):
que.delete() que.delete()
return sng.first() return str(sng.first())
except IndexError: except IndexError:
que.delete() que.delete()
return return ""
filename = slugify(f"{track.artists[0].name} - {track.title}") filename = f"_{str(randint(10000, 9999999))}"
orig_path = f"{settings.MEDIA_ROOT}/{filename}.mp3" orig_path = f"{settings.MEDIA_ROOT}/{filename}.mp3"
album = track.albums[0] album = track.albums[0]
track.download(filename=orig_path, codec="mp3") track.download(filename=orig_path, codec="mp3")
img_pth = str(settings.MEDIA_ROOT + f"/_{str(randint(10000, 99999))}.png") img_pth = str(settings.MEDIA_ROOT + f"/_{str(randint(10000, 99999))}.png")
try:
track.download_cover(filename=img_pth) track.download_cover(filename=img_pth)
except NotFoundError:
img_pth = None
try:
lyrics = track.get_lyrics("LRC").fetch_lyrics()
except NotFoundError:
lyrics = ""
song = load_track( song = load_track(
orig_path, orig_path,
img_pth, img_pth,
@ -77,6 +87,9 @@ def load_file_meta(track: int, user_id: int):
track.title, track.title,
release=album.release_date, release=album.release_date,
genre=album.genre, genre=album.genre,
lyrics=lyrics,
explicit=track.explicit,
track_source=track.track_source,
) )
if os.path.exists(orig_path): if os.path.exists(orig_path):
os.remove(orig_path) os.remove(orig_path)
@ -96,3 +109,64 @@ def load_playlist(link: str, user_id: int):
tasks.load_ym_file_meta.apply_async( tasks.load_ym_file_meta.apply_async(
kwargs={"track": track.track.id, "user_id": user_id} kwargs={"track": track.track.id, "user_id": user_id}
) )
def update_album_info(album: AlbumModel) -> None:
client = login()
search = client.search(album.name, type_="album") # type: Search
if search.albums:
search_album = search.albums.results[0]
data = {
"name": search_album.title,
"tracks": search_album.track_count,
"explicit": search_album.explicit,
"year": search_album.year,
"genre": search_album.genre,
"description": search_album.description,
"type": search_album.type,
}
album.meta = data
image_path = str(settings.MEDIA_ROOT + f"/_{str(randint(10000, 99999))}.png")
if search_album.cover_uri:
search_album.download_cover(filename=image_path)
with open(image_path, "rb") as f:
album.image = File(f, name=image_path.split("/")[-1])
album.save()
os.remove(image_path)
authors = []
if search_album.artists:
for x in search_album.artists:
try:
authors.append(Author.objects.get(name=x.name))
except Author.DoesNotExist:
authors.append(Author.objects.create(name=x.name))
album.authors.set([x.id for x in authors])
album.save()
def update_author_info(author: Author) -> None:
client = login()
search = client.search(author.name, type_="artist") # type: Search
if search.artists:
search_artist = search.artists.results[0]
data = {
"name": search_artist.name,
"description": search_artist.description,
"genres": search_artist.genres,
}
author.meta = data
image_path = str(settings.MEDIA_ROOT + f"/_{str(randint(10000, 99999))}.png")
if not search_artist.cover:
author.save()
return
search_artist.cover.download(filename=image_path)
with open(image_path, "rb") as f:
author.image = File(f, name=image_path.split("/")[-1])
author.save()
os.remove(image_path)

View File

@ -4,6 +4,7 @@
from django.dispatch import receiver from django.dispatch import receiver
from akarpov.music.models import Album, Author, PlaylistSong, Song, SongUserRating from akarpov.music.models import Album, Author, PlaylistSong, Song, SongUserRating
from akarpov.music.services.yandex import update_album_info, update_author_info
@receiver(post_delete, sender=Song) @receiver(post_delete, sender=Song)
@ -16,15 +17,13 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
@receiver(post_save, sender=Author) @receiver(post_save, sender=Author)
def author_create(sender, instance, created, **kwargs): def author_create(sender, instance, created, **kwargs):
if created: if created:
# TODO: add logic to retrieve author info here update_author_info(instance)
return
@receiver(post_save, sender=Album) @receiver(post_save, sender=Album)
def album_create(sender, instance, created, **kwargs): def album_create(sender, instance, created, **kwargs):
if created: if created:
# TODO: add logic to retrieve author info here update_album_info(instance)
return
@receiver(post_save) @receiver(post_save)

View File

@ -3,7 +3,6 @@
set -o errexit set -o errexit
set -o nounset set -o nounset
python manage.py makemigrations
python manage.py migrate python manage.py migrate
rm -f './celerybeat.pid' rm -f './celerybeat.pid'
celery -A config.celery_app beat -l INFO celery -A config.celery_app beat -l INFO

1929
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,12 @@ django-crispy-forms = "^2.1"
crispy-bootstrap5 = "^0.7" crispy-bootstrap5 = "^0.7"
django-redis = "^5.2.0" django-redis = "^5.2.0"
django-ckeditor = "^6.5.1" django-ckeditor = "^6.5.1"
django-colorfield = "^0.8.0" django-colorfield = "^0.11.0"
djangorestframework = "^3.14.0" djangorestframework = "^3.14.0"
django-rest-auth = "^0.9.5" django-rest-auth = "^0.9.5"
django-cors-headers = "^4.0.0" django-cors-headers = "^4.0.0"
drf-spectacular = "^0.26.2" drf-spectacular = "^0.26.2"
werkzeug = {extras = ["watchdog"], version = "^2.3.4"} werkzeug = {extras = ["watchdog"], version = "^3.0.1"}
ipdb = "^0.13.13" ipdb = "^0.13.13"
watchfiles = "^0.18.1" watchfiles = "^0.18.1"
mypy = "^0.991" mypy = "^0.991"
@ -77,7 +77,7 @@ pydub = "^0.25.1"
python-mpd2 = "^3.0.5" python-mpd2 = "^3.0.5"
yandex-music = "^2.1.1" yandex-music = "^2.1.1"
pyjwt = "^2.8.0" pyjwt = "^2.8.0"
rawpy = "^0.18.1" rawpy = "^0.19.0"
xvfbwrapper = "^0.2.9" xvfbwrapper = "^0.2.9"
vtk = "^9.2.6" vtk = "^9.2.6"
ffmpeg-python = "^0.2.0" ffmpeg-python = "^0.2.0"