+9
-3
backend/src/backend/api/albums.py
+9
-3
backend/src/backend/api/albums.py
···
18
18
from backend.models import Album, Artist, Track, TrackLike, get_db
19
19
from backend.schemas import TrackResponse
20
20
from backend.storage import storage
21
-
from backend.utilities.aggregations import get_comment_counts, get_like_counts
21
+
from backend.utilities.aggregations import (
22
+
get_comment_counts,
23
+
get_like_counts,
24
+
get_track_tags,
25
+
)
22
26
from backend.utilities.hashing import CHUNK_SIZE
23
27
24
28
router = APIRouter(prefix="/albums", tags=["albums"])
···
262
266
tracks = track_result.scalars().all()
263
267
track_ids = [track.id for track in tracks]
264
268
if track_ids:
265
-
like_counts, comment_counts = await asyncio.gather(
269
+
like_counts, comment_counts, track_tags = await asyncio.gather(
266
270
get_like_counts(db, track_ids),
267
271
get_comment_counts(db, track_ids),
272
+
get_track_tags(db, track_ids),
268
273
)
269
274
else:
270
-
like_counts, comment_counts = {}, {}
275
+
like_counts, comment_counts, track_tags = {}, {}, {}
271
276
272
277
# get authenticated user's likes for this album's tracks only
273
278
liked_track_ids: set[int] | None = None
···
310
315
liked_track_ids,
311
316
like_counts,
312
317
comment_counts,
318
+
track_tags=track_tags,
313
319
)
314
320
for track in tracks
315
321
]
+6
-2
backend/src/backend/api/tracks/playback.py
+6
-2
backend/src/backend/api/tracks/playback.py
···
10
10
from backend._internal.auth import get_session
11
11
from backend.models import Artist, Track, TrackLike, get_db
12
12
from backend.schemas import TrackResponse
13
-
from backend.utilities.aggregations import get_like_counts
13
+
from backend.utilities.aggregations import get_like_counts, get_track_tags
14
14
15
15
from .router import router
16
16
···
50
50
raise HTTPException(status_code=404, detail="track not found")
51
51
52
52
like_counts = await get_like_counts(db, [track_id])
53
+
track_tags = await get_track_tags(db, [track_id])
53
54
54
55
return await TrackResponse.from_track(
55
-
track, liked_track_ids=liked_track_ids, like_counts=like_counts
56
+
track,
57
+
liked_track_ids=liked_track_ids,
58
+
like_counts=like_counts,
59
+
track_tags=track_tags,
56
60
)
57
61
58
62
+32
frontend/src/routes/track/[id]/+page.svelte
+32
frontend/src/routes/track/[id]/+page.svelte
···
407
407
{/if}
408
408
</div>
409
409
410
+
{#if track.tags && track.tags.length > 0}
411
+
<div class="track-tags">
412
+
{#each track.tags as tag}
413
+
<a href="/tag/{encodeURIComponent(tag)}" class="tag-badge">{tag}</a>
414
+
{/each}
415
+
</div>
416
+
{/if}
417
+
410
418
<div class="track-stats">
411
419
<span class="plays">{track.play_count} {track.play_count === 1 ? 'play' : 'plays'}</span>
412
420
{#if track.like_count && track.like_count > 0}
···
768
776
769
777
.track-stats .separator {
770
778
font-size: 0.7rem;
779
+
}
780
+
781
+
.track-tags {
782
+
display: flex;
783
+
flex-wrap: wrap;
784
+
gap: 0.5rem;
785
+
justify-content: center;
786
+
}
787
+
788
+
.tag-badge {
789
+
display: inline-block;
790
+
padding: 0.25rem 0.6rem;
791
+
background: rgba(138, 179, 255, 0.15);
792
+
color: #8ab3ff;
793
+
border-radius: 4px;
794
+
font-size: 0.85rem;
795
+
font-weight: 500;
796
+
text-decoration: none;
797
+
transition: all 0.15s;
798
+
}
799
+
800
+
.tag-badge:hover {
801
+
background: rgba(138, 179, 255, 0.25);
802
+
color: #a8c8ff;
771
803
}
772
804
773
805
.mobile-side-buttons {