@saooti/octopus-sdk 41.8.2 → 41.8.4

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/CHANGELOG.md CHANGED
@@ -1,10 +1,24 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 41.8.4 (24/03/2026)
4
+
5
+ **Fix**
6
+
7
+ - **14291** - Correction vérification sur droits de transcription
8
+
9
+ ## 41.8.3 (23/03/2026)
10
+
11
+ **Fix**
12
+
13
+ - **14083** - Correction affichage saisons sur page émission s'il y a un creux
14
+ (par exemple saisons 2, 3, et pas de 1)
15
+ - **14373** - Correction taille icone Radio France
16
+
3
17
  ## 41.8.2 (23/03/2026)
4
18
 
5
19
  **Fix**
6
20
 
7
- - **14083** Ajustements système de saison
21
+ - **14083** - Ajustements système de saison
8
22
  - Le bon dernier épisode est récupéré sur les pages d'émissions
9
23
  - Les saisons sont correctement énumérées sur les pages d'émissions
10
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "41.8.2",
3
+ "version": "41.8.4",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -0,0 +1,47 @@
1
+ import { classicApi, ModuleApi } from ".yalc/@saooti/octopus-sdk";
2
+
3
+ /** State of the translation */
4
+ export enum TranslationState {
5
+ /** Translation in progress */
6
+ TRANSLATING = 'TRANSLATING',
7
+ /** Translation is finished */
8
+ FINISHED = 'FINISHED',
9
+ /** Translation has failed */
10
+ FAILED = 'FAILED',
11
+ /** AI limit has exceeded during translation */
12
+ AI_LIMIT_EXCEEDED = 'AI_LIMIT_EXCEEDED'
13
+ }
14
+
15
+ /** Data for one translation */
16
+ export interface TranslationData {
17
+ /** Language of translation */
18
+ language: string;
19
+ /** State of the translation */
20
+ state: TranslationState;
21
+ }
22
+
23
+ /** Translation data for one podcast. Contains data of all generated translations. */
24
+ export interface PodcastTranslationData {
25
+ /** ID of the podcast */
26
+ podcastId: number;
27
+ /** Native language of the podcast */
28
+ nativeLanguage: string;
29
+ /** Translation data */
30
+ translations: Array<TranslationData>;
31
+ }
32
+
33
+ /**
34
+ * Returns the translations defined on the podcast
35
+ * @param podcastId ID of the podcast
36
+ * @returns Promise containing the translation data for the podcast
37
+ */
38
+ async function getTranslations(podcastId: number): Promise<PodcastTranslationData> {
39
+ return classicApi.fetchData<PodcastTranslationData>({
40
+ api: ModuleApi.SPEECHTOTEXT,
41
+ path: 'transcription/' + podcastId
42
+ });
43
+ }
44
+
45
+ export const transcriptionApi = {
46
+ getTranslations
47
+ };
@@ -66,7 +66,7 @@ export const useRights = () => {
66
66
  }
67
67
 
68
68
  function canDuplicatePodcast(): boolean {
69
- // Same as creationm but notably without PODCAST_CRUD and
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
- return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION');
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 {
@@ -41,11 +41,11 @@
41
41
  v-model:active-tab="activeSeasonTab"
42
42
  :tab-number="emission.seasons.length"
43
43
  >
44
- <template v-for="season in emission.seasons" #[tabNameSlot(season)]>
44
+ <template v-for="(season, i) in emission.seasons" #[tabNameSlot(i)]>
45
45
  {{ $t('Podcast - Season N', { season }) }}
46
46
  </template>
47
47
 
48
- <template v-for="season in emission.seasons" #[tabContentSlot(season)] :key="season">
48
+ <template v-for="(season, i) in emission.seasons" #[tabContentSlot(i)] :key="season">
49
49
  <PodcastList
50
50
  class="flex-grow-1"
51
51
  :first="dfirst"
@@ -137,7 +137,7 @@ const showSeasons = computed(() => {
137
137
  return props.emission !== undefined && areSeasonsEnabled(props.emission) && (props.emission.seasons?.length ?? 0) > 0;
138
138
  });
139
139
 
140
- const activeSeasonTab = ref(showSeasons.value ? (getMaxSeason(props.emission) - 1) : 0);
140
+ const activeSeasonTab = ref(showSeasons.value ? props.emission.seasons?.indexOf(getMaxSeason(props.emission)) ?? 0 : 0);
141
141
 
142
142
  const sort = computed((): PodcastSort => {
143
143
  if(showSeasons.value === true) {
@@ -166,11 +166,11 @@ function fetch(podcasts: Array<Podcast>, season?: number): void {
166
166
  }
167
167
 
168
168
  /** Name of the slot for the tab's title */
169
- function tabNameSlot(season: number): string {
170
- return `${season - 1}`;
169
+ function tabNameSlot(index: number): string {
170
+ return `${index}`;
171
171
  }
172
172
  /** Name of the slot for the tab's content */
173
- function tabContentSlot(season: number): string {
174
- return `tab${season - 1}`;
173
+ function tabContentSlot(index: number): string {
174
+ return `tab${index}`;
175
175
  }
176
176
  </script>
@@ -15,25 +15,25 @@
15
15
  fill-rule="evenodd"
16
16
  clip-rule="evenodd"
17
17
  d="M37.0892 33.8774C37.0892 35.7523 36.5961 37.5174 35.7265 39.0643H50.1862C50.5743 37.3938 50.7809 35.6586 50.7809 33.8774C50.7809 23.6473 44.0269 14.9123 34.5121 11.4499V0.596397H21.9553V23.3202C23.0498 22.9984 24.2131 22.8248 25.419 22.8248C31.8644 22.8248 37.0892 27.7733 37.0892 33.8774Z"
18
- fill="url(#paint0_linear_75_103)"
18
+ :fill="'url(#' + uid + 'p0)'"
19
19
  />
20
20
  <path
21
21
  fill-rule="evenodd"
22
22
  clip-rule="evenodd"
23
23
  d="M29.5724 44.3362C28.2958 44.7861 26.9145 45.0321 25.4717 45.0321C18.9987 45.0321 13.7507 40.0835 13.7507 33.9785C13.7507 32.2762 14.1593 30.664 14.8881 29.2236H0.500687C0.173332 30.7608 0 32.3507 0 33.9785C0 47.2455 11.404 58 25.4717 58C26.8678 58 28.2374 57.8924 29.5724 57.6889V44.3362Z"
24
- fill="url(#paint1_linear_75_103)"
24
+ :fill="'url(#' + uid + 'p1)'"
25
25
  />
26
26
  <path
27
27
  fill-rule="evenodd"
28
28
  clip-rule="evenodd"
29
29
  d="M37.1502 33.909C37.1502 35.7895 36.6555 37.5598 35.7831 39.1113H50.2891C50.6784 37.4358 50.8857 35.6954 50.8857 33.909C50.8857 23.6484 44.1101 14.8873 34.5649 11.4146V0.528656H21.9678V23.3202C23.0658 22.9975 24.2329 22.8234 25.4426 22.8234C31.9087 22.8234 37.1502 27.7867 37.1502 33.909Z"
30
- fill="url(#paint2_linear_75_103)"
30
+ :fill="'url(#' + uid + 'p2)'"
31
31
  />
32
32
  <path
33
33
  fill-rule="evenodd"
34
34
  clip-rule="evenodd"
35
35
  d="M29.5386 44.2967C28.2634 44.7479 26.8837 44.9945 25.4426 44.9945C18.977 44.9945 13.735 40.0316 13.735 33.9089C13.735 32.2017 14.1431 30.5848 14.8711 29.1403H0.500114C0.173133 30.682 0 32.2764 0 33.9089C0 47.2143 11.3909 58 25.4426 58C26.8371 58 28.2051 57.8921 29.5386 57.688V44.2967Z"
36
- fill="url(#paint3_linear_75_103)"
36
+ :fill="'url(#' + uid + 'p3)'"
37
37
  />
38
38
  <path
39
39
  fill-rule="evenodd"
@@ -98,7 +98,7 @@
98
98
  />
99
99
  <defs>
100
100
  <linearGradient
101
- id="paint0_linear_75_103"
101
+ :id="uid + 'p0'"
102
102
  x1="37.483"
103
103
  y1="3.20798"
104
104
  x2="21.3118"
@@ -112,7 +112,7 @@
112
112
  <stop offset="1" stop-color="#FD0323" />
113
113
  </linearGradient>
114
114
  <linearGradient
115
- id="paint1_linear_75_103"
115
+ :id="uid + 'p1'"
116
116
  x1="8.26123"
117
117
  y1="34.1806"
118
118
  x2="25.2003"
@@ -124,7 +124,7 @@
124
124
  <stop offset="1" stop-color="#FF0101" />
125
125
  </linearGradient>
126
126
  <linearGradient
127
- id="paint2_linear_75_103"
127
+ :id="uid + 'p2'"
128
128
  x1="37.5457"
129
129
  y1="3.14803"
130
130
  x2="21.3224"
@@ -138,7 +138,7 @@
138
138
  <stop offset="1" stop-color="#FD0323" />
139
139
  </linearGradient>
140
140
  <linearGradient
141
- id="paint3_linear_75_103"
141
+ :id="uid + 'p3'"
142
142
  x1="8.25178"
143
143
  y1="34.035"
144
144
  x2="25.2587"
@@ -155,8 +155,12 @@
155
155
  </template>
156
156
 
157
157
  <script setup lang="ts">
158
+ import { useId } from 'vue'
159
+
160
+ const uid = useId()
161
+
158
162
  defineProps({
159
- size: { default: 50, type: Number },
163
+ size: { default: 24, type: Number },
160
164
  title: { default: undefined, type: String },
161
165
  })
162
166
  </script>
@@ -245,6 +245,31 @@ describe('useRights', () => {
245
245
  });
246
246
  });
247
247
 
248
+ describe('canEditTranscript', () => {
249
+ const ownPodcast = { podcastId: 1, createdByUserId: 'test-user-123' } as Podcast;
250
+ const otherPodcast = { podcastId: 2, createdByUserId: 'other-user' } as Podcast;
251
+
252
+ ['ADMIN', 'ORGANISATION', 'PRODUCTION'].forEach(role => {
253
+ it(`allows ${role} to edit transcript of any podcast`, async () => {
254
+ await setup([role]);
255
+ expect(useRights().canEditTranscript(otherPodcast)).toBe(true);
256
+ });
257
+ });
258
+
259
+ ['RESTRICTED_PRODUCTION', 'RESTRICTED_ANIMATION'].forEach(role => {
260
+ it(`${role} can only edit transcript of own podcast`, async () => {
261
+ await setup([role]);
262
+ expect(useRights().canEditTranscript(ownPodcast)).toBe(true);
263
+ expect(useRights().canEditTranscript(otherPodcast)).toBe(false);
264
+ });
265
+ });
266
+
267
+ it('denies unrelated roles', async () => {
268
+ await setup(['PODCAST_CRUD']);
269
+ expect(useRights().canEditTranscript(ownPodcast)).toBe(false);
270
+ });
271
+ });
272
+
248
273
  describe('Other permissions', () => {
249
274
  describe('canEditCodeInsertPlayer', () => {
250
275
  ['ADMIN', 'ORGANISATION'].forEach(role => {