-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmediaPlayer.go
1256 lines (1097 loc) · 34 KB
/
mediaPlayer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package rui
import (
"fmt"
"math"
"strconv"
"strings"
)
// Constants which related to media player properties and events
const (
// Controls is the constant for "controls" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Controls whether the browser need to provide controls to allow user to control audio playback, volume, seeking and
// pause/resume playback. Default value is false.
//
// Supported types: bool, int, string.
//
// Values:
// - true, 1, "true", "yes", "on", "1" - The browser will offer controls to allow the user to control audio playback, volume, seeking and pause/resume playback.
// - false, 0, "false", "no", "off", "0" - No controls will be visible to the end user.
Controls PropertyName = "controls"
// Loop is the constant for "loop" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Controls whether the audio player will play media in a loop. Default value is false.
//
// Supported types: bool, int, string.
//
// Values:
// - true, 1, "true", "yes", "on", "1" - The audio player will automatically seek back to the start upon reaching the end of the audio.
// - false, 0, "false", "no", "off", "0" - Audio player will stop playing when the end of the media file has been reached.
Loop PropertyName = "loop"
// Muted is the constant for "muted" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Controls whether the audio will be initially silenced. Default value is false.
//
// Supported types: bool, int, string.
//
// Values:
// - true, 1, "true", "yes", "on", "1" - Audio will be muted.
// - false, 0, "false", "no", "off", "0" - Audio playing normally.
Muted PropertyName = "muted"
// Preload is the constant for "preload" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Property is intended to provide a hint to the browser about what the author thinks will lead to the best user
// experience. Default value is different for each browser.
//
// Supported types: int, string.
//
// Values:
// - 0 (PreloadNone) or "none" - Media file must not be pre-loaded.
// - 1 (PreloadMetadata) or "metadata" - Only metadata is preloaded.
// - 2 (PreloadAuto) or "auto" - The entire media file can be downloaded even if the user doesn't have to use it.
Preload PropertyName = "preload"
// AbortEvent is the constant for "abort-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Fired when the resource was not fully loaded, but not as the result of an error.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
AbortEvent PropertyName = "abort-event"
// CanPlayEvent is the constant for "can-play-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the browser can play the media, but estimates that not enough data has been loaded to play the media up to
// its end without having to stop for further buffering of content.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
CanPlayEvent PropertyName = "can-play-event"
// CanPlayThroughEvent is the constant for "can-play-through-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the browser estimates it can play the media up to its end without stopping for content buffering.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
CanPlayThroughEvent PropertyName = "can-play-through-event"
// CompleteEvent is the constant for "complete-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the rendering of an OfflineAudioContext has been terminated.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
CompleteEvent PropertyName = "complete-event"
// DurationChangedEvent is the constant for "duration-changed-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the duration attribute has been updated.
//
// General listener format:
//
// func(player rui.MediaPlayer, duration float64).
//
// where:
// - player - Interface of a player which generated this event,
// - duration - Current duration.
//
// Allowed listener formats:
//
// func(player rui.MediaPlayer),
// func(duration float64),
// func()
DurationChangedEvent PropertyName = "duration-changed-event"
// EmptiedEvent is the constant for "emptied-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the media has become empty; for example, this event is sent if the media has already been loaded(or
// partially loaded), and the HTMLMediaElement.load method is called to reload it.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
EmptiedEvent PropertyName = "emptied-event"
// EndedEvent is the constant for "ended-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback has stopped because the end of the media was reached.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
EndedEvent PropertyName = "ended-event"
// LoadedDataEvent is the constant for "loaded-data-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the first frame of the media has finished loading.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
LoadedDataEvent PropertyName = "loaded-data-event"
// LoadedMetadataEvent is the constant for "loaded-metadata-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the metadata has been loaded.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
LoadedMetadataEvent PropertyName = "loaded-metadata-event"
// LoadStartEvent is the constant for "load-start-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Fired when the browser has started to load a resource.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
LoadStartEvent PropertyName = "load-start-event"
// PauseEvent is the constant for "pause-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback has been paused.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
PauseEvent PropertyName = "pause-event"
// PlayEvent is the constant for "play-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback has begun.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
PlayEvent PropertyName = "play-event"
// PlayingEvent is the constant for "playing-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback is ready to start after having been paused or delayed due to lack of data.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
PlayingEvent PropertyName = "playing-event"
// ProgressEvent is the constant for "progress-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Fired periodically as the browser loads a resource.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
ProgressEvent PropertyName = "progress-event"
// RateChangedEvent is the constant for "rate-changed-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback rate has changed.
//
// General listener format:
//
// func(player rui.MediaPlayer, rate float64).
//
// where:
// - player - Interface of a player which generated this event,
// - rate - Playback rate.
//
// Allowed listener formats:
//
// func(player rui.MediaPlayer),
// func(rate float64),
// func()
RateChangedEvent PropertyName = "rate-changed-event"
// SeekedEvent is the constant for "seeked-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when a seek operation completed.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
SeekedEvent PropertyName = "seeked-event"
// SeekingEvent is the constant for "seeking-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when a seek operation has began.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
SeekingEvent PropertyName = "seeking-event"
// StalledEvent is the constant for "stalled-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
StalledEvent PropertyName = "stalled-event"
// SuspendEvent is the constant for "suspend-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the media data loading has been suspended.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
SuspendEvent PropertyName = "suspend-event"
// TimeUpdateEvent is the constant for "time-update-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the time indicated by the currentTime attribute has been updated.
//
// General listener format:
//
// func(player rui.MediaPlayer, time float64).
//
// where:
// - player - Interface of a player which generated this event,
// - time - Current time.
//
// Allowed listener formats:
//
// func(player rui.MediaPlayer),
// func(time float64),
// func()
TimeUpdateEvent PropertyName = "time-update-event"
// VolumeChangedEvent is the constant for "volume-changed-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the volume has changed.
//
// General listener format:
//
// func(player rui.MediaPlayer, volume float64).
//
// where:
// - player - Interface of a player which generated this event,
// - volume - New volume level.
//
// Allowed listener formats:
//
// func(player rui.MediaPlayer),
// func(volume float64),
// func()
VolumeChangedEvent PropertyName = "volume-changed-event"
// WaitingEvent is the constant for "waiting-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Occur when the playback has stopped because of a temporary lack of data.
//
// General listener format:
//
// func(player rui.MediaPlayer)
//
// where:
// player - Interface of a player which generated this event.
//
// Allowed listener formats:
//
// func()
WaitingEvent PropertyName = "waiting-event"
// PlayerErrorEvent is the constant for "player-error-event" property tag.
//
// Used by AudioPlayer, VideoPlayer.
//
// Fired when the resource could not be loaded due to an error(for example, a network connectivity problem).
//
// General listener format:
//
// func(player rui.MediaPlayer, code int, message string).
//
// where:
// - player - Interface of a player which generated this event,
// - code - Error code. See below,
// - message - Error message,
// Error codes:
// - 0 (PlayerErrorUnknown) - Unknown error,
// - 1 (PlayerErrorAborted) - Fetching the associated resource was interrupted by a user request,
// - 2 (PlayerErrorNetwork) - Some kind of network error has occurred that prevented the media from successfully ejecting, even though it was previously available,
// - 3 (PlayerErrorDecode) - Although the resource was previously identified as being used, an error occurred while trying to decode the media resource,
// - 4 (PlayerErrorSourceNotSupported) - The associated resource object or media provider was found to be invalid.
//
// Allowed listener formats:
//
// func(code int, message string),
// func(player rui.MediaPlayer),
// func()
PlayerErrorEvent PropertyName = "player-error-event"
// PreloadNone - value of the view "preload" property: indicates that the audio/video should not be preloaded.
PreloadNone = 0
// PreloadMetadata - value of the view "preload" property: indicates that only audio/video metadata (e.g. length) is fetched.
PreloadMetadata = 1
// PreloadAuto - value of the view "preload" property: indicates that the whole audio file can be downloaded,
// even if the user is not expected to use it.
PreloadAuto = 2
// PlayerErrorUnknown - MediaPlayer error code: An unknown error.
PlayerErrorUnknown = 0
// PlayerErrorAborted - MediaPlayer error code: The fetching of the associated resource was aborted by the user's request.
PlayerErrorAborted = 1
// PlayerErrorNetwork - MediaPlayer error code: Some kind of network error occurred which prevented the media
// from being successfully fetched, despite having previously been available.
PlayerErrorNetwork = 2
// PlayerErrorDecode - MediaPlayer error code: Despite having previously been determined to be usable,
// an error occurred while trying to decode the media resource, resulting in an error.
PlayerErrorDecode = 3
// PlayerErrorSourceNotSupported - MediaPlayer error code: The associated resource or media provider object has been found to be unsuitable.
PlayerErrorSourceNotSupported = 4
)
// MediaPlayer is a common interface for media player views like [AudioPlayer] and [VideoPlayer].
type MediaPlayer interface {
View
// Play attempts to begin playback of the media.
Play()
// Pause will pause playback of the media, if the media is already in a paused state this method will have no effect.
Pause()
// SetCurrentTime sets the current playback time in seconds.
SetCurrentTime(seconds float64)
// CurrentTime returns the current playback time in seconds.
CurrentTime() float64
// Duration returns the value indicating the total duration of the media in seconds.
// If no media data is available, the returned value is NaN.
Duration() float64
// SetPlaybackRate sets the rate at which the media is being played back. This is used to implement user controls
// for fast forward, slow motion, and so forth. The normal playback rate is multiplied by this value to obtain
// the current rate, so a value of 1.0 indicates normal speed.
SetPlaybackRate(rate float64)
// PlaybackRate returns the rate at which the media is being played back.
PlaybackRate() float64
// SetVolume sets the audio volume, from 0.0 (silent) to 1.0 (loudest).
SetVolume(volume float64)
// Volume returns the audio volume, from 0.0 (silent) to 1.0 (loudest).
Volume() float64
// IsEnded function tells whether the media element is ended.
IsEnded() bool
// IsPaused function tells whether the media element is paused.
IsPaused() bool
}
type mediaPlayerData struct {
viewData
}
// MediaSource represent one media file source
type MediaSource struct {
// Url of the source
Url string
// MimeType of the source
MimeType string
}
func (player *mediaPlayerData) init(session Session) {
player.viewData.init(session)
player.tag = "MediaPlayer"
player.set = player.setFunc
player.changed = player.propertyChanged
}
func (player *mediaPlayerData) Focusable() bool {
return true
}
func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag {
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
return setNoArgEventListener[MediaPlayer](player, tag, value)
case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
return setOneArgEventListener[MediaPlayer, float64](player, tag, value)
case PlayerErrorEvent:
if listeners, ok := valueToPlayerErrorListeners(value); ok {
return setArrayPropertyValue(player, tag, listeners)
}
notCompatibleType(tag, value)
return nil
case Source:
return setMediaPlayerSource(player, value)
}
return player.viewData.setFunc(tag, value)
}
func setMediaPlayerSource(properties Properties, value any) []PropertyName {
switch value := value.(type) {
case string:
src := MediaSource{Url: value, MimeType: ""}
properties.setRaw(Source, []MediaSource{src})
case MediaSource:
properties.setRaw(Source, []MediaSource{value})
case []MediaSource:
properties.setRaw(Source, value)
case DataObject:
url, ok := value.PropertyValue("src")
if !ok || url == "" {
invalidPropertyValue(Source, value)
return nil
}
mimeType, _ := value.PropertyValue("mime-type")
src := MediaSource{Url: url, MimeType: mimeType}
properties.setRaw(Source, []MediaSource{src})
case []DataValue:
src := []MediaSource{}
for _, val := range value {
if val.IsObject() {
obj := val.Object()
if url, ok := obj.PropertyValue("src"); ok && url != "" {
mimeType, _ := obj.PropertyValue("mime-type")
src = append(src, MediaSource{Url: url, MimeType: mimeType})
} else {
invalidPropertyValue(Source, value)
return nil
}
} else {
src = append(src, MediaSource{Url: val.Value(), MimeType: ""})
}
}
if len(src) == 0 {
invalidPropertyValue(Source, value)
return nil
}
properties.setRaw(Source, src)
default:
notCompatibleType(Source, value)
return nil
}
return []PropertyName{Source}
}
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(MediaPlayer, int, string):
return []func(MediaPlayer, int, string){value}, true
case func(int, string):
fn := func(_ MediaPlayer, code int, message string) {
value(code, message)
}
return []func(MediaPlayer, int, string){fn}, true
case func(MediaPlayer):
fn := func(player MediaPlayer, _ int, _ string) {
value(player)
}
return []func(MediaPlayer, int, string){fn}, true
case func():
fn := func(MediaPlayer, int, string) {
value()
}
return []func(MediaPlayer, int, string){fn}, true
case []func(MediaPlayer, int, string):
if len(value) == 0 {
return nil, true
}
for _, fn := range value {
if fn == nil {
return nil, false
}
}
return value, true
case []func(int, string):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(MediaPlayer, int, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ MediaPlayer, code int, message string) {
v(code, message)
}
}
return listeners, true
case []func(MediaPlayer):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(MediaPlayer, int, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(player MediaPlayer, _ int, _ string) {
v(player)
}
}
return listeners, true
case []func():
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(MediaPlayer, int, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(MediaPlayer, int, string) {
v()
}
}
return listeners, true
case []any:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(MediaPlayer, int, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(MediaPlayer, int, string):
listeners[i] = v
case func(int, string):
listeners[i] = func(_ MediaPlayer, code int, message string) {
v(code, message)
}
case func(MediaPlayer):
listeners[i] = func(player MediaPlayer, _ int, _ string) {
v(player)
}
case func():
listeners[i] = func(MediaPlayer, int, string) {
v()
}
default:
return nil, false
}
}
return listeners, true
}
return nil, false
}
func mediaPlayerEvents() map[PropertyName]string {
return map[PropertyName]string{
AbortEvent: "onabort",
CanPlayEvent: "oncanplay",
CanPlayThroughEvent: "oncanplaythrough",
CompleteEvent: "oncomplete",
EmptiedEvent: "onemptied",
EndedEvent: "ended",
LoadedDataEvent: "onloadeddata",
LoadedMetadataEvent: "onloadedmetadata",
LoadStartEvent: "onloadstart",
PauseEvent: "onpause",
PlayEvent: "onplay",
PlayingEvent: "onplaying",
ProgressEvent: "onprogress",
SeekedEvent: "onseeked",
SeekingEvent: "onseeking",
StalledEvent: "onstalled",
SuspendEvent: "onsuspend",
WaitingEvent: "onwaiting",
}
}
func (player *mediaPlayerData) propertyChanged(tag PropertyName) {
session := player.Session()
switch tag {
case Controls, Loop:
value, _ := boolProperty(player, tag, session)
if value {
session.updateProperty(player.htmlID(), string(tag), value)
} else {
session.removeProperty(player.htmlID(), string(tag))
}
case Muted:
value, _ := boolProperty(player, Muted, session)
session.callFunc("setMediaMuted", player.htmlID(), value)
case Preload:
value, _ := enumProperty(player, Preload, session, 0)
values := enumProperties[Preload].values
session.updateProperty(player.htmlID(), string(Preload), values[value])
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent,
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent,
LoadStartEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
if cssTag, ok := mediaPlayerEvents()[tag]; ok {
fn := ""
if value := player.getRaw(tag); value != nil {
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
fn = fmt.Sprintf(`viewEvent(this, "%s")`, string(tag))
}
}
session.updateProperty(player.htmlID(), cssTag, fn)
}
case TimeUpdateEvent:
if value := player.getRaw(tag); value != nil {
session.updateProperty(player.htmlID(), "ontimeupdate", "viewTimeUpdatedEvent(this)")
} else {
session.updateProperty(player.htmlID(), "ontimeupdate", "")
}
case VolumeChangedEvent:
if value := player.getRaw(tag); value != nil {
session.updateProperty(player.htmlID(), "onvolumechange", "viewVolumeChangedEvent(this)")
} else {
session.updateProperty(player.htmlID(), "onvolumechange", "")
}
case DurationChangedEvent:
if value := player.getRaw(tag); value != nil {
session.updateProperty(player.htmlID(), "ondurationchange", "viewDurationChangedEvent(this)")
} else {
session.updateProperty(player.htmlID(), "ondurationchange", "")
}
case RateChangedEvent:
if value := player.getRaw(tag); value != nil {
session.updateProperty(player.htmlID(), "onratechange", "viewRateChangedEvent(this)")
} else {
session.updateProperty(player.htmlID(), "onratechange", "")
}
case PlayerErrorEvent:
if value := player.getRaw(tag); value != nil {
session.updateProperty(player.htmlID(), "onerror", "viewErrorEvent(this)")
} else {
session.updateProperty(player.htmlID(), "onerror", "")
}
case Source:
updateInnerHTML(player.htmlID(), session)
default:
player.viewData.propertyChanged(tag)
}
}
func (player *mediaPlayerData) htmlSubviews(self View, buffer *strings.Builder) {
if value := player.getRaw(Source); value != nil {
if sources, ok := value.([]MediaSource); ok && len(sources) > 0 {
session := player.session
for _, src := range sources {
if url, ok := session.resolveConstants(src.Url); ok && url != "" {
buffer.WriteString(`<source src="`)
buffer.WriteString(url)
buffer.WriteRune('"')
if mime, ok := session.resolveConstants(src.MimeType); ok && mime != "" {
buffer.WriteString(` type="`)
buffer.WriteString(mime)
buffer.WriteRune('"')
}
buffer.WriteRune('>')
}
}
}
}
}
func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder) {
player.viewData.htmlProperties(self, buffer)
for _, tag := range []PropertyName{Controls, Loop, Muted, Preload} {
if value, _ := boolProperty(player, tag, player.session); value {
buffer.WriteRune(' ')
buffer.WriteString(string(tag))
}
}
if value, ok := enumProperty(player, Preload, player.session, 0); ok {
values := enumProperties[Preload].values
buffer.WriteString(` preload="`)
buffer.WriteString(values[value])
buffer.WriteRune('"')
}
for tag, cssTag := range mediaPlayerEvents() {
if value := player.getRaw(tag); value != nil {
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
buffer.WriteString(` `)
buffer.WriteString(cssTag)
buffer.WriteString(`="playerEvent(this, '`)
buffer.WriteString(string(tag))
buffer.WriteString(`')"`)
}
}
}
if value := player.getRaw(TimeUpdateEvent); value != nil {
buffer.WriteString(` ontimeupdate="playerTimeUpdatedEvent(this)"`)
}
if value := player.getRaw(VolumeChangedEvent); value != nil {
buffer.WriteString(` onvolumechange="playerVolumeChangedEvent(this)"`)
}
if value := player.getRaw(DurationChangedEvent); value != nil {
buffer.WriteString(` ondurationchange="playerDurationChangedEvent(this)"`)
}
if value := player.getRaw(RateChangedEvent); value != nil {
buffer.WriteString(` onratechange="playerRateChangedEvent(this)"`)
}
if value := player.getRaw(PlayerErrorEvent); value != nil {
buffer.WriteString(` onerror="playerErrorEvent(this)"`)
}
}
func (player *mediaPlayerData) handleCommand(self View, command PropertyName, data DataObject) bool {
switch command {
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, LoadStartEvent,
EmptiedEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent,
PlayingEvent, ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent,
WaitingEvent:
if value := player.getRaw(command); value != nil {
if listeners, ok := value.([]func(MediaPlayer)); ok {
for _, listener := range listeners {
listener(player)
}
}
}
case TimeUpdateEvent, DurationChangedEvent, RateChangedEvent, VolumeChangedEvent:
if value := player.getRaw(command); value != nil {
if listeners, ok := value.([]func(MediaPlayer, float64)); ok {
time := dataFloatProperty(data, "value")
for _, listener := range listeners {
listener(player, time)
}
}
}