mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-21 15:41:00 +00:00
Add profiles
This commit is contained in:
parent
5fa2944627
commit
c45510cb6e
11 changed files with 350 additions and 109 deletions
|
@ -1,7 +1,7 @@
|
|||
import "phoenix_html"
|
||||
import {Socket} from "phoenix"
|
||||
// import {LiveSocket} from "./phoenix_live_view"
|
||||
import {LiveSocket} from "phoenix_live_view"
|
||||
import {LiveSocket} from "./phoenix_live_view"
|
||||
// import {LiveSocket} from "phoenix_live_view"
|
||||
import topbar from "../vendor/topbar"
|
||||
|
||||
let nowSeconds = () => Math.round(Date.now() / 1000)
|
||||
|
@ -54,9 +54,8 @@ Hooks.AudioPlayer = {
|
|||
this.play({sync: true})
|
||||
}
|
||||
})
|
||||
this.handleEvent("pause", () => {
|
||||
this.pause()
|
||||
})
|
||||
this.handleEvent("pause", () => this.pause())
|
||||
this.handleEvent("stop", () => this.stop())
|
||||
},
|
||||
|
||||
play(opts = {}){
|
||||
|
@ -76,10 +75,19 @@ Hooks.AudioPlayer = {
|
|||
this.player.pause()
|
||||
},
|
||||
|
||||
stop(){
|
||||
clearInterval(this.progressTimer)
|
||||
this.player.pause()
|
||||
this.player.currentTime = 0
|
||||
this.updateProgress()
|
||||
this.duration.innerText = ""
|
||||
this.currentTime.innerText = ""
|
||||
},
|
||||
|
||||
updateProgress(){
|
||||
if(isNaN(this.player.duration)){ return false }
|
||||
if(this.player.currentTime >= this.player.duration){
|
||||
this.pushEvent("next-song-auto")
|
||||
this.pushEvent("next_song_auto")
|
||||
clearInterval(this.progressTimer)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,6 +6,17 @@ defmodule LiveBeats.Accounts do
|
|||
alias LiveBeats.Accounts.{User, Identity}
|
||||
|
||||
@admin_emails ["chris@chrismccord.com"]
|
||||
@pubsub LiveBeats.PubSub
|
||||
|
||||
def subscribe(user_id) do
|
||||
Phoenix.PubSub.subscribe(@pubsub, topic(user_id))
|
||||
end
|
||||
|
||||
def unsubscribe(user_id) do
|
||||
Phoenix.PubSub.unsubscribe(@pubsub, topic(user_id))
|
||||
end
|
||||
|
||||
defp topic(user_id), do: "user:#{user_id}"
|
||||
|
||||
def list_users(opts) do
|
||||
Repo.all(from u in User, limit: ^Keyword.fetch!(opts, :limit))
|
||||
|
@ -47,6 +58,27 @@ defmodule LiveBeats.Accounts do
|
|||
"""
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
def get_user_by!(fields), do: Repo.get_by!(User, fields)
|
||||
|
||||
def update_active_profile(%User{active_profile_user_id: same_id} = current_user, same_id) do
|
||||
current_user
|
||||
end
|
||||
|
||||
def update_active_profile(%User{} = current_user, profile_user_id) do
|
||||
{1, _} =
|
||||
Repo.update_all(from(u in User, where: u.id == ^current_user.id),
|
||||
set: [active_profile_user_id: profile_user_id]
|
||||
)
|
||||
|
||||
Phoenix.PubSub.broadcast!(
|
||||
@pubsub,
|
||||
topic(current_user.id),
|
||||
{__MODULE__, :active_profile_changed, current_user, %{user_id: profile_user_id}}
|
||||
)
|
||||
|
||||
%User{current_user | active_profile_user_id: profile_user_id}
|
||||
end
|
||||
|
||||
## User registration
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule LiveBeats.Accounts.User do
|
|||
field :confirmed_at, :naive_datetime
|
||||
field :role, :string, default: "subscriber"
|
||||
field :profile_tagline, :string
|
||||
field :active_profile_user_id, :id
|
||||
|
||||
has_many :identities, Identity
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
require Logger
|
||||
import Ecto.Query, warn: false
|
||||
alias LiveBeats.{Repo, MP3Stat, Accounts}
|
||||
alias LiveBeats.MediaLibrary.{Song, Genre}
|
||||
alias LiveBeats.MediaLibrary.{Profile, Song, Genre}
|
||||
alias Ecto.{Multi, Changeset}
|
||||
|
||||
@pubsub LiveBeats.PubSub
|
||||
|
@ -16,15 +16,25 @@ defmodule LiveBeats.MediaLibrary do
|
|||
defdelegate playing?(song), to: Song
|
||||
defdelegate paused?(song), to: Song
|
||||
|
||||
def subscribe(%Accounts.User{} = user) do
|
||||
Phoenix.PubSub.subscribe(@pubsub, topic(user.id))
|
||||
def subscribe_to_profile(%Profile{} = profile, from \\ nil) do
|
||||
Phoenix.PubSub.subscribe(@pubsub, topic(profile.user_id))
|
||||
end
|
||||
|
||||
def unsubscribe_to_profile(%Profile{} = profile) do
|
||||
Phoenix.PubSub.unsubscribe(@pubsub, topic(profile.user_id))
|
||||
end
|
||||
|
||||
def local_filepath(filename_uuid) when is_binary(filename_uuid) do
|
||||
Path.join("priv/uploads/songs", filename_uuid)
|
||||
end
|
||||
|
||||
def play_song(%Song{id: id}), do: play_song(id)
|
||||
def can_control_playback?(%Accounts.User{} = user, %Song{} = song) do
|
||||
user.id == song.user_id
|
||||
end
|
||||
|
||||
def play_song(%Song{id: id}) do
|
||||
play_song(id)
|
||||
end
|
||||
|
||||
def play_song(id) do
|
||||
song = get_song!(id)
|
||||
|
@ -60,7 +70,14 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> Repo.transaction()
|
||||
|
||||
elapsed = elapsed_playback(new_song)
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(song.user_id), {:play, song, %{elapsed: elapsed}})
|
||||
|
||||
Phoenix.PubSub.broadcast!(
|
||||
@pubsub,
|
||||
topic(song.user_id),
|
||||
{__MODULE__, :play, song, %{elapsed: elapsed}}
|
||||
)
|
||||
|
||||
new_song
|
||||
end
|
||||
|
||||
def pause_song(%Song{} = song) do
|
||||
|
@ -79,36 +96,36 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> Multi.update_all(:now_paused, fn _ -> pause_query end, [])
|
||||
|> Repo.transaction()
|
||||
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(song.user_id), {:pause, song})
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(song.user_id), {__MODULE__, :pause, song})
|
||||
end
|
||||
|
||||
def play_next_song_auto(user_id) do
|
||||
song = get_current_active_song(user_id) || get_first_song(user_id)
|
||||
def play_next_song_auto(%Profile{} = profile) do
|
||||
song = get_current_active_song(profile) || get_first_song(profile)
|
||||
|
||||
if song && elapsed_playback(song) >= song.duration - @auto_next_threshold_seconds do
|
||||
song
|
||||
|> get_next_song()
|
||||
|> get_next_song(profile)
|
||||
|> play_song()
|
||||
end
|
||||
end
|
||||
|
||||
def play_prev_song(user_id) do
|
||||
song = get_current_active_song(user_id) || get_first_song(user_id)
|
||||
def play_prev_song(%Profile{} = profile) do
|
||||
song = get_current_active_song(profile) || get_first_song(profile)
|
||||
|
||||
if prev_song = get_prev_song(song) do
|
||||
if prev_song = get_prev_song(song, profile) do
|
||||
play_song(prev_song)
|
||||
end
|
||||
end
|
||||
|
||||
def play_next_song(user_id) do
|
||||
song = get_current_active_song(user_id) || get_first_song(user_id)
|
||||
def play_next_song(%Profile{} = profile) do
|
||||
song = get_current_active_song(profile) || get_first_song(profile)
|
||||
|
||||
if next_song = get_next_song(song) do
|
||||
if next_song = get_next_song(song, profile) do
|
||||
play_song(next_song)
|
||||
end
|
||||
end
|
||||
|
||||
defp topic(user_id), do: "room:#{user_id}"
|
||||
defp topic(user_id) when is_integer(user_id), do: "profile:#{user_id}"
|
||||
|
||||
def store_mp3(%Song{} = song, tmp_path) do
|
||||
File.mkdir_p!(Path.dirname(song.mp3_filepath))
|
||||
|
@ -170,16 +187,28 @@ defmodule LiveBeats.MediaLibrary do
|
|||
Repo.all(Genre, order_by: [asc: :title])
|
||||
end
|
||||
|
||||
def list_songs(limit \\ 100) do
|
||||
from(s in Song, limit: ^limit)
|
||||
def list_profile_songs(%Profile{} = profile, limit \\ 100) do
|
||||
from(s in Song, where: s.user_id == ^profile.user_id, limit: ^limit)
|
||||
|> order_by_playlist(:asc)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def get_current_active_song(user_id) do
|
||||
def get_current_active_song(%Profile{user_id: user_id}) do
|
||||
Repo.one(from s in Song, where: s.user_id == ^user_id and s.status in [:playing, :paused])
|
||||
end
|
||||
|
||||
def get_profile!(%Accounts.User{} = user) do
|
||||
%Profile{user_id: user.id, username: user.username, tagline: user.profile_tagline}
|
||||
end
|
||||
|
||||
def owns_profile?(%Accounts.User{} = user, %Profile{} = profile) do
|
||||
user.id == profile.user_id
|
||||
end
|
||||
|
||||
def owns_song?(%Profile{} = profile, %Song{} = song) do
|
||||
profile.user_id == song.user_id
|
||||
end
|
||||
|
||||
def elapsed_playback(%Song{} = song) do
|
||||
cond do
|
||||
playing?(song) ->
|
||||
|
@ -196,7 +225,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|
||||
def get_song!(id), do: Repo.get!(Song, id)
|
||||
|
||||
def get_first_song(user_id) do
|
||||
def get_first_song(%Profile{user_id: user_id}) do
|
||||
from(s in Song,
|
||||
where: s.user_id == ^user_id,
|
||||
limit: 1
|
||||
|
@ -205,7 +234,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
def get_last_song(user_id) do
|
||||
def get_last_song(%Profile{user_id: user_id}) do
|
||||
from(s in Song,
|
||||
where: s.user_id == ^user_id,
|
||||
limit: 1
|
||||
|
@ -214,7 +243,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
def get_next_song(%Song{} = song) do
|
||||
def get_next_song(%Song{} = song, %Profile{} = profile) do
|
||||
next =
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id > ^song.id,
|
||||
|
@ -223,10 +252,10 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> order_by_playlist(:asc)
|
||||
|> Repo.one()
|
||||
|
||||
next || get_first_song(song.user_id)
|
||||
next || get_first_song(profile)
|
||||
end
|
||||
|
||||
def get_prev_song(%Song{} = song) do
|
||||
def get_prev_song(%Song{} = song, %Profile{} = profile) do
|
||||
prev =
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id < ^song.id,
|
||||
|
@ -236,7 +265,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> order_by_playlist(:desc)
|
||||
|> Repo.one()
|
||||
|
||||
prev || get_last_song(song.user_id)
|
||||
prev || get_last_song(profile)
|
||||
end
|
||||
|
||||
def create_song(attrs \\ %{}) do
|
||||
|
|
3
lib/live_beats/media_library/profile.ex
Normal file
3
lib/live_beats/media_library/profile.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule LiveBeats.MediaLibrary.Profile do
|
||||
defstruct user_id: nil, username: nil, tagline: nil
|
||||
end
|
|
@ -5,9 +5,17 @@ defmodule LiveBeatsWeb.FileController do
|
|||
|
||||
def show(conn, %{"id" => filename_uuid, "token" => token}) do
|
||||
case Phoenix.Token.verify(conn, "file", token, max_age: :timer.minutes(10)) do
|
||||
{:ok, ^filename_uuid} -> send_file(conn, 200, MediaLibrary.local_filepath(filename_uuid))
|
||||
{:ok, ^filename_uuid} -> do_send_file(conn, MediaLibrary.local_filepath(filename_uuid))
|
||||
{:ok, _} -> send_resp(conn, :unauthorized, "")
|
||||
{:error, _} -> send_resp(conn, :unauthorized, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_send_file(conn, path) do
|
||||
# accept-ranges headers required for chrome to seek via currentTime
|
||||
conn
|
||||
|> put_resp_header("content-type", "audio/mp3")
|
||||
|> put_resp_header("accept-ranges", "bytes")
|
||||
|> send_file(200, path)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule LiveBeatsWeb.HomeLive do
|
||||
use LiveBeatsWeb, :live_view
|
||||
|
||||
alias LiveBeats.MediaLibrary
|
||||
# alias LiveBeats.MediaLibrary
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
@ -346,6 +346,6 @@ defmodule LiveBeatsWeb.HomeLive do
|
|||
end
|
||||
|
||||
defp fetch_songs(_socket) do
|
||||
MediaLibrary.list_songs()
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule LiveBeatsWeb.PlayerLive do
|
||||
use LiveBeatsWeb, {:live_view, container: {:div, []}}
|
||||
|
||||
alias LiveBeats.MediaLibrary
|
||||
alias LiveBeats.{Accounts, MediaLibrary}
|
||||
alias LiveBeats.MediaLibrary.Song
|
||||
|
||||
on_mount {LiveBeatsWeb.UserAuth, :current_user}
|
||||
|
@ -36,14 +36,20 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 text-black dark:bg-gray-900 dark:text-white px-1 sm:px-3 lg:px-1 xl:px-3 grid grid-cols-5 items-center">
|
||||
<button type="button" class="mx-auto scale-75">
|
||||
<svg width="24" height="24" fill="none">
|
||||
<path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<%= if @profile do %>
|
||||
<.link
|
||||
redirect_to={Routes.song_index_path(@socket, :index, @profile.username)}
|
||||
class="mx-auto flex outline border-2 border-white border-opacity-20 rounded-md p-1 pr-2"
|
||||
>
|
||||
<span class="mt-1"><.icon name={:user_circle}/></span>
|
||||
<p class="ml-2"><%= @profile.username %></p>
|
||||
</.link>
|
||||
<% else %>
|
||||
<div class="mx-auto flex"></div>
|
||||
<% end %>
|
||||
|
||||
<!-- prev -->
|
||||
<button type="button" class="sm:block xl:block mx-auto scale-75" phx-click="prev-song">
|
||||
<button type="button" class="sm:block xl:block mx-auto scale-75" phx-click={js_prev(@own_profile?)}>
|
||||
<svg width="17" height="18">
|
||||
<path d="M0 0h2v18H0V0zM4 9l13-9v18L4 9z" fill="currentColor" />
|
||||
</svg>
|
||||
|
@ -51,7 +57,7 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
<!-- /prev -->
|
||||
|
||||
<!-- pause -->
|
||||
<button type="button" class="mx-auto scale-75" phx-click={JS.push("play_pause") |> js_play_pause()}>
|
||||
<button type="button" class="mx-auto scale-75" phx-click={js_play_pause(@own_profile?)}>
|
||||
<%= if @playing do %>
|
||||
<svg id="player-pause" width="50" height="50" fill="none">
|
||||
<circle class="text-gray-300 dark:text-gray-500" cx="25" cy="25" r="24" stroke="currentColor" stroke-width="1.5" />
|
||||
|
@ -67,7 +73,7 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
<!-- /pause -->
|
||||
|
||||
<!-- next -->
|
||||
<button type="button" class="mx-auto scale-75" phx-click="next-song">
|
||||
<button type="button" class="mx-auto scale-75" phx-click={js_next(@own_profile?)}>
|
||||
<svg width="17" height="18" viewBox="0 0 17 18" fill="none">
|
||||
<path d="M17 0H15V18H17V0Z" fill="currentColor" />
|
||||
<path d="M13 9L0 0V18L13 9Z" fill="currentColor" />
|
||||
|
@ -85,84 +91,153 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
Your browser needs a click event to enable playback
|
||||
<:confirm>Listen Now</:confirm>
|
||||
</.modal>
|
||||
|
||||
<%= if @profile do %>
|
||||
<.modal id="not-authorized" on_confirm={hide_modal("not-authorized")}>
|
||||
<:title>You can't do that</:title>
|
||||
|
||||
Only <%= @profile.username %> can control playback
|
||||
|
||||
<:confirm>Ok</:confirm>
|
||||
</.modal>
|
||||
<% end %>
|
||||
</div>
|
||||
<!-- /player -->
|
||||
"""
|
||||
end
|
||||
|
||||
def mount(_parmas, _session, socket) do
|
||||
if connected?(socket) and socket.assigns.current_user do
|
||||
MediaLibrary.subscribe(socket.assigns.current_user)
|
||||
send(self(), :play_current)
|
||||
%{current_user: current_user} = socket.assigns
|
||||
|
||||
if connected?(socket) do
|
||||
Accounts.subscribe(current_user.id)
|
||||
end
|
||||
|
||||
socket =
|
||||
assign(socket,
|
||||
socket
|
||||
|> assign(
|
||||
song: nil,
|
||||
playing: false,
|
||||
current_user_id: socket.assigns.current_user.id,
|
||||
# todo use actual room user id
|
||||
room_user_id: socket.assigns.current_user.id
|
||||
profile: nil,
|
||||
current_user_id: current_user.id,
|
||||
own_profile?: false
|
||||
)
|
||||
|> switch_profile(current_user.active_profile_user_id || current_user.id)
|
||||
|
||||
{:ok, socket, layout: false, temporary_assigns: [current_user: nil]}
|
||||
{:ok, socket, layout: false, temporary_assigns: []}
|
||||
end
|
||||
|
||||
defp switch_profile(socket, nil) do
|
||||
current_user = Accounts.update_active_profile(socket.assigns.current_user, nil)
|
||||
|
||||
socket
|
||||
|> assign(current_user: current_user)
|
||||
|> assign_profile(nil)
|
||||
end
|
||||
|
||||
defp switch_profile(socket, profile_user_id) do
|
||||
profile = get_profile(profile_user_id)
|
||||
|
||||
if connected?(socket) and profile do
|
||||
current_user = Accounts.update_active_profile(socket.assigns.current_user, profile.user_id)
|
||||
|
||||
send(self(), :play_current)
|
||||
|
||||
socket
|
||||
|> assign(current_user: current_user)
|
||||
|> assign_profile(profile)
|
||||
else
|
||||
assign_profile(socket, nil)
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_profile(socket, profile) do
|
||||
if prev_profile = connected?(socket) && socket.assigns.profile do
|
||||
MediaLibrary.unsubscribe_to_profile(prev_profile)
|
||||
end
|
||||
if profile, do: MediaLibrary.subscribe_to_profile(profile)
|
||||
|
||||
assign(socket,
|
||||
profile: profile,
|
||||
own_profile?: profile && MediaLibrary.owns_profile?(socket.assigns.current_user, profile)
|
||||
)
|
||||
end
|
||||
|
||||
defp assign_inactive_profile(socket, %Song{} = song) do
|
||||
if socket.assigns.profile && MediaLibrary.owns_song?(socket.assigns.profile, song) do
|
||||
socket
|
||||
else
|
||||
switch_profile(socket, song.user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("play_pause", _, socket) do
|
||||
%{song: song, playing: playing} = socket.assigns
|
||||
%{song: song, playing: playing, current_user: current_user} = socket.assigns
|
||||
song = MediaLibrary.get_song!(song.id)
|
||||
|
||||
cond do
|
||||
song && playing ->
|
||||
song && playing and MediaLibrary.can_control_playback?(current_user, song) ->
|
||||
MediaLibrary.pause_song(song)
|
||||
{:noreply, assign(socket, playing: false)}
|
||||
|
||||
song ->
|
||||
song && MediaLibrary.can_control_playback?(current_user, song) ->
|
||||
MediaLibrary.play_song(song)
|
||||
{:noreply, assign(socket, playing: true)}
|
||||
|
||||
true ->
|
||||
{:noreply, assign(socket, playing: false)}
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("next-song", _, socket) do
|
||||
if socket.assigns.song do
|
||||
MediaLibrary.play_next_song(socket.assigns.song.user_id)
|
||||
def handle_event("switch_profile", %{"user_id" => user_id}, socket) do
|
||||
{:noreply, switch_profile(socket, user_id)}
|
||||
end
|
||||
|
||||
def handle_event("next_song", _, socket) do
|
||||
%{song: song, current_user: current_user} = socket.assigns
|
||||
|
||||
if song && MediaLibrary.can_control_playback?(current_user, song) do
|
||||
MediaLibrary.play_next_song(socket.assigns.profile)
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("prev-song", _, socket) do
|
||||
if socket.assigns.song do
|
||||
MediaLibrary.play_prev_song(socket.assigns.song.user_id)
|
||||
def handle_event("prev_song", _, socket) do
|
||||
%{song: song, current_user: current_user} = socket.assigns
|
||||
|
||||
if song && MediaLibrary.can_control_playback?(current_user, song) do
|
||||
MediaLibrary.play_prev_song(socket.assigns.profile)
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("next-song-auto", _, socket) do
|
||||
def handle_event("next_song_auto", _, socket) do
|
||||
if socket.assigns.song do
|
||||
MediaLibrary.play_next_song_auto(socket.assigns.song.user_id)
|
||||
MediaLibrary.play_next_song_auto(socket.assigns.profile)
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({Accounts, :active_profile_changed, _cur_user, %{user_id: user_id}}, socket) do
|
||||
if user_id do
|
||||
{:noreply, assign(socket, profile: get_profile(user_id))}
|
||||
else
|
||||
{:noreply, socket |> assign_profile(nil) |> stop_song()}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(:play_current, socket) do
|
||||
# we raced a pubsub, noop
|
||||
if socket.assigns.song do
|
||||
{:noreply, socket}
|
||||
else
|
||||
{:noreply, play_current_song(socket)}
|
||||
end
|
||||
{:noreply, play_current_song(socket)}
|
||||
end
|
||||
|
||||
def handle_info({:pause, _}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> push_event("pause", %{})
|
||||
|> assign(playing: false)}
|
||||
def handle_info({MediaLibrary, :pause, _}, socket) do
|
||||
{:noreply, push_pause(socket)}
|
||||
end
|
||||
|
||||
def handle_info({:play, %Song{} = song, %{elapsed: elapsed}}, socket) do
|
||||
def handle_info({MediaLibrary, :play, %Song{} = song, %{elapsed: elapsed}}, socket) do
|
||||
{:noreply, play_song(socket, song, elapsed)}
|
||||
end
|
||||
|
||||
|
@ -170,18 +245,17 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
socket
|
||||
|> push_play(song, elapsed)
|
||||
|> assign(song: song, playing: true)
|
||||
|> assign_inactive_profile(song)
|
||||
end
|
||||
|
||||
defp js_play_pause(%JS{} = js) do
|
||||
JS.dispatch(js, "js:play_pause", to: "#audio-player")
|
||||
end
|
||||
|
||||
defp js_listen_now(js \\ %JS{}) do
|
||||
JS.dispatch(js, "js:listen_now", to: "#audio-player")
|
||||
defp stop_song(socket) do
|
||||
socket
|
||||
|> push_event("stop", %{})
|
||||
|> assign(song: nil, playing: false)
|
||||
end
|
||||
|
||||
defp play_current_song(socket) do
|
||||
song = MediaLibrary.get_current_active_song(socket.assigns.room_user_id)
|
||||
song = MediaLibrary.get_current_active_song(socket.assigns.profile)
|
||||
|
||||
cond do
|
||||
song && MediaLibrary.playing?(song) ->
|
||||
|
@ -197,6 +271,7 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
|
||||
defp push_play(socket, %Song{} = song, elapsed) do
|
||||
token = Phoenix.Token.sign(socket.endpoint, "file", song.mp3_filename)
|
||||
|
||||
push_event(socket, "play", %{
|
||||
paused: Song.paused?(song),
|
||||
elapsed: elapsed,
|
||||
|
@ -204,4 +279,43 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
url: song.mp3_url
|
||||
})
|
||||
end
|
||||
|
||||
defp push_pause(socket) do
|
||||
socket
|
||||
|> push_event("pause", %{})
|
||||
|> assign(playing: false)
|
||||
end
|
||||
|
||||
defp js_play_pause(own_profile?) do
|
||||
if own_profile? do
|
||||
JS.push("play_pause")
|
||||
|> JS.dispatch("js:play_pause", to: "#audio-player")
|
||||
else
|
||||
show_modal("not-authorized")
|
||||
end
|
||||
end
|
||||
|
||||
defp js_prev(own_profile?) do
|
||||
if own_profile? do
|
||||
JS.push("prev_song")
|
||||
else
|
||||
show_modal("not-authorized")
|
||||
end
|
||||
end
|
||||
|
||||
defp js_next(own_profile?) do
|
||||
if own_profile? do
|
||||
JS.push("next_song")
|
||||
else
|
||||
show_modal("not-authorized")
|
||||
end
|
||||
end
|
||||
|
||||
defp js_listen_now(js \\ %JS{}) do
|
||||
JS.dispatch(js, "js:listen_now", to: "#audio-player")
|
||||
end
|
||||
|
||||
defp get_profile(user_id) do
|
||||
user_id && Accounts.get_user!(user_id) |> MediaLibrary.get_profile!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,34 @@
|
|||
defmodule LiveBeatsWeb.SongLive.Index do
|
||||
use LiveBeatsWeb, :live_view
|
||||
|
||||
alias LiveBeats.{MediaLibrary, MP3Stat}
|
||||
alias LiveBeats.{Accounts, MediaLibrary, MP3Stat}
|
||||
alias LiveBeatsWeb.LayoutComponent
|
||||
alias LiveBeatsWeb.SongLive.{SongRowComponent, UploadFormComponent}
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<.title_bar>
|
||||
Listing Songs
|
||||
<%= @profile.tagline %> <%= if @owns_profile? do %>(you)<% end %>
|
||||
|
||||
<:actions>
|
||||
<.button primary patch_to={Routes.song_index_path(@socket, :new)}>Upload Songs</.button>
|
||||
<%= if @active_profile_id == @profile.user_id do %>
|
||||
<.button primary
|
||||
phx-click={JS.push("switch_profile", value: %{user_id: nil}, target: "#player", loading: "#player")}
|
||||
>
|
||||
<.icon name={:stop}/><span class="ml-2">Stop Listening</span>
|
||||
</.button>
|
||||
<% else %>
|
||||
<.button primary
|
||||
phx-click={JS.push("switch_profile", value: %{user_id: @profile.user_id}, target: "#player", loading: "#player")}
|
||||
>
|
||||
<.icon name={:play}/><span class="ml-2">Listen</span>
|
||||
</.button>
|
||||
<% end %>
|
||||
<%= if @owns_profile? do %>
|
||||
<.button primary patch_to={Routes.song_index_path(@socket, :new)}>
|
||||
<.icon name={:upload}/><span class="ml-2">Upload Songs</span>
|
||||
</.button>
|
||||
<% end %>
|
||||
</:actions>
|
||||
</.title_bar>
|
||||
|
||||
|
@ -45,20 +62,35 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
"""
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
def mount(%{"profile_username" => profile_username}, _session, socket) do
|
||||
%{current_user: current_user} = socket.assigns
|
||||
|
||||
profile =
|
||||
Accounts.get_user_by!(username: profile_username)
|
||||
|> MediaLibrary.get_profile!()
|
||||
|
||||
if connected?(socket) do
|
||||
MediaLibrary.subscribe(current_user)
|
||||
MediaLibrary.subscribe_to_profile(profile, __MODULE__)
|
||||
Accounts.subscribe(current_user.id)
|
||||
end
|
||||
|
||||
active_id =
|
||||
if song = MediaLibrary.get_current_active_song(current_user.id) do
|
||||
active_song_id =
|
||||
if song = MediaLibrary.get_current_active_song(profile) do
|
||||
SongRowComponent.send_status(song.id, song.status)
|
||||
song.id
|
||||
end
|
||||
|
||||
{:ok, assign(socket, songs: list_songs(), active_id: active_id), temporary_assigns: [songs: []]}
|
||||
socket =
|
||||
socket
|
||||
|> assign(
|
||||
active_song_id: active_song_id,
|
||||
active_profile_id: current_user.active_profile_user_id,
|
||||
profile: profile,
|
||||
owns_profile?: MediaLibrary.owns_profile?(current_user, profile)
|
||||
)
|
||||
|> list_songs()
|
||||
|
||||
{:ok, socket, temporary_assigns: [songs: []]}
|
||||
end
|
||||
|
||||
def handle_params(params, _url, socket) do
|
||||
|
@ -67,10 +99,17 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
|
||||
def handle_event("play_or_pause", %{"id" => id}, socket) do
|
||||
song = MediaLibrary.get_song!(id)
|
||||
if socket.assigns.active_id == id and MediaLibrary.playing?(song) do
|
||||
MediaLibrary.pause_song(song)
|
||||
else
|
||||
MediaLibrary.play_song(id)
|
||||
can_playback? = MediaLibrary.can_control_playback?(socket.assigns.current_user, song)
|
||||
|
||||
cond do
|
||||
can_playback? and socket.assigns.active_song_id == id and MediaLibrary.playing?(song) ->
|
||||
MediaLibrary.pause_song(song)
|
||||
|
||||
can_playback? ->
|
||||
MediaLibrary.play_song(id)
|
||||
|
||||
true ->
|
||||
:noop
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
|
@ -78,23 +117,29 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
song = MediaLibrary.get_song!(id)
|
||||
{:ok, _} = MediaLibrary.delete_song(song)
|
||||
if song.user_id == socket.assigns.current_user.id do
|
||||
{:ok, _} = MediaLibrary.delete_song(song)
|
||||
end
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({:play, %MediaLibrary.Song{} = song, _meta}, socket) do
|
||||
def handle_info({Accounts, :active_profile_changed, _cur_user, %{user_id: user_id}}, socket) do
|
||||
{:noreply, assign(socket, active_profile_id: user_id)}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, :play, %MediaLibrary.Song{} = song, _meta}, socket) do
|
||||
{:noreply, play_song(socket, song)}
|
||||
end
|
||||
|
||||
def handle_info({:pause, %MediaLibrary.Song{} = song}, socket) do
|
||||
def handle_info({MediaLibrary, :pause, %MediaLibrary.Song{} = song}, socket) do
|
||||
{:noreply, pause_song(socket, song.id)}
|
||||
end
|
||||
|
||||
defp stop_song(socket, song_id) do
|
||||
SongRowComponent.send_status(song_id, :stopped)
|
||||
|
||||
if socket.assigns.active_id == song_id do
|
||||
assign(socket, :active_id, nil)
|
||||
if socket.assigns.active_song_id == song_id do
|
||||
assign(socket, :active_song_id, nil)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
@ -106,23 +151,23 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
end
|
||||
|
||||
defp play_song(socket, %MediaLibrary.Song{} = song) do
|
||||
%{active_id: active_id} = socket.assigns
|
||||
%{active_song_id: active_song_id} = socket.assigns
|
||||
|
||||
cond do
|
||||
active_id == song.id ->
|
||||
active_song_id == song.id ->
|
||||
SongRowComponent.send_status(song.id, :playing)
|
||||
socket
|
||||
|
||||
active_id ->
|
||||
active_song_id ->
|
||||
SongRowComponent.send_status(song.id, :playing)
|
||||
|
||||
socket
|
||||
|> stop_song(active_id)
|
||||
|> assign(active_id: song.id)
|
||||
|> stop_song(active_song_id)
|
||||
|> assign(active_song_id: song.id)
|
||||
|
||||
true ->
|
||||
SongRowComponent.send_status(song.id, :playing)
|
||||
assign(socket, active_id: song.id)
|
||||
assign(socket, active_song_id: song.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -155,7 +200,7 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
|> assign(:song, nil)
|
||||
end
|
||||
|
||||
defp list_songs do
|
||||
MediaLibrary.list_songs(50)
|
||||
defp list_songs(socket) do
|
||||
assign(socket, songs: MediaLibrary.list_profile_songs(socket.assigns.profile, 50))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ defmodule LiveBeatsWeb.Router do
|
|||
live_session :authenticated,
|
||||
on_mount: [{LiveBeatsWeb.UserAuth, :ensure_authenticated}, LiveBeatsWeb.Nav] do
|
||||
live "/songs/new", SongLive.Index, :new
|
||||
live "/:user_id", SongLive.Index, :index
|
||||
live "/:profile_username", SongLive.Index, :index
|
||||
live "/profile/settings", SettingsLive, :edit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule LiveBeats.Repo.Migrations.CreateUserAuth do
|
|||
add :role, :string, null: false
|
||||
add :confirmed_at, :naive_datetime
|
||||
add :profile_tagline, :string
|
||||
add :active_profile_user_id, references(:users, on_delete: :nilify_all)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue