@saooti/octopus-sdk 31.0.18 → 31.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,514 +0,0 @@
1
- <template>
2
- <div
3
- class="w-100 transition-height bg-dark"
4
- :style="{ height: playerHeight }"
5
- >
6
- <div
7
- class="player-container"
8
- :style="{ height: playerHeight }"
9
- @transitionend="onHidden"
10
- >
11
- <div
12
- v-if="isBarTop"
13
- class="progress c-hand"
14
- @mouseup="seekTo"
15
- >
16
- <div
17
- class="progress-bar primary-bg"
18
- role="progressbar"
19
- aria-valuenow="0"
20
- aria-valuemin="0"
21
- aria-valuemax="100"
22
- :style="'width: ' + percentProgress + '%'"
23
- />
24
- <div class="player-progress-border" />
25
- </div>
26
- <div
27
- v-if="display"
28
- class="d-flex align-items-center flex-grow-1 ps-2"
29
- >
30
- <audio
31
- id="audio-player"
32
- :src="!live? audioUrl: undefined"
33
- autoplay
34
- @timeupdate="onTimeUpdate"
35
- @ended="onFinished"
36
- @playing="onPlay"
37
- @durationChange="onTimeUpdate"
38
- @error="onError"
39
- />
40
- <PlayerButtons
41
- :player-error="playerError"
42
- />
43
- <PlayerProgressBar
44
- v-model:notListenTime="notListenTime"
45
- :hls-ready="hlsReady"
46
- :show-timeline="showTimeline"
47
- :comments="comments"
48
- :display-alert-bar="displayAlertBar"
49
- :percent-live-progress="percentLiveProgress"
50
- :duration-live-position="durationLivePosition"
51
- :player-error="playerError"
52
- :listen-time="listenTime"
53
- />
54
- <div
55
- class="play-button-box primary-bg text-light"
56
- @click="stopPlayer"
57
- >
58
- <div
59
- class="saooti-cross"
60
- :title="$t('Close')"
61
- />
62
- </div>
63
- <PlayerClockAndTimeline
64
- v-model:showTimeline="showTimeline"
65
- :comments="comments"
66
- />
67
- </div>
68
- </div>
69
- </div>
70
- </template>
71
- <script lang="ts">
72
- import { mapState } from 'vuex';
73
- import { state } from '../../store/paramStore';
74
- import octopusApi from '@saooti/octopus-api';
75
- /* eslint-disable */
76
- let Hls:any = null;
77
- /* eslint-enable */
78
- import { CommentPodcast } from '@/store/class/general/comment';
79
- import { cookies } from '../mixins/functions';
80
- import { StoreState } from '@/store/typeAppStore';
81
- import PlayerProgressBar from './PlayerProgressBar.vue';
82
- import PlayerButtons from './PlayerButtons.vue';
83
- import PlayerClockAndTimeline from './PlayerClockAndTimeline.vue';
84
- import { defineComponent } from 'vue';
85
- import { FetchParam } from '@/store/class/general/fetchParam';
86
- export default defineComponent({
87
- name: 'Player',
88
-
89
- components: {
90
- PlayerProgressBar,
91
- PlayerButtons,
92
- PlayerClockAndTimeline
93
- },
94
- mixins:[cookies],
95
- emits: ['hide'],
96
- data() {
97
- return {
98
- forceHide: false as boolean,
99
- listenTime: 0 as number,
100
- notListenTime: 0 as number,
101
- lastSend: 0 as number,
102
- downloadId: null as string|null,
103
- playerError: false as boolean,
104
- listenError: false as boolean,
105
- percentLiveProgress: 0 as number,
106
- durationLivePosition: 0 as number,
107
- displayAlertBar: false as boolean,
108
- hlsReady: false as boolean,
109
- comments: [] as Array<CommentPodcast>,
110
- showTimeline: false as boolean,
111
- };
112
- },
113
- computed: {
114
- isBarTop(): boolean {
115
- return (state.player.barTop as boolean);
116
- },
117
- ...mapState({
118
- display: (state: StoreState) => 'STOPPED' !== state.player.status,
119
- playerHeight(state: StoreState) {
120
- if ('STOPPED' === state.player.status || this.forceHide) return 0;
121
- if (window.innerWidth > 450 && !this.showTimeline) return '5rem';
122
- if (window.innerWidth > 450 && this.showTimeline) return '6rem';
123
- return '3.5rem';
124
- },
125
- podcast: (state: StoreState) => state.player.podcast,
126
- media: (state: StoreState) => state.player.media,
127
- live: (state: StoreState) => state.player.live,
128
- volume: (state: StoreState) => state.player.volume,
129
- commentsLoaded: (state: StoreState) => state.comments.loadedComments,
130
- percentProgress: (state: StoreState) => {
131
- if(!state.player.elapsed){return 0;}
132
- return state.player.elapsed * 100;
133
- },
134
- playerSeekTime: (state: StoreState) => state.player.seekTime,
135
- }),
136
- audioUrl(): string {
137
- if (this.media) return this.media.audioUrl? this.media.audioUrl:"";
138
- if (!this.podcast) return '';
139
- if (!this.podcast.availability.visibility)
140
- return this.podcast.audioStorageUrl;
141
- if (this.listenError) return this.podcast.audioStorageUrl;
142
- const parameters = [];
143
- parameters.push('origin=octopus');
144
- parameters.push('cookieName=player_' + this.podcast.podcastId);
145
- parameters.push('listenerId='+this.getListenerId());
146
- if (
147
- this.$store.state.authentication &&
148
- this.$store.state.authentication.organisationId
149
- ) {
150
- parameters.push(
151
- 'distributorId=' + this.$store.state.authentication.organisationId
152
- );
153
- }
154
- return this.podcast.audioUrl + '?' + parameters.join('&');
155
- },
156
- organisationId(): string|undefined {
157
- return state.generalParameters.organisationId;
158
- },
159
- },
160
-
161
- watch: {
162
- playerSeekTime(){
163
- if(!this.playerSeekTime){return;}
164
- if (this.$store.state.player.podcast || this.$store.state.player.live) {
165
- this.notListenTime = this.playerSeekTime - this.listenTime;
166
- }
167
- const audioPlayer: HTMLAudioElement | null = document.querySelector('#audio-player');
168
- if (!audioPlayer) return;
169
- audioPlayer.currentTime = this.playerSeekTime;
170
- },
171
- live: {
172
- deep: true,
173
- async handler(){
174
- this.hlsReady = false;
175
- this.setDownloadId(null);
176
- this.listenError = false;
177
- await this.playLive();
178
- this.initComments();
179
- }
180
- },
181
- playerHeight(): void {
182
- this.$emit('hide', 0 === this.playerHeight ? true : false);
183
- },
184
- podcast: {
185
- deep: true,
186
- handler(){
187
- this.setDownloadId(null);
188
- this.listenError = false;
189
- this.initComments();
190
- }
191
- },
192
- async listenTime(newVal): Promise<void> {
193
- if ((!this.podcast && !this.live)||(!this.getDownloadId())||(newVal - this.lastSend < 10)) {
194
- return;
195
- }
196
- this.lastSend = newVal;
197
- await octopusApi.updatePlayerTime(
198
- this.getDownloadId(),
199
- Math.round(newVal)
200
- );
201
- },
202
- commentsLoaded(): void {
203
- this.initComments(true);
204
- },
205
- audioUrl(): void{
206
- this.playerError = false;
207
- }
208
- },
209
-
210
- mounted() {
211
- window.addEventListener('beforeunload', this.endListeningProgress);
212
- this.watchPlayerStatus();
213
- },
214
-
215
- methods: {
216
- stopPlayer(): void {
217
- this.$store.commit('playerPlayPodcast');
218
- },
219
- getListenerId(): string{
220
- let listenerId = this.getCookie("octopus_listenerId");
221
- if(!listenerId){
222
- listenerId = new Date().valueOf().toString() + Math.random();
223
- let domain = "";
224
- const domainArray: RegExpExecArray | null = /\.(.+)/.exec(window.location.host);
225
- if(domainArray && null !== domainArray){
226
- domain = domainArray[1];
227
- }
228
- this.setCookie("octopus_listenerId", listenerId, ';domain='+domain);
229
- }
230
- return listenerId;
231
- },
232
- watchPlayerStatus(): void {
233
- this.$store.watch(
234
- (state: StoreState) => state.player.status,
235
- (newValue: string) => {
236
- const audioPlayer: HTMLAudioElement | null = document.querySelector('#audio-player');
237
- if (!audioPlayer) return;
238
- if (this.live && !this.hlsReady) {
239
- audioPlayer.pause();
240
- this.percentLiveProgress = 0;
241
- this.durationLivePosition = 0;
242
- return;
243
- }
244
- if ('PAUSED' === newValue) {
245
- audioPlayer.pause();
246
- }else if ('PLAYING' === newValue){
247
- audioPlayer.play();
248
- }
249
- }
250
- );
251
- },
252
- getDownloadId(): string|null {
253
- return this.downloadId;
254
- },
255
- setDownloadId(newValue: string|null): void {
256
- this.endListeningProgress();
257
- this.downloadId = newValue;
258
- },
259
- onError(): void {
260
- if (this.podcast && !this.listenError) {
261
- this.listenError = true;
262
- } else if (this.podcast || this.media) {
263
- this.playerError = true;
264
- }
265
- },
266
- onTimeUpdate(event: Event): void {
267
- const mediaTarget = (event.currentTarget as HTMLMediaElement);
268
- if (this.podcast || this.live) {
269
- if (!this.getDownloadId()) {
270
- this.loadDownloadId();
271
- }
272
- if (
273
- this.live &&
274
- 0 === this.listenTime &&
275
- 0 !== mediaTarget.currentTime
276
- ) {
277
- this.notListenTime = mediaTarget.currentTime;
278
- this.listenTime = 1;
279
- } else {
280
- this.listenTime =
281
- mediaTarget.currentTime - this.notListenTime;
282
- }
283
- }
284
- const streamDuration = mediaTarget.duration;
285
- if (!streamDuration) return;
286
- const playerCurrentTime = mediaTarget.currentTime;
287
- if (!playerCurrentTime) return;
288
- if (!this.live) {
289
- this.displayAlertBar = false;
290
- this.percentLiveProgress = 100;
291
- this.$store.commit('playerTotalTime', streamDuration);
292
- this.$store.commit('playerElapsed', playerCurrentTime / streamDuration);
293
- return;
294
- }
295
- const scheduledDuration = this.live.duration / 1000;
296
- if (scheduledDuration > streamDuration) {
297
- this.displayAlertBar = false;
298
- this.percentLiveProgress = (streamDuration / scheduledDuration) * 100;
299
- this.$store.commit('playerTotalTime', scheduledDuration);
300
- this.$store.commit(
301
- 'playerElapsed',
302
- playerCurrentTime / scheduledDuration
303
- );
304
- } else {
305
- this.percentLiveProgress = 100;
306
- this.displayAlertBar = true;
307
- this.durationLivePosition = (scheduledDuration / streamDuration) * 100;
308
- this.$store.commit('playerTotalTime', streamDuration);
309
- this.$store.commit('playerElapsed', playerCurrentTime / streamDuration);
310
- }
311
- },
312
- onPlay(): void {
313
- this.$store.commit('playerPause', false);
314
- },
315
- onFinished(): void {
316
- this.setDownloadId(null);
317
- if (this.live) {
318
- const audio: HTMLElement|null = document.getElementById('audio-player');
319
- if(audio){
320
- (audio as HTMLAudioElement).src = '';
321
- }
322
- }
323
- this.forceHide = true;
324
- },
325
- onHidden(): void {
326
- if (this.forceHide) {
327
- this.$store.commit('playerPlayPodcast');
328
- this.forceHide = false;
329
- }
330
- },
331
- loadDownloadId(): void {
332
- if (!this.podcast) return;
333
- const matching_cookies = document.cookie
334
- .split(';')
335
- .map(item => {
336
- const _return = item.trim().split('=');
337
- return _return.map(item => item.trim());
338
- })
339
- .filter(item => {
340
- if(!this.podcast){return '';}
341
- return 'player_' + this.podcast.podcastId === item[0];
342
- });
343
- if (1 === matching_cookies.length) {
344
- this.setDownloadId(matching_cookies[0][1]);
345
- }
346
- },
347
- async endListeningProgress(): Promise<void> {
348
- if (!this.getDownloadId()) return;
349
- await octopusApi.updatePlayerTime(
350
- this.getDownloadId(),
351
- Math.round(this.listenTime)
352
- );
353
- this.setDownloadId(null);
354
- this.notListenTime = 0;
355
- this.lastSend = 0;
356
- this.listenTime = 0;
357
- },
358
- async initHls(hlsStreamUrl: string): Promise<void> {
359
- return new Promise<void>(async(resolve, reject) => {
360
- if(null === Hls){
361
- //TODO -> Version light min quand ce sera possible
362
- await import('hls.js/dist/hls.js').then((hlsLibrary) => {
363
- Hls = hlsLibrary.default;
364
- })
365
- await import('hls.js').then((hlsLibrary) => {
366
- Hls = hlsLibrary.default;
367
- })
368
- }
369
- if (!Hls.isSupported()) {
370
- reject('Hls is not supported ! ');
371
- }
372
- const hls = new Hls();
373
- hls.on(Hls.Events.MANIFEST_PARSED, async () => {
374
- if(!this.live){ return; }
375
- let downloadId = null;
376
- try {
377
- downloadId = await octopusApi.requestLiveDownloadId(
378
- this.live.livePodcastId
379
- );
380
- await octopusApi.markPlayingLive(
381
- this.live.livePodcastId,
382
- downloadId,
383
- 'octopus',
384
- this.$store.state.authentication.organisationId
385
- );
386
- this.setDownloadId(downloadId);
387
- } catch (error) {
388
- console.log('ERROR downloadId');
389
- }
390
- this.hlsReady = true;
391
- const audio: HTMLElement|null = document.getElementById('audio-player');
392
- hls.attachMedia((audio as HTMLAudioElement));
393
- await (audio as HTMLAudioElement).play();
394
- this.onPlay();
395
- resolve();
396
- });
397
- hls.on(Hls.Events.ERROR, async() => {
398
- reject('There is an error while reading media content');
399
- });
400
- hls.loadSource(hlsStreamUrl);
401
- });
402
- },
403
- async playLive(): Promise<void> {
404
- if (!this.live) return;
405
- const hlsStreamUrl =
406
- state.podcastPage.hlsUri +
407
- 'stream/dev.' +
408
- this.live.conferenceId +
409
- '/index.m3u8';
410
- try {
411
- await this.initHls(hlsStreamUrl);
412
- } catch (error) {
413
- console.log(error);
414
- setTimeout(() => {
415
- this.playLive();
416
- }, 1000);
417
- }
418
- },
419
- editRight(organisation: string): boolean {
420
- if (
421
- (state.generalParameters.isCommments &&
422
- this.organisationId === organisation) ||
423
- state.generalParameters.isAdmin
424
- )
425
- return true;
426
- return false;
427
- },
428
- async initComments(refresh = false): Promise<void> {
429
- let podcastId, organisation;
430
- if (this.podcast) {
431
- podcastId = this.podcast.podcastId;
432
- organisation = this.podcast.organisation.id;
433
- } else if (this.live) {
434
- podcastId = this.live.livePodcastId;
435
- organisation = this.live.organisation.id;
436
- }
437
- if (
438
- refresh &&
439
- podcastId &&
440
- this.$store.state.comments.actualPodcastId !== podcastId
441
- ) {
442
- return;
443
- }
444
- let first = 0;
445
- let count = 0;
446
- const size = 50;
447
- if (
448
- podcastId &&
449
- this.$store.state.comments.actualPodcastId === podcastId
450
- ) {
451
- this.comments = this.commentsLoaded;
452
- if (
453
- this.commentsLoaded &&
454
- this.commentsLoaded.length < this.$store.state.comments.totalCount
455
- ) {
456
- first = this.commentsLoaded.length;
457
- count = this.$store.state.comments.totalCount;
458
- }
459
- }
460
- if (
461
- (!podcastId ||
462
- this.$store.state.comments.actualPodcastId === podcastId) &&
463
- 0 === first
464
- )
465
- return;
466
- while (0 === first || this.comments.length < count) {
467
- const param: FetchParam = {
468
- first: first,
469
- size: size,
470
- podcastId: podcastId,
471
- };
472
- if (!this.editRight(organisation? organisation : '')) {
473
- param.status = ['Valid'];
474
- }
475
- const data = await octopusApi.fetchRootComments(param);
476
- first += size;
477
- count = data.totalElements;
478
- this.comments = this.comments.concat(data.content).filter((c: CommentPodcast) => {
479
- return null !== c;
480
- });
481
- }
482
- },
483
- },
484
- })
485
- </script>
486
-
487
- <style lang="scss">
488
- .octopus-app{
489
- .player-container {
490
- position: fixed;
491
- overflow: hidden;
492
- z-index: 12;
493
- width: 100%;
494
- bottom: 0;
495
- display: flex;
496
- flex-direction: column;
497
- transition: height 1s;
498
- background: #282828 !important;
499
- font-size: 1rem;
500
-
501
- .player-progress-border {
502
- height: 10px;
503
- width: 3px;
504
- background: black;
505
- }
506
-
507
- @media (max-width: 960px) {
508
- .d-flex {
509
- flex-wrap: nowrap !important;
510
- }
511
- }
512
- }
513
- }
514
- </style>
@@ -1,143 +0,0 @@
1
- <template>
2
- <router-link
3
- v-if="isImage && podcastImage"
4
- :to="podcastShareUrl"
5
- >
6
- <img
7
- v-lazy="podcastImage"
8
- :alt="$t('Podcast image')"
9
- class="player-image"
10
- >
11
- </router-link>
12
- <div
13
- v-if="!playerError"
14
- class="play-button-box text-light primary-bg"
15
- @click="switchPausePlay"
16
- >
17
- <div
18
- :title="$t('Play')"
19
- :class="{
20
- 'saooti-play2-bounty': isPaused,
21
- 'saooti-pause-bounty': isPlaying,
22
- 'spinner-border':!isPaused&&!isPlaying
23
- }"
24
- />
25
- </div>
26
- </template>
27
-
28
- <script lang="ts">
29
- import { state } from '../../store/paramStore';
30
- import { defineComponent } from 'vue';
31
- import { Media } from '@/store/class/general/media';
32
- import { Podcast } from '@/store/class/general/podcast';
33
- import { RouteLocationRaw } from 'vue-router';
34
- export default defineComponent({
35
- name: 'PlayerButtons',
36
-
37
- props: {
38
- playerError: { default: false, type: Boolean},
39
- },
40
-
41
- computed: {
42
- isPlaying(): boolean {
43
- return 'PLAYING' === this.$store.state.player.status;
44
- },
45
- isPaused(): boolean {
46
- return 'PAUSED' === this.$store.state.player.status;
47
- },
48
- isImage(): boolean {
49
- return (state.player.image as boolean);
50
- },
51
- podcastImage(): string{
52
- if (this.$store.state.player.podcast) return this.$store.state.player.podcast.imageUrl;
53
- return '';
54
- },
55
- media(): undefined|Media{
56
- return this.$store.state.player.media;
57
- },
58
- podcast(): undefined|Podcast{
59
- return this.$store.state.player.podcast;
60
- },
61
-
62
- podcastShareUrl(): RouteLocationRaw|string {
63
- if (this.podcast) {
64
- return {
65
- name: 'podcast',
66
- params: { podcastId: this.podcast.podcastId.toString() },
67
- query: { productor: this.$store.state.filter.organisationId },
68
- };
69
- }
70
- return '';
71
- },
72
- },
73
- created(){
74
- window.addEventListener('keydown', this.addKeyboardControl);
75
- },
76
- beforeUnmount() {
77
- window.removeEventListener('keydown', this.addKeyboardControl);
78
- },
79
-
80
- methods: {
81
- addKeyboardControl(event: KeyboardEvent): void{
82
- if(!event || null ===event){return;}
83
- const element = event.target as HTMLElement;
84
- if (!element || 'INPUT' == element.tagName.toUpperCase() || 'TEXTAREA' == element.tagName.toUpperCase()){return;}
85
- if (' ' === event.key || 'Spacebar' === event.key) {
86
- event.preventDefault();
87
- this.switchPausePlay();
88
- }else if ('ArrowRight' === event.key && event.ctrlKey) {
89
- const audioPlayer: HTMLAudioElement|null = document.querySelector('#audio-player');
90
- if(!audioPlayer){return;}
91
- audioPlayer.currentTime += 15;
92
- }else if ('ArrowLeft' === event.key && event.ctrlKey) {
93
- const audioPlayer: HTMLAudioElement|null = document.querySelector('#audio-player');
94
- if(!audioPlayer){return;}
95
- audioPlayer.currentTime -=15;
96
- }
97
- },
98
- switchPausePlay(): void {
99
- const audioPlayer: HTMLAudioElement|null = document.querySelector('#audio-player');
100
- if(!audioPlayer){return;}
101
- if (audioPlayer.paused) {
102
- this.onPlay();
103
- } else {
104
- this.onPause();
105
- }
106
- },
107
- onPlay(): void {
108
- this.$store.commit('playerPause', false);
109
- },
110
- onPause(): void {
111
- this.$store.commit('playerPause', true);
112
- },
113
- },
114
- })
115
- </script>
116
-
117
- <style lang="scss">
118
- .octopus-app{
119
- .player-image {
120
- border-radius: 0.2rem;
121
- height: 2.4rem;
122
- width: 2.4rem;
123
- cursor: pointer;
124
- /** PHONES*/
125
- @media (max-width: 450px) {
126
- height: 1.8rem;
127
- width: 1.8rem;
128
- }
129
- }
130
- .play-button-box {
131
- height: 2.5rem;
132
- width: 2.5rem;
133
- display: flex;
134
- align-items: center;
135
- justify-content: center;
136
- margin: 0 0.5rem;
137
- border-radius: 50%;
138
- font-size: 1.2rem;
139
- flex-shrink: 0;
140
- cursor: pointer;
141
- }
142
- }
143
- </style>