@saooti/octopus-sdk 38.0.16 → 38.0.18
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.
- package/package.json +1 -1
- package/src/assets/general.scss +1 -0
- package/src/assets/progressbar.scss +13 -0
- package/src/components/display/sharing/ShareButtonsIntern.vue +2 -2
- package/src/components/misc/ClassicPopover.vue +9 -3
- package/src/components/misc/ProgressBar.vue +54 -4
- package/src/components/misc/player/ChapteringModal.vue +75 -0
- package/src/components/misc/player/PlayerChaptering.vue +72 -0
- package/src/components/misc/player/PlayerCompact.vue +3 -0
- package/src/components/misc/player/PlayerComponent.vue +18 -1
- package/src/components/misc/player/PlayerLarge.vue +3 -0
- package/src/components/pages/RadioPage.vue +2 -1
- package/src/helper/duration.ts +6 -8
- package/src/locale/de.ts +1 -0
- package/src/locale/en.ts +1 -0
- package/src/locale/es.ts +1 -0
- package/src/locale/fr.ts +1 -0
- package/src/locale/it.ts +1 -0
- package/src/locale/sl.ts +1 -0
- package/src/stores/ParamSdkStore.ts +11 -11
- package/src/stores/PlayerStore.ts +39 -5
- package/src/stores/class/chaptering/chaptering.ts +12 -0
package/package.json
CHANGED
package/src/assets/general.scss
CHANGED
|
@@ -20,6 +20,19 @@
|
|
|
20
20
|
background-color: $octopus-primary-color;
|
|
21
21
|
transition: width 0.6s ease;
|
|
22
22
|
}
|
|
23
|
+
.octopus-chapter{
|
|
24
|
+
position: absolute;
|
|
25
|
+
background: transparent;
|
|
26
|
+
background-clip: content-box;
|
|
27
|
+
padding: 0 5px;
|
|
28
|
+
box-shadow: inset -2px 1px 0px 0px rgb(0 0 0),
|
|
29
|
+
inset 2px 1px 0px 0px rgb(0 0 0);
|
|
30
|
+
|
|
31
|
+
&:hover{
|
|
32
|
+
background: rgba(255, 255, 255, 0.4);
|
|
33
|
+
box-shadow: -4px 1px 0px 0px rgb(0 0 0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
23
36
|
&,.octopus-progress-bar{
|
|
24
37
|
height: 4px;
|
|
25
38
|
@media (max-width: 960px) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<div :class="button.icon" />
|
|
19
19
|
</a>
|
|
20
20
|
</template>
|
|
21
|
-
<router-link
|
|
21
|
+
<!-- <router-link
|
|
22
22
|
v-if="!isPodcastmaker && authenticated && podcast && isProduction"
|
|
23
23
|
:class="getClass('saooti-share')"
|
|
24
24
|
:title="$t('Advanced sharing')"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
name: 'advancedShare',
|
|
27
27
|
params: { podcastId: podcast.podcastId },
|
|
28
28
|
}"
|
|
29
|
-
/>
|
|
29
|
+
/> -->
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
32
32
|
<div v-if="podcast || emission || playlist" class="d-flex flex-column me-2">
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
ref="popover"
|
|
6
6
|
tabindex="0"
|
|
7
7
|
class="octopus-popover"
|
|
8
|
-
:class="onlyClick ? 'octopus-dropdown' : ''"
|
|
8
|
+
:class="[onlyClick ? 'octopus-dropdown' : '', popoverClass]"
|
|
9
9
|
:style="positionInlineStyle"
|
|
10
10
|
@blur="clearDataBlur"
|
|
11
11
|
>
|
|
@@ -28,10 +28,12 @@ export default defineComponent({
|
|
|
28
28
|
target: { type: String, required: true },
|
|
29
29
|
disable: { type: Boolean, default: false },
|
|
30
30
|
onlyClick: { type: Boolean, default: false },
|
|
31
|
+
onlyMouse: { type: Boolean, default: false },
|
|
31
32
|
isFixed: { type: Boolean, default: false },
|
|
32
33
|
relativeClass: { type: String, default: undefined },
|
|
33
34
|
leftPos: { type: Boolean, default: false },
|
|
34
35
|
topPos: { type: Boolean, default: false },
|
|
36
|
+
popoverClass: { type: String, default: undefined },
|
|
35
37
|
},
|
|
36
38
|
data() {
|
|
37
39
|
return {
|
|
@@ -67,7 +69,9 @@ export default defineComponent({
|
|
|
67
69
|
);
|
|
68
70
|
this.targetElement.addEventListener("mouseleave", this.clearData);
|
|
69
71
|
}
|
|
70
|
-
|
|
72
|
+
if (!this.onlyMouse) {
|
|
73
|
+
this.targetElement.addEventListener("click", this.setPopoverData);
|
|
74
|
+
}
|
|
71
75
|
this.targetElement.addEventListener("blur", this.clearDataBlur);
|
|
72
76
|
}
|
|
73
77
|
},
|
|
@@ -80,7 +84,9 @@ export default defineComponent({
|
|
|
80
84
|
);
|
|
81
85
|
this.targetElement.removeEventListener("mouseleave", this.clearData);
|
|
82
86
|
}
|
|
83
|
-
|
|
87
|
+
if (!this.onlyMouse) {
|
|
88
|
+
this.targetElement.removeEventListener("click", this.setPopoverData);
|
|
89
|
+
}
|
|
84
90
|
this.targetElement.addEventListener("blur", this.clearDataBlur);
|
|
85
91
|
}
|
|
86
92
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="octopus-progress">
|
|
2
|
+
<div v-if="display" id="test-menu-dropdown" class="octopus-progress">
|
|
3
3
|
<div
|
|
4
4
|
v-if="secondaryProgress"
|
|
5
5
|
class="octopus-progress-bar bg-light"
|
|
@@ -48,18 +48,50 @@
|
|
|
48
48
|
/>
|
|
49
49
|
<div
|
|
50
50
|
v-if="isProgressCursor"
|
|
51
|
-
class="progress-bar-cursor"
|
|
51
|
+
class="octopus-progress-bar-cursor"
|
|
52
52
|
:style="'left:' + mainProgress + '%'"
|
|
53
53
|
/>
|
|
54
|
+
<template v-if="playerChapteringPercent">
|
|
55
|
+
<template v-for="chapter in playerChapteringPercent" :key="chapter">
|
|
56
|
+
<div
|
|
57
|
+
:id="'chapter-' + chapter.startPercent"
|
|
58
|
+
class="octopus-progress-bar octopus-chapter"
|
|
59
|
+
role="progressbar"
|
|
60
|
+
aria-valuenow="0"
|
|
61
|
+
aria-valuemin="0"
|
|
62
|
+
aria-valuemax="100"
|
|
63
|
+
:style="{
|
|
64
|
+
left: chapter.startPercent + '%',
|
|
65
|
+
right: 100 - chapter.endPercent + '%',
|
|
66
|
+
}"
|
|
67
|
+
/>
|
|
68
|
+
<Teleport to="#octopus-player-component">
|
|
69
|
+
<ClassicPopover
|
|
70
|
+
:target="'chapter-' + chapter.startPercent"
|
|
71
|
+
:is-fixed="true"
|
|
72
|
+
relative-class="player-container"
|
|
73
|
+
:only-mouse="true"
|
|
74
|
+
popover-class="octopus-small-popover"
|
|
75
|
+
:content="chapter.title"
|
|
76
|
+
/>
|
|
77
|
+
</Teleport>
|
|
78
|
+
</template>
|
|
79
|
+
</template>
|
|
54
80
|
</div>
|
|
55
81
|
</template>
|
|
56
82
|
|
|
57
83
|
<script lang="ts">
|
|
58
84
|
import { usePlayerStore } from "@/stores/PlayerStore";
|
|
59
85
|
import { mapState } from "pinia";
|
|
60
|
-
import { defineComponent } from "vue";
|
|
86
|
+
import { defineAsyncComponent, defineComponent } from "vue";
|
|
87
|
+
const ClassicPopover = defineAsyncComponent(
|
|
88
|
+
() => import("../misc/ClassicPopover.vue"),
|
|
89
|
+
);
|
|
61
90
|
export default defineComponent({
|
|
62
91
|
name: "ProgressBar",
|
|
92
|
+
components: {
|
|
93
|
+
ClassicPopover,
|
|
94
|
+
},
|
|
63
95
|
props: {
|
|
64
96
|
alertBar: { default: undefined, type: Number },
|
|
65
97
|
mainProgress: { default: 0, type: Number },
|
|
@@ -73,7 +105,14 @@ export default defineComponent({
|
|
|
73
105
|
};
|
|
74
106
|
},
|
|
75
107
|
computed: {
|
|
76
|
-
...mapState(usePlayerStore, [
|
|
108
|
+
...mapState(usePlayerStore, [
|
|
109
|
+
"playerMedia",
|
|
110
|
+
"playerChapteringPercent",
|
|
111
|
+
"playerStatus",
|
|
112
|
+
]),
|
|
113
|
+
display() {
|
|
114
|
+
return "STOPPED" !== this.playerStatus;
|
|
115
|
+
},
|
|
77
116
|
},
|
|
78
117
|
watch: {
|
|
79
118
|
playerMedia: {
|
|
@@ -104,4 +143,15 @@ export default defineComponent({
|
|
|
104
143
|
|
|
105
144
|
<style lang="scss">
|
|
106
145
|
@import "../../assets/progressbar.scss";
|
|
146
|
+
.octopus-app .player-container {
|
|
147
|
+
.octopus-small-popover {
|
|
148
|
+
font-size: 0.7rem;
|
|
149
|
+
background: #282828;
|
|
150
|
+
color: white;
|
|
151
|
+
border: 0;
|
|
152
|
+
.p-2 {
|
|
153
|
+
padding: 0.2rem !important;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
107
157
|
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ClassicModal
|
|
3
|
+
id-modal="chaptering-modal"
|
|
4
|
+
:title-modal="$t('Chaptering')"
|
|
5
|
+
@close="closePopup"
|
|
6
|
+
>
|
|
7
|
+
<template #body>
|
|
8
|
+
<div class="d-flex flex-column">
|
|
9
|
+
<a
|
|
10
|
+
v-for="(chapter, index) in playerChapteringPercent"
|
|
11
|
+
:key="chapter"
|
|
12
|
+
class="c-hand text-truncate mb-1"
|
|
13
|
+
@click="goToChapter(index)"
|
|
14
|
+
>
|
|
15
|
+
{{ chapter.startTime + " - " + chapter.title }}
|
|
16
|
+
</a>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
<template #footer>
|
|
20
|
+
<button class="btn m-1" @click="closePopup">
|
|
21
|
+
{{ $t("Close") }}
|
|
22
|
+
</button>
|
|
23
|
+
</template>
|
|
24
|
+
</ClassicModal>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
import { usePlayerStore } from "@/stores/PlayerStore";
|
|
29
|
+
import { mapState, mapActions } from "pinia";
|
|
30
|
+
import ClassicModal from "../modal/ClassicModal.vue";
|
|
31
|
+
import { defineComponent } from "vue";
|
|
32
|
+
export default defineComponent({
|
|
33
|
+
name: "ChapteringModal",
|
|
34
|
+
components: {
|
|
35
|
+
ClassicModal,
|
|
36
|
+
},
|
|
37
|
+
props: {},
|
|
38
|
+
emits: ["close"],
|
|
39
|
+
data() {
|
|
40
|
+
return {
|
|
41
|
+
audioPlayer: null as HTMLAudioElement | null,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
computed: {
|
|
45
|
+
...mapState(usePlayerStore, [
|
|
46
|
+
"playerPodcast",
|
|
47
|
+
"playerLive",
|
|
48
|
+
"playerChapteringPercent",
|
|
49
|
+
"playerTotal",
|
|
50
|
+
"playerElapsed",
|
|
51
|
+
]),
|
|
52
|
+
},
|
|
53
|
+
created() {
|
|
54
|
+
this.audioPlayer = document.querySelector("#audio-player");
|
|
55
|
+
},
|
|
56
|
+
methods: {
|
|
57
|
+
...mapActions(usePlayerStore, ["playerUpdateSeekTime"]),
|
|
58
|
+
closePopup(): void {
|
|
59
|
+
this.$emit("close");
|
|
60
|
+
},
|
|
61
|
+
goToChapter(index: number) {
|
|
62
|
+
if (!this.playerChapteringPercent || !this.audioPlayer) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const seekTime =
|
|
66
|
+
this.playerTotal *
|
|
67
|
+
(this.playerChapteringPercent[index].startPercent / 100);
|
|
68
|
+
if (this.playerPodcast || this.playerLive) {
|
|
69
|
+
this.playerUpdateSeekTime(seekTime);
|
|
70
|
+
}
|
|
71
|
+
this.audioPlayer.currentTime = seekTime;
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="actualChapter" class="d-flex mb-1">
|
|
3
|
+
<button
|
|
4
|
+
class="btn-transparent d-flex align-items-center text-truncate medium-text text-light"
|
|
5
|
+
@click="showChaptering = !showChaptering"
|
|
6
|
+
>
|
|
7
|
+
<div class="text-truncate">{{ "• " + actualChapter.title }}</div>
|
|
8
|
+
<span class="saooti-right small-text" />
|
|
9
|
+
</button>
|
|
10
|
+
<ChapteringModal v-if="showChaptering" @close="showChaptering = false" />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { ChapterPercent } from "@/stores/class/chaptering/chaptering";
|
|
15
|
+
import { usePlayerStore } from "@/stores/PlayerStore";
|
|
16
|
+
import { mapState } from "pinia";
|
|
17
|
+
import { defineAsyncComponent, defineComponent } from "vue";
|
|
18
|
+
const ChapteringModal = defineAsyncComponent(
|
|
19
|
+
() => import("./ChapteringModal.vue"),
|
|
20
|
+
);
|
|
21
|
+
export default defineComponent({
|
|
22
|
+
name: "PlayerChaptering",
|
|
23
|
+
|
|
24
|
+
components: {
|
|
25
|
+
ChapteringModal,
|
|
26
|
+
},
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
actualChapter: undefined as ChapterPercent | undefined,
|
|
30
|
+
showChaptering: false as boolean,
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
computed: {
|
|
34
|
+
...mapState(usePlayerStore, ["playerChapteringPercent", "playerElapsed"]),
|
|
35
|
+
},
|
|
36
|
+
watch: {
|
|
37
|
+
playerElapsed: {
|
|
38
|
+
immediate: true,
|
|
39
|
+
handler() {
|
|
40
|
+
if (!this.playerChapteringPercent) {
|
|
41
|
+
this.actualChapter = undefined;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const progressPercent = (this.playerElapsed ?? 0) * 100;
|
|
45
|
+
if (
|
|
46
|
+
this.actualChapter &&
|
|
47
|
+
this.isInChapter(progressPercent, this.actualChapter)
|
|
48
|
+
) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (
|
|
52
|
+
let i = 0, len = this.playerChapteringPercent.length;
|
|
53
|
+
i < len;
|
|
54
|
+
i++
|
|
55
|
+
) {
|
|
56
|
+
if (
|
|
57
|
+
this.isInChapter(progressPercent, this.playerChapteringPercent[i])
|
|
58
|
+
) {
|
|
59
|
+
this.actualChapter = this.playerChapteringPercent[i];
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
methods: {
|
|
67
|
+
isInChapter(val: number, chapter: ChapterPercent) {
|
|
68
|
+
return chapter.startPercent <= val && val < chapter.endPercent;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
</script>
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
{{ playedTime }} / {{ totalTime }}
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
|
+
<PlayerChaptering />
|
|
37
38
|
<PlayerProgressBar
|
|
38
39
|
v-if="!radioUrl"
|
|
39
40
|
:show-timeline="showTimeline"
|
|
@@ -69,6 +70,7 @@ import { playerDisplay } from "../../mixins/player/playerDisplay";
|
|
|
69
70
|
import imageProxy from "../../mixins/imageProxy";
|
|
70
71
|
import ClassicSpinner from "../ClassicSpinner.vue";
|
|
71
72
|
import PlayerTimeline from "./PlayerTimeline.vue";
|
|
73
|
+
import PlayerChaptering from "./PlayerChaptering.vue";
|
|
72
74
|
import { defineAsyncComponent, defineComponent } from "vue";
|
|
73
75
|
const RadioProgressBar = defineAsyncComponent(
|
|
74
76
|
() => import("./radio/RadioProgressBar.vue"),
|
|
@@ -84,6 +86,7 @@ export default defineComponent({
|
|
|
84
86
|
RadioProgressBar,
|
|
85
87
|
PlayerTimeline,
|
|
86
88
|
ClassicSpinner,
|
|
89
|
+
PlayerChaptering,
|
|
87
90
|
},
|
|
88
91
|
mixins: [playerDisplay, imageProxy],
|
|
89
92
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
+
id="octopus-player-component"
|
|
3
4
|
class="player-container"
|
|
4
5
|
:class="playerVideo ? 'player-video' : ''"
|
|
5
6
|
:style="{ height: playerHeight }"
|
|
6
7
|
@transitionend="onHidden"
|
|
7
8
|
>
|
|
8
|
-
<template v-if="
|
|
9
|
+
<template v-if="displayWithTimeout">
|
|
9
10
|
<PlayerVideo v-if="playerVideo" />
|
|
10
11
|
<template v-else>
|
|
11
12
|
<audio
|
|
@@ -86,6 +87,7 @@ export default defineComponent({
|
|
|
86
87
|
comments: [] as Array<CommentPodcast>,
|
|
87
88
|
audioUrlToPlay: "" as string,
|
|
88
89
|
hlsReady: false as boolean,
|
|
90
|
+
displayWithTimeout: false as boolean,
|
|
89
91
|
};
|
|
90
92
|
},
|
|
91
93
|
computed: {
|
|
@@ -104,6 +106,15 @@ export default defineComponent({
|
|
|
104
106
|
playerHeight(): void {
|
|
105
107
|
this.$emit("hide", 0 === this.playerHeight);
|
|
106
108
|
},
|
|
109
|
+
display(): void {
|
|
110
|
+
if (this.display) {
|
|
111
|
+
this.displayWithTimeout = this.display;
|
|
112
|
+
} else {
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
this.displayWithTimeout = this.display;
|
|
115
|
+
}, 3000);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
107
118
|
},
|
|
108
119
|
|
|
109
120
|
methods: {
|
|
@@ -141,6 +152,12 @@ export default defineComponent({
|
|
|
141
152
|
transition: height 1s;
|
|
142
153
|
background: #282828 !important;
|
|
143
154
|
font-size: 1rem;
|
|
155
|
+
.medium-text {
|
|
156
|
+
font-size: 0.75rem;
|
|
157
|
+
}
|
|
158
|
+
.small-text {
|
|
159
|
+
font-size: 0.6rem;
|
|
160
|
+
}
|
|
144
161
|
|
|
145
162
|
@media (max-width: 960px) {
|
|
146
163
|
.d-flex {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
{{ podcastTitle }}
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
|
+
<PlayerChaptering class="w-100" />
|
|
27
28
|
<div class="player-grow-large-content">
|
|
28
29
|
<PlayerProgressBar
|
|
29
30
|
v-if="!radioUrl"
|
|
@@ -86,6 +87,7 @@ import ClassicSpinner from "../ClassicSpinner.vue";
|
|
|
86
87
|
import { playerDisplay } from "../../mixins/player/playerDisplay";
|
|
87
88
|
import imageProxy from "../../mixins/imageProxy";
|
|
88
89
|
import PlayerTimeline from "./PlayerTimeline.vue";
|
|
90
|
+
import PlayerChaptering from "./PlayerChaptering.vue";
|
|
89
91
|
import { defineAsyncComponent, defineComponent } from "vue";
|
|
90
92
|
import { CommentPodcast } from "@/stores/class/general/comment";
|
|
91
93
|
const RadioProgressBar = defineAsyncComponent(
|
|
@@ -106,6 +108,7 @@ export default defineComponent({
|
|
|
106
108
|
PlayerTimeline,
|
|
107
109
|
ClassicSpinner,
|
|
108
110
|
RadioHistory,
|
|
111
|
+
PlayerChaptering,
|
|
109
112
|
},
|
|
110
113
|
mixins: [playerDisplay, imageProxy],
|
|
111
114
|
|
|
@@ -91,7 +91,8 @@ export default defineComponent({
|
|
|
91
91
|
computed: {
|
|
92
92
|
editRight(): boolean {
|
|
93
93
|
return (
|
|
94
|
-
(true === this.authenticated &&
|
|
94
|
+
(true === this.authenticated &&
|
|
95
|
+
true === state.generalParameters.isRadio &&
|
|
95
96
|
this.myOrganisationId === this.radio?.organisationId) ||
|
|
96
97
|
true === state.generalParameters.isAdmin
|
|
97
98
|
);
|
package/src/helper/duration.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
export default {
|
|
2
|
+
convertTimestamptoSeconds(timestamp: string){
|
|
3
|
+
const [hours, minutes, seconds] = timestamp.split(':');
|
|
4
|
+
return Number(hours) * 60 * 60 + Number(minutes) * 60 + Number(seconds);
|
|
5
|
+
},
|
|
2
6
|
formatToString(value: number) {
|
|
3
7
|
if (value < 10) {
|
|
4
8
|
return "0" + value;
|
|
5
9
|
}
|
|
6
10
|
return value.toString();
|
|
7
11
|
},
|
|
8
|
-
formatDuration(totalSeconds: number): string {
|
|
12
|
+
formatDuration(totalSeconds: number, separator="'", isLast=true): string {
|
|
9
13
|
const hours = Math.floor(totalSeconds / 3600);
|
|
10
14
|
const minutes = Math.floor((totalSeconds - hours * 3600) / 60);
|
|
11
15
|
const seconds = totalSeconds - hours * 3600 - minutes * 60;
|
|
12
|
-
return (
|
|
13
|
-
(hours > 0 ? this.formatToString(hours) + "'" : "") +
|
|
14
|
-
this.formatToString(minutes) +
|
|
15
|
-
"'" +
|
|
16
|
-
this.formatToString(seconds) +
|
|
17
|
-
"''"
|
|
18
|
-
);
|
|
16
|
+
return (hours > 0? this.formatToString(hours)+separator:"") + this.formatToString(minutes) +separator+ this.formatToString(seconds) + (isLast?separator:separator );
|
|
19
17
|
},
|
|
20
18
|
};
|
package/src/locale/de.ts
CHANGED
package/src/locale/en.ts
CHANGED
package/src/locale/es.ts
CHANGED
package/src/locale/fr.ts
CHANGED
package/src/locale/it.ts
CHANGED
package/src/locale/sl.ts
CHANGED
|
@@ -13,7 +13,7 @@ const state: ParamStore = {
|
|
|
13
13
|
isProduction: false,
|
|
14
14
|
isContribution: false,
|
|
15
15
|
isRadio: false,
|
|
16
|
-
ApiUri: "https://api.
|
|
16
|
+
ApiUri: "https://api.dev2.saooti.org/",
|
|
17
17
|
podcastmaker: false,
|
|
18
18
|
buttonPlus: true,
|
|
19
19
|
allCategories: [],
|
|
@@ -27,8 +27,8 @@ const state: ParamStore = {
|
|
|
27
27
|
SharePlayer: true,
|
|
28
28
|
ShareButtons: true,
|
|
29
29
|
ShareDistribution: true,
|
|
30
|
-
MiniplayerUri: "https://playerbeta.
|
|
31
|
-
hlsUri: "https://hls.live.
|
|
30
|
+
MiniplayerUri: "https://playerbeta.dev2.saooti.org/",
|
|
31
|
+
hlsUri: "https://hls.live.dev2.saooti.org/",
|
|
32
32
|
mainRubrique: 0,
|
|
33
33
|
resourceUrl: undefined,
|
|
34
34
|
podcastItemShowEmission: false,
|
|
@@ -80,14 +80,14 @@ const state: ParamStore = {
|
|
|
80
80
|
userName: "",
|
|
81
81
|
},
|
|
82
82
|
octopusApi: {
|
|
83
|
-
url: "https://api.
|
|
84
|
-
commentsUrl: "https://comments.
|
|
85
|
-
imageUrl: "https://imageproxy.
|
|
86
|
-
studioUrl: "https://studio.
|
|
87
|
-
playerUrl: "https://playerbeta.
|
|
88
|
-
speechToTextUrl: "https://speech2text.
|
|
89
|
-
radioUrl:"https://radio.
|
|
90
|
-
recoUrl: "https://reco.
|
|
83
|
+
url: "https://api.dev2.saooti.org/",
|
|
84
|
+
commentsUrl: "https://comments.dev2.saooti.org/",
|
|
85
|
+
imageUrl: "https://imageproxy.dev2.saooti.org/",
|
|
86
|
+
studioUrl: "https://studio.dev2.saooti.org/",
|
|
87
|
+
playerUrl: "https://playerbeta.dev2.saooti.org/",
|
|
88
|
+
speechToTextUrl: "https://speech2text.dev2.saooti.org/",
|
|
89
|
+
radioUrl:"https://radio.dev2.saooti.org/",
|
|
90
|
+
recoUrl: "https://reco.dev2.saooti.org/",
|
|
91
91
|
organisationId: undefined,
|
|
92
92
|
rubriqueIdFilter: undefined,
|
|
93
93
|
},
|
|
@@ -3,6 +3,8 @@ import { Media } from "@/stores/class/general/media";
|
|
|
3
3
|
import { MediaRadio, Radio } from "@/stores/class/general/player";
|
|
4
4
|
import { Podcast } from "@/stores/class/general/podcast";
|
|
5
5
|
import { defineStore } from "pinia";
|
|
6
|
+
import { Chaptering, ChapteringPercent } from "./class/chaptering/chaptering";
|
|
7
|
+
import octopusApi from "@saooti/octopus-api";
|
|
6
8
|
interface Transcript {
|
|
7
9
|
actual: number;
|
|
8
10
|
actualText: string;
|
|
@@ -22,6 +24,7 @@ interface PlayerState {
|
|
|
22
24
|
playerTranscript?: Transcript;
|
|
23
25
|
playerLargeVersion: boolean;
|
|
24
26
|
playerVideo: boolean;
|
|
27
|
+
playerChaptering?: Chaptering;
|
|
25
28
|
}
|
|
26
29
|
export const usePlayerStore = defineStore("PlayerStore", {
|
|
27
30
|
state: (): PlayerState => ({
|
|
@@ -36,13 +39,33 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
36
39
|
playerSeekTime: 0,
|
|
37
40
|
playerLargeVersion: false,
|
|
38
41
|
playerVideo: false,
|
|
42
|
+
playerChaptering: undefined,
|
|
39
43
|
}),
|
|
40
44
|
getters: {
|
|
45
|
+
playerChapteringPercent(): ChapteringPercent|undefined{
|
|
46
|
+
if(!this.playerChaptering || 0===this.playerTotal){
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const chapteringKeys = Object.keys(this.playerChaptering);
|
|
50
|
+
let chapteringPercent: ChapteringPercent = [];
|
|
51
|
+
for (let i = 0, len = chapteringKeys.length; i < len; i++) {
|
|
52
|
+
chapteringPercent.push({
|
|
53
|
+
startTime : chapteringKeys[i],
|
|
54
|
+
startPercent: (DurationHelper.convertTimestamptoSeconds(chapteringKeys[i]) * 100 ) / (Math.round(this.playerTotal)),
|
|
55
|
+
endPercent:100,
|
|
56
|
+
title: this.playerChaptering[chapteringKeys[i]]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (let i = 0, len = chapteringPercent.length; i < len; i++) {
|
|
60
|
+
chapteringPercent[i].endPercent = chapteringPercent[i].startPercent + ((chapteringPercent[i+1]?.startPercent ?? 100) - chapteringPercent[i].startPercent);
|
|
61
|
+
}
|
|
62
|
+
return chapteringPercent;
|
|
63
|
+
},
|
|
41
64
|
playerHeight() {
|
|
42
65
|
if ("STOPPED" === this.playerStatus) return 0;
|
|
43
66
|
if (this.playerVideo) return "0px"/* "281px" */;
|
|
44
67
|
if (this.playerLargeVersion) return "27rem";
|
|
45
|
-
if (window.innerWidth > 450) return "
|
|
68
|
+
if (window.innerWidth > 450) return "6rem";
|
|
46
69
|
return "3.5rem";
|
|
47
70
|
},
|
|
48
71
|
playedTime(): string {
|
|
@@ -91,7 +114,7 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
91
114
|
},
|
|
92
115
|
},
|
|
93
116
|
actions: {
|
|
94
|
-
playerPlay(param?: any, isVideo = false) {
|
|
117
|
+
async playerPlay(param?: any, isVideo = false) {
|
|
95
118
|
if (!param) {
|
|
96
119
|
this.playerStatus = "STOPPED";
|
|
97
120
|
this.playerPodcast = undefined;
|
|
@@ -100,6 +123,7 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
100
123
|
this.playerRadio = undefined;
|
|
101
124
|
this.playerElapsed = 0;
|
|
102
125
|
this.playerVideo = false;
|
|
126
|
+
this.playerChaptering=undefined;
|
|
103
127
|
return;
|
|
104
128
|
}
|
|
105
129
|
if (
|
|
@@ -118,16 +142,26 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
118
142
|
this.playerRadio = undefined;
|
|
119
143
|
this.playerVideo = isVideo;
|
|
120
144
|
this.playerElapsed = 0;
|
|
145
|
+
this.playerChaptering=undefined;
|
|
121
146
|
if (
|
|
122
147
|
param.conferenceId &&
|
|
123
148
|
(!param.podcastId || param.processingStatus !== "READY")
|
|
124
149
|
) {
|
|
125
150
|
this.playerLive = param;
|
|
126
|
-
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (param.podcastId) {
|
|
127
154
|
this.playerPodcast = param;
|
|
128
|
-
|
|
155
|
+
if(param.annotations?.chaptering){
|
|
156
|
+
this.playerChaptering = await octopusApi.fetchDataPublic<Chaptering>(4, (param.annotations.chaptering as string));
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (param.mediaId) {
|
|
129
161
|
this.playerMedia = param;
|
|
130
|
-
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (param.canalId) {
|
|
131
165
|
this.playerRadio = { ...param, ...{ isInit: false } };
|
|
132
166
|
}
|
|
133
167
|
},
|