From 67007e8bddb6aea4b511d5eb05ed931d352d5467 Mon Sep 17 00:00:00 2001 From: Miguel Ángel Moreno Date: Thu, 5 Jan 2023 15:08:09 +0100 Subject: feat(frontend): Introduce video.js for stream playback --- src/frontend/tau/components/player.cljs | 80 ++++++++++++++++++++++++++------- src/frontend/tau/views/stream.cljs | 13 +++--- 2 files changed, 71 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/frontend/tau/components/player.cljs b/src/frontend/tau/components/player.cljs index 19f76bf..11cc9c5 100644 --- a/src/frontend/tau/components/player.cljs +++ b/src/frontend/tau/components/player.cljs @@ -1,24 +1,70 @@ (ns tau.components.player (:require + [reagent.core :as r] + [reagent.dom :as rdom] [re-frame.core :as rf] [reitit.frontend.easy :as rfe] - [tau.events :as events])) + [tau.events :as events] + ["video.js" :as videojs])) (defn global-player [] - (let [{:keys [uploader-name uploader-url name stream url service-color]} @(rf/subscribe [:global-stream]) - show-global-player? @(rf/subscribe [:show-global-player])] - (when show-global-player? - [:div.sticky.bottom-0.z-50.bg-neutral-900.p-5.absolute - {:style {:borderColor service-color :borderTopWidth "2px" :borderStyle "solid"}} - [:div.flex.items-center.justify-between - [:div.flex.flex-wrap.items-center - [:div.flex.flex-col - [:a.text-xs - {:href (rfe/href :tau.router/stream nil {:url url})} name] - [:a.text-xs.text-gray-300 - {:href (rfe/href :tau.router/channel nil {:url uploader-url})} uploader-name]] - [:div.px-2.py-0.md:pt-4 - [:audio {:src stream :controls true}]]] - [:div.px-2 - [:i.fa-solid.fa-close.cursor-pointer {:on-click #(rf/dispatch [::events/toggle-global-player])}]]]]))) + (let [!player (r/atom nil) + !loop? (r/atom nil)] + (fn [] + (let [{:keys [uploader-name uploader-url name stream url service-color]} @(rf/subscribe [:global-stream]) + show-global-player? @(rf/subscribe [:show-global-player])] + (when show-global-player? + [:div.sticky.bottom-0.z-50.bg-neutral-900.p-5.absolute.box-border.m-0 + {:style {:borderColor service-color :borderTopWidth "2px" :borderStyle "solid"}} + [:div.flex.items-center.justify-between + [:div.flex.flex-wrap.items-center + [:div.flex.flex-col + [:a.text-xs + {:href (rfe/href :tau.router/stream nil {:url url})} name] + [:a.text-xs.text-gray-300 + {:href (rfe/href :tau.router/channel nil {:url uploader-url})} uploader-name]] + [:div.px-2.py-0.md:pt-4 + [:audio {:src stream :ref #(reset! !player %) :loop @!loop?}]] + [:div.mx-2 + [:button.focus:ring-transparent.mx-2 + {:on-click (fn [] (swap! !loop? #(not %)))} + [:i.fa-solid.fa-repeat + {:style {:color (when @!loop? service-color)}}]] + [:button.focus:ring-transparent.mx-2 + {:on-click #(when-let [player @!player] + (if (.-paused player) + (.play player) + (.pause player)))} + (if @!player + (if (.-paused @!player) + [:i.fa-solid.fa-play] + [:i.fa-solid.fa-pause]) + [:i.fa-solid.fa-play])]]] + [:div.px-2 + [:i.fa-solid.fa-close.cursor-pointer + {:on-click (fn [] + (rf/dispatch [::events/toggle-global-player]) + (.pause @!player))}]]]]))))) + +(defn stream-player + [options url] + (let [!player (atom nil)] + (r/create-class + {:display-name "StreamPlayer" + :component-did-mount + (fn [this] + (reset! !player (videojs (rdom/dom-node this) (clj->js options)))) + :component-did-update + (fn [this [_ prev-argv prev-more]] + (when (and @!player (not= prev-more (first (r/children this)))) + (.src @!player (apply array (map #(js-obj "type" % "src" (first (r/children this))) + ["video/mp4" "video/webm"]))) + (.ready @!player #(.play @!player)))) + :component-will-unmount + (fn [_] + (when @!player + (.dispose @!player))) + :reagent-render + (fn [options url] + [:video-js.vjs-default-skin.vjs-big-play-centered.bottom-0.object-cover.min-h-full.max-h-full.min-w-full])}))) diff --git a/src/frontend/tau/views/stream.cljs b/src/frontend/tau/views/stream.cljs index 78a1b1e..3363d6a 100644 --- a/src/frontend/tau/views/stream.cljs +++ b/src/frontend/tau/views/stream.cljs @@ -7,6 +7,7 @@ [tau.components.loading :as loading] [tau.components.navigation :as navigation] [tau.components.comments :as comments] + [tau.components.player :as player] [tau.util :as util])) (defn stream @@ -27,11 +28,13 @@ [:div.w-full.pb-4.relative {:class "ml:w-4/5 xl:w-3/5"} [navigation/back-button service-color] [:div.flex.justify-center.relative - {:style {:background (str "center / cover no-repeat url('" thumbnail-url"')")} - :class "ml:h-[450px] lg:h-[600px]"} - [:video.bottom-0.object-cover.min-h-full.max-h-full.min-w-full - {:src content :controls true} - "This browser can't play the stream format."]] + {:class "ml:h-[450px] lg:h-[600px]"} + (when stream-format + [player/stream-player {"sources" [{"src" content "type" "video/mp4"} + {"src" content "type" "video/webm"}] + "poster" thumbnail-url + "controls" true} + content])] [:div.px-4.ml:p-0 [:div.flex.flex.w-full.mt-3 (when stream-format -- cgit v1.2.3