Compare commits

..

No commits in common. "115356df2d37aea8c5e5469b4f8eeb19fe490b36" and "446b861a672658b503ed1ce84a2ee8e280d4aab7" have entirely different histories.

5 changed files with 21 additions and 51 deletions

View file

@ -163,7 +163,6 @@ defmodule LiveBeats.MediaLibrary do
multi = multi =
Ecto.Multi.new() Ecto.Multi.new()
|> lock_playlist(user.id)
|> Ecto.Multi.run(:starting_position, fn repo, _changes -> |> Ecto.Multi.run(:starting_position, fn repo, _changes ->
count = repo.one(from s in Song, where: s.user_id == ^user.id, select: count(s.id)) count = repo.one(from s in Song, where: s.user_id == ^user.id, select: count(s.id))
{:ok, count - 1} {:ok, count - 1}
@ -359,28 +358,27 @@ defmodule LiveBeats.MediaLibrary do
multi = multi =
Ecto.Multi.new() Ecto.Multi.new()
|> lock_playlist(song.user_id) |> Ecto.Multi.run(:valid_index, fn repo, _changes ->
|> Ecto.Multi.run(:index, fn repo, _changes ->
case repo.one(from s in Song, where: s.user_id == ^song.user_id, select: count(s.id)) do case repo.one(from s in Song, where: s.user_id == ^song.user_id, select: count(s.id)) do
count when new_index < count -> {:ok, new_index} count when new_index < count -> {:ok, count}
count -> {:ok, count - 1} _count -> {:error, :index_out_of_range}
end end
end) end)
|> multi_update_all(:dec_positions, fn %{index: new_index} -> |> multi_update_all(:dec_positions, fn _ ->
from(s in Song, from(s in Song,
where: s.user_id == ^song.user_id and s.id != ^song.id, where: s.user_id == ^song.user_id and s.id != ^song.id,
where: s.position > ^old_index and s.position <= ^new_index, where: s.position > ^old_index and s.position <= ^new_index,
update: [inc: [position: -1]] update: [inc: [position: -1]]
) )
end) end)
|> multi_update_all(:inc_positions, fn %{index: new_index} -> |> multi_update_all(:inc_positions, fn _ ->
from(s in Song, from(s in Song,
where: s.user_id == ^song.user_id and s.id != ^song.id, where: s.user_id == ^song.user_id and s.id != ^song.id,
where: s.position < ^old_index and s.position >= ^new_index, where: s.position < ^old_index and s.position >= ^new_index,
update: [inc: [position: 1]] update: [inc: [position: 1]]
) )
end) end)
|> multi_update_all(:position, fn %{index: new_index} -> |> multi_update_all(:position, fn _ ->
from(s in Song, from(s in Song,
where: s.id == ^song.id, where: s.id == ^song.id,
update: [set: [position: ^new_index]] update: [set: [position: ^new_index]]
@ -408,7 +406,6 @@ defmodule LiveBeats.MediaLibrary do
old_index = song.position old_index = song.position
Ecto.Multi.new() Ecto.Multi.new()
|> lock_playlist(song.user_id)
|> Ecto.Multi.delete(:delete, song) |> Ecto.Multi.delete(:delete, song)
|> multi_update_all(:dec_positions, fn _ -> |> multi_update_all(:dec_positions, fn _ ->
from(s in Song, from(s in Song,
@ -536,8 +533,4 @@ defmodule LiveBeats.MediaLibrary do
defp multi_update_all(multi, name, func, opts \\ []) do defp multi_update_all(multi, name, func, opts \\ []) do
Ecto.Multi.update_all(multi, name, func, opts) Ecto.Multi.update_all(multi, name, func, opts)
end end
defp lock_playlist(%Ecto.Multi{} = multi, user_id) do
Repo.multi_transaction_lock(multi, :playlist, user_id)
end
end end

View file

@ -13,7 +13,7 @@ defmodule LiveBeats.MediaLibrary.Song do
field :date_recorded, :naive_datetime field :date_recorded, :naive_datetime
field :date_released, :naive_datetime field :date_released, :naive_datetime
field :duration, :integer field :duration, :integer
field :status, Ecto.Enum, values: [stopped: 1, playing: 2, paused: 3], default: :stopped field :status, Ecto.Enum, values: [stopped: 1, playing: 2, paused: 3]
field :title, :string field :title, :string
field :attribution, :string field :attribution, :string
field :mp3_url, :string field :mp3_url, :string

View file

@ -4,16 +4,6 @@ defmodule LiveBeats.Repo do
adapter: Ecto.Adapters.Postgres adapter: Ecto.Adapters.Postgres
def replica, do: LiveBeats.config([:replica]) def replica, do: LiveBeats.config([:replica])
@locks %{playlist: 1}
def multi_transaction_lock(multi, scope, id) when is_atom(scope) and is_integer(id) do
scope_int = Map.fetch!(@locks, scope)
Ecto.Multi.run(multi, scope, fn repo, _changes ->
repo.query("SELECT pg_advisory_xact_lock(#{scope_int}, #{id})")
end)
end
end end
defmodule LiveBeats.ReplicaRepo do defmodule LiveBeats.ReplicaRepo do

View file

@ -582,7 +582,7 @@ defmodule LiveBeatsWeb.CoreComponents do
<tbody <tbody
id={"#{@id}-body"} id={"#{@id}-body"}
class="bg-white divide-y divide-gray-100" class="bg-white divide-y divide-gray-100"
phx-update={@streamable && "stream"} phx-stream={@streamable}
phx-hook={@sortable_drop && "Sortable"} phx-hook={@sortable_drop && "Sortable"}
data-drop={@sortable_drop} data-drop={@sortable_drop}
> >

View file

@ -16,7 +16,6 @@ defmodule LiveBeatsWeb.ProfileLive do
<%= if @owns_profile? do %> <%= if @owns_profile? do %>
(you) (you)
<% end %> <% end %>
<%= @songs_count %>
</div> </div>
<.link href={@profile.external_homepage_url} target="_blank" class="text-sm text-gray-600"> <.link href={@profile.external_homepage_url} target="_blank" class="text-sm text-gray-600">
<.icon name={:code} /> <span class=""><%= url_text(@profile.external_homepage_url) %></span> <.icon name={:code} /> <span class=""><%= url_text(@profile.external_homepage_url) %></span>
@ -60,8 +59,8 @@ defmodule LiveBeatsWeb.ProfileLive do
total_count={@presences_count} total_count={@presences_count}
/> />
<div id="dialogs" phx-update="stream"> <div id="dialogs" phx-update="append">
<%= for {_id, song} <- if(@owns_profile?, do: @streams.songs, else: []), id = "delete-modal-#{song.id}" do %> <%= for {_id, song} <- if(@owns_profile?, do: @songs, else: []), id = "delete-modal-#{song.id}" do %>
<.modal <.modal
id={id} id={id}
on_confirm={ on_confirm={
@ -81,18 +80,14 @@ defmodule LiveBeatsWeb.ProfileLive do
<.table <.table
id="songs" id="songs"
rows={@streams.songs} rows={@songs}
row_id={fn {id, _song} -> id end} row_id={fn {id, _song} -> id end}
row_click={fn {_id, song} -> JS.push("play_or_pause", value: %{id: song.id}) end} row_click={fn {_id, song} -> JS.push("play_or_pause", value: %{id: song.id}) end}
row_remove={fn {id, _song} -> hide("##{id}") end} row_remove={fn {id, _song} -> hide("##{id}") end}
streamable streamable
sortable_drop="row_dropped" sortable_drop="row_dropped"
> >
<:col <:col :let={{_id, song}} label="Title" class!="px-6 py-3 text-sm font-medium text-gray-900 min-w-[20rem] cursor-pointer">
:let={{_id, song}}
label="Title"
class!="px-6 py-3 text-sm font-medium text-gray-900 md:min-w-[20rem] cursor-pointer"
>
<span :if={song.status == :playing} class="flex pt-1 relative mr-2 w-4"> <span :if={song.status == :playing} class="flex pt-1 relative mr-2 w-4">
<span class="w-3 h-3 animate-ping bg-purple-400 rounded-full absolute"></span> <span class="w-3 h-3 animate-ping bg-purple-400 rounded-full absolute"></span>
<.icon name={:volume_up} class="h-5 w-5 -mt-1 -ml-1" aria-label="Playing" role="button" /> <.icon name={:volume_up} class="h-5 w-5 -mt-1 -ml-1" aria-label="Playing" role="button" />
@ -122,7 +117,7 @@ defmodule LiveBeatsWeb.ProfileLive do
label="Attribution" label="Attribution"
class="max-w-5xl break-words text-gray-600 font-light" class="max-w-5xl break-words text-gray-600 font-light"
> >
<%= song.position %> <%= song.attribution %>
</:col> </:col>
<:col :let={{_id, song}} label="Duration"><%= MP3Stat.to_mmss(song.duration) %></:col> <:col :let={{_id, song}} label="Duration"><%= MP3Stat.to_mmss(song.duration) %></:col>
<:col :let={{_id, song}} :if={@owns_profile?} label=""> <:col :let={{_id, song}} :if={@owns_profile?} label="">
@ -156,18 +151,15 @@ defmodule LiveBeatsWeb.ProfileLive do
song.id song.id
end end
songs = MediaLibrary.list_profile_songs(profile, 50)
socket = socket =
socket socket
|> assign( |> assign(
active_song_id: active_song_id, active_song_id: active_song_id,
active_profile_id: current_user.active_profile_user_id, active_profile_id: current_user.active_profile_user_id,
profile: profile, profile: profile,
owns_profile?: MediaLibrary.owns_profile?(current_user, profile), owns_profile?: MediaLibrary.owns_profile?(current_user, profile)
songs_count: Enum.count(songs)
) )
|> stream(:songs, songs) |> stream_songs()
|> assign_presences() |> assign_presences()
{:ok, socket, temporary_assigns: [presences: %{}]} {:ok, socket, temporary_assigns: [presences: %{}]}
@ -209,7 +201,6 @@ defmodule LiveBeatsWeb.ProfileLive do
def handle_event("row_dropped", %{"id" => dom_id, "old" => old_idx, "new" => new_idx}, socket) do def handle_event("row_dropped", %{"id" => dom_id, "old" => old_idx, "new" => new_idx}, socket) do
"songs-" <> id = dom_id "songs-" <> id = dom_id
song = MediaLibrary.get_song!(id) song = MediaLibrary.get_song!(id)
if song.user_id == socket.assigns.current_user.id and song.position == old_idx do if song.user_id == socket.assigns.current_user.id and song.position == old_idx do
:ok = MediaLibrary.update_song_position(song, new_idx) :ok = MediaLibrary.update_song_position(song, new_idx)
{:noreply, socket} {:noreply, socket}
@ -256,19 +247,11 @@ defmodule LiveBeatsWeb.ProfileLive do
end end
def handle_info({MediaLibrary, %MediaLibrary.Events.SongsImported{songs: songs}}, socket) do def handle_info({MediaLibrary, %MediaLibrary.Events.SongsImported{songs: songs}}, socket) do
{:noreply, {:noreply, Enum.reduce(songs, socket, fn song, acc -> stream_insert(acc, :songs, song) end)}
Enum.reduce(songs, socket, fn song, acc ->
acc
|> update(:songs_count, &(&1 + 1))
|> stream_insert(:songs, song)
end)}
end end
def handle_info({MediaLibrary, %MediaLibrary.Events.SongDeleted{song: song}}, socket) do def handle_info({MediaLibrary, %MediaLibrary.Events.SongDeleted{song: song}}, socket) do
{:noreply, {:noreply, stream_delete(socket, :songs, song)}
socket
|> update(:songs_count, &(&1 - 1))
|> stream_delete(:songs, song)}
end end
def handle_info({MediaLibrary, {:ping, ping}}, socket) do def handle_info({MediaLibrary, {:ping, ping}}, socket) do
@ -355,6 +338,10 @@ defmodule LiveBeatsWeb.ProfileLive do
socket socket
end end
defp stream_songs(socket) do
stream(socket, :songs, MediaLibrary.list_profile_songs(socket.assigns.profile, 50))
end
defp assign_presences(socket) do defp assign_presences(socket) do
socket = assign(socket, presences_count: 0, presences: %{}, presence_ids: %{}) socket = assign(socket, presences_count: 0, presences: %{}, presence_ids: %{})