Display tracks for records with a selected release

This commit is contained in:
Claudio Ortolina
2025-05-04 21:10:40 +01:00
parent 0952bcd399
commit f4fe638a95
7 changed files with 450 additions and 42 deletions
+50
View File
@@ -0,0 +1,50 @@
defmodule MusicBrainz.Release do
@enforce_keys [:id, :date, :barcode, :media]
defstruct [:id, :date, :barcode, :media]
defmodule Medium do
@enforce_keys [:format, :number, :track_count, :tracks]
defstruct [:format, :number, :track_count, :tracks]
end
defmodule Track do
@enforce_keys [:id, :title, :length, :number, :position]
defstruct [:id, :title, :length, :number, :position]
end
def media_count(release) do
Enum.count(release.media)
end
def from_api_response(r) do
%__MODULE__{
id: r["id"],
date: r["date"],
barcode: r["barcode"],
media: parse_media(r["media"])
}
end
defp parse_media(media) do
Enum.map(media, fn m ->
%Medium{
format: m["format"],
number: m["position"],
track_count: m["track-count"],
tracks: parse_tracks(m["tracks"])
}
end)
end
defp parse_tracks(tracks) do
Enum.map(tracks, fn t ->
%Track{
id: t["id"],
title: t["title"],
length: t["length"],
number: t["number"],
position: t["position"]
}
end)
end
end
@@ -33,6 +33,18 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
def handle_params(%{"id" => id}, _, socket) do
record = Records.get_record!(id)
socket =
if record.selected_release_id do
socket
|> assign_async(:release_with_tracks, fn ->
with {:ok, release} <- MusicBrainz.get_release(record.selected_release_id) do
{:ok, %{release_with_tracks: MusicBrainz.Release.from_api_response(release)}}
end
end)
else
socket
end
{:noreply,
socket
|> assign(:nav_section, :records)
@@ -216,8 +216,21 @@
<dt class="text-xs md:text-sm font-medium leading-6 text-zinc-900 dark:text-zinc-400">
{gettext("Collected release")}
</dt>
<dd class="mt-1 text-xs md:text-sm leading-6 text-zinc-700 dark:text-zinc-300 sm:col-span-2 sm:mt-0">
{selected_release_label(@record)}
<dd class="mt-1 text-xs md:text-sm flex justify-between leading-6 text-zinc-700 dark:text-zinc-300 sm:col-span-2 sm:mt-0">
<span>{selected_release_label(@record)}</span>
<button
:if={@record.selected_release_id}
phx-click={Fluxon.open_dialog("tracks-sheet")}
>
<span class="sr-only">{gettext("Show Tracks")}</span>
<.icon
name="hero-numbered-list"
class="-mt-1 h-5 w-5"
aria-hidden="true"
data-slot="icon"
/>
</button>
</dd>
</div>
<div
@@ -261,6 +274,51 @@
<pre><code class="text-xs sm:text-sm"><%= Jason.encode!(@record.musicbrainz_data, pretty: true) %></code></pre>
</details>
<Fluxon.Components.Sheet.sheet
:if={@record.selected_release_id}
id="tracks-sheet"
placement="right"
open
>
<h3 class="text-lg font-semibold mb-4">{gettext("Tracks")}</h3>
<div class="space-y-4">
<.async_result :let={release_with_tracks} assign={@release_with_tracks}>
<:loading>
<span class="sr-only">{gettext("Loading release with tracks")}</span>
<Fluxon.Components.Loading.loading />
</:loading>
<:failed :let={_failure}>
<div class="mt-4 text-sm leading-5 text-zinc-500 dark:text-zinc-400">
<.icon
name="hero-exclamation-triangle"
class="-mt-1 mr-1 h-5 w-5"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Error loading tracks")}
</div>
</:failed>
<div :for={medium <- release_with_tracks.media} class="space-y-4">
<h4
:if={MusicBrainz.Release.media_count(release_with_tracks) > 1}
class="text-sm font-semibold"
>
{medium.number}
</h4>
<ul class="list-disc pl-6">
<li
:for={track <- medium.tracks}
class="text-sm leading-5 text-zinc-700 dark:text-zinc-300"
>
{track.position} - {track.title}
</li>
</ul>
</div>
</.async_result>
</div>
</Fluxon.Components.Sheet.sheet>
<.modal
:if={@live_action == :edit}
id="record-modal"
+16
View File
@@ -673,6 +673,7 @@ msgstr ""
msgid "Albums"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Tracks"
@@ -758,3 +759,18 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Digital download"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Error loading tracks"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Loading release with tracks"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Show Tracks"
msgstr ""
+16
View File
@@ -673,6 +673,7 @@ msgstr ""
msgid "Albums"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Tracks"
@@ -758,3 +759,18 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Digital download"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Error loading tracks"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Loading release with tracks"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Show Tracks"
msgstr ""
@@ -6,12 +6,17 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do
import MusicLibraryWeb.RecordComponents,
only: [format_label: 1, type_label: 1, selected_release_label: 1]
alias MusicBrainz.Fixtures
alias MusicLibrary.Records.Record
describe "Edit record from show page" do
test "can navigate to the record edit form", %{conn: conn} do
record = record()
Req.Test.stub(MusicBrainz.API, fn conn ->
Req.Test.json(conn, Fixtures.Release.release(:marbles))
end)
conn
|> visit(~p"/collection/#{record.id}")
|> assert_has("a", text: "Edit")
@@ -25,6 +30,10 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do
record = record()
cover_url = ~p"/covers/#{record.id}?vsn=#{record.cover_hash}"
Req.Test.stub(MusicBrainz.API, fn conn ->
Req.Test.json(conn, Fixtures.Release.release(:marbles))
end)
session =
conn
|> visit(~p"/collection/#{record.id}")
@@ -1,51 +1,298 @@
{
"asin": "B0002Y4TM4",
"barcode": "5037300650128",
"country": "GB",
"cover-art-archive": {
"artwork": true,
"back": true,
"count": 14,
"darkened": false,
"front": true
},
"date": "2004-05-03",
"disambiguation": "",
"id": "d3f9b9e2-73f5-4b47-a2a7-2c2199aad608",
"packaging": "Jewel Case",
"packaging-id": "ec27701a-4a22-37f4-bfac-6616e0f9750a",
"quality": "normal",
"release-events": [
{
"area": {
"disambiguation": "",
"id": "8a754a16-0027-3a29-b6d7-2b40ea0481ed",
"iso-3166-1-codes": [
"GB"
],
"name": "United Kingdom",
"sort-name": "United Kingdom",
"type": null,
"type-id": null
"sort-name": "Europe",
"id": "89a675c2-3e37-3518-b83c-418bad59a85a",
"iso-3166-1-codes": [
"XE"
],
"disambiguation": "",
"type-id": null,
"name": "Europe"
},
"date": "2004-05-03"
}
],
"release-group": {
"media": [
{
"format": "CD",
"format-id": "9712d52a-4509-3d4b-a1a2-67c88c643e31",
"track-offset": 0,
"position": 1,
"tracks": [
{
"recording": {
"disambiguation": "",
"first-release-date": "2004-05-03",
"id": "20790e26-98e4-3ad3-a67f-b674758b942d",
"primary-type": "Album",
"primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc",
"secondary-type-ids": [],
"secondary-types": [],
"title": "Marbles"
"video": false,
"title": "The Invisible Man",
"length": 817613,
"id": "03c612c6-5a13-455f-ab2f-0fe40906250a"
},
"status": "Official",
"status-id": "4e304316-386d-3409-af2e-78857eec5cfe",
"text-representation": {
"language": "eng",
"script": "Latn"
"number": "1",
"id": "ea424146-3f1f-4651-b873-50e678a0fc7a",
"position": 1,
"length": 817613,
"title": "The Invisible Man"
},
"title": "Marbles"
{
"number": "2",
"recording": {
"id": "40a23122-cd24-466c-be01-187f6f3c9acf",
"length": 102746,
"title": "Marbles I",
"video": false,
"first-release-date": "2004-05-03",
"disambiguation": ""
},
"position": 2,
"length": 102746,
"id": "76e900f8-0156-4306-b490-1945ae96d240",
"title": "Marbles I"
},
{
"position": 3,
"length": 294293,
"id": "86187f02-6779-4ded-b84e-9d21d8abb8ca",
"number": "3",
"recording": {
"id": "f633fea4-8f29-4fda-a15f-86627f9048da",
"length": 294293,
"video": false,
"title": "Genie",
"first-release-date": "2004-05-03",
"disambiguation": ""
},
"title": "Genie"
},
{
"title": "Fantastic Place",
"recording": {
"id": "f703b00d-0216-4e08-88cd-875c183d1e3c",
"length": 372520,
"title": "Fantastic Place",
"video": false,
"first-release-date": "2004-05-03",
"disambiguation": ""
},
"number": "4",
"position": 4,
"id": "71c3f5aa-54c2-4af4-a7b8-e269879a8ba5",
"length": 372520
},
{
"title": "The Only Unforgivable Thing",
"length": 433293,
"position": 5,
"id": "0ecee1da-d20e-4d9e-9899-d5c1c15b0588",
"recording": {
"title": "The Only Unforgivable Thing",
"video": false,
"length": 433293,
"id": "af263b9a-d21c-4210-b61e-bff2be7c3424",
"disambiguation": "",
"first-release-date": "2004-05-03"
},
"number": "5"
},
{
"title": "Marbles II",
"position": 6,
"length": 122880,
"id": "b71db64f-4eee-4f44-a478-2d20e52b6adf",
"recording": {
"video": false,
"title": "Marbles II",
"id": "0b20b4eb-60ac-4c19-b4ba-8ac2ace07136",
"length": 122880,
"disambiguation": "",
"first-release-date": "2004-05-03"
},
"number": "6"
},
{
"recording": {
"first-release-date": "2004-05-03",
"disambiguation": "",
"length": 1078253,
"id": "08864ce2-20fd-49d4-8ef4-d916cec4f5c9",
"title": "Ocean Cloud",
"video": false
},
"number": "7",
"position": 7,
"length": 1078253,
"id": "0d3a4853-22f9-46c9-a8ee-354e62c6db87",
"title": "Ocean Cloud"
}
],
"title": "",
"track-count": 7
},
{
"format": "CD",
"format-id": "9712d52a-4509-3d4b-a1a2-67c88c643e31",
"track-offset": 0,
"title": "",
"track-count": 8,
"position": 2,
"tracks": [
{
"title": "Marbles III",
"recording": {
"first-release-date": "2004-05-03",
"disambiguation": "",
"id": "d576eda2-5f8c-4f8b-b741-c1ccc5aa1216",
"length": 111866,
"title": "Marbles III",
"video": false
},
"number": "1",
"position": 1,
"id": "683cae13-1de9-43f5-bb80-d72a50ffd539",
"length": 111866
},
{
"title": "The Damage",
"position": 2,
"length": 275186,
"id": "435ce489-3766-4b0c-be24-7a9506fc9e90",
"number": "2",
"recording": {
"length": 275186,
"id": "24e40cd4-1be3-4a41-a78f-8290a4a4b983",
"title": "The Damage",
"video": false,
"first-release-date": "2004-04-19",
"disambiguation": ""
}
},
{
"title": "Dont Hurt Yourself",
"recording": {
"first-release-date": "2004-05-03",
"disambiguation": "",
"length": 348746,
"id": "c9ada0dc-548f-4f06-86e2-ac4a4c55b1c6",
"title": "Dont Hurt Yourself",
"video": false
},
"number": "3",
"position": 3,
"id": "5548e295-5db5-4894-aa75-985255820362",
"length": 348746
},
{
"title": "Youre Gone",
"recording": {
"id": "9eac6df4-b14d-492c-b142-3aa081d842a9",
"length": 385720,
"video": false,
"title": "Youre Gone",
"first-release-date": "2004-04-19",
"disambiguation": ""
},
"number": "4",
"length": 385720,
"position": 4,
"id": "dd0f6929-cc08-416a-982d-454e34809aaf"
},
{
"title": "Angelina",
"id": "df6bd598-8333-4e3a-870a-63893dd87c01",
"position": 5,
"length": 462613,
"number": "5",
"recording": {
"first-release-date": "2004-05-03",
"disambiguation": "",
"length": 462613,
"id": "d718635b-8fd3-4d94-9fe3-68465737fed5",
"title": "Angelina",
"video": false
}
},
{
"title": "Drilling Holes",
"recording": {
"video": false,
"title": "Drilling Holes",
"length": 311666,
"id": "969d518d-5528-46c6-ac2b-ac2de5e78ba1",
"disambiguation": "",
"first-release-date": "2004-05-03"
},
"number": "6",
"position": 6,
"id": "d17e2d73-1365-4473-bf6a-b3250115f8ef",
"length": 311666
},
{
"recording": {
"video": false,
"title": "Marbles IV",
"id": "5f5c3acf-5701-4ddf-b1c8-173608cdbb7b",
"length": 86146,
"disambiguation": "",
"first-release-date": "2004-05-03"
},
"number": "7",
"position": 7,
"length": 86146,
"id": "acafcabd-c91a-4aa3-80fa-4f2c4b64a962",
"title": "Marbles IV"
},
{
"number": "8",
"recording": {
"first-release-date": "2004-05-03",
"disambiguation": "",
"id": "18b32261-ef00-46a1-a573-7c910cd68fb9",
"length": 730054,
"video": false,
"title": "Neverland"
},
"length": 730054,
"position": 8,
"id": "635021dd-a956-4830-af47-c7b9a5540deb",
"title": "Neverland"
}
]
}
],
"country": "XE",
"status": "Official",
"packaging-id": "ec27701a-4a22-37f4-bfac-6616e0f9750a",
"release-group": {
"first-release-date": "2004-05-03",
"primary-type": "Album",
"secondary-types": [],
"disambiguation": "",
"id": "20790e26-98e4-3ad3-a67f-b674758b942d",
"secondary-type-ids": [],
"title": "Marbles",
"primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc"
},
"disambiguation": "nondeluxe double CD with ocard",
"cover-art-archive": {
"count": 0,
"front": false,
"back": false,
"darkened": false,
"artwork": false
},
"barcode": "",
"asin": null,
"quality": "normal",
"date": "2004-05-03",
"title": "Marbles",
"text-representation": {
"script": "Latn",
"language": "eng"
},
"packaging": "Jewel Case",
"id": "d3f9b9e2-73f5-4b47-a2a7-2c2199aad608",
"status-id": "4e304316-386d-3409-af2e-78857eec5cfe"
}