Can print an individual medium tracklist

This commit is contained in:
Claudio Ortolina
2026-03-09 14:52:26 +00:00
parent 4c6baa6e13
commit c352ad4809
3 changed files with 97 additions and 18 deletions
+16 -7
View File
@@ -16,19 +16,28 @@ defmodule MusicLibrary.Records.TracklistPdf do
@spec generate(MusicLibrary.Records.Record.t(), Release.t()) ::
{:ok, binary()} | {:error, term()}
def generate(record, release) do
markup = build_markup(record, release)
markup = build_markup(record, release.media)
Typst.render_to_pdf(markup)
end
defp build_markup(record, release) do
media_count = Release.media_count(release)
track_count = Enum.sum(Enum.map(release.media, &length(&1.tracks)))
@spec generate_medium(MusicLibrary.Records.Record.t(), Release.t(), integer()) ::
{:ok, binary()} | {:error, term()}
def generate_medium(record, release, medium_number) do
case Release.get_medium(release, medium_number) do
nil -> {:error, :medium_not_found}
medium -> generate(record, %{release | media: [medium]})
end
end
defp build_markup(record, media) do
media_count = length(media)
track_count = Enum.sum(Enum.map(media, &length(&1.tracks)))
header_count = if media_count > 1, do: media_count, else: 0
total_items = track_count + header_count
{columns, font_size, show_duration} = layout_params(total_items, media_count)
content = build_media(release, media_count, font_size, show_duration)
content = build_media(media, media_count, font_size, show_duration)
"""
#set page(width: 120mm, height: 120mm, margin: 5mm, background: rect(width: 100%, height: 100%, stroke: 0.5pt + black))
@@ -56,8 +65,8 @@ defmodule MusicLibrary.Records.TracklistPdf do
"""
end
defp build_media(release, media_count, font_size, show_duration) do
release.media
defp build_media(media, media_count, font_size, show_duration) do
media
|> Enum.map_join("\n", fn medium ->
build_medium(medium, media_count, font_size, show_duration)
end)
+59 -11
View File
@@ -136,6 +136,7 @@ defmodule MusicLibraryWeb.Components.Release do
media_count={MusicBrainz.Release.media_count(release_with_tracks)}
selected_tracks={@selected_tracks}
myself={@myself}
record={@record}
/>
</.async_result>
</div>
@@ -151,6 +152,7 @@ defmodule MusicLibraryWeb.Components.Release do
attr :already_scrobbled, :boolean, required: true
attr :selected_tracks, :any, required: true
attr :myself, :any, required: true
attr :record, :any, default: nil
def medium(assigns) do
~H"""
@@ -174,17 +176,29 @@ defmodule MusicLibraryWeb.Components.Release do
{@medium.format}
</.badge>
</label>
<.button
:if={@can_scrobble?}
size="sm"
disabled={@already_scrobbled || MapSet.size(@selected_tracks) > 0}
phx-click="scrobble_medium"
phx-value-number={@medium.number}
phx-target={@myself}
phx-disable-with={gettext("Scrobbling...")}
>
{medium_scrobble_label(@medium.format)}
</.button>
<div class="flex items-center gap-2">
<.button
:if={@record}
variant="ghost"
size="sm"
phx-click="print_medium_tracklist"
phx-value-medium-number={@medium.number}
phx-target={@myself}
>
<.icon name="hero-printer" class="h-4 w-4" />
</.button>
<.button
:if={@can_scrobble?}
size="sm"
disabled={@already_scrobbled || MapSet.size(@selected_tracks) > 0}
phx-click="scrobble_medium"
phx-value-number={@medium.number}
phx-target={@myself}
phx-disable-with={gettext("Scrobbling...")}
>
{medium_scrobble_label(@medium.format)}
</.button>
</div>
</div>
<.track_list
medium_number={@medium.number}
@@ -431,6 +445,40 @@ defmodule MusicLibraryWeb.Components.Release do
{:noreply, socket}
end
def handle_event("print_medium_tracklist", %{"medium-number" => number}, socket)
when release_loaded?(socket.assigns) do
release = socket.assigns.release_with_tracks.result
record = socket.assigns.record
{number, ""} = Integer.parse(number)
case TracklistPdf.generate_medium(record, release, number) do
{:ok, pdf_binary} ->
filename = "#{record.title} - Disc #{number} - Tracklist.pdf"
{:noreply,
push_event(socket, "music_library:download", %{
data: Base.encode64(pdf_binary),
filename: filename,
content_type: "application/pdf"
})}
{:error, reason} ->
Logger.error("Error generating medium tracklist PDF: #{inspect(reason)}")
put_toast!(
:error,
gettext("Error generating tracklist PDF") <>
": " <> ErrorMessages.friendly_message(reason)
)
{:noreply, socket}
end
end
def handle_event("print_medium_tracklist", _params, socket) do
{:noreply, socket}
end
def handle_event("load_release_tracks", _params, socket) do
selected_release_id = socket.assigns.record.selected_release_id