So we have this code for a video feed loading videos into the socket:
def handle_event("video_visible", %{"id" => activity_id, "videoUrl" => video_url}, socket) do
socket = assign(socket, :current_video_url, video_url)
{:noreply,
push_patch(socket,
to: "#{socket.assigns.globals.request_uri.path}?activity_id=#{activity_id}"
)}
end
def handle_event("toggle_mute", _params, %{assigns: %{muted: muted}} = socket),
do: {:noreply, assign(socket, muted: !muted)}
# Called from inside video_tile function
def handle_event("show_activity_modal", %{"id" => id}, socket) do
{:ok, activity} = @data.get_activity_by_id(id)
socket = assign(socket, :featured_activity, activity)
{:noreply, push_patch(socket, to: "#{socket.assigns.globals.request_uri.path}?activity_id=#{id}")}
end
@impl Phoenix.LiveView
# Called from inside app.js for infinite scroll
def handle_event("load_more_videos", _params, socket) do
{:noreply, load_videos(socket)}
end
@impl Phoenix.LiveView
def handle_params(%{"activity_id" => activity_id}, _uri, socket) do
{:ok, activity} = @data.get_activity_by_id(activity_id)
socket = assign(socket, :page_title, Scorpion.PageTitles.activity(activity))
{:noreply, socket}
end
def handle_params(_params, _uri, socket) do
{:noreply, socket}
end
defp load_videos(socket) do
socket = assign(socket, :current_page, socket.assigns.current_page + 1)
deep_linked_activity = socket.assigns.featured_activity
{:ok, search} =
SearchParamsUtil.search(socket.assigns.search_params,
page: socket.assigns.current_page,
page_size: 5
)
entries =
if deep_linked_activity do
[
deep_linked_activity
| Enum.reject(search.results.entries, &(&1.id == deep_linked_activity.id))
]
else
search.results.entries
end
videos =
Enum.map(entries, fn entry ->
%Video{
id: entry.id,
url: entry.videos |> Enum.random() |> Map.fetch!(:url_mp4),
activity: entry,
liked: entry.id in socket.assigns.liked_activity_ids,
poster: List.first(entry.image_urls)
}
end)
socket = assign(socket, :videos, Enum.concat(socket.assigns.videos, videos))
socket = assign(socket, :region, search.region)
if search.region && Enum.empty?(videos) do
push_navigate(socket, to: region_path(search.region, true))
else
socket
end
end
and this javascript
Hooks.VideoDeepLinkOnScroll = {
mounted () {
const $this = this
var observer = new IntersectionObserver(onIntersection, { threshold: 0.5 })
const id = this.el.getAttribute('data-id')
const videoUrl = this.el.getAttribute('data-url')
const videoPlaytimeMap = {}
const currentVideo = null
function onIntersection (entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
window.Peek.Analytics.track('Video Viewed', {
activity_id: id,
activity_name: $this.el.getAttribute('data-name'),
url: videoUrl
})
$this.pushEvent('video_visible', { id, videoUrl })
firstSource = document.getElementsByTagName('source')[0]
if (firstSource) {
firstSource.setAttribute('src', videoUrl)
document.getElementsByTagName('video')[0].load()
}
}
})
}
observer.observe(this.el)
}
}
and this html
<ActivityComponents.video_overlay :if={@current_video_url} url={@current_video_url} />
<div class="h-screen overflow-y-scroll snap-y snap-mandatory relative inset-x-0" phx-hook="StopStartVideo" id="video-wrap" phx-update="append">
<%= for {video, index} <- Enum.with_index(@videos) do %>
<div
phx-hook="VideoDeepLinkOnScroll"
data-name={video.activity.name}
data-url={video.url}
data-id={video.id}
data-poster={video.poster}
class="relative mx-auto max-w-full sm:max-w-[700px]"
id={"video-wrapper-#{video.id}"}
>
<div class="h-screen object-cover w-full snap-start"><ActivityComponents.video_tile globals={@globals} video={video} index={index} /></div>
</div>
<% end %>
</div>
And so my question is… is there a way to refactor or improve performance for this? The main thing being: our poster image is a random shot from somewhere in the middle of the video. I tried to use javascript to draw a “canvas” from the first frame of the video, but it doesn’t like the format of the video not being html5. I’m wondering if that’s because it’s loaded into the socket from a url? Anyway, it’s mostly the flash of the poster being different that is making the performance janky, and I’m wondering if there’s a way around this. Thank you!