@saooti/octopus-sdk 41.8.3 → 41.9.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.
- package/.claude/settings.local.json +2 -1
- package/CHANGELOG.md +47 -0
- package/eslint-config.d.ts +3 -0
- package/eslint-config.mjs +43 -0
- package/eslint.config.mjs +2 -43
- package/index.ts +15 -0
- package/package.json +14 -2
- package/src/api/transcriptionApi.ts +107 -0
- package/src/components/composable/player/usePlayerDisplayTime.ts +1 -0
- package/src/components/composable/player/usePlayerTranscript.ts +66 -17
- package/src/components/composable/share/useSharePath.ts +1 -1
- package/src/components/composable/share/useSharePlatforms.ts +16 -16
- package/src/components/composable/useRights.ts +11 -3
- package/src/components/composable/useTranslation.ts +174 -0
- package/src/components/display/comments/CommentName.vue +1 -0
- package/src/components/display/comments/modal/CheckIdentityModal.vue +1 -0
- package/src/components/display/comments/modal/ReportAbuseModal.vue +1 -0
- package/src/components/display/emission/EmissionInlineList.vue +7 -9
- package/src/components/display/filter/ProductorSearch.vue +1 -1
- package/src/components/display/list/SwiperList.vue +1 -0
- package/src/components/display/organisation/OrganisationChooser.vue +1 -1
- package/src/components/display/organisation/OrganisationChooserLight.vue +1 -1
- package/src/components/display/participant/ParticipantInlineList.vue +14 -13
- package/src/components/display/podcasts/PodcastItemInfo.vue +94 -58
- package/src/components/display/podcasts/PodcastModuleBox.vue +29 -3
- package/src/components/display/podcasts/PodcastRawTranscript.vue +155 -81
- package/src/components/display/sharing/SharePlayer.vue +1 -1
- package/src/components/form/ClassicDatePicker.vue +8 -3
- package/src/components/form/ClassicInputText.vue +1 -1
- package/src/components/form/ClassicLoading.vue +6 -2
- package/src/components/form/ClassicSelect.vue +2 -2
- package/src/components/misc/ClassicSpinner.vue +18 -4
- package/src/components/misc/FooterSection.vue +1 -1
- package/src/components/misc/player/PlayerComponent.vue +1 -1
- package/src/components/misc/player/PlayerLarge.vue +14 -0
- package/src/components/pages/EmissionPage.vue +16 -2
- package/src/components/pages/LivesPage.vue +1 -1
- package/src/helper/language.ts +38 -0
- package/src/locale/de.json +3 -1
- package/src/locale/en.json +3 -1
- package/src/locale/es.json +3 -1
- package/src/locale/fr.json +4 -1
- package/src/locale/it.json +3 -1
- package/src/locale/sl.json +3 -1
- package/src/main.ts +3 -29
- package/src/stores/AuthStore.ts +1 -1
- package/src/stores/ParamSdkStore.ts +10 -3
- package/src/stores/class/general/emission.ts +4 -1
- package/src/stores/class/general/organisation.ts +10 -1
- package/src/stores/class/general/podcast.ts +6 -1
- package/src/stores/class/transcript/transcriptParams.ts +25 -0
- package/tests/api/transcriptionApi.spec.ts +58 -0
- package/tests/components/composable/player/usePlayerTranscript.spec.ts +238 -0
- package/tests/components/composable/useRights.spec.ts +25 -0
- package/tests/components/composable/useTranslation.spec.ts +144 -0
- package/tests/components/display/podcasts/PodcastFilterList.spec.ts +4 -4
- package/tests/components/display/podcasts/PodcastItemInfo.spec.ts +65 -0
- package/tests/components/display/podcasts/PodcastModuleBox.spec.ts +64 -0
- package/tests/components/display/podcasts/PodcastRawTranscript.spec.ts +253 -0
- package/tests/components/misc/player/PlayerLarge.spec.ts +71 -0
- package/tests/components/pages/EmissionPage.spec.ts +31 -0
- package/tests/helper/language.spec.ts +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 41.9.0 (31/03/2026)
|
|
4
|
+
|
|
5
|
+
**Features**
|
|
6
|
+
|
|
7
|
+
- **12535** - Implémentation de la traduction des sous-titres
|
|
8
|
+
- Mise en place de l'api `transcriptionApi` pour simplifier les appels
|
|
9
|
+
- Ajout du composable `useTranslation` pour regrouper les opérations relatives
|
|
10
|
+
aux traductions
|
|
11
|
+
- `PodcastRawTranscript` permet maintenant de choisir la langue de la
|
|
12
|
+
transcription
|
|
13
|
+
- La langue la plus pertinente pour l'utilisateur est sélectionnée par
|
|
14
|
+
défaut
|
|
15
|
+
- La transcription affichée dans le player correspond à la langue la plus
|
|
16
|
+
pertinente disponible
|
|
17
|
+
- **14357** - Affichage des sous-titres et résumés d'épisodes, et des
|
|
18
|
+
sous-titres des émissions
|
|
19
|
+
- Ajout de nouveaux paramètres pour contrôler cet affichage :
|
|
20
|
+
- `hideSubtitle` dans `emissionPage` & `podcastPage`, pour cacher les
|
|
21
|
+
sous-titres
|
|
22
|
+
- `descriptionOrSummary` dans `emissionPage`, pour choisir quel élément
|
|
23
|
+
afficher
|
|
24
|
+
- **14387** - Affichage du numéro de saison et d'épisode, ainsi que du type
|
|
25
|
+
d'épisode dans les `PodcastItemInfo`
|
|
26
|
+
- La configuration eslint est maintenant exportée, elle peut donc être intégrée
|
|
27
|
+
telle quelle dans les projets se basant sur le SDK
|
|
28
|
+
- Ajout d'options de configuration pour `ClassicLoading` et `ClassicSpinner`
|
|
29
|
+
|
|
30
|
+
**Fix**
|
|
31
|
+
|
|
32
|
+
- **14404** - Correction affichage de la transcription dans le cas de preroll/
|
|
33
|
+
postrolls multiples
|
|
34
|
+
- Correction `z-index` des boutons de `SwiperList`
|
|
35
|
+
- La propriété `focus` de `ClassicInputText` est à `false` par défaut
|
|
36
|
+
|
|
37
|
+
**Misc**
|
|
38
|
+
|
|
39
|
+
- Correction warning avec les icônes de `useSharePlatforms`
|
|
40
|
+
- Ajustement composants pour les podcastmakers
|
|
41
|
+
- Mise à jour des dépendances
|
|
42
|
+
- Les exports d'organisation viennent du SDK et non plus du package parent
|
|
43
|
+
|
|
44
|
+
## 41.8.4 (24/03/2026)
|
|
45
|
+
|
|
46
|
+
**Fix**
|
|
47
|
+
|
|
48
|
+
- **14291** - Correction vérification sur droits de transcription
|
|
49
|
+
|
|
3
50
|
## 41.8.3 (23/03/2026)
|
|
4
51
|
|
|
5
52
|
**Fix**
|
|
@@ -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
|
|
2
|
-
|
|
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
|
@@ -161,6 +161,7 @@ import classicApi from "./src/api/classicApi.ts";
|
|
|
161
161
|
|
|
162
162
|
// API
|
|
163
163
|
export { emissionApi } from "./src/api/emissionApi.ts";
|
|
164
|
+
export { transcriptionApi } from "./src/api/transcriptionApi.ts";
|
|
164
165
|
export * from "./src/api/groupsApi.ts";
|
|
165
166
|
export { organisationApi } from "./src/api/organisationApi.ts";
|
|
166
167
|
export { playlistApi } from "./src/api/playlistApi.ts";
|
|
@@ -168,9 +169,23 @@ export { podcastApi, PodcastSort, type PodcastSearchOptions } from "./src/api/po
|
|
|
168
169
|
|
|
169
170
|
// Types
|
|
170
171
|
export { type Emission, SeasonMode, emptyEmissionData } from "./src/stores/class/general/emission.ts";
|
|
172
|
+
export { type Organisation, type OrganisationAttributes, emptyOrganisationData, emptyOrgaData } from "./src/stores/class/general/organisation.ts";
|
|
171
173
|
export { type Podcast, type PodcastAvailability, PodcastType } from "./src/stores/class/general/podcast.ts";
|
|
172
174
|
export { type Playlist, type PlaylistRule } from "./src/stores/class/general/playlist.ts";
|
|
173
175
|
export { type Annotations } from "./src/stores/class/general";
|
|
176
|
+
export {
|
|
177
|
+
CreateTranslation,
|
|
178
|
+
type TranslationConfiguration,
|
|
179
|
+
defaultTranslationConfig,
|
|
180
|
+
defaultModifyPodcastConfig,
|
|
181
|
+
defaultTtsParams,
|
|
182
|
+
type ModifyPodcastConfig,
|
|
183
|
+
ModifyPodcastEnum,
|
|
184
|
+
type TranscriptParams,
|
|
185
|
+
type TtsParams,
|
|
186
|
+
type ProviderTts,
|
|
187
|
+
type Voice
|
|
188
|
+
} from "./src/stores/class/transcript/transcriptParams.ts";
|
|
174
189
|
|
|
175
190
|
//Icons
|
|
176
191
|
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.
|
|
3
|
+
"version": "41.9.0",
|
|
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
|
|
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
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ModuleApi } from "./apiConnection";
|
|
2
|
+
import classicApi from "./classicApi";
|
|
3
|
+
|
|
4
|
+
/** State of the translation */
|
|
5
|
+
export enum TranslationState {
|
|
6
|
+
/** Translation in progress */
|
|
7
|
+
TRANSLATING = 'TRANSLATING',
|
|
8
|
+
/** Translation is finished */
|
|
9
|
+
FINISHED = 'FINISHED',
|
|
10
|
+
/** Translation has failed */
|
|
11
|
+
FAILED = 'FAILED',
|
|
12
|
+
/** AI limit has exceeded during translation */
|
|
13
|
+
AI_LIMIT_EXCEEDED = 'AI_LIMIT_EXCEEDED'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Data for one translation */
|
|
17
|
+
export interface TranslationData {
|
|
18
|
+
/** Language of translation */
|
|
19
|
+
language: string;
|
|
20
|
+
/** State of the translation */
|
|
21
|
+
state: TranslationState;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Translation data for one podcast. Contains data of all generated translations. */
|
|
25
|
+
export interface PodcastTranslationData {
|
|
26
|
+
/** ID of the podcast */
|
|
27
|
+
podcastId: number;
|
|
28
|
+
/** Native language of the podcast */
|
|
29
|
+
nativeLanguage: string;
|
|
30
|
+
/** Translation data */
|
|
31
|
+
translations: Array<TranslationData>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface TranslationProgress {
|
|
35
|
+
started: boolean;
|
|
36
|
+
percent: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns the translations defined on the podcast
|
|
41
|
+
* @param podcastId ID of the podcast
|
|
42
|
+
* @returns Promise containing the translation data for the podcast
|
|
43
|
+
*/
|
|
44
|
+
async function getTranslations(podcastId: number): Promise<PodcastTranslationData> {
|
|
45
|
+
return classicApi.fetchData<PodcastTranslationData>({
|
|
46
|
+
api: ModuleApi.SPEECHTOTEXT,
|
|
47
|
+
path: `transcription/${podcastId}/languages`
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns the translation in a given language for the podcast
|
|
53
|
+
* @param podcastId ID of the podcast
|
|
54
|
+
* @param language The target language
|
|
55
|
+
* @param mayCreate *(optional)* If set to true, request creation if not available
|
|
56
|
+
* @returns The transcription
|
|
57
|
+
*/
|
|
58
|
+
async function getTranslation(podcastId: number, language: string, mayCreate?: boolean): Promise<string> {
|
|
59
|
+
let path = `transcription/${podcastId}/languages/${language}/srt`;
|
|
60
|
+
if (mayCreate !== undefined) {
|
|
61
|
+
path = `${path}?mayCreateIfNotExists=${mayCreate}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let found = false;
|
|
65
|
+
let timeout = 1000;
|
|
66
|
+
const timeoutStep = 100;
|
|
67
|
+
while (!found) {
|
|
68
|
+
const result = await classicApi.fetchData<string|TranslationProgress>({
|
|
69
|
+
api: ModuleApi.SPEECHTOTEXT,
|
|
70
|
+
path
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Stop when we get the proper result
|
|
74
|
+
if (typeof result === 'string') {
|
|
75
|
+
found = true;
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Wait some time before retrying
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
81
|
+
timeout += timeoutStep;
|
|
82
|
+
|
|
83
|
+
if (timeout >= 60 * 1000) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw new Error('Timeout lors de la récupération de la transcription');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get raw transcription for the given podcast
|
|
93
|
+
* @param podcastId ID of the podcast
|
|
94
|
+
* @returns The raw transcript
|
|
95
|
+
*/
|
|
96
|
+
async function getRawTranscription(podcastId: number): Promise<string> {
|
|
97
|
+
return classicApi.fetchData({
|
|
98
|
+
api: ModuleApi.SPEECHTOTEXT,
|
|
99
|
+
path: `transcription/text/${podcastId}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const transcriptionApi = {
|
|
104
|
+
getTranslations,
|
|
105
|
+
getTranslation,
|
|
106
|
+
getRawTranscription
|
|
107
|
+
};
|
|
@@ -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){
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { OrganisationAttributes } from "
|
|
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
|
}];
|
|
@@ -66,7 +66,7 @@ export const useRights = () => {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function canDuplicatePodcast(): boolean {
|
|
69
|
-
// Same as
|
|
69
|
+
// Same as creation but notably without PODCAST_CRUD and
|
|
70
70
|
// RESTRICTED_ANIMATION
|
|
71
71
|
return roleContainsAny(
|
|
72
72
|
'ADMIN',
|
|
@@ -149,8 +149,16 @@ export const useRights = () => {
|
|
|
149
149
|
return roleContainsAny('ADMIN', 'ORGANISATION');
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function canEditTranscript(): boolean {
|
|
153
|
-
|
|
152
|
+
function canEditTranscript(podcast: Podcast): boolean {
|
|
153
|
+
if(roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION')) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (roleContainsAny('RESTRICTED_PRODUCTION', 'RESTRICTED_ANIMATION')) {
|
|
158
|
+
return podcast.createdByUserId === authStore.authProfile?.userId;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return false;
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
function canSeeHistory(): boolean {
|