mirror of
https://github.com/algora-io/tv.git
synced 2024-11-26 01:00:20 +02:00
add PlayerComponent (#52)
This commit is contained in:
parent
8e88fbd5fd
commit
cd2d8a2fbb
@ -150,8 +150,10 @@ const Hooks = {
|
||||
mounted() {
|
||||
const backdrop = document.querySelector("#video-backdrop");
|
||||
|
||||
this.player = videojs("video-player", {
|
||||
autoplay: true,
|
||||
this.playerId = this.el.id;
|
||||
|
||||
this.player = videojs(this.el, {
|
||||
autoplay: "any",
|
||||
liveui: true,
|
||||
html5: {
|
||||
vhs: {
|
||||
@ -161,6 +163,7 @@ const Hooks = {
|
||||
});
|
||||
|
||||
const playVideo = (opts: {
|
||||
player_id: string;
|
||||
id: string;
|
||||
url: string;
|
||||
title: string;
|
||||
@ -168,6 +171,10 @@ const Hooks = {
|
||||
current_time?: number;
|
||||
channel_name: string;
|
||||
}) => {
|
||||
if (this.playerId !== opts.player_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setMediaSession = () => {
|
||||
if (!("mediaSession" in navigator)) {
|
||||
return;
|
||||
@ -193,22 +200,21 @@ const Hooks = {
|
||||
: {}),
|
||||
});
|
||||
this.player.src({ src: opts.url, type: opts.player_type });
|
||||
this.player.play();
|
||||
|
||||
setMediaSession();
|
||||
|
||||
if (opts.current_time && opts.player_type !== "video/youtube") {
|
||||
this.player.currentTime(opts.current_time);
|
||||
}
|
||||
this.player.el().parentElement.classList.remove("hidden");
|
||||
this.player.el().parentElement.classList.add("flex");
|
||||
|
||||
if (backdrop) {
|
||||
backdrop.classList.remove("opacity-10");
|
||||
backdrop.classList.add("opacity-20");
|
||||
}
|
||||
|
||||
this.pushEventTo("#clipper", "video_loaded", { id: opts.id });
|
||||
if (this.playerId === "video-player") {
|
||||
this.pushEventTo("#clipper", "video_loaded", { id: opts.id });
|
||||
}
|
||||
};
|
||||
|
||||
this.handleEvent("play_video", playVideo);
|
||||
@ -350,33 +356,6 @@ let liveSocket = new LiveSocket("/live", Socket, {
|
||||
let routeUpdated = () => {
|
||||
// TODO: uncomment
|
||||
// Focus.focusMain();
|
||||
|
||||
const player = document.querySelector("#video-player")?.parentElement;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { pathname } = new URL(window.location.href);
|
||||
if (pathname.endsWith("/embed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pipClasses = [
|
||||
"fixed",
|
||||
"bottom-0",
|
||||
"right-0",
|
||||
"z-[1000]",
|
||||
"w-[100vw]",
|
||||
"sm:w-[30vw]",
|
||||
];
|
||||
|
||||
if (/^\/[^\/]+\/\d+$/.test(pathname)) {
|
||||
player.classList.add("lg:pr-[24rem]");
|
||||
player.classList.remove(...pipClasses);
|
||||
} else {
|
||||
player.classList.remove("lg:pr-[24rem]");
|
||||
player.classList.add(...pipClasses);
|
||||
}
|
||||
};
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
@ -199,4 +199,21 @@ defmodule Algora.Events do
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def log_watched(user, video) do
|
||||
actor_id = if user, do: "user_#{user.id}", else: "guest_#{hash_actor_id()}"
|
||||
|
||||
%Event{
|
||||
actor_id: actor_id,
|
||||
user_id: user && user.id,
|
||||
video_id: video.id,
|
||||
channel_id: video.user_id,
|
||||
name: :watched
|
||||
}
|
||||
|> Event.changeset(%{})
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
# TODO:
|
||||
defp hash_actor_id, do: ""
|
||||
end
|
||||
|
@ -314,7 +314,6 @@
|
||||
<.live_component module={AlgoraWeb.LayoutComponent} id="layout" />
|
||||
|
||||
<main class="flex-1 relative z-0 overflow-y-auto focus:outline-none">
|
||||
<%= live_render(@socket, AlgoraWeb.PlayerLive, id: "player", session: %{}, sticky: true) %>
|
||||
<%= live_render(@socket, AlgoraWeb.ClipperLive, id: "clipper", session: %{}, sticky: true) %>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
|
@ -10,7 +10,6 @@
|
||||
<.live_component module={AlgoraWeb.LayoutComponent} id="layout" />
|
||||
|
||||
<main class="flex-1 relative z-0 overflow-y-auto focus:outline-none">
|
||||
<%= live_render(@socket, AlgoraWeb.PlayerLive, id: "player", session: %{}, sticky: true) %>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
</div>
|
||||
|
@ -3,17 +3,12 @@ defmodule AlgoraWeb.EmbedLive do
|
||||
require Logger
|
||||
|
||||
alias Algora.{Accounts, Library, Storage, Chat}
|
||||
alias AlgoraWeb.{LayoutComponent, Presence}
|
||||
alias AlgoraWeb.{LayoutComponent, Presence, PlayerComponent}
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="w-full">
|
||||
<video
|
||||
id="video-player"
|
||||
phx-hook="VideoPlayer"
|
||||
class="video-js vjs-default-skin vjs-fluid flex-1 overflow-hidden"
|
||||
controls
|
||||
/>
|
||||
<div class="w-full" id="embed-player-container" phx-update="ignore">
|
||||
<.live_component module={PlayerComponent} id="embed-player" />
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
@ -23,17 +18,23 @@ defmodule AlgoraWeb.EmbedLive do
|
||||
Accounts.get_user_by!(handle: channel_handle)
|
||||
|> Library.get_channel!()
|
||||
|
||||
videos = Library.list_channel_videos(channel, 50)
|
||||
|
||||
video = Library.get_video!(video_id)
|
||||
|
||||
if connected?(socket) do
|
||||
Library.subscribe_to_livestreams()
|
||||
Library.subscribe_to_channel(channel)
|
||||
|
||||
Presence.subscribe(channel_handle)
|
||||
|
||||
send_update(PlayerComponent, %{
|
||||
id: "embed-player",
|
||||
video: video,
|
||||
current_user: nil
|
||||
})
|
||||
end
|
||||
|
||||
videos = Library.list_channel_videos(channel, 50)
|
||||
|
||||
video = Library.get_video!(video_id)
|
||||
|
||||
subtitles = Library.list_subtitles(%Library.Video{id: video_id})
|
||||
|
||||
data = %{}
|
||||
@ -63,8 +64,6 @@ defmodule AlgoraWeb.EmbedLive do
|
||||
|> stream(:videos, videos)
|
||||
|> stream(:presences, Presence.list_online_users(channel_handle))
|
||||
|
||||
if connected?(socket), do: send(self(), {:play, video})
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@ -73,20 +72,6 @@ defmodule AlgoraWeb.EmbedLive do
|
||||
{:noreply, socket |> apply_action(socket.assigns.live_action, params)}
|
||||
end
|
||||
|
||||
def handle_info({:play, video}, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> push_event("play_video", %{
|
||||
id: video.id,
|
||||
url: video.url,
|
||||
title: video.title,
|
||||
player_type: Library.player_type(video),
|
||||
channel_name: video.channel_name
|
||||
})
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({Presence, {:join, presence}}, socket) do
|
||||
{:noreply, stream_insert(socket, :presences, presence)}
|
||||
end
|
||||
|
56
lib/algora_web/live/player_component.ex
Normal file
56
lib/algora_web/live/player_component.ex
Normal file
@ -0,0 +1,56 @@
|
||||
defmodule AlgoraWeb.PlayerComponent do
|
||||
use AlgoraWeb, :live_component
|
||||
|
||||
alias Algora.{Library, Events}
|
||||
alias AlgoraWeb.Presence
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<video
|
||||
id={@id}
|
||||
phx-hook="VideoPlayer"
|
||||
class="video-js vjs-default-skin aspect-video h-full w-full flex-1 lg:rounded-2xl overflow-hidden"
|
||||
controls
|
||||
/>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
# TODO: log at regular intervals
|
||||
# if socket.current_user && socket.assigns.video.is_live do
|
||||
# schedule_watch_event(:timer.seconds(2))
|
||||
# end
|
||||
|
||||
socket =
|
||||
case assigns[:video] do
|
||||
nil ->
|
||||
socket
|
||||
|
||||
video ->
|
||||
%{current_user: current_user} = assigns
|
||||
|
||||
Events.log_watched(current_user, video)
|
||||
|
||||
Presence.track_user(video.channel_handle, %{
|
||||
id: if(current_user, do: current_user.handle, else: "")
|
||||
})
|
||||
|
||||
socket
|
||||
|> push_event("play_video", %{
|
||||
player_id: assigns.id,
|
||||
id: video.id,
|
||||
url: video.url,
|
||||
title: video.title,
|
||||
player_type: Library.player_type(video),
|
||||
channel_name: video.channel_name,
|
||||
current_time: assigns[:current_time]
|
||||
})
|
||||
end
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:id, assigns[:id])}
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
defmodule AlgoraWeb.PlayerLive do
|
||||
use AlgoraWeb, {:live_view, container: {:div, []}}
|
||||
|
||||
on_mount {AlgoraWeb.UserAuth, :current_user}
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="lg:px-4">
|
||||
<div class="w-full hidden lg:pr-[24rem]">
|
||||
<video
|
||||
id="video-player"
|
||||
phx-hook="VideoPlayer"
|
||||
class="video-js vjs-default-skin aspect-video h-full w-full flex-1 lg:rounded-2xl overflow-hidden"
|
||||
controls
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, socket, layout: false, temporary_assigns: []}
|
||||
end
|
||||
|
||||
def handle_info({Library, _}, socket), do: {:noreply, socket}
|
||||
end
|
@ -8,8 +8,6 @@ defmodule AlgoraWeb.SubtitleLive.Index do
|
||||
def mount(%{"video_id" => video_id}, _session, socket) do
|
||||
video = Library.get_video!(video_id)
|
||||
|
||||
if connected?(socket), do: send(self(), :play_video)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:video, video)
|
||||
@ -39,21 +37,6 @@ defmodule AlgoraWeb.SubtitleLive.Index do
|
||||
|> assign(:subtitle, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:play_video, socket) do
|
||||
video = socket.assigns.video
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> push_event("play_video", %{
|
||||
id: video.id,
|
||||
url: video.url,
|
||||
title: video.title,
|
||||
player_type: Library.player_type(video),
|
||||
channel_name: video.channel_name
|
||||
})}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({AlgoraWeb.SubtitleLive.FormComponent, {:saved, subtitle}}, socket) do
|
||||
{:noreply, stream_insert(socket, :subtitles, subtitle)}
|
||||
|
@ -7,26 +7,9 @@ defmodule AlgoraWeb.SubtitleLive.Show do
|
||||
def mount(%{"video_id" => video_id}, _session, socket) do
|
||||
video = Library.get_video!(video_id)
|
||||
|
||||
if connected?(socket), do: send(self(), :play_video)
|
||||
|
||||
{:ok, socket |> assign(:video, video)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:play_video, socket) do
|
||||
video = socket.assigns.video
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> push_event("play_video", %{
|
||||
id: video.id,
|
||||
url: video.url,
|
||||
title: video.title,
|
||||
player_type: Library.player_type(video),
|
||||
channel_name: video.channel_name
|
||||
})}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(%{"id" => id}, _, socket) do
|
||||
{:noreply,
|
||||
|
@ -5,14 +5,23 @@ defmodule AlgoraWeb.VideoLive do
|
||||
|
||||
alias Algora.{Accounts, Library, Storage, Chat, Repo}
|
||||
alias Algora.Events.Event
|
||||
alias AlgoraWeb.{LayoutComponent, Presence}
|
||||
|
||||
alias AlgoraWeb.{
|
||||
LayoutComponent,
|
||||
Presence,
|
||||
RTMPDestinationIconComponent,
|
||||
PlayerComponent
|
||||
}
|
||||
|
||||
alias AlgoraWeb.ChannelLive.{StreamFormComponent}
|
||||
alias AlgoraWeb.RTMPDestinationIconComponent
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="lg:mr-[24rem] h-[calc(100svh-56.25vw-64px)] lg:h-auto">
|
||||
<div class="px-4" id="video-player-container" phx-update="ignore">
|
||||
<.live_component module={PlayerComponent} id="video-player" />
|
||||
</div>
|
||||
<div class="lg:border-b lg:border-gray-700 py-4">
|
||||
<figure class="relative isolate -mt-4 pt-4 pb-4">
|
||||
<svg
|
||||
@ -539,11 +548,14 @@ defmodule AlgoraWeb.VideoLive do
|
||||
Library.subscribe_to_channel(channel)
|
||||
Chat.subscribe_to_room(video)
|
||||
|
||||
Presence.track_user(channel_handle, %{
|
||||
id: if(current_user, do: current_user.handle, else: "")
|
||||
})
|
||||
|
||||
Presence.subscribe(channel_handle)
|
||||
|
||||
send_update(PlayerComponent, %{
|
||||
id: "video-player",
|
||||
video: video,
|
||||
current_user: current_user,
|
||||
current_time: params["t"]
|
||||
})
|
||||
end
|
||||
|
||||
videos = Library.list_channel_videos(channel, 50)
|
||||
@ -585,8 +597,6 @@ defmodule AlgoraWeb.VideoLive do
|
||||
|> stream(:messages, Chat.list_messages(video))
|
||||
|> stream(:presences, Presence.list_online_users(channel_handle))
|
||||
|
||||
if connected?(socket), do: send(self(), {:play, {video, params["t"]}})
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@ -602,34 +612,6 @@ defmodule AlgoraWeb.VideoLive do
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:play, {video, t}}, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> push_event("play_video", %{
|
||||
id: video.id,
|
||||
url: video.url,
|
||||
title: video.title,
|
||||
player_type: Library.player_type(video),
|
||||
channel_name: video.channel_name,
|
||||
current_time: t
|
||||
})
|
||||
|
||||
schedule_watch_event()
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info(:watch_event, socket) do
|
||||
log_watch_event(socket.assigns.current_user, socket.assigns.video)
|
||||
|
||||
# TODO: enable later
|
||||
# if socket.assigns.current_user && socket.assigns.video.is_live do
|
||||
# schedule_watch_event(:timer.seconds(2))
|
||||
# end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({Presence, {:join, presence}}, socket) do
|
||||
{:noreply, stream_insert(socket, :presences, presence)}
|
||||
end
|
||||
@ -884,25 +866,4 @@ defmodule AlgoraWeb.VideoLive do
|
||||
|
||||
socket
|
||||
end
|
||||
|
||||
defp schedule_watch_event(ms \\ 0) do
|
||||
Process.send_after(self(), :watch_event, ms)
|
||||
end
|
||||
|
||||
defp log_watch_event(user, video) do
|
||||
actor_id = if user, do: "user_#{user.id}", else: "guest_#{hash_actor_id()}"
|
||||
|
||||
%Event{
|
||||
actor_id: actor_id,
|
||||
user_id: user && user.id,
|
||||
video_id: video.id,
|
||||
channel_id: video.user_id,
|
||||
name: :watched
|
||||
}
|
||||
|> Event.changeset(%{})
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
# TODO:
|
||||
defp hash_actor_id, do: ""
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user