Skip to content

Commit c8fc872

Browse files
committed
feat:geospatial artist localization editor, including itowns map, and musicBrainz api usage (WIP)
refactored api naming
1 parent 7b56fdd commit c8fc872

38 files changed

+947
-136
lines changed

.env

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
MUSIC_LIBRARY_PATH=D:\mp3
22
MUSIC_PATH_MAX_LENGTH=500
33
PG_DATA=./pgdata
4+
5+
API_HEADER_MAIL=

README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Frontend :
1010
- App : [Vue3](https://vuejs.org/), [Nuxt3](https://nuxt.com/), [Pinia](https://pinia.vuejs.org/)
1111
- Component Framework : [Vuetify3](https://vuetifyjs.com/)
1212
- Music Vizualiser : [projectM](https://github.com/projectM-visualizer/projectm) via [webAssembly](https://webassembly.org/)
13+
- Geospatial Map : [Itowns](https://www.itowns-project.org/) and [Threejs](https://threejs.org/)
1314

1415
Backend :
1516
- Server : [python3](https://www.python.org/), [Django](https://www.djangoproject.com/), [Nginx](https://nginx.org/)
@@ -21,14 +22,20 @@ Backend :
2122

2223
## Features
2324

24-
Navigate through your local music library, play songs in your web browser and enjoy Milkdrop integration.
25+
- Music library accessing in readonly mode to your local library
26+
- Database persistency with metadata exposition
27+
- Geospatial music localizer editor & filtering - WIP (ne_110m_admin_0_countries)
28+
- ProjectM Milkdrop vizualisation
2529

2630
Musics from your fileSystem are saved in the database on the first reach.
2731

2832
For now, music files can be populated with [/filesystem/list](http://localhost:8000/filesystem/list) endpoint.
2933

3034
A checksum is calculated based on music name, artist name and album name, to make the database resilient to filesystem changes.
3135

36+
- metadata editor (db persistency) :
37+
- In addition of reading music file metadata, you can fetch artist country via [musicbrainz](https://musicbrainz.org). If the match has a unique result with a score of 100, it will be saved automatically, otherwise you'll have to check and save.
38+
3239
## Setup
3340

3441
[Docker](https://www.docker.com/)
@@ -40,6 +47,7 @@ edit .env example before first launch :
4047
- `MUSIC_LIBRARY_PATH` : path to your music folder
4148
- `MUSIC_PATH_MAX_LENGTH` : default 500 character, increase that number if your music paths exceed this value, then restart backend.
4249
- `PG_DATA` : database persistency.
50+
- `API_HEADER_MAIL` : mandatory to use properly musicbrainz.org API
4351

4452
## Start up
4553

@@ -52,7 +60,7 @@ edit .env example before first launch :
5260
- Nginx serving your music filesystem(`http://localhost:8081/music`)
5361
- database access (`0.0.0.0:54333`)
5462
- postgres/postgres
55-
- database and schema : `music`.`library`
63+
- database and schema : `music`.`public`
5664
- external database manager recommended [DBeaver](https://dbeaver.io/), for as long as I don't provide pgadmin container.
5765

5866
### Notes

docker-compose.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ services:
2121
read_only: true
2222
- ./services/backend:/app
2323
- ./data:/data
24+
env_file:
25+
- .env
26+
- .env.dev
2427
environment:
2528
- GDAL_LIBRARY_PATH=/usr/lib/ogdi/4.1/libgdal.so
26-
- MUSIC_PATH_MAX_LENGTH=${MUSIC_PATH_MAX_LENGTH}
29+
- APP_VERSION=0.1
30+
- APP_NAME=web-music-library
2731
depends_on:
2832
- db
2933
- nginx
@@ -38,7 +42,6 @@ services:
3842
- 54333:5432
3943
volumes:
4044
- ${PG_DATA}:/var/lib/postgresql/data
41-
- ./services/migration_install.sql:/docker-entrypoint-initdb.d/migration_install.sql
4245

4346
nginx:
4447
image: nginx:stable-alpine3.19-perl

services/backend/backend-django/appbackend/settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
# Application definition
3232

3333
INSTALLED_APPS = [
34-
"filesystem.apps.FilesystemConfig",
34+
"music.apps.MusicConfig",
3535
"rest_framework",
3636
"corsheaders",
3737
"django.contrib.gis",

services/backend/backend-django/appbackend/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
from django.urls import include, path
2020

2121
urlpatterns = [
22-
path("filesystem/", include("filesystem.urls")),
22+
path("api/", include("music.urls")),
2323
path("admin/", admin.site.urls),
2424
]

services/backend/backend-django/filesystem/migrations/0001_initial.py

-71
This file was deleted.

services/backend/backend-django/filesystem/urls.py

-12
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from rest_framework import status
2+
from rest_framework.response import Response
3+
from rest_framework.views import APIView
4+
from ..models import Music, Artist, MusicSerializer, ArtistSerializer
5+
from django.http import JsonResponse
6+
7+
8+
class IncrementMusicPlayedView(APIView):
9+
def post(self, request, item_id):
10+
try:
11+
music = Music.objects.get(id=item_id)
12+
music.count_played += 1
13+
music.save()
14+
except Music.DoesNotExist:
15+
return Response(status=status.HTTP_404_NOT_FOUND)
16+
return JsonResponse(MusicSerializer(music).data)
17+
18+
19+
class ArtistsListView(APIView):
20+
def get(self, request):
21+
artists = Artist.objects.all()
22+
return JsonResponse(
23+
{"artists": [ArtistSerializer(artist).data for artist in artists]}
24+
)
25+
26+
27+
class ArtistUpdateView(APIView):
28+
def put(self, request, artist_id):
29+
artist = Artist.objects.get(id=artist_id)
30+
serializer = ArtistSerializer(artist, data=request.data)
31+
if serializer.is_valid():
32+
serializer.save()
33+
return Response(status=status.HTTP_204_NO_CONTENT)
34+
else:
35+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import os
2+
from django.http import HttpResponse, JsonResponse
3+
from rest_framework.views import APIView
4+
import musicbrainzngs
5+
6+
MAIL = os.environ.get("API_HEADER_MAIL")
7+
APP_VERSION = os.environ.get("APP_VERSION")
8+
APP_NAME = os.environ.get("APP_NAME")
9+
10+
11+
def configure_musicbrainz():
12+
if not MAIL:
13+
return HttpResponse("API header mail is required", status=403)
14+
15+
musicbrainzngs.set_useragent(APP_NAME, APP_VERSION, contact=MAIL)
16+
musicbrainzngs.set_format(fmt="json")
17+
musicbrainzngs.set_rate_limit(limit_or_interval=1.0, new_requests=1)
18+
19+
20+
class ArtistCountryView(APIView):
21+
def get(self, request, artist_name):
22+
configure_musicbrainz()
23+
# Search for the artist, returning 25 results by default.
24+
search_data = musicbrainzngs.search_artists(
25+
query=f"artist:{artist_name}", limit=1, offset=None, strict=True
26+
)
27+
country = (
28+
"UNKNOWN"
29+
if search_data["count"] == 0
30+
else search_data["artists"][0]["country"]
31+
)
32+
return JsonResponse({"artist_name": artist_name, "country": country})

services/backend/backend-django/filesystem/views.py services/backend/backend-django/music/api_views/filesystem_views.py

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import os
22
from django.http import JsonResponse
3-
from rest_framework import status
4-
from rest_framework.response import Response
5-
from rest_framework.views import APIView
6-
from .models import Music, Artist, Album, MusicSerializer
3+
from ..models import Music, Artist, Album, MusicSerializer
74
from uuid import uuid4
85

96
MUSIC_PATH = "/music"
@@ -55,14 +52,3 @@ def updateDb(music_name: str, music_path: str, artist_name: str, album_name: str
5552
music.save()
5653
existing_music = Music.objects.filter(checksum=music.checksum).first()
5754
return existing_music
58-
59-
60-
class IncrementMusicPlayedView(APIView):
61-
def post(self, request, item_id):
62-
try:
63-
music = Music.objects.get(id=item_id)
64-
music.count_played += 1
65-
music.save()
66-
except Music.DoesNotExist:
67-
return Response(status=status.HTTP_404_NOT_FOUND)
68-
return JsonResponse(MusicSerializer(music).data)
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.apps import AppConfig
22

33

4-
class FilesystemConfig(AppConfig):
4+
class MusicConfig(AppConfig):
55
default_auto_field = "django.db.models.BigAutoField"
6-
name = "filesystem"
6+
name = "music"

services/backend/backend-django/music/fixtures/dump_countries.json

+1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)