@saooti/octopus-sdk 41.8.4 → 41.9.1

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 (66) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CHANGELOG.md +64 -0
  3. package/eslint-config.d.ts +3 -0
  4. package/eslint-config.mjs +43 -0
  5. package/eslint.config.mjs +2 -43
  6. package/index.ts +16 -0
  7. package/package.json +14 -2
  8. package/src/api/transcriptionApi.ts +129 -3
  9. package/src/components/composable/player/usePlayerDisplayTime.ts +1 -0
  10. package/src/components/composable/player/usePlayerTranscript.ts +66 -17
  11. package/src/components/composable/podcasts/usePodcastView.ts +2 -3
  12. package/src/components/composable/share/useSharePath.ts +1 -1
  13. package/src/components/composable/share/useSharePlatforms.ts +16 -16
  14. package/src/components/composable/useDayjs.ts +24 -0
  15. package/src/components/composable/useRights.ts +11 -1
  16. package/src/components/composable/useTranslation.ts +175 -0
  17. package/src/components/display/comments/CommentName.vue +1 -0
  18. package/src/components/display/comments/modal/CheckIdentityModal.vue +1 -0
  19. package/src/components/display/comments/modal/ReportAbuseModal.vue +1 -0
  20. package/src/components/display/emission/EmissionInlineList.vue +7 -9
  21. package/src/components/display/filter/ProductorSearch.vue +1 -1
  22. package/src/components/display/list/SwiperList.vue +1 -0
  23. package/src/components/display/organisation/OrganisationChooser.vue +1 -1
  24. package/src/components/display/organisation/OrganisationChooserLight.vue +1 -1
  25. package/src/components/display/participant/ParticipantInlineList.vue +14 -13
  26. package/src/components/display/podcasts/PodcastItemInfo.vue +97 -59
  27. package/src/components/display/podcasts/PodcastModuleBox.vue +42 -5
  28. package/src/components/display/podcasts/PodcastRawTranscript.vue +166 -81
  29. package/src/components/display/sharing/SharePlayer.vue +1 -1
  30. package/src/components/form/ClassicDatePicker.vue +8 -3
  31. package/src/components/form/ClassicInputText.vue +12 -2
  32. package/src/components/form/ClassicLoading.vue +6 -2
  33. package/src/components/form/ClassicSelect.vue +2 -2
  34. package/src/components/misc/ClassicAlert.vue +26 -14
  35. package/src/components/misc/ClassicSpinner.vue +18 -4
  36. package/src/components/misc/FooterSection.vue +1 -1
  37. package/src/components/misc/player/PlayerComponent.vue +1 -1
  38. package/src/components/misc/player/PlayerLarge.vue +14 -0
  39. package/src/components/pages/EmissionPage.vue +22 -5
  40. package/src/components/pages/LivesPage.vue +1 -1
  41. package/src/helper/language.ts +38 -0
  42. package/src/i18n.ts +0 -7
  43. package/src/locale/de.json +3 -1
  44. package/src/locale/en.json +3 -1
  45. package/src/locale/es.json +3 -1
  46. package/src/locale/fr.json +4 -1
  47. package/src/locale/it.json +3 -1
  48. package/src/locale/sl.json +3 -1
  49. package/src/main.ts +3 -29
  50. package/src/stores/AuthStore.ts +1 -1
  51. package/src/stores/ParamSdkStore.ts +10 -3
  52. package/src/stores/class/general/emission.ts +4 -1
  53. package/src/stores/class/general/organisation.ts +10 -1
  54. package/src/stores/class/general/podcast.ts +6 -1
  55. package/src/stores/class/transcript/transcriptParams.ts +25 -0
  56. package/tests/api/transcriptionApi.spec.ts +58 -0
  57. package/tests/components/composable/player/usePlayerTranscript.spec.ts +238 -0
  58. package/tests/components/composable/useRights.spec.ts +44 -2
  59. package/tests/components/composable/useTranslation.spec.ts +144 -0
  60. package/tests/components/display/podcasts/PodcastFilterList.spec.ts +4 -4
  61. package/tests/components/display/podcasts/PodcastItemInfo.spec.ts +65 -0
  62. package/tests/components/display/podcasts/PodcastModuleBox.spec.ts +64 -0
  63. package/tests/components/display/podcasts/PodcastRawTranscript.spec.ts +253 -0
  64. package/tests/components/misc/player/PlayerLarge.spec.ts +71 -0
  65. package/tests/components/pages/EmissionPage.spec.ts +31 -0
  66. package/tests/helper/language.spec.ts +45 -0
@@ -10,7 +10,8 @@
10
10
  "Bash(node --version:*)",
11
11
  "Bash(npm:*)",
12
12
  "Bash(npx vitest:*)",
13
- "Bash(./node_modules/.bin/vitest run:*)"
13
+ "Bash(./node_modules/.bin/vitest run:*)",
14
+ "Bash(npx eslint:*)"
14
15
  ]
15
16
  }
16
17
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 41.9.1 (07/04/2026)
4
+
5
+ **Features**
6
+
7
+ - **12535** - Améliorations pour la traduction
8
+ - La régénration de la transcription invalide les données de
9
+ `PodcastRawTranscript`
10
+ - **14406** - Ajout de `useDayjs` qui permet d'avoir des dates réactives
11
+ - Mise à jour des droits relatifs aux transcriptions/traductions
12
+ - Ajout de nouveaux endpoints à `transcriptionApi`
13
+ - Ajout nouveaux exports:
14
+ - composable `useTranslation`
15
+ - types `TranslationData`, `ModifyPodcastConfig`
16
+ - énumération `TranslationState`
17
+ - Améliorations composants :
18
+ - `ClassicInputText` en mode `textarea` scrolle en haut du contenu par défaut
19
+ - `ClassicAlert` a maintenant un props `text` qui enlève le font et la bordure
20
+
21
+ **Fixes**
22
+
23
+ - **14330** - La page d'émission propose uniquement le dernier épisode
24
+ **valide** à la lecture
25
+
26
+ ## 41.9.0 (31/03/2026)
27
+
28
+ **Features**
29
+
30
+ - **12535** - Implémentation de la traduction des sous-titres
31
+ - Mise en place de l'api `transcriptionApi` pour simplifier les appels
32
+ - Ajout du composable `useTranslation` pour regrouper les opérations relatives
33
+ aux traductions
34
+ - `PodcastRawTranscript` permet maintenant de choisir la langue de la
35
+ transcription
36
+ - La langue la plus pertinente pour l'utilisateur est sélectionnée par
37
+ défaut
38
+ - La transcription affichée dans le player correspond à la langue la plus
39
+ pertinente disponible
40
+ - **14357** - Affichage des sous-titres et résumés d'épisodes, et des
41
+ sous-titres des émissions
42
+ - Ajout de nouveaux paramètres pour contrôler cet affichage :
43
+ - `hideSubtitle` dans `emissionPage` & `podcastPage`, pour cacher les
44
+ sous-titres
45
+ - `descriptionOrSummary` dans `emissionPage`, pour choisir quel élément
46
+ afficher
47
+ - **14387** - Affichage du numéro de saison et d'épisode, ainsi que du type
48
+ d'épisode dans les `PodcastItemInfo`
49
+ - La configuration eslint est maintenant exportée, elle peut donc être intégrée
50
+ telle quelle dans les projets se basant sur le SDK
51
+ - Ajout d'options de configuration pour `ClassicLoading` et `ClassicSpinner`
52
+
53
+ **Fix**
54
+
55
+ - **14404** - Correction affichage de la transcription dans le cas de preroll/
56
+ postrolls multiples
57
+ - Correction `z-index` des boutons de `SwiperList`
58
+ - La propriété `focus` de `ClassicInputText` est à `false` par défaut
59
+
60
+ **Misc**
61
+
62
+ - Correction warning avec les icônes de `useSharePlatforms`
63
+ - Ajustement composants pour les podcastmakers
64
+ - Mise à jour des dépendances
65
+ - Les exports d'organisation viennent du SDK et non plus du package parent
66
+
3
67
  ## 41.8.4 (24/03/2026)
4
68
 
5
69
  **Fix**
@@ -0,0 +1,3 @@
1
+ import type { ConfigArray } from 'typescript-eslint';
2
+ declare const config: ConfigArray;
3
+ export default config;
@@ -0,0 +1,43 @@
1
+ import eslint from '@eslint/js';
2
+ import eslintPluginVue from 'eslint-plugin-vue';
3
+ import globals from 'globals';
4
+ import typescriptEslint from 'typescript-eslint';
5
+
6
+ export default typescriptEslint.config(
7
+ { ignores: ['*.d.ts', '**/coverage', '**/dist'] },
8
+ {
9
+ extends: [
10
+ eslint.configs.recommended,
11
+ ...typescriptEslint.configs.recommended,
12
+ ...eslintPluginVue.configs['flat/recommended'],
13
+ ],
14
+ files: ['**/*.{ts,vue}'],
15
+ languageOptions: {
16
+ ecmaVersion: 'latest',
17
+ sourceType: 'module',
18
+ globals: globals.browser,
19
+ parserOptions: {
20
+ parser: typescriptEslint.parser,
21
+ },
22
+ },
23
+ rules: {
24
+ // your rules
25
+ "curly": ['error'],
26
+
27
+ // Please don't use console statements and duplicated imports, TODOs are marked
28
+ "no-console": ['warn', { allow: ['warn', 'error'] }],
29
+ "no-warning-comments": ['warn'],
30
+ "no-duplicate-imports": ['warn'],
31
+
32
+ // Prevent errors when testing on refs (instead of value of ref)
33
+ "vue/no-ref-as-operand": ['error'],
34
+
35
+ // Indentation
36
+ "vue/html-indent": ['warn', 4],
37
+ "vue/script-indent": ['warn', 4],
38
+
39
+ // Number of attributes per line (increase because sometimes two is not a lot)
40
+ "vue/max-attributes-per-line": ['warn', { singleline: 2 } ]
41
+ },
42
+ }
43
+ );
package/eslint.config.mjs CHANGED
@@ -1,43 +1,2 @@
1
- import eslint from '@eslint/js';
2
- import eslintPluginVue from 'eslint-plugin-vue';
3
- import globals from 'globals';
4
- import typescriptEslint from 'typescript-eslint';
5
-
6
- export default typescriptEslint.config(
7
- { ignores: ['*.d.ts', '**/coverage', '**/dist'] },
8
- {
9
- extends: [
10
- eslint.configs.recommended,
11
- ...typescriptEslint.configs.recommended,
12
- ...eslintPluginVue.configs['flat/recommended'],
13
- ],
14
- files: ['**/*.{ts,vue}'],
15
- languageOptions: {
16
- ecmaVersion: 'latest',
17
- sourceType: 'module',
18
- globals: globals.browser,
19
- parserOptions: {
20
- parser: typescriptEslint.parser,
21
- },
22
- },
23
- rules: {
24
- // your rules
25
- "curly": ['error'],
26
-
27
- // Please don't use console statements and duplicated imports, TODOs are marked
28
- "no-console": ['warn', { allow: ['warn', 'error'] }],
29
- "no-warning-comments": ['warn'],
30
- "no-duplicate-imports": ['warn'],
31
-
32
- // Prevent errors when testing on refs (instead of value of ref)
33
- "vue/no-ref-as-operand": ['error'],
34
-
35
- // Indentation
36
- "vue/html-indent": ['warn', 4],
37
- "vue/script-indent": ['warn', 4],
38
-
39
- // Number of attributes per line (increase because sometimes two is not a lot)
40
- "vue/max-attributes-per-line": ['warn', { singleline: 2 } ]
41
- },
42
- }
43
- );
1
+ import sdkConfig from './eslint-config.mjs';
2
+ export default sdkConfig;
package/index.ts CHANGED
@@ -135,6 +135,7 @@ export { useSharePath } from "./src/components/composable/share/useSharePath.ts"
135
135
  export { useOrgaComputed } from "./src/components/composable/useOrgaComputed.ts";
136
136
  export { useSeoTitleUrl } from "./src/components/composable/route/useSeoTitleUrl.ts";
137
137
  export { useSeasonsManagement } from "./src/components/composable/useSeasonsManagement.ts";
138
+ export { useTranslation } from "./src/components/composable/useTranslation.ts";
138
139
 
139
140
  //helper
140
141
  import domHelper from "./src/helper/domHelper.ts";
@@ -161,6 +162,7 @@ import classicApi from "./src/api/classicApi.ts";
161
162
 
162
163
  // API
163
164
  export { emissionApi } from "./src/api/emissionApi.ts";
165
+ export { transcriptionApi, type TranslationData, TranslationState } from "./src/api/transcriptionApi.ts";
164
166
  export * from "./src/api/groupsApi.ts";
165
167
  export { organisationApi } from "./src/api/organisationApi.ts";
166
168
  export { playlistApi } from "./src/api/playlistApi.ts";
@@ -168,9 +170,23 @@ export { podcastApi, PodcastSort, type PodcastSearchOptions } from "./src/api/po
168
170
 
169
171
  // Types
170
172
  export { type Emission, SeasonMode, emptyEmissionData } from "./src/stores/class/general/emission.ts";
173
+ export { type Organisation, type OrganisationAttributes, emptyOrganisationData, emptyOrgaData } from "./src/stores/class/general/organisation.ts";
171
174
  export { type Podcast, type PodcastAvailability, PodcastType } from "./src/stores/class/general/podcast.ts";
172
175
  export { type Playlist, type PlaylistRule } from "./src/stores/class/general/playlist.ts";
173
176
  export { type Annotations } from "./src/stores/class/general";
177
+ export {
178
+ CreateTranslation,
179
+ type TranslationConfiguration,
180
+ defaultTranslationConfig,
181
+ defaultModifyPodcastConfig,
182
+ defaultTtsParams,
183
+ type ModifyPodcastConfig,
184
+ ModifyPodcastEnum,
185
+ type TranscriptParams,
186
+ type TtsParams,
187
+ type ProviderTts,
188
+ type Voice
189
+ } from "./src/stores/class/transcript/transcriptParams.ts";
174
190
 
175
191
  //Icons
176
192
  export const getAmazonMusicIcon = () => import("./src/components/icons/AmazonMusicIcon.vue");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "41.8.4",
3
+ "version": "41.9.1",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -12,7 +12,7 @@
12
12
  "bundle": "vite-bundle-visualizer",
13
13
  "proxy_authentifié": "node proxy.ts",
14
14
  "proxy_non_authentifié": "node proxy.ts false",
15
- "lint": "eslint --fix src",
15
+ "lint": "eslint --fix --max-warnings 0",
16
16
  "stylelint": "stylelint **/*.{scss,vue} --fix",
17
17
  "sonar": "sonar -Dsonar.host.url=http://localhost:9000"
18
18
  },
@@ -95,5 +95,17 @@
95
95
  },
96
96
  "peerDependencies": {
97
97
  "pinia": ">=2.3.0"
98
+ },
99
+ "exports": {
100
+ ".": {
101
+ "types": "./index.d.ts",
102
+ "default": "./index.ts"
103
+ },
104
+ "./src/*": "./src/*",
105
+ "./tests/*": "./tests/*",
106
+ "./eslint-config": {
107
+ "types": "./eslint-config.d.ts",
108
+ "default": "./eslint-config.mjs"
109
+ }
98
110
  }
99
111
  }
@@ -1,4 +1,6 @@
1
- import { classicApi, ModuleApi } from ".yalc/@saooti/octopus-sdk";
1
+ import { ModifyPodcastConfig } from "../stores/class/transcript/transcriptParams";
2
+ import { ModuleApi } from "./apiConnection";
3
+ import classicApi from "./classicApi";
2
4
 
3
5
  /** State of the translation */
4
6
  export enum TranslationState {
@@ -30,6 +32,11 @@ export interface PodcastTranslationData {
30
32
  translations: Array<TranslationData>;
31
33
  }
32
34
 
35
+ interface TranslationProgress {
36
+ started: boolean;
37
+ percent: number;
38
+ }
39
+
33
40
  /**
34
41
  * Returns the translations defined on the podcast
35
42
  * @param podcastId ID of the podcast
@@ -38,10 +45,129 @@ export interface PodcastTranslationData {
38
45
  async function getTranslations(podcastId: number): Promise<PodcastTranslationData> {
39
46
  return classicApi.fetchData<PodcastTranslationData>({
40
47
  api: ModuleApi.SPEECHTOTEXT,
41
- path: 'transcription/' + podcastId
48
+ path: `transcription/${podcastId}/languages`
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Returns the translation in a given language for the podcast
54
+ * @param podcastId ID of the podcast
55
+ * @param language The target language
56
+ * @param mayCreate *(optional)* If set to true, request creation if not available
57
+ * @returns The transcription
58
+ */
59
+ async function getTranslation(podcastId: number, language: string, mayCreate?: boolean): Promise<string> {
60
+ let path = `transcription/${podcastId}/languages/${language}/srt`;
61
+ if (mayCreate !== undefined) {
62
+ path = `${path}?mayCreateIfNotExists=${mayCreate}`;
63
+ }
64
+
65
+ let found = false;
66
+ let timeout = 1000;
67
+ const timeoutStep = 100;
68
+ while (!found) {
69
+ const result = await classicApi.fetchData<string|TranslationProgress>({
70
+ api: ModuleApi.SPEECHTOTEXT,
71
+ path
72
+ });
73
+
74
+ // Stop when we get the proper result
75
+ if (typeof result === 'string') {
76
+ found = true;
77
+ return result;
78
+ }
79
+
80
+ // Wait some time before retrying
81
+ await new Promise((resolve) => setTimeout(resolve, timeout));
82
+ timeout += timeoutStep;
83
+
84
+ if (timeout >= 60 * 1000) {
85
+ break;
86
+ }
87
+ }
88
+
89
+ throw new Error('Timeout lors de la récupération de la transcription');
90
+ }
91
+
92
+ /**
93
+ * Get raw transcription for the given podcast
94
+ * @param podcastId ID of the podcast
95
+ * @returns The raw transcript
96
+ */
97
+ async function getRawTranscription(podcastId: number): Promise<string> {
98
+ return classicApi.fetchData({
99
+ api: ModuleApi.SPEECHTOTEXT,
100
+ path: `transcription/text/${podcastId}`,
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Generate the transcription on the given podcast
106
+ * @param podcastId ID of the podcast on which to do the transcription
107
+ * @param langauge Target language. Should be the native language of the podcast
108
+ * @param params Transcription parameters
109
+ */
110
+ async function generateTranscription(podcastId: number, language: string, params: ModifyPodcastConfig): Promise<void> {
111
+ await classicApi.putData({
112
+ api: ModuleApi.SPEECHTOTEXT,
113
+ path: `convert/${language}/${podcastId}`,
114
+ dataToSend: params
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Regenerate the transcription on the given podcast
120
+ * Currently does the same as `generateTranscription`, but should be used when
121
+ * relevant in case of future evolutions.
122
+ * @param podcastId ID of the podcast on which to do the transcription
123
+ * @param langauge Target language. Should be the native language of the podcast
124
+ * @param params Transcription parameters
125
+ */
126
+ async function regenerateTranscription(podcastId: number, language: string, params: ModifyPodcastConfig): Promise<void> {
127
+ await classicApi.putData({
128
+ api: ModuleApi.SPEECHTOTEXT,
129
+ path: `regenerate/${language}/${podcastId}`,
130
+ dataToSend: params
131
+ });
132
+ }
133
+
134
+ /**
135
+ * Change the visibility of the podcast
136
+ * @param podcastId ID of the podcast for which to change the visibility
137
+ * @param visibility New visibility state
138
+ */
139
+ async function changeTranscriptionVisibility(podcastId: number, visibility: boolean): Promise<void> {
140
+ await classicApi.putData({
141
+ api: 11,
142
+ path: "visibility/" + podcastId,
143
+ parameters: {
144
+ visibility
145
+ }
146
+ });
147
+ }
148
+
149
+ /**
150
+ * Update the transcription of a podcast
151
+ * @param podcastId ID of the podcast
152
+ * @param transcript The new transcript
153
+ */
154
+ async function updateTranscription(podcastId: number, transcript: string): Promise<void> {
155
+ await classicApi.postData({
156
+ api: ModuleApi.SPEECHTOTEXT,
157
+ path: "update/" + podcastId,
158
+ dataToSend: {
159
+ file: new File([transcript], "file")
160
+ },
161
+ contentType: "formData"
42
162
  });
43
163
  }
44
164
 
45
165
  export const transcriptionApi = {
46
- getTranslations
166
+ changeTranscriptionVisibility,
167
+ getTranslations,
168
+ getTranslation,
169
+ getRawTranscription,
170
+ generateTranscription,
171
+ regenerateTranscription,
172
+ updateTranscription
47
173
  };
@@ -20,6 +20,7 @@ export const usePlayerDisplayTime = ()=>{
20
20
  }
21
21
  return playerStore.totalTime;
22
22
  });
23
+
23
24
  const transcriptText = computed(() => {
24
25
  return playerStore.transcriptText;
25
26
  });
@@ -2,45 +2,74 @@ import { usePlayerStore } from "../../../stores/PlayerStore";
2
2
  import { useVastStore } from "../../../stores/VastStore";
3
3
  import classicApi from "../../../api/classicApi";
4
4
  import { AdserverOtherEmission } from "@/stores/class/adserver/adserverOtherEmission";
5
+ import { useTranslation } from "../useTranslation";
6
+ import { transcriptionApi } from "../../../api/transcriptionApi";
7
+ import { ref } from "vue";
8
+
9
+ /** Contains the language of the transcript being generated */
10
+ const generatingTranscriptLanguage = ref<string|null>(null);
11
+
5
12
  export const usePlayerTranscript = ()=>{
6
13
 
7
14
  const playerStore = usePlayerStore();
8
15
  const vastStore = useVastStore();
16
+ const { getMostRelevantLanguage } = useTranslation();
9
17
 
10
18
  async function checkDelaytWithStitching(){
11
19
  playerStore.playerUpdateDelayStitching(0);
12
- if(vastStore.useVastPlayerPodcast){return;}
20
+ if(vastStore.useVastPlayerPodcast){
21
+ return;
22
+ }
23
+
13
24
  const audioPlayer = document.querySelector("#audio-player") as HTMLAudioElement;
14
25
  if (!playerStore.playerTranscript || !audioPlayer || !playerStore.playerPodcast ||
15
- audioPlayer.duration <= playerStore.playerPodcast.duration / 1000 + 5)
16
- {
26
+ audioPlayer.duration <= playerStore.playerPodcast.duration / 1000 + 5) {
17
27
  return;
18
28
  }
29
+
19
30
  const adserverConfig = await classicApi.fetchData<AdserverOtherEmission>({
20
31
  api:0,
21
32
  path:`ad/test/podcast/${playerStore.playerPodcast.podcastId}`,
22
33
  isNotAuth:true
23
34
  });
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{
35
+
36
+ // In case of midroll ads, we can't properly display transcription, so
37
+ // we disable it.
38
+ const hasOtherThanPreOrPost = adserverConfig.config.doublets
39
+ .filter(doublet => !(["pre", "post"].includes(doublet.timing.insertion)))
40
+ .length > 0;
41
+ if (hasOtherThanPreOrPost) {
42
+ console.warn("This episode's ad settings doesn't allow for transcription");
30
43
  playerStore.playerUpdateChaptering();
31
44
  playerStore.playerUpdateTranscript();
45
+ return;
46
+ }
47
+
48
+ // In case of preroll ads, delay start of transcription
49
+ const hasPre = adserverConfig.config.doublets
50
+ .filter(doublet => doublet.timing.insertion === "pre")
51
+ .length > 0;
52
+ if(hasPre) {
53
+ // Since we have the expected time (from playerStore) and effective time
54
+ // (from audioPlayer, which include ads), we just delay by the delta
55
+ playerStore.playerUpdateDelayStitching( audioPlayer.duration - (playerStore.playerPodcast.duration / 1000));
32
56
  }
33
57
  }
34
58
 
35
59
  async function getTranscription(): Promise<void> {
60
+ generatingTranscriptLanguage.value = null;
36
61
  if (!playerStore.playerPodcast) {
37
62
  playerStore.playerUpdateTranscript();
38
63
  return;
39
64
  }
40
- const result = await classicApi.fetchData<string>({
41
- api:11,
42
- path:`response/${playerStore.playerPodcast.podcastId}`,
43
- });
65
+
66
+ // Retrieve best language for transcription
67
+ const podcastId = playerStore.playerPodcast.podcastId;
68
+ const translationData = await transcriptionApi.getTranslations(podcastId);
69
+ const { ready, available } = await getMostRelevantLanguage(translationData);
70
+
71
+ // Retrieve transcription
72
+ const result = await transcriptionApi.getTranslation(podcastId, ready);
44
73
 
45
74
  const arrayTranscript = parseSrt(result);
46
75
  const actualText =
@@ -53,6 +82,26 @@ export const usePlayerTranscript = ()=>{
53
82
  actualText: actualText,
54
83
  value: arrayTranscript
55
84
  });
85
+
86
+ if (available !== undefined && ready !== available) {
87
+ generatingTranscriptLanguage.value = available;
88
+
89
+ // If there's a better language available, trigger its generation
90
+ const result = await transcriptionApi.getTranslation(podcastId, available, true);
91
+
92
+ const arrayTranscript = parseSrt(result);
93
+ const actualText =
94
+ arrayTranscript?.[0]?.startTime === 0 ? arrayTranscript[0].text : "";
95
+ if(!arrayTranscript){
96
+ return;
97
+ }
98
+ playerStore.playerUpdateTranscript({
99
+ actual: playerStore.playerTranscript?.actual,
100
+ actualText: actualText,
101
+ value: arrayTranscript
102
+ });
103
+ generatingTranscriptLanguage.value = null;
104
+ }
56
105
  }
57
106
 
58
107
  function parseSrt(transcript: string) {
@@ -110,11 +159,11 @@ export const usePlayerTranscript = ()=>{
110
159
  }
111
160
  }
112
161
 
113
-
114
- return {
162
+ return {
115
163
  checkDelaytWithStitching,
116
164
  getTranscription,
117
165
  onTimeUpdateTranscript,
118
- onSeekedTranscript
119
- }
166
+ onSeekedTranscript,
167
+ generatingTranscriptLanguage
168
+ }
120
169
  }
@@ -1,18 +1,17 @@
1
1
  import { Conference } from '@/stores/class/conference/conference';
2
2
  import { Podcast } from '@/stores/class/general/podcast';
3
- import dayjs from 'dayjs';
4
- import duration from "dayjs/plugin/duration";
5
- dayjs.extend(duration);
6
3
  // @ts-expect-error Bibliothèque non typée
7
4
  import humanizeDuration from "humanize-duration";
8
5
  import {computed, Ref} from 'vue';
9
6
  import { useI18n } from 'vue-i18n';
10
7
  import {useOrgaComputed} from "../useOrgaComputed"
11
8
  import { state } from '../../../stores/ParamSdkStore';
9
+ import { useDayjs } from '../useDayjs';
12
10
 
13
11
  export const usePodcastView = (podcast: Ref<Podcast|undefined>, podcastConference: Ref<Conference|undefined>)=>{
14
12
 
15
13
  const {locale} = useI18n();
14
+ const { dayjs } = useDayjs();
16
15
 
17
16
  const {isEditRights, isPodcastmaker} = useOrgaComputed();
18
17
 
@@ -1,4 +1,4 @@
1
- import { OrganisationAttributes } from "@/stores/class/general/organisation";
1
+ import { OrganisationAttributes } from "../../../stores/class/general/organisation";
2
2
  import { RouteLocationAsRelativeTyped, useRouter } from "vue-router";
3
3
  import { useSeoTitleUrl } from "../route/useSeoTitleUrl";
4
4
 
@@ -14,7 +14,7 @@ import RadioFranceIcon from "../../icons/RadioFranceIcon.vue";
14
14
  import YoutubeIcon from "vue-material-design-icons/Youtube.vue";
15
15
  import SpotifyIcon from "vue-material-design-icons/Spotify.vue";
16
16
  import { Annotations } from "@/stores/class/general";
17
- import { computed, type Component } from "vue";
17
+ import { computed, markRaw, type Component } from "vue";
18
18
 
19
19
  export enum SharePlatformName {
20
20
  APPLE = "applePodcast",
@@ -55,77 +55,77 @@ export const useSharePlatforms = () => {
55
55
  const platforms = computed((): Array<SharePlatform> => {
56
56
  return [{
57
57
  name: SharePlatformName.APPLE,
58
- icon: ApplePodcastIcon,
58
+ icon: markRaw(ApplePodcastIcon),
59
59
  title: "Apple Podcast | iTunes",
60
60
  color:"#aa1dd3"
61
61
  }, {
62
62
  name: SharePlatformName.DEEZER,
63
- icon: DeezerIcon,
63
+ icon: markRaw(DeezerIcon),
64
64
  title: "Deezer",
65
65
  color:"#a238ff",
66
66
  }, {
67
67
  name: SharePlatformName.SPOTIFY,
68
- icon: SpotifyIcon,
68
+ icon: markRaw(SpotifyIcon),
69
69
  title: "Spotify",
70
70
  color: "#1ed760",
71
71
  }, {
72
72
  name: SharePlatformName.AMAZON,
73
- icon: AmazonMusicIcon,
73
+ icon: markRaw(AmazonMusicIcon),
74
74
  title: "Amazon Music",
75
75
  color: "#0c6cb3",
76
76
  }, {
77
77
  name: SharePlatformName.I_HEART,
78
- icon: IHeartIcon,
78
+ icon: markRaw(IHeartIcon),
79
79
  title: "iHeart",
80
80
  color:"#e11b22"
81
81
  }, {
82
82
  name: SharePlatformName.PLAYER_FM,
83
- icon: PlayerFmIcon,
83
+ icon: markRaw(PlayerFmIcon),
84
84
  title: "Player FM",
85
85
  color:"#bb202a"
86
86
  }, {
87
87
  name: SharePlatformName.POCKET_CASTS,
88
- icon: PocketCastIcon,
88
+ icon: markRaw(PocketCastIcon),
89
89
  title: "Pocket Casts",
90
90
  color:"#f43e37"
91
91
  }, {
92
92
  name: SharePlatformName.PODCAST_ADDICT,
93
- icon: PodcastAddictIcon,
93
+ icon: markRaw(PodcastAddictIcon),
94
94
  title: "Podcast Addict",
95
95
  color:"#f4842d"
96
96
  }, {
97
97
  name: SharePlatformName.RADIOLINE,
98
- icon: RadiolineIcon,
98
+ icon: markRaw(RadiolineIcon),
99
99
  title: "Radioline",
100
100
  color:"#1678bd"
101
101
  }, {
102
102
  name: SharePlatformName.TUNE_IN,
103
- icon: TuninIcon,
103
+ icon: markRaw(TuninIcon),
104
104
  title: "TuneIn",
105
105
  color:"#36b4a7"
106
106
  }, {
107
107
  name: SharePlatformName.YOUTUBE,
108
- icon: YoutubeIcon,
108
+ icon: markRaw(YoutubeIcon),
109
109
  title: "YouTube Music",
110
110
  color: "#fe0000",
111
111
  }, {
112
112
  name: SharePlatformName.CASTBOX,
113
- icon: CastboxIcon,
113
+ icon: markRaw(CastboxIcon),
114
114
  title: "Castbox",
115
115
  color: "#fe6222",
116
116
  }, {
117
117
  name: SharePlatformName.PODBEAN,
118
- icon: PodbeanIcon,
118
+ icon: markRaw(PodbeanIcon),
119
119
  title: "PodBean",
120
120
  color: "#428200",
121
121
  }, {
122
122
  name: SharePlatformName.PODCAST_REPUBLIC,
123
- icon: PodcastRepublicIcon,
123
+ icon: markRaw(PodcastRepublicIcon),
124
124
  title: "Podcast Republic",
125
125
  color: "#5c85dd",
126
126
  }, {
127
127
  name: SharePlatformName.RADIO_FRANCE,
128
- icon: RadioFranceIcon,
128
+ icon: markRaw(RadioFranceIcon),
129
129
  title: "Radio France",
130
130
  color: "#a90041",
131
131
  }];