Can print an individual medium tracklist
This commit is contained in:
@@ -16,19 +16,28 @@ defmodule MusicLibrary.Records.TracklistPdf do
|
|||||||
@spec generate(MusicLibrary.Records.Record.t(), Release.t()) ::
|
@spec generate(MusicLibrary.Records.Record.t(), Release.t()) ::
|
||||||
{:ok, binary()} | {:error, term()}
|
{:ok, binary()} | {:error, term()}
|
||||||
def generate(record, release) do
|
def generate(record, release) do
|
||||||
markup = build_markup(record, release)
|
markup = build_markup(record, release.media)
|
||||||
Typst.render_to_pdf(markup)
|
Typst.render_to_pdf(markup)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_markup(record, release) do
|
@spec generate_medium(MusicLibrary.Records.Record.t(), Release.t(), integer()) ::
|
||||||
media_count = Release.media_count(release)
|
{:ok, binary()} | {:error, term()}
|
||||||
track_count = Enum.sum(Enum.map(release.media, &length(&1.tracks)))
|
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
|
header_count = if media_count > 1, do: media_count, else: 0
|
||||||
total_items = track_count + header_count
|
total_items = track_count + header_count
|
||||||
|
|
||||||
{columns, font_size, show_duration} = layout_params(total_items, media_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))
|
#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
|
end
|
||||||
|
|
||||||
defp build_media(release, media_count, font_size, show_duration) do
|
defp build_media(media, media_count, font_size, show_duration) do
|
||||||
release.media
|
media
|
||||||
|> Enum.map_join("\n", fn medium ->
|
|> Enum.map_join("\n", fn medium ->
|
||||||
build_medium(medium, media_count, font_size, show_duration)
|
build_medium(medium, media_count, font_size, show_duration)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ defmodule MusicLibraryWeb.Components.Release do
|
|||||||
media_count={MusicBrainz.Release.media_count(release_with_tracks)}
|
media_count={MusicBrainz.Release.media_count(release_with_tracks)}
|
||||||
selected_tracks={@selected_tracks}
|
selected_tracks={@selected_tracks}
|
||||||
myself={@myself}
|
myself={@myself}
|
||||||
|
record={@record}
|
||||||
/>
|
/>
|
||||||
</.async_result>
|
</.async_result>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,6 +152,7 @@ defmodule MusicLibraryWeb.Components.Release do
|
|||||||
attr :already_scrobbled, :boolean, required: true
|
attr :already_scrobbled, :boolean, required: true
|
||||||
attr :selected_tracks, :any, required: true
|
attr :selected_tracks, :any, required: true
|
||||||
attr :myself, :any, required: true
|
attr :myself, :any, required: true
|
||||||
|
attr :record, :any, default: nil
|
||||||
|
|
||||||
def medium(assigns) do
|
def medium(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
@@ -174,17 +176,29 @@ defmodule MusicLibraryWeb.Components.Release do
|
|||||||
{@medium.format}
|
{@medium.format}
|
||||||
</.badge>
|
</.badge>
|
||||||
</label>
|
</label>
|
||||||
<.button
|
<div class="flex items-center gap-2">
|
||||||
:if={@can_scrobble?}
|
<.button
|
||||||
size="sm"
|
:if={@record}
|
||||||
disabled={@already_scrobbled || MapSet.size(@selected_tracks) > 0}
|
variant="ghost"
|
||||||
phx-click="scrobble_medium"
|
size="sm"
|
||||||
phx-value-number={@medium.number}
|
phx-click="print_medium_tracklist"
|
||||||
phx-target={@myself}
|
phx-value-medium-number={@medium.number}
|
||||||
phx-disable-with={gettext("Scrobbling...")}
|
phx-target={@myself}
|
||||||
>
|
>
|
||||||
{medium_scrobble_label(@medium.format)}
|
<.icon name="hero-printer" class="h-4 w-4" />
|
||||||
</.button>
|
</.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>
|
</div>
|
||||||
<.track_list
|
<.track_list
|
||||||
medium_number={@medium.number}
|
medium_number={@medium.number}
|
||||||
@@ -431,6 +445,40 @@ defmodule MusicLibraryWeb.Components.Release do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
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
|
def handle_event("load_release_tracks", _params, socket) do
|
||||||
selected_release_id = socket.assigns.record.selected_release_id
|
selected_release_id = socket.assigns.record.selected_release_id
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,28 @@ defmodule MusicLibrary.Records.TracklistPdfTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "generate_medium/3" do
|
||||||
|
test "generates PDF for a single medium from a multi-disc release" do
|
||||||
|
record = build_record(%{title: "Marbles", artists: [%{name: "Marillion"}]})
|
||||||
|
api_response = MusicBrainz.Fixtures.Release.release_with_media(:marbles)
|
||||||
|
release = Release.from_api_response(api_response)
|
||||||
|
|
||||||
|
assert {:ok, <<@pdf_magic_bytes, _::binary>>} =
|
||||||
|
TracklistPdf.generate_medium(record, release, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error for non-existent medium number" do
|
||||||
|
record = build_record(%{title: "Test Album", artists: [%{name: "Test Artist"}]})
|
||||||
|
|
||||||
|
release =
|
||||||
|
build_release([
|
||||||
|
build_medium(1, [build_track(1, "Track One", 200_000)])
|
||||||
|
])
|
||||||
|
|
||||||
|
assert {:error, :medium_not_found} = TracklistPdf.generate_medium(record, release, 99)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "layout_params/2" do
|
describe "layout_params/2" do
|
||||||
test "single column for small track count" do
|
test "single column for small track count" do
|
||||||
assert {1, 8, true} = TracklistPdf.layout_params(10, 1)
|
assert {1, 8, true} = TracklistPdf.layout_params(10, 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user