@saooti/octopus-sdk 40.0.3 → 40.1.0

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.
Files changed (174) hide show
  1. package/index.ts +31 -31
  2. package/package.json +1 -1
  3. package/plateform.conf +1 -1
  4. package/src/App.vue +9 -3
  5. package/src/api/classicApi.ts +1 -1
  6. package/src/components/composable/player/usePlayerDisplayTime.ts +33 -0
  7. package/src/components/composable/player/usePlayerLive.ts +150 -0
  8. package/src/components/composable/player/usePlayerLogic.ts +265 -0
  9. package/src/components/composable/player/usePlayerLogicProgress.ts +135 -0
  10. package/src/components/composable/player/usePlayerStitching.ts +213 -0
  11. package/src/components/composable/player/usePlayerTranscript.ts +120 -0
  12. package/src/components/composable/player/usePlayerVast.ts +207 -0
  13. package/src/components/composable/podcasts/useCountdown.ts +53 -0
  14. package/src/components/composable/podcasts/usePodcastView.ts +85 -0
  15. package/src/components/composable/radio/usefetchRadioData.ts +75 -0
  16. package/src/components/composable/route/useAdvancedParamInit.ts +165 -0
  17. package/src/components/composable/route/useRouteUpdateParams.ts +48 -0
  18. package/src/components/composable/route/useRubriquesFilterComputed.ts +24 -0
  19. package/src/components/composable/route/useRubriquesFilterParam.ts +32 -0
  20. package/src/components/composable/route/useSeoTitleUrl.ts +30 -0
  21. package/src/components/composable/route/useSimplePageParam.ts +50 -0
  22. package/src/components/composable/useErrorHandler.ts +25 -0
  23. package/src/components/composable/useImageProxy.ts +28 -0
  24. package/src/components/composable/useInit.ts +38 -0
  25. package/src/components/composable/useMetaTitle.ts +19 -0
  26. package/src/components/composable/useMetaTitleWatch.ts +14 -0
  27. package/src/components/composable/useOrgaComputed.ts +23 -0
  28. package/src/components/composable/useOrganisationFilter.ts +65 -0
  29. package/src/components/composable/useResizePhone.ts +27 -0
  30. package/src/components/composable/useSelenium.ts +10 -0
  31. package/src/components/composable/useTagOf.ts +21 -0
  32. package/src/components/display/accessibility/AccessibilityModal.vue +136 -0
  33. package/src/components/display/aggregator/RssSection.vue +1 -4
  34. package/src/components/display/categories/CategoryChooser.vue +1 -1
  35. package/src/components/display/categories/CategoryFilter.vue +7 -3
  36. package/src/components/display/categories/CategoryList.vue +5 -2
  37. package/src/components/display/comments/CommentInput.vue +0 -3
  38. package/src/components/display/comments/CommentList.vue +6 -2
  39. package/src/components/display/comments/CommentPlayer.vue +5 -2
  40. package/src/components/display/comments/CommentSection.vue +0 -2
  41. package/src/components/display/comments/item/CommentBasicView.vue +4 -4
  42. package/src/components/display/comments/item/CommentItem.vue +0 -4
  43. package/src/components/display/comments/item/CommentMoreActions.vue +6 -2
  44. package/src/components/display/emission/EmissionInlineList.vue +6 -6
  45. package/src/components/display/emission/EmissionItem.vue +13 -6
  46. package/src/components/display/emission/EmissionList.vue +5 -2
  47. package/src/components/display/emission/EmissionPlayerItem.vue +8 -5
  48. package/src/components/display/emission/EmissionPresentationItem.vue +16 -7
  49. package/src/components/display/emission/EmissionPresentationList.vue +10 -7
  50. package/src/components/display/filter/AdvancedSearch.vue +9 -6
  51. package/src/components/display/filter/CategorySearchFilter.vue +0 -2
  52. package/src/components/display/filter/ProductorSearch.vue +8 -3
  53. package/src/components/display/filter/RubriqueFilter.vue +6 -2
  54. package/src/components/display/list/ListPaginate.vue +9 -6
  55. package/src/components/display/list/SwiperList.vue +6 -5
  56. package/src/components/display/live/CountDown.vue +7 -15
  57. package/src/components/display/live/CountdownOctopus.vue +10 -30
  58. package/src/components/display/live/LiveItem.vue +0 -2
  59. package/src/components/display/live/LiveList.vue +9 -4
  60. package/src/components/display/live/RadioCurrently.vue +9 -5
  61. package/src/components/display/live/RadioImage.vue +6 -4
  62. package/src/components/display/live/RadioItem.vue +1 -2
  63. package/src/components/display/live/RadioList.vue +5 -3
  64. package/src/components/display/live/RadioPlanning.vue +10 -7
  65. package/src/components/display/organisation/OrganisationChooser.vue +8 -4
  66. package/src/components/display/participant/ParticipantInlineList.vue +5 -5
  67. package/src/components/display/participant/ParticipantItem.vue +13 -5
  68. package/src/components/display/participant/ParticipantList.vue +6 -3
  69. package/src/components/display/playlist/PlaylistItem.vue +12 -5
  70. package/src/components/display/playlist/PlaylistList.vue +6 -3
  71. package/src/components/display/playlist/PodcastList.vue +8 -4
  72. package/src/components/display/podcastmaker/PodcastmakerHeader.vue +6 -3
  73. package/src/components/display/podcasts/DownloadPodcastButton.vue +2 -4
  74. package/src/components/display/podcasts/PodcastImage.vue +6 -3
  75. package/src/components/display/podcasts/PodcastInlineListTemplate.vue +7 -4
  76. package/src/components/display/podcasts/PodcastItem.vue +1 -1
  77. package/src/components/display/podcasts/PodcastItemInfo.vue +6 -3
  78. package/src/components/display/podcasts/PodcastList.vue +6 -3
  79. package/src/components/display/podcasts/PodcastModuleBox.vue +22 -9
  80. package/src/components/display/podcasts/PodcastPlayBar.vue +1 -3
  81. package/src/components/display/podcasts/PodcastPlayButton.vue +1 -3
  82. package/src/components/display/podcasts/PodcastRawTranscript.vue +79 -10
  83. package/src/components/display/podcasts/PodcastRubriqueList.vue +4 -3
  84. package/src/components/display/podcasts/TagList.vue +5 -2
  85. package/src/components/display/podcasts/VideoModuleBox.vue +6 -3
  86. package/src/components/display/rubriques/RubriqueChooser.vue +0 -2
  87. package/src/components/display/rubriques/RubriqueList.vue +7 -5
  88. package/src/components/display/sharing/ShareButtons.vue +4 -2
  89. package/src/components/display/sharing/ShareDistribution.vue +4 -2
  90. package/src/components/form/ClassicEmojiPicker.vue +5 -3
  91. package/src/components/misc/ClassicLazy.vue +2 -2
  92. package/src/components/misc/FooterGarSection.vue +2 -3
  93. package/src/components/misc/FooterSection.vue +9 -5
  94. package/src/components/misc/MobileMenu.vue +5 -3
  95. package/src/components/misc/TopBar.vue +8 -6
  96. package/src/components/misc/TopBarMainContent.vue +8 -4
  97. package/src/components/misc/modal/ClassicModalInBody.vue +2 -3
  98. package/src/components/misc/modal/ClipboardModal.vue +4 -3
  99. package/src/components/misc/modal/NewsletterModal.vue +4 -3
  100. package/src/components/misc/modal/ShareModalPlayer.vue +4 -2
  101. package/src/components/misc/player/PlayerCompact.vue +30 -41
  102. package/src/components/misc/player/PlayerComponent.vue +69 -84
  103. package/src/components/misc/player/PlayerLarge.vue +43 -58
  104. package/src/components/misc/player/elements/PlayerImage.vue +6 -3
  105. package/src/components/misc/player/elements/PlayerTitle.vue +6 -2
  106. package/src/components/misc/player/radio/RadioHistory.vue +2 -4
  107. package/src/components/misc/player/video/PlayerVideoHls.vue +6 -7
  108. package/src/components/pages/EmissionPage.vue +19 -7
  109. package/src/components/pages/EmissionsPage.vue +31 -23
  110. package/src/components/pages/HomePage.vue +5 -2
  111. package/src/components/pages/PageLogout.vue +0 -2
  112. package/src/components/pages/PageNotFound.vue +11 -15
  113. package/src/components/pages/ParticipantPage.vue +18 -7
  114. package/src/components/pages/ParticipantsPage.vue +16 -21
  115. package/src/components/pages/PlaylistPage.vue +18 -7
  116. package/src/components/pages/PlaylistsPage.vue +20 -29
  117. package/src/components/pages/PodcastPage.vue +12 -6
  118. package/src/components/pages/PodcastsPage.vue +46 -48
  119. package/src/components/pages/RadioPage.vue +9 -6
  120. package/src/components/pages/RubriquePage.vue +41 -52
  121. package/src/components/pages/VideoPage.vue +152 -166
  122. package/src/helper/cookiesHelper.ts +20 -0
  123. package/src/helper/displayHelper.ts +15 -0
  124. package/src/helper/downloadHelper.ts +30 -0
  125. package/src/helper/{fetch.ts → fetchHelper.ts} +0 -1
  126. package/src/helper/loadScript.ts +22 -0
  127. package/src/helper/radio/radioHelper.ts +15 -0
  128. package/src/helper/{string.ts → stringHelper.ts} +5 -0
  129. package/src/locale/de.ts +4 -0
  130. package/src/locale/en.ts +4 -0
  131. package/src/locale/es.ts +4 -0
  132. package/src/locale/fr.ts +4 -0
  133. package/src/locale/it.ts +4 -0
  134. package/src/locale/sl.ts +4 -0
  135. package/src/stores/AuthStore.ts +1 -2
  136. package/src/stores/CommentStore.ts +8 -8
  137. package/src/stores/PlayerStore.ts +1 -1
  138. package/src/style/general.scss +15 -1
  139. package/src/components/mixins/cookies.ts +0 -22
  140. package/src/components/mixins/displayMethods.ts +0 -17
  141. package/src/components/mixins/download.ts +0 -33
  142. package/src/components/mixins/fetchParameters.ts +0 -16
  143. package/src/components/mixins/handle403.ts +0 -22
  144. package/src/components/mixins/imageProxy.ts +0 -28
  145. package/src/components/mixins/init.ts +0 -34
  146. package/src/components/mixins/loadScript.ts +0 -25
  147. package/src/components/mixins/metaTitle.ts +0 -15
  148. package/src/components/mixins/metaTitleWatch.ts +0 -26
  149. package/src/components/mixins/orgaComputed.ts +0 -23
  150. package/src/components/mixins/organisationFilter.ts +0 -56
  151. package/src/components/mixins/player/playerDisplayTime.ts +0 -35
  152. package/src/components/mixins/player/playerLive.ts +0 -144
  153. package/src/components/mixins/player/playerLogic.ts +0 -275
  154. package/src/components/mixins/player/playerLogicProgress.ts +0 -135
  155. package/src/components/mixins/player/playerStitching.ts +0 -202
  156. package/src/components/mixins/player/playerTranscript.ts +0 -113
  157. package/src/components/mixins/player/playerVast.ts +0 -198
  158. package/src/components/mixins/podcast/countdown.ts +0 -45
  159. package/src/components/mixins/podcast/podcastView.ts +0 -63
  160. package/src/components/mixins/radio/fetchRadioData.ts +0 -77
  161. package/src/components/mixins/resizePhone.ts +0 -26
  162. package/src/components/mixins/routeParam/advancedParamInit.ts +0 -202
  163. package/src/components/mixins/routeParam/paginateParamInit.ts +0 -43
  164. package/src/components/mixins/routeParam/routeParams.ts +0 -33
  165. package/src/components/mixins/routeParam/rubriquesFilterComputed.ts +0 -22
  166. package/src/components/mixins/routeParam/rubriquesFilterParam.ts +0 -29
  167. package/src/components/mixins/selenium.ts +0 -7
  168. package/src/components/mixins/seoTitleUrl.ts +0 -26
  169. package/src/components/mixins/tagOfMixins.ts +0 -19
  170. package/src/helper/uuidGenerator.ts +0 -7
  171. /package/src/{helper → components/composable}/useEventListener.ts +0 -0
  172. /package/src/{components/mixins/debounce.ts → helper/debounceHelper.ts} +0 -0
  173. /package/src/helper/{dom.ts → domHelper.ts} +0 -0
  174. /package/src/helper/{duration.ts → durationHelper.ts} +0 -0
@@ -0,0 +1,135 @@
1
+ import { usePlayerStore } from "../../../stores/PlayerStore";
2
+ import { useAuthStore } from "../../../stores/AuthStore";
3
+ import { onMounted, Ref, ref, watch} from 'vue';
4
+ import classicApi from "../../../api/classicApi";
5
+
6
+ export const usePlayerLogicProgress = ()=>{
7
+ const listenTime= ref(0);
8
+ const notListenTime= ref(0);
9
+ const lastSend= ref(0);
10
+ const downloadId: Ref<string | null>= ref(null);
11
+
12
+ const playerStore = usePlayerStore();
13
+ const authStore = useAuthStore();
14
+
15
+
16
+
17
+ watch(listenTime, async (newVal) => {
18
+ if (
19
+ (playerStore.playerRadio && !playerStore.playerPodcast && !playerStore.playerLive) ||
20
+ !downloadId.value ||
21
+ newVal - lastSend.value < 10
22
+ ) {
23
+ return;
24
+ }
25
+ lastSend.value = newVal;
26
+ await classicApi.putData({
27
+ api: 0,
28
+ path:"podcast/listen/" + downloadId.value + "?seconds=" + Math.round(newVal),
29
+ isNotAuth:true
30
+ });
31
+ });
32
+
33
+ watch(()=>playerStore.playerSeekTime, async () => {
34
+ if (undefined===playerStore.playerSeekTime) {
35
+ return;
36
+ }
37
+ if (playerStore.playerPodcast || playerStore.playerLive) {
38
+ notListenTime.value = playerStore.playerSeekTime - listenTime.value;
39
+ }
40
+ if(playerStore.playerVideo){
41
+ return;
42
+ }
43
+ const audioPlayer: HTMLAudioElement | null = document.querySelector("#audio-player");
44
+ if (!audioPlayer) return;
45
+ audioPlayer.currentTime = playerStore.playerSeekTime;
46
+ });
47
+
48
+
49
+
50
+ onMounted(() => {
51
+ window.addEventListener("beforeunload", endListeningProgress);
52
+ })
53
+
54
+ async function initLiveDownloadId() {
55
+ if (!playerStore.playerLive || downloadId.value) {
56
+ return;
57
+ }
58
+ try {
59
+ const mediaType = playerStore.playerVideo ? "VIDEO":"AUDIO";
60
+ const downloadId = await classicApi.putData<string | null>({
61
+ api: 0,
62
+ path:"podcast/prepare/live/" + playerStore.playerLive.podcastId+"?mediaType="+mediaType,
63
+ isNotAuth:true
64
+ });
65
+ await classicApi.fetchData<string | null>({
66
+ api:0,
67
+ path: "podcast/download/live/" + playerStore.playerLive.podcastId + ".m3u8",
68
+ parameters:{
69
+ downloadId: downloadId ?? undefined,
70
+ origin: "octopus",
71
+ distributorId: authStore.authOrgaId,
72
+ },
73
+ isNotAuth:true
74
+ });
75
+ setDownloadId(downloadId);
76
+ } catch {
77
+ downloadId.value = null;
78
+ console.log("ERROR downloadId");
79
+ }
80
+ }
81
+
82
+ function onTimeUpdateProgress(currentTime: number): void {
83
+ if (!downloadId.value) {
84
+ return;
85
+ }
86
+ if (
87
+ playerStore.playerLive &&
88
+ 0 === listenTime.value &&
89
+ 0 !== currentTime
90
+ ) {
91
+ notListenTime.value = currentTime;
92
+ listenTime.value = 1;
93
+ } else {
94
+ const newListenTime = currentTime - notListenTime.value;
95
+ const diffTime = newListenTime - listenTime.value;
96
+ if(diffTime > 0 && diffTime<1){
97
+ listenTime.value = newListenTime;
98
+ }
99
+ }
100
+ }
101
+
102
+ function setDownloadId(newValue: string | null): void {
103
+ endListeningProgress();
104
+ downloadId.value = newValue;
105
+ }
106
+
107
+ async function endListeningProgress(): Promise<void> {
108
+ if (!downloadId.value) return;
109
+ try {
110
+ await classicApi.putData<string | null>({
111
+ api: 0,
112
+ path:"podcast/listen/" +
113
+ downloadId.value +
114
+ "?seconds=" +
115
+ Math.round(listenTime.value),
116
+ isNotAuth:true
117
+ });
118
+ } catch {
119
+ //Do nothing
120
+ }
121
+ downloadId.value = null;
122
+ notListenTime.value = 0;
123
+ lastSend.value = 0;
124
+ listenTime.value = 0;
125
+ }
126
+
127
+
128
+ return {
129
+ listenTime,
130
+ downloadId,
131
+ initLiveDownloadId,
132
+ setDownloadId,
133
+ onTimeUpdateProgress
134
+ }
135
+ }
@@ -0,0 +1,213 @@
1
+ import { usePlayerStore } from "../../../stores/PlayerStore";
2
+ import { useVastStore } from "../../../stores/VastStore";
3
+ import { useSaveFetchStore } from "../../../stores/SaveFetchStore";
4
+ import { usePlayerVast } from "./usePlayerVast";
5
+ import fetchHelper from "../../../helper/fetchHelper";
6
+
7
+ import {computed, onMounted, Ref, ref, watch} from 'vue';
8
+ import { useRoute } from "vue-router";
9
+ import dayjs from "dayjs";
10
+ import classicApi from "../../../api/classicApi";
11
+ import { AdserverOtherEmission } from "@/stores/class/adserver/adserverOtherEmission";
12
+ import { AdPosition } from "@/stores/class/adserver/adPosition";
13
+ import { AdserverTiming } from "@/stores/class/adserver/adserverTiming";
14
+
15
+ export const usePlayerStitching = ()=>{
16
+
17
+ const { isAdRequested, prepareIMA, onRequestAd,destroyAdManager, contentEndedAdsLoader } = usePlayerVast();
18
+
19
+ const radioInterval : Ref<ReturnType<typeof setTimeout> | undefined> = ref(undefined);
20
+
21
+ const route = useRoute()
22
+ const playerStore = usePlayerStore();
23
+ const vastStore = useVastStore();
24
+ const saveFetchStore= useSaveFetchStore();
25
+
26
+ const radioNextAdvertisingStartDate = computed(() => {
27
+ return playerStore.playerRadio?.nextAdvertising?.startDate;
28
+ });
29
+
30
+
31
+ watch(() => playerStore.playerCurrentChange, () => {
32
+ if(playerStore.playerCurrentChange && playerStore.playerCurrentChange > 0 && !checkUsePlayerPodcastStitching()){return;}
33
+ onPlayerChange();
34
+ });
35
+
36
+ //launch advertising for podcast
37
+ watch(() => playerStore.playerElapsedSeconds, () => {
38
+ if(!checkUsePlayerPodcastStitching()){return;}
39
+ if(!isAdRequested.value && checkAdNeedToBeLaunch()){
40
+ onRequestAd(vastStore.adPositionsPodcasts[playerStore.playerCurrentChange??0][vastStore.adPositionIndex].vastUrl);
41
+ vastStore.updateAdPositionIndex(vastStore.adPositionIndex+1);
42
+ }
43
+ });
44
+
45
+ // launch advertising for radio
46
+ watch(radioNextAdvertisingStartDate, () => {
47
+ if(!radioNextAdvertisingStartDate.value){
48
+ return;
49
+ }
50
+ defineRadioInterval();
51
+ }, { immediate: true });
52
+
53
+
54
+ onMounted(() => {
55
+ vastStore.updateuseVastPlayerPodcast("true"===route.query.vast);
56
+ })
57
+
58
+ function checkUsePlayerPodcastStitching():boolean{
59
+ return vastStore.useVastPlayerPodcast;
60
+ }
61
+
62
+ function defineRadioInterval(){
63
+ clearRadioInterval();
64
+ const timeRemaining = dayjs(radioNextAdvertisingStartDate.value).diff(dayjs(), "millisecond");
65
+ console.log("TimeRemaining "+timeRemaining);
66
+ if(timeRemaining < 0){
67
+ return;
68
+ }
69
+ radioInterval.value = setTimeout(() => {
70
+ radioIntervalExecute();
71
+ }, timeRemaining);
72
+ }
73
+
74
+ async function radioIntervalExecute(){
75
+ //If pause when ad needs to be played then skipped (TO THINK)
76
+ if("PAUSED"===playerStore.playerStatus){return;}
77
+ const vastUrl = await getVastUrl(playerStore.playerRadio?.nextAdvertising?.tag ??"5e385e1b51c86", playerStore.playerRadio?.nextAdvertising?.adCount ?? 1);
78
+ console.log("vastUrl "+vastUrl);
79
+ onRequestAd(vastUrl);
80
+ }
81
+
82
+ function clearRadioInterval() {
83
+ clearInterval(radioInterval.value as unknown as number);
84
+ radioInterval.value = undefined;
85
+ }
86
+
87
+ function checkAdNeedToBeLaunch(){
88
+ if(!playerStore.playerCurrentChange || !vastStore.adPositionsPodcasts[playerStore.playerCurrentChange]?.[vastStore.adPositionIndex]){return false;}
89
+ return vastStore.adPositionsPodcasts[playerStore.playerCurrentChange][vastStore.adPositionIndex]?.seconds <= playerStore.playerElapsedSeconds;
90
+ }
91
+
92
+ async function onPlayerChange(){
93
+ destroyAdManager();
94
+ if(null==playerStore.playerCurrentChange){return;}
95
+ prepareIMA();
96
+ await fetchPodcastAdConfig();
97
+ }
98
+
99
+ async function fetchPodcastAdConfig(){
100
+ if(!playerStore.playerCurrentChange || !playerStore.playerPodcast ||(playerStore.playerCurrentChange && vastStore.adPositionsPodcasts[playerStore.playerCurrentChange])){
101
+ return;
102
+ }
103
+ const adserverConfig = await classicApi.fetchData<AdserverOtherEmission>({
104
+ api:0,
105
+ path: `ad/test/podcast/${playerStore.playerCurrentChange}`,
106
+ isNotAuth:true
107
+ });
108
+ if(!adserverConfig || "SOUNDCAST_VAST"!==adserverConfig.config?.server){
109
+ vastStore.updateAdPositionsPodcasts(playerStore.playerCurrentChange, []);
110
+ return;
111
+ }
112
+ const podcastDurationSeconds = Math.round((playerStore.playerPodcast?.duration??0) / 1000);
113
+ const allAdPositions =generateAllAdPositions(adserverConfig.config.doublets, podcastDurationSeconds);
114
+ const selectedAdPositions = await selectCorrectAdPositions(allAdPositions, podcastDurationSeconds, adserverConfig.config.minIntervalDuration, adserverConfig.config.minTailDuration);
115
+ vastStore.updateAdPositionsPodcasts(playerStore.playerCurrentChange, selectedAdPositions);
116
+ }
117
+
118
+ function generateAllAdPositions(doublets: Array<AdserverTiming>, podcastDuration: number): Array<AdPosition>{
119
+ const adPositions: Array<AdPosition> = [];
120
+ if(doublets.some((element: AdserverTiming)=>{return "TAG_NO_AD"===element.tag})){
121
+ return [];
122
+ }
123
+ for (const doublet of doublets) {
124
+ if(!doublet.tag){continue;}
125
+ let seconds = 0;
126
+ if("post"===doublet.timing.insertion){
127
+ seconds = podcastDuration;
128
+ }else if("mid"===doublet.timing.insertion){
129
+ const mesure = doublet.timing.mesure ?? 0;
130
+ switch (doublet.timing.unit){
131
+ case "MILLISECOND": seconds = mesure * 1000; break;
132
+ case "MINUTE": seconds = Math.round(mesure / 60); break;
133
+ case "PERCENT": seconds = Math.round((mesure * podcastDuration) / 100); break;
134
+ default: seconds = mesure; break;
135
+ }
136
+ }
137
+ adPositions.push({
138
+ impressId: doublet.tag,
139
+ policy: doublet.timing.insertion,
140
+ seconds: seconds,
141
+ vastUrl: ""
142
+ });
143
+ }
144
+ return adPositions.sort((a: AdPosition, b: AdPosition) => {
145
+ if (a.seconds > b.seconds) {
146
+ return 1;
147
+ }
148
+ return b.seconds > a.seconds ? -1 : 0;
149
+ });
150
+ }
151
+
152
+ async function selectCorrectAdPositions(allAdPositions: Array<AdPosition>, podcastDuration: number, minIntervalDuration:number, minTailDuration:number): Promise<Array<AdPosition>>{
153
+ const adPositions: Array<AdPosition> = [];
154
+ let previousPosition = -1;
155
+ for(const adPosition of allAdPositions){
156
+ switch (adPosition.policy) {
157
+ case "pre":
158
+ adPositions.push(await defineVastUrl(adPosition));
159
+ previousPosition = 0;
160
+ break;
161
+ case "mid":
162
+ if (adPosition.seconds > podcastDuration - minTailDuration) {
163
+ //Too close to end
164
+ continue;
165
+ }
166
+ if (previousPosition >= 0 && adPosition.seconds < previousPosition + minIntervalDuration) {
167
+ //Too close to previous ad
168
+ continue;
169
+ }
170
+ adPositions.push(await defineVastUrl(adPosition));
171
+ previousPosition = adPosition.seconds;
172
+ break;
173
+ case "post":
174
+ if (previousPosition >= 0 && previousPosition > podcastDuration - minIntervalDuration) {
175
+ //Too close to previous ad
176
+ continue;
177
+ }
178
+ adPositions.push(await defineVastUrl(adPosition));
179
+ break;
180
+ default:break;
181
+ }
182
+ }
183
+ return adPositions;
184
+ }
185
+
186
+ async function defineVastUrl(adPosition: AdPosition): Promise<AdPosition>{
187
+ adPosition.vastUrl = await getVastUrl(adPosition.impressId, 1);
188
+ return adPosition;
189
+ }
190
+
191
+ async function getVastUrl(tag: string, adCount: number): Promise<string>{
192
+ const baseUrl = "https://api.soundcast.io/v1/vast/"+tag;
193
+ let keywords: Array<string> = [];
194
+ if(playerStore.playerPodcast?.tags?.length){
195
+ const attributes = await saveFetchStore.getOrgaAttributes(playerStore.playerPodcast.organisation.id);
196
+ if ("true"===attributes["AD_CONFIG_PODCAST_TAG"]) {
197
+ keywords = playerStore.playerPodcast.tags.map((e) => {
198
+ return "tag:" + e;
199
+ });
200
+ }
201
+ }
202
+ const parameters = fetchHelper.getUriSearchParams({
203
+ adCount: adCount ?? 1,
204
+ pageUrl:document.referrer,
205
+ keywords:keywords
206
+ });
207
+ return baseUrl + '?' + parameters.toString();
208
+ }
209
+
210
+ return {
211
+ contentEndedAdsLoader
212
+ }
213
+ }
@@ -0,0 +1,120 @@
1
+ import { usePlayerStore } from "../../../stores/PlayerStore";
2
+ import { useVastStore } from "../../../stores/VastStore";
3
+ import classicApi from "../../../api/classicApi";
4
+ import { AdserverOtherEmission } from "@/stores/class/adserver/adserverOtherEmission";
5
+ export const usePlayerTranscript = ()=>{
6
+
7
+ const playerStore = usePlayerStore();
8
+ const vastStore = useVastStore();
9
+
10
+ async function checkDelaytWithStitching(){
11
+ playerStore.playerUpdateDelayStitching(0);
12
+ if(vastStore.useVastPlayerPodcast){return;}
13
+ const audioPlayer = document.querySelector("#audio-player") as HTMLAudioElement;
14
+ if (!playerStore.playerTranscript || !audioPlayer || !playerStore.playerPodcast ||
15
+ audioPlayer.duration <= playerStore.playerPodcast.duration / 1000 + 5)
16
+ {
17
+ return;
18
+ }
19
+ const adserverConfig = await classicApi.fetchData<AdserverOtherEmission>({
20
+ api:0,
21
+ path:`ad/test/podcast/${playerStore.playerPodcast.podcastId}`,
22
+ isNotAuth:true
23
+ });
24
+ const doubletsLength = adserverConfig.config.doublets.length;
25
+ if(1=== doubletsLength && "pre" === adserverConfig.config.doublets[0].timing.insertion){
26
+ playerStore.playerUpdateDelayStitching( audioPlayer.duration - (playerStore.playerPodcast.duration / 1000));
27
+ }else if(0===doubletsLength || 1=== doubletsLength && "post" === adserverConfig.config.doublets[0].timing.insertion){
28
+ return;
29
+ }else{
30
+ playerStore.playerUpdateChaptering();
31
+ playerStore.playerUpdateTranscript();
32
+ }
33
+ }
34
+
35
+ async function getTranscription(): Promise<void> {
36
+ if (!playerStore.playerPodcast) {
37
+ playerStore.playerUpdateTranscript();
38
+ return;
39
+ }
40
+ const result = await classicApi.fetchData<string>({
41
+ api:11,
42
+ path:`response/${playerStore.playerPodcast.podcastId}`,
43
+ });
44
+
45
+ const arrayTranscript = parseSrt(result);
46
+ const actualText =
47
+ arrayTranscript?.[0]?.startTime === 0 ? arrayTranscript[0].text : "";
48
+ if(!arrayTranscript){
49
+ return;
50
+ }
51
+ playerStore.playerUpdateTranscript({
52
+ actual: 0,
53
+ actualText: actualText,
54
+ value: arrayTranscript
55
+ });
56
+ }
57
+
58
+ function parseSrt(transcript: string) {
59
+ const result = [];
60
+ if (typeof transcript != "string") {
61
+ return;
62
+ }
63
+ if (transcript == null) {
64
+ return;
65
+ }
66
+ const pattern =
67
+ /(\d+)\n([\d:,]+)\s+-{2}>\s+([\d:,]+)\n([\s\S]*?(?=\n{2}|$))/gm;
68
+ transcript = transcript.replace(/\r\n|\r|\n|\t/g, "\n");
69
+ let matches;
70
+ while ((matches = pattern.exec(transcript)) != null) {
71
+ result.push({
72
+ startTime: srtTimeToSeconds(matches[2]),
73
+ endTime: srtTimeToSeconds(matches[3]),
74
+ text: matches[4],
75
+ });
76
+ }
77
+ return result;
78
+ }
79
+
80
+ function srtTimeToSeconds(time: string): number {
81
+ const a = time.split(":");
82
+ return +a[0] * 60 * 60 + +a[1] * 60 + +parseFloat(a[2]);
83
+ }
84
+
85
+ function onTimeUpdateTranscript(currentTime: number) {
86
+ if(!playerStore.playerTranscript?.value.length){
87
+ return;
88
+ }
89
+ const startTime = (playerStore.playerTranscript.value[playerStore.playerTranscript.actual]?.startTime ?? 0) + playerStore.playerDelayStitching;
90
+ if (startTime <= currentTime) {
91
+ playerStore.playerTranscript.actualText = playerStore.playerTranscript.value[playerStore.playerTranscript?.actual]?.text ??"";
92
+ }
93
+ if (
94
+ (playerStore.playerTranscript.value[playerStore.playerTranscript.actual]?.endTime ??Infinity) + playerStore.playerDelayStitching< currentTime
95
+ ) {
96
+ playerStore.playerTranscript.actual += 1;
97
+ playerStore.playerTranscript.actualText =
98
+ playerStore.playerTranscript?.value[playerStore.playerTranscript.actual]?.text ??
99
+ "";
100
+ }
101
+ }
102
+
103
+ function onSeekedTranscript(currentTime: number) {
104
+ if (playerStore.playerTranscript) {
105
+ let newActual = 0;
106
+ while (currentTime >(playerStore.playerTranscript.value[newActual]?.endTime ?? Infinity) + playerStore.playerDelayStitching) {
107
+ newActual += 1;
108
+ }
109
+ playerStore.playerTranscript.actual = newActual;
110
+ }
111
+ }
112
+
113
+
114
+ return {
115
+ checkDelaytWithStitching,
116
+ getTranscription,
117
+ onTimeUpdateTranscript,
118
+ onSeekedTranscript
119
+ }
120
+ }
@@ -0,0 +1,207 @@
1
+ import { usePlayerStore } from "../../../stores/PlayerStore";
2
+ import { useVastStore } from "../../../stores/VastStore";
3
+ import { loadScript } from "../../../helper/loadScript";
4
+ import {Ref, ref, watch} from 'vue';
5
+ let adsLoader: any;
6
+ let adsManager:any;
7
+ let adDisplayContainer:any;
8
+ let adsRequest: any;
9
+ export const usePlayerVast = ()=>{
10
+ const imaLoaded = ref(false);
11
+ const isContentFinished = ref(false);
12
+ const audioContainer : Ref<HTMLAudioElement|null>= ref(null);
13
+ const isAdRequested = ref(false);
14
+ const statusPlayerWhenLoaded = ref("");
15
+
16
+ const playerStore = usePlayerStore();
17
+ const vastStore = useVastStore();
18
+
19
+ watch(() => vastStore.isAdPaused, () => {onAdChangePlayingStatus();});
20
+ watch(() => vastStore.isAdSkipped, () => {onAdSkipped();});
21
+
22
+ function prepareIMA(){
23
+ if(!imaLoaded.value){
24
+ imaLoaded.value = true;
25
+ loadIMA();
26
+ }else{
27
+ initializeIMA();
28
+ }
29
+ }
30
+
31
+ function loadIMA(){
32
+ loadScript('//imasdk.googleapis.com/js/sdkloader/ima3.js', true, (isIMALoaded:boolean) => {
33
+ if(isIMALoaded) {
34
+ initializeIMA();
35
+ }
36
+ });
37
+ }
38
+
39
+ function initializeIMA(): void {
40
+ console.log("Initialize IMA");
41
+ initializeDisplayContainer();
42
+ if(!adDisplayContainer || !adsLoader){
43
+ return;
44
+ }
45
+ }
46
+
47
+ function onRequestAd(vastUrl: string){
48
+ isAdRequested.value = true;
49
+ console.log("Request ad "+vastUrl);
50
+ initializeAdsRequest(vastUrl);
51
+ adsLoader.requestAds(adsRequest);
52
+ }
53
+
54
+ function initializeDisplayContainer():void{
55
+ if(adDisplayContainer){return};
56
+ audioContainer.value = (document.getElementById('audio-player') as HTMLAudioElement);
57
+ if(null===audioContainer.value){return;}
58
+ adDisplayContainer = new google.ima.AdDisplayContainer(document.getElementById('ad-container'), audioContainer.value);
59
+ adDisplayContainer.initialize();
60
+ initializeAdsLoader();
61
+ }
62
+
63
+ function initializeAdsLoader():void{
64
+ adsLoader = new google.ima.AdsLoader(adDisplayContainer);
65
+ adsManager = null;
66
+ adsLoader.addEventListener(
67
+ google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
68
+ onAdsManagerLoaded,
69
+ false
70
+ );
71
+ adsLoader.addEventListener(
72
+ google.ima.AdErrorEvent.Type.AD_ERROR,
73
+ onAdError,
74
+ false
75
+ );
76
+ }
77
+
78
+ function initializeAdsRequest(vastUrl: string):void{
79
+ adsRequest = new google.ima.AdsRequest();
80
+ adsRequest.setAdWillAutoPlay(true);
81
+ adsRequest.adTagUrl = vastUrl;
82
+ }
83
+
84
+ function onAdsManagerLoaded(adsManagerLoadedEvent: any) {
85
+ const adsRenderingSettings = new google.ima.AdsRenderingSettings();
86
+ adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
87
+ adsManager = adsManagerLoadedEvent.getAdsManager(audioContainer.value, adsRenderingSettings);
88
+ startAdManager();
89
+ }
90
+
91
+ function startAdManager(){
92
+ console.log("Start manager");
93
+ initAdManagerEvents();
94
+ try {
95
+ adsManager.init(0, 0, google.ima.ViewMode.NORMAL);
96
+ adsManager.start();
97
+ } catch (adError) {
98
+ console.log(adError);
99
+ playerStore.playerChangeStatus(false);
100
+ destroyAdManager();
101
+ }
102
+ }
103
+
104
+ function initAdManagerEvents(){
105
+ adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,onAdError);
106
+ adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
107
+ adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested);
108
+ const events = [
109
+ google.ima.AdEvent.Type.ALL_ADS_COMPLETED, google.ima.AdEvent.Type.AD_PROGRESS,
110
+ google.ima.AdEvent.Type.COMPLETE, google.ima.AdEvent.Type.LOADED,
111
+ google.ima.AdEvent.Type.PAUSED, google.ima.AdEvent.Type.STARTED,
112
+ ];
113
+ for (const index in events) {
114
+ adsManager.addEventListener(events[index], onAdEvent);
115
+ }
116
+ }
117
+
118
+ function onAdError(adErrorEvent: any) {
119
+ console.log(adErrorEvent.getError());
120
+ destroyAdManager();
121
+ }
122
+
123
+ function destroyAdManager(){
124
+ if(!adsManager){return;}
125
+ console.log("Destroy manager");
126
+ adsManager?.destroy();
127
+ adsManager = null;
128
+ vastStore.restartVastData();
129
+ isContentFinished.value =false;
130
+ isAdRequested.value = false;
131
+ }
132
+
133
+ function onAdEvent(adEvent: any) {
134
+ const ad = adEvent.getAd();
135
+ if(ad){
136
+ vastStore.updateCurrentAd(ad);
137
+ }
138
+ switch (adEvent.type) {
139
+ case google.ima.AdEvent.Type.LOADED:
140
+ statusPlayerWhenLoaded.value = playerStore.playerStatus;
141
+ if (!ad.isLinear()) {
142
+ playerStore.playerChangeStatus(false);
143
+ isAdRequested.value = false;
144
+ }
145
+ break;
146
+ case google.ima.AdEvent.Type.STARTED:
147
+ console.log("Launched status : "+statusPlayerWhenLoaded.value);
148
+ if("PLAYING"!==statusPlayerWhenLoaded.value){
149
+ vastStore.updateIsAdPaused(true);
150
+ adsManager.pause();
151
+ }
152
+ break;
153
+ case google.ima.AdEvent.Type.AD_PROGRESS:{
154
+ isAdRequested.value = false;
155
+ const adProgressData = adEvent.getAdData();
156
+ vastStore.updateProgressionData(adProgressData.duration,adProgressData.currentTime);
157
+ vastStore.updateSkippableData(
158
+ vastStore.currentAd.getSkipTimeOffset()!== -1,
159
+ adsManager.getAdSkippableState(),
160
+ Math.ceil(vastStore.currentAd.getSkipTimeOffset() - adProgressData.currentTime));
161
+ break;
162
+ }
163
+ }
164
+ }
165
+
166
+ function onContentPauseRequested() {
167
+ vastStore.updateIsAdPlaying(true);
168
+ playerStore.playerChangeStatus(true);
169
+ }
170
+
171
+ function onContentResumeRequested() {
172
+ vastStore.updateIsAdPlaying(false);
173
+ if (!isContentFinished.value) {
174
+ playerStore.playerChangeStatus(false);
175
+ }
176
+ }
177
+
178
+ function contentEndedAdsLoader():void{
179
+ if (vastStore.isAdPlaying) return;
180
+ isContentFinished.value = true;
181
+ adsLoader?.contentComplete();
182
+ }
183
+
184
+ function onAdChangePlayingStatus(){
185
+ if(!adsManager){return;}
186
+ if(vastStore.isAdPaused){
187
+ adsManager.pause();
188
+ }else{
189
+ adsManager.resume();
190
+ }
191
+ }
192
+
193
+ function onAdSkipped(){
194
+ if(vastStore.isAdSkipped){
195
+ adsManager.skip();
196
+ }
197
+ }
198
+
199
+
200
+ return {
201
+ isAdRequested,
202
+ prepareIMA,
203
+ onRequestAd,
204
+ destroyAdManager,
205
+ contentEndedAdsLoader
206
+ }
207
+ }