mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-21 23:50:59 +00:00
WIP
This commit is contained in:
parent
b676f7aeb4
commit
7b43323813
6 changed files with 88 additions and 53 deletions
|
@ -3,6 +3,7 @@ import {Socket} from "phoenix"
|
||||||
// import {LiveSocket} from "phoenix_live_view"
|
// import {LiveSocket} from "phoenix_live_view"
|
||||||
import {LiveSocket} from "/Users/chris/oss/phoenix_live_view/assets/js/phoenix_live_view"
|
import {LiveSocket} from "/Users/chris/oss/phoenix_live_view/assets/js/phoenix_live_view"
|
||||||
import topbar from "../vendor/topbar"
|
import topbar from "../vendor/topbar"
|
||||||
|
import Sortable from "../vendor/sortable"
|
||||||
|
|
||||||
let nowSeconds = () => Math.round(Date.now() / 1000)
|
let nowSeconds = () => Math.round(Date.now() / 1000)
|
||||||
let rand = (min, max) => Math.floor(Math.random() * (max - min) + min)
|
let rand = (min, max) => Math.floor(Math.random() * (max - min) + min)
|
||||||
|
|
|
@ -161,18 +161,24 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
# refetch user for fresh song count
|
# refetch user for fresh song count
|
||||||
user = Accounts.get_user!(user.id)
|
user = Accounts.get_user!(user.id)
|
||||||
|
|
||||||
|
multi =
|
||||||
|
Ecto.Multi.new()
|
||||||
|
|> 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))
|
||||||
|
{:ok, count - 1}
|
||||||
|
end)
|
||||||
|
|
||||||
multi =
|
multi =
|
||||||
changesets
|
changesets
|
||||||
|> Enum.with_index()
|
|> Enum.with_index()
|
||||||
|> Enum.reduce(Ecto.Multi.new(), fn {{ref, chset}, idx}, acc ->
|
|> Enum.reduce(multi, fn {{ref, chset}, i}, acc ->
|
||||||
chset =
|
Ecto.Multi.insert(acc, {:song, ref}, fn %{starting_position: pos_start} ->
|
||||||
chset
|
chset
|
||||||
|> Song.put_user(user)
|
|> Song.put_user(user)
|
||||||
|> Song.put_mp3_path()
|
|> Song.put_mp3_path()
|
||||||
|> Song.put_server_ip()
|
|> Song.put_server_ip()
|
||||||
|> Ecto.Changeset.put_change(:position, idx)
|
|> Ecto.Changeset.put_change(:position, pos_start + i + 1)
|
||||||
|
end)
|
||||||
Ecto.Multi.insert(acc, {:song, ref}, chset)
|
|
||||||
end)
|
end)
|
||||||
|> Ecto.Multi.run(:valid_songs_count, fn _repo, changes ->
|
|> Ecto.Multi.run(:valid_songs_count, fn _repo, changes ->
|
||||||
new_songs_count = changes |> Enum.filter(&match?({{:song, _ref}, _}, &1)) |> Enum.count()
|
new_songs_count = changes |> Enum.filter(&match?({{:song, _ref}, _}, &1)) |> Enum.count()
|
||||||
|
@ -244,7 +250,10 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_profile_songs(%Profile{} = profile, limit \\ 100) do
|
def list_profile_songs(%Profile{} = profile, limit \\ 100) do
|
||||||
from(s in Song, where: s.user_id == ^profile.user_id, limit: ^limit, order_by: [asc: :position])
|
from(s in Song,
|
||||||
|
where: s.user_id == ^profile.user_id,
|
||||||
|
limit: ^limit
|
||||||
|
)
|
||||||
|> order_by_playlist(:asc)
|
|> order_by_playlist(:asc)
|
||||||
|> Repo.replica().all()
|
|> Repo.replica().all()
|
||||||
end
|
end
|
||||||
|
@ -323,7 +332,7 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
def get_next_song(%Song{} = song, %Profile{} = profile) do
|
def get_next_song(%Song{} = song, %Profile{} = profile) do
|
||||||
next =
|
next =
|
||||||
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.position > ^song.position,
|
||||||
limit: 1
|
limit: 1
|
||||||
)
|
)
|
||||||
|> order_by_playlist(:asc)
|
|> order_by_playlist(:asc)
|
||||||
|
@ -335,8 +344,7 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
def get_prev_song(%Song{} = song, %Profile{} = profile) do
|
def get_prev_song(%Song{} = song, %Profile{} = profile) do
|
||||||
prev =
|
prev =
|
||||||
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.position < ^song.position,
|
||||||
order_by: [desc: s.inserted_at, desc: s.id],
|
|
||||||
limit: 1
|
limit: 1
|
||||||
)
|
)
|
||||||
|> order_by_playlist(:desc)
|
|> order_by_playlist(:desc)
|
||||||
|
@ -356,26 +364,26 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
_count -> {:error, :index_out_of_range}
|
_count -> {:error, :index_out_of_range}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Ecto.Multi.update_all(:dec_positions, fn _ ->
|
|> 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)
|
||||||
|> Ecto.Multi.update_all(:inc_positions, fn _ ->
|
|> 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)
|
||||||
|> Ecto.Multi.update_all(:position, fn _ ->
|
|> 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]]
|
||||||
)
|
)
|
||||||
end, [])
|
end)
|
||||||
|
|
||||||
case LiveBeats.Repo.transaction(multi) do
|
case LiveBeats.Repo.transaction(multi) do
|
||||||
{:ok, _} ->
|
{:ok, _} ->
|
||||||
|
@ -395,14 +403,26 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
|
|
||||||
def delete_song(%Song{} = song) do
|
def delete_song(%Song{} = song) do
|
||||||
delete_song_file(song)
|
delete_song_file(song)
|
||||||
|
old_index = song.position
|
||||||
|
|
||||||
Ecto.Multi.new()
|
Ecto.Multi.new()
|
||||||
|> Ecto.Multi.delete(:delete, song)
|
|> Ecto.Multi.delete(:delete, song)
|
||||||
|
|> multi_update_all(:dec_positions, fn _ ->
|
||||||
|
from(s in Song,
|
||||||
|
where: s.user_id == ^song.user_id,
|
||||||
|
where: s.position > ^old_index,
|
||||||
|
update: [inc: [position: -1]]
|
||||||
|
)
|
||||||
|
end)
|
||||||
|> update_user_songs_count(song.user_id, -1)
|
|> update_user_songs_count(song.user_id, -1)
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, _} -> :ok
|
{:ok, _} ->
|
||||||
other -> other
|
broadcast!(song.user_id, %Events.SongDeleted{song: song})
|
||||||
|
:ok
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -493,7 +513,7 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
||||||
from(s in query, order_by: [{^direction, s.inserted_at}, {^direction, s.id}])
|
from(s in query, order_by: [{^direction, s.position}])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp broadcast!(user_id, msg) when is_integer(user_id) do
|
defp broadcast!(user_id, msg) when is_integer(user_id) do
|
||||||
|
@ -509,4 +529,8 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
{:error, :songs_limit_exceeded}
|
{:error, :songs_limit_exceeded}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp multi_update_all(multi, name, func, opts \\ []) do
|
||||||
|
Ecto.Multi.update_all(multi, name, func, opts)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,4 +18,8 @@ defmodule LiveBeats.MediaLibrary.Events do
|
||||||
defmodule NewPosition do
|
defmodule NewPosition do
|
||||||
defstruct song: nil
|
defstruct song: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule SongDeleted do
|
||||||
|
defstruct song: nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -554,6 +554,7 @@ defmodule LiveBeatsWeb.CoreComponents do
|
||||||
attr :id, :any, default: nil
|
attr :id, :any, default: nil
|
||||||
attr :row_id, :any, default: false
|
attr :row_id, :any, default: false
|
||||||
attr :row_click, :any, default: nil
|
attr :row_click, :any, default: nil
|
||||||
|
attr :row_remove, :any, default: nil
|
||||||
attr :rows, :list, required: true
|
attr :rows, :list, required: true
|
||||||
attr :streamable, :boolean, default: false
|
attr :streamable, :boolean, default: false
|
||||||
attr :sortable_drop, :string, default: nil
|
attr :sortable_drop, :string, default: nil
|
||||||
|
@ -585,10 +586,14 @@ defmodule LiveBeatsWeb.CoreComponents do
|
||||||
phx-hook={@sortable_drop && "Sortable"}
|
phx-hook={@sortable_drop && "Sortable"}
|
||||||
data-drop={@sortable_drop}
|
data-drop={@sortable_drop}
|
||||||
>
|
>
|
||||||
<%= for {row, i} <- Enum.with_index(@rows) do %>
|
<tr
|
||||||
<tr id={@row_id && @row_id.(row)} class="hover:bg-gray-50">
|
:for={{row, i} <- Enum.with_index(@rows)}
|
||||||
<%= for col <- @col do %>
|
id={@row_id && @row_id.(row)}
|
||||||
|
phx-remove={@row_remove && @row_remove.(row)}
|
||||||
|
class="hover:bg-gray-50"
|
||||||
|
>
|
||||||
<td
|
<td
|
||||||
|
:for={col <- @col}
|
||||||
phx-click={@row_click && @row_click.(row)}
|
phx-click={@row_click && @row_click.(row)}
|
||||||
class={
|
class={
|
||||||
col[:class!] ||
|
col[:class!] ||
|
||||||
|
@ -599,9 +604,7 @@ defmodule LiveBeatsWeb.CoreComponents do
|
||||||
<%= render_slot(col, row) %>
|
<%= render_slot(col, row) %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<% end %>
|
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
<%= assigns[:page_title] || "LiveBeats" %>
|
<%= assigns[:page_title] || "LiveBeats" %>
|
||||||
</.live_title>
|
</.live_title>
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||||
<script defer type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
|
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}></script>
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -83,6 +83,7 @@ defmodule LiveBeatsWeb.ProfileLive do
|
||||||
rows={@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}
|
||||||
streamable
|
streamable
|
||||||
sortable_drop="row_dropped"
|
sortable_drop="row_dropped"
|
||||||
>
|
>
|
||||||
|
@ -108,7 +109,7 @@ defmodule LiveBeatsWeb.ProfileLive do
|
||||||
role="button"
|
role="button"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<%= song.title %> <%= @count %>
|
<%= song.title %>
|
||||||
</:col>
|
</:col>
|
||||||
<:col :let={{_id, song}} label="Artist"><%= song.artist %></:col>
|
<:col :let={{_id, song}} label="Artist"><%= song.artist %></:col>
|
||||||
<:col
|
<:col
|
||||||
|
@ -153,7 +154,6 @@ defmodule LiveBeatsWeb.ProfileLive do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(
|
|> assign(
|
||||||
count: 0,
|
|
||||||
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,
|
||||||
|
@ -247,7 +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, update(socket, :songs, &(&1 ++ songs))}
|
{:noreply, Enum.reduce(songs, socket, fn song, acc -> stream_insert(acc, :songs, song) end)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({MediaLibrary, %MediaLibrary.Events.SongDeleted{song: song}}, socket) do
|
||||||
|
{:noreply, stream_delete(socket, :songs, song)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info({MediaLibrary, {:ping, ping}}, socket) do
|
def handle_info({MediaLibrary, {:ping, ping}}, socket) do
|
||||||
|
|
Loading…
Reference in a new issue