defmodule MusicLibraryWeb.CollectionLive.Show do use MusicLibraryWeb, :live_view import MusicLibrary.ListeningStats, only: [localize_scrobbled_at: 2] import MusicLibraryWeb.RecordComponents, only: [ record_cover: 1, record_debug_sheet: 1, record_external_links: 1, record_genres: 1, record_includes: 1, record_published_releases: 1, record_sets_list: 1, record_show_action_bar: 1, record_show_chat: 1, record_show_edit_modal: 1, record_show_release_sheet: 1, record_show_selected_release_row: 1, record_timestamps: 1, record_title_and_metadata: 1 ] alias MusicLibrary.{ListeningStats, Records, ScrobbleActivity} alias MusicLibrary.Records.Similarity alias MusicLibraryWeb.ErrorMessages alias MusicLibraryWeb.LiveHelpers.RecordShow @common_record_events [ "refresh_musicbrainz_data", "refresh_cover", "populate_genres", "extract_colors" ] @impl true def render(assigns) do ~H"""
<.record_cover record={@record} class="w-full rounded-lg drop-shadow-sm" />
<.record_show_action_bar record={@record} can_scrobble?={@can_scrobble?} chat_count={@chat_count} edit_path={~p"/collection/#{@record}/show/edit"} > <:dropdown_start> <.dropdown_link id={"actions-#{@record.id}-notes"} phx-click={MusicLibraryWeb.Components.Notes.open("record-notes-sheet")} > <.icon name="hero-pencil" class="mr-1 size-4" aria-hidden="true" data-slot="icon" /> {gettext("Notes")} <:dropdown_extra> <.dropdown_link id={"actions-#{@record.id}-regenerate-embeddings"} phx-click="regenerate_embeddings" > <.icon name="hero-sparkles" class="phx-click-loading:animate-shake mr-1 size-4" aria-hidden="true" data-slot="icon" /> {gettext("Regenerate embeddings")} <.record_title_and_metadata record={@record} /> <.record_external_links record={@record} />
<.record_genres record={@record} section={:collection} /> <.dl_row label={gettext("Purchased on")}> {Records.Record.format_as_date(@record.purchased_at)} <.dl_row label={gettext("Last listened at")}> {localize_scrobbled_at(@last_listened_track.scrobbled_at_uts, @timezone)} {gettext("Never")} <.link :if={@play_count > 0} navigate={~p"/scrobbled-tracks?#{%{query: "record:#{@record.id}"}}"} class="text-zinc-900 underline dark:text-zinc-100" > {ngettext("(1 scrobble)", "(%{count} scrobbles)", @play_count)} <.record_published_releases record={@record} /> <.record_show_selected_release_row record={@record} label={gettext("Collected release")} copy_selected_release_id?={true} /> <.record_includes record={@record} /> <.record_sets_list record_sets={@record_sets} />
<.record_timestamps record={@record} />
<.similar_records similar_records={@similar_records} /> <.record_debug_sheet record={@record} embedding_text={@embedding_text} /> <.record_show_release_sheet record={@record} show_print?={true} timezone={@timezone} /> <.live_component id="record-notes" sheet_id="record-notes-sheet" module={MusicLibraryWeb.Components.Notes} entity={:record} musicbrainz_id={@record.musicbrainz_id} /> <.record_show_chat record={@record} embedding_text={@embedding_text} /> <.record_show_edit_modal record={@record} live_action={@live_action} show_purchased_at={true} close_path={~p"/collection/#{@record}"} patch_path={~p"/collection/#{@record}"} />
""" end @impl true def mount(_params, _session, socket) do {:ok, socket |> assign(:current_section, :collection) |> assign(:can_scrobble?, ScrobbleActivity.can_scrobble?())} end @impl true def handle_params(%{"id" => id}, _, socket) do {:noreply, socket |> RecordShow.assign_common_record(id, gettext("Collection")) |> assign_collection_record_context()} end @impl true def handle_event("delete", _params, socket) do RecordShow.delete_record(socket, ~p"/collection") end def handle_event(event, _params, socket) when event in @common_record_events do RecordShow.handle_common_event(event, socket) end def handle_event("regenerate_embeddings", _params, socket) do record = socket.assigns.record case Similarity.generate_embedding_async(record) do {:ok, _worker} -> {:noreply, socket |> put_toast(:info, gettext("In progress - record will update automatically"))} {:error, reason} -> {:noreply, socket |> put_toast( :error, gettext("Error") <> ": " <> ErrorMessages.friendly_message(reason) )} end end def handle_event("scrobble_release", _params, socket) do RecordShow.scrobble_release(socket) end @impl true def handle_async(:scrobble_release, result, socket) do RecordShow.handle_scrobble_release(result, socket) end @impl true def handle_info({MusicLibraryWeb.Components.RecordForm, {:saved, record}}, socket) do RecordShow.handle_saved_record(socket, record, &assign_similar_records/1) end def handle_info({MusicLibraryWeb.Components.Chat, :chats_changed}, socket) do RecordShow.handle_chats_changed(socket) end def handle_info({MusicLibraryWeb.Components.Release, {:loaded, _release}}, socket) do RecordShow.handle_release_loaded(socket) end def handle_info({:update, record}, socket) do RecordShow.handle_record_update(socket, record, &assign_similar_records/1) end attr :similar_records, :list, required: true defp similar_records(assigns) do ~H"""

{gettext("Similar Records")}

{gettext("Based on genres, artists, and musical style")}
""" end defp assign_collection_record_context(socket) do record = socket.assigns.record socket |> assign(:last_listened_track, ListeningStats.get_last_listened_track(record)) |> assign(:play_count, ListeningStats.play_count(record)) |> assign_similar_records() end defp assign_similar_records(socket) do similar_records = Similarity.find_similar(socket.assigns.record.id, limit: 6, scope: :collection) assign(socket, :similar_records, similar_records) end end