|
15 | 15 | (def ^:const audio-path "/messages/audio")
|
16 | 16 | (def ^:const uri-param "?messageId=")
|
17 | 17 |
|
18 |
| -(defonce current-player-message-id (atom nil)) |
19 |
| -(defonce current-active-state-ref-ref (atom nil)) |
| 18 | +(defonce active-players (atom {})) |
| 19 | +(defonce audio-uris (atom {})) |
20 | 20 | (defonce progress-timer (atom nil))
|
| 21 | +(defonce current-player-message-id (atom nil)) |
| 22 | + |
| 23 | +(defn- get-player-key |
| 24 | + [message-id in-pinned-view?] |
| 25 | + (str (some? in-pinned-view?) message-id)) |
21 | 26 |
|
22 | 27 | (defn update-state
|
23 |
| - [player-ref |
24 |
| - {:keys [state-ref progress-ref message-id seek-to-ms audio-duration-ms |
25 |
| - slider-new-state-seeking? unloaded? error]}] |
26 |
| - (let [player-state (audio/get-state @player-ref) |
| 28 | + [{:keys [state-ref progress-ref seek-to-ms audio-duration-ms |
| 29 | + player-key slider-new-state-seeking? unloaded? error]}] |
| 30 | + (let [player (@active-players player-key) |
| 31 | + player-state (audio/get-state player) |
27 | 32 | slider-seeking (if (some? slider-new-state-seeking?)
|
28 | 33 | slider-new-state-seeking?
|
29 | 34 | (:slider-seeking @state-ref))
|
30 | 35 | general (cond
|
31 |
| - (some? error) :error |
32 |
| - (or unloaded? (not= message-id @current-player-message-id)) :not-loaded |
33 |
| - slider-seeking (:general |
34 |
| - @state-ref) ; persist |
35 |
| - ; player |
36 |
| - ; state |
37 |
| - ; at |
38 |
| - ; the |
39 |
| - ; time |
40 |
| - ; user |
41 |
| - ; started |
42 |
| - ; sliding |
43 |
| - (= player-state audio/PLAYING) :playing |
44 |
| - (= player-state audio/PAUSED) :paused |
45 |
| - (= player-state audio/SEEKING) :seeking |
46 |
| - (= player-state audio/PREPARED) :ready-to-play |
47 |
| - :else :preparing) |
| 36 | + (some? error) :error |
| 37 | + (or unloaded? (nil? (@active-players player-key))) :not-loaded |
| 38 | + slider-seeking (:general |
| 39 | + @state-ref) ; persist |
| 40 | + ; player |
| 41 | + ; state |
| 42 | + ; at |
| 43 | + ; the |
| 44 | + ; time |
| 45 | + ; user |
| 46 | + ; started |
| 47 | + ; sliding |
| 48 | + (= player-state audio/PLAYING) :playing |
| 49 | + (= player-state audio/PAUSED) :paused |
| 50 | + (= player-state audio/SEEKING) :seeking |
| 51 | + (= player-state audio/PREPARED) :ready-to-play |
| 52 | + :else :preparing) |
48 | 53 | new-state {:general general
|
49 | 54 | :error-msg error
|
50 | 55 | :duration (cond (not (#{:preparing :not-loaded :error} general))
|
51 |
| - (audio/get-player-duration @player-ref) |
| 56 | + (audio/get-player-duration player) |
52 | 57 |
|
53 | 58 | audio-duration-ms audio-duration-ms
|
54 | 59 |
|
|
70 | 75 | (reset! (:progress-ref new-state) seek-to-ms))))
|
71 | 76 |
|
72 | 77 | (defn destroy-player
|
73 |
| - [player-ref {:keys [message-id reloading?]}] |
74 |
| - (when (and @player-ref |
75 |
| - (or reloading? |
76 |
| - (= message-id @current-player-message-id))) |
77 |
| - (audio/destroy-player @player-ref) |
78 |
| - (reset! player-ref nil) |
79 |
| - (when @current-active-state-ref-ref |
80 |
| - (update-state player-ref {:state-ref @current-active-state-ref-ref :unloaded? true})) |
81 |
| - (reset! current-player-message-id nil) |
82 |
| - (reset! current-active-state-ref-ref nil))) |
| 78 | + [{:keys [message-id reloading? player-key]}] |
| 79 | + (when (and (@active-players player-key) reloading?) |
| 80 | + (audio/destroy-player (@active-players player-key)) |
| 81 | + (swap! active-players dissoc message-id))) |
83 | 82 |
|
84 | 83 | (defonce last-seek (atom (js/Date.now)))
|
85 | 84 |
|
86 | 85 | (defn seek
|
87 |
| - [player-ref {:keys [message-id] :as params} value immediate? on-success] |
88 |
| - (when (and @player-ref (= message-id @current-player-message-id)) |
| 86 | + [{:keys [player-key] :as params} value immediate? on-success] |
| 87 | + (when-let [player (@active-players player-key)] |
89 | 88 | (let [now (js/Date.now)]
|
90 | 89 | (when (or immediate? (> (- now @last-seek) 200))
|
91 | 90 | (reset! last-seek (js/Date.now))
|
92 | 91 | (audio/seek-player
|
93 |
| - @player-ref |
| 92 | + player |
94 | 93 | value
|
95 | 94 | #(do
|
96 |
| - (update-state player-ref params) |
| 95 | + (update-state params) |
97 | 96 | (when on-success (on-success)))
|
98 |
| - #(update-state player-ref (merge params {:error (:message %)})))))) |
99 |
| - (update-state player-ref (merge params {:seek-to-ms value}))) |
| 97 | + #(update-state (merge params {:error (:message %)})))))) |
| 98 | + (update-state (merge params {:seek-to-ms value}))) |
100 | 99 |
|
101 | 100 | (defn download-audio-http
|
102 | 101 | [base64-uri on-success]
|
|
106 | 105 | (.catch #(log/error "could not fetch audio"))))
|
107 | 106 |
|
108 | 107 | (defn reload-player
|
109 |
| - [player-ref {:keys [message-id state-ref progress-ref] :as params} audio-url on-success] |
| 108 | + [{:keys [progress-ref player-key] :as params} audio-url on-success] |
110 | 109 | ;; to avoid reloading player while is initializing,
|
111 | 110 | ;; we go ahead only if there is no player or
|
112 | 111 | ;; if it is already prepared
|
113 |
| - (when (or (nil? @player-ref) (audio/can-play? @player-ref)) |
114 |
| - (when @player-ref |
115 |
| - (destroy-player player-ref (merge params {:reloading? true}))) |
116 |
| - (download-audio-http |
117 |
| - audio-url |
118 |
| - (fn [base64-data] |
119 |
| - (reset! player-ref (audio/new-player |
120 |
| - (str "data:audio/acc;base64," base64-data) |
121 |
| - {:autoDestroy false |
122 |
| - :continuesToPlayInBackground false} |
123 |
| - (fn [] |
124 |
| - (seek player-ref params 0 true nil) |
125 |
| - (reset! progress-ref 0)))) |
| 112 | + (destroy-player params) |
| 113 | + (download-audio-http |
| 114 | + audio-url |
| 115 | + (fn [base64-data] |
| 116 | + (let [player (audio/new-player |
| 117 | + (str "data:audio/acc;base64," base64-data) |
| 118 | + {:autoDestroy false |
| 119 | + :continuesToPlayInBackground false} |
| 120 | + (fn [] |
| 121 | + (seek params 0 true nil) |
| 122 | + (reset! progress-ref 0)))] |
| 123 | + (swap! active-players assoc player-key player) |
126 | 124 | (audio/prepare-player
|
127 |
| - @player-ref |
| 125 | + player |
128 | 126 | #(when on-success (on-success))
|
129 |
| - #(update-state player-ref (merge params {:error (:message %)}))) |
130 |
| - (reset! current-player-message-id message-id) |
131 |
| - (reset! current-active-state-ref-ref state-ref) |
132 |
| - (update-state player-ref params))))) |
| 127 | + #(update-state (merge params {:error (:message %)}))) |
| 128 | + (update-state params))))) |
133 | 129 |
|
134 | 130 | (defn play-pause
|
135 |
| - [player-ref {:keys [message-id progress-ref progress-timer-ref] :as params} seeking-audio?] |
| 131 | + [player {:keys [message-id progress-ref progress-timer-ref player-key] :as params} seeking-audio?] |
136 | 132 | (let [mediaserver-port (rf/sub [:mediaserver/port])
|
137 | 133 | audio-uri (str media-server-uri-prefix
|
138 | 134 | mediaserver-port
|
139 | 135 | audio-path
|
140 | 136 | uri-param
|
141 | 137 | message-id)]
|
142 |
| - (if (not= message-id @current-player-message-id) |
| 138 | + (if (and player |
| 139 | + (= (@audio-uris message-id) audio-uri)) |
| 140 | + (let [player (@active-players player-key)] |
| 141 | + (audio/toggle-playpause-player |
| 142 | + player |
| 143 | + (fn [] |
| 144 | + (update-state params) |
| 145 | + (when @progress-timer-ref |
| 146 | + (js/clearInterval @progress-timer-ref)) |
| 147 | + (reset! progress-timer-ref |
| 148 | + (js/setInterval |
| 149 | + (fn [] |
| 150 | + (let [current-time (audio/get-player-current-time player) |
| 151 | + player-state (audio/get-state player) |
| 152 | + playing? (= player-state audio/PLAYING)] |
| 153 | + (when (and playing? (not @seeking-audio?) (> current-time 0)) |
| 154 | + (reset! progress-ref current-time)))) |
| 155 | + 100)) |
| 156 | + (reset! current-player-message-id message-id)) |
| 157 | + (fn [] |
| 158 | + (update-state params) |
| 159 | + (when @progress-timer-ref |
| 160 | + (js/clearInterval @progress-timer-ref) |
| 161 | + (reset! progress-timer-ref nil))) |
| 162 | + #(update-state (merge params {:error (:message %)})))) |
143 | 163 | ;; player has audio from another message, we need to reload
|
144 |
| - (reload-player player-ref |
145 |
| - params |
| 164 | + (reload-player params |
146 | 165 | audio-uri
|
147 | 166 | ;; on-success: audio is loaded, do we have an existing value to seek to?
|
148 | 167 | (fn []
|
149 | 168 | (reset! seeking-audio? false)
|
150 | 169 | (if (> @progress-ref 0)
|
151 | 170 | ;; check seek time against real audio duration and play
|
152 |
| - (let [audio-duration (audio/get-player-duration @player-ref) |
| 171 | + (let [audio-duration (audio/get-player-duration (@active-players player-key)) |
153 | 172 | seek-time (* audio-duration @progress-ref)
|
154 | 173 | checked-seek-time (min audio-duration seek-time)]
|
155 |
| - (seek player-ref |
156 |
| - params |
| 174 | + (seek params |
157 | 175 | checked-seek-time
|
158 | 176 | true
|
159 |
| - (fn [] (play-pause player-ref params seeking-audio?)))) |
| 177 | + (fn [] |
| 178 | + (play-pause (@active-players player-key) params seeking-audio?)))) |
160 | 179 |
|
161 | 180 | ;; nothing to seek to, play
|
162 |
| - (play-pause player-ref params seeking-audio?)))) |
163 |
| - |
164 |
| - ;; loaded audio corresponds to current message we can play |
165 |
| - (when @player-ref |
166 |
| - (audio/toggle-playpause-player |
167 |
| - @player-ref |
168 |
| - (fn [] |
169 |
| - (update-state player-ref params) |
170 |
| - (reset! progress-timer-ref |
171 |
| - (js/setInterval |
172 |
| - (fn [] |
173 |
| - (let [current-time (audio/get-player-current-time @player-ref) |
174 |
| - player-state (audio/get-state @player-ref) |
175 |
| - playing? (= player-state audio/PLAYING)] |
176 |
| - (when (and playing? (not @seeking-audio?) (> current-time 0)) |
177 |
| - (reset! progress-ref current-time)))) |
178 |
| - 100))) |
179 |
| - (fn [] |
180 |
| - (update-state player-ref params) |
181 |
| - (when @progress-timer-ref |
182 |
| - (js/clearInterval @progress-timer-ref) |
183 |
| - (reset! progress-timer-ref nil))) |
184 |
| - #(update-state player-ref (merge params {:error (:message %)}))))))) |
| 181 | + (play-pause (@active-players player-key) params seeking-audio?))))) |
| 182 | + (swap! audio-uris assoc message-id audio-uri))) |
185 | 183 |
|
186 | 184 | (defn- play-pause-button
|
187 | 185 | [state-ref on-press]
|
|
198 | 196 | :color colors/white}]])
|
199 | 197 |
|
200 | 198 | (defn audio-message
|
201 |
| - [{:keys [audio-duration-ms message-id]}] |
202 |
| - (let [player-ref (atom nil) |
203 |
| - state (reagent/atom nil) |
| 199 | + [{:keys [audio-duration-ms message-id in-pinned-view?]}] |
| 200 | + (let [state (reagent/atom nil) |
204 | 201 | progress (reagent/atom 0)
|
205 | 202 | progress-timer (atom nil)
|
206 |
| - seeking-audio? (reagent/atom false)] |
| 203 | + seeking-audio? (reagent/atom false) |
| 204 | + player-key (get-player-key message-id in-pinned-view?)] |
207 | 205 | [:f>
|
208 | 206 | (fn []
|
209 | 207 | (let [base-params {:state-ref state
|
210 | 208 | :message-id message-id
|
| 209 | + :player-key player-key |
211 | 210 | :progress-ref progress
|
212 | 211 | :progress-timer-ref progress-timer}
|
213 | 212 | duration (:duration @state)
|
|
218 | 217 | 1000)]
|
219 | 218 | (rn/use-effect
|
220 | 219 | (fn []
|
221 |
| - (update-state player-ref |
222 |
| - {:state-ref state |
| 220 | + (update-state {:state-ref state |
223 | 221 | :audio-duration-ms audio-duration-ms
|
224 | 222 | :message-id message-id
|
| 223 | + :player-key player-key |
225 | 224 | :unloaded? true
|
226 | 225 | :progress-ref progress})
|
227 |
| - (fn [] |
228 |
| - (destroy-player player-ref {:state-ref state :message-id message-id}) |
229 |
| - (when (= @current-player-message-id message-id) |
230 |
| - (reset! current-active-state-ref-ref nil) |
231 |
| - (reset! current-player-message-id nil)) |
232 |
| - (reset! state nil)))) |
| 226 | + (fn [] (destroy-player {:state-ref state :message-id message-id})))) |
| 227 | + (rn/use-effect |
| 228 | + (fn [] |
| 229 | + (when (and (some? @current-player-message-id) |
| 230 | + (not= @current-player-message-id message-id) |
| 231 | + (#{:playing} (:general @state))) |
| 232 | + (play-pause (@active-players player-key) base-params seeking-audio?))) |
| 233 | + [@current-player-message-id]) |
233 | 234 | (if (= (:general @state) :error)
|
234 | 235 | [quo/text
|
235 | 236 | {:style {:typography :main-medium
|
236 | 237 | :margin-bottom 16}}
|
237 | 238 | (:error-msg @state)]
|
238 | 239 | [rn/view {:style (style/container)}
|
239 |
| - [play-pause-button state #(play-pause player-ref base-params seeking-audio?)] |
| 240 | + [play-pause-button state |
| 241 | + #(play-pause (@active-players player-key) base-params seeking-audio?)] |
240 | 242 | [quo/soundtrack
|
241 | 243 | {:style style/slider-container
|
242 | 244 | :audio-current-time-ms progress
|
243 |
| - :player-ref player-ref |
| 245 | + :player-ref (@active-players player-key) |
244 | 246 | :seeking-audio? seeking-audio?}]
|
245 | 247 | [quo/text
|
246 | 248 | {:style style/timestamp
|
|
0 commit comments