@saooti/octopus-sdk 41.10.4 → 41.10.6
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/BUILD_FIX.md +105 -0
- package/CHANGELOG.md +17 -1
- package/package.json +1 -1
- package/src/components/composable/player/usePlayerLogic.ts +32 -5
- package/src/components/display/emission/EmissionPlayerItem.vue +5 -0
- package/src/components/display/podcasts/PodcastFilterList.vue +8 -2
- package/src/stores/PlayerStore.ts +42 -19
- package/src/stores/class/general/organisation.ts +10 -1
- package/src/stores/class/securisation/privateOrganisation.ts +8 -2
package/BUILD_FIX.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Fixing `@` alias resolution when consumed by other projects
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`octopus-sdk` currently publishes raw TypeScript source. When a consumer (e.g. frontoffice)
|
|
6
|
+
bundles the SDK, it processes source files with its own Vite config, so `@/` imports resolve
|
|
7
|
+
against the consumer's `src/` instead of the SDK's. This affects both dev and production builds.
|
|
8
|
+
|
|
9
|
+
## Solution: build the SDK as a library
|
|
10
|
+
|
|
11
|
+
The SDK should be pre-built so all `@` aliases are resolved before any consumer sees the files.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Install `vite-plugin-dts`
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install -D vite-plugin-dts
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Update `vite.config.js`
|
|
24
|
+
|
|
25
|
+
Add a `lib` build alongside the existing app config. The alias resolution in the `resolve` block
|
|
26
|
+
ensures `@` is rewritten to relative paths in the output.
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import dts from 'vite-plugin-dts';
|
|
30
|
+
|
|
31
|
+
// In the build config:
|
|
32
|
+
build: {
|
|
33
|
+
lib: {
|
|
34
|
+
entry: path.resolve(__dirname, 'index.ts'),
|
|
35
|
+
formats: ['es'],
|
|
36
|
+
fileName: 'index',
|
|
37
|
+
},
|
|
38
|
+
outDir: 'dist',
|
|
39
|
+
rollupOptions: {
|
|
40
|
+
// Mark all dependencies as external so they are not bundled
|
|
41
|
+
external: (id) => !id.startsWith('.') && !path.isAbsolute(id),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
plugins: [
|
|
45
|
+
vue(),
|
|
46
|
+
dts({ rollupTypes: true }),
|
|
47
|
+
],
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run `npm run build` to produce `dist/index.js` and `dist/index.d.ts`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Update `package.json`
|
|
55
|
+
|
|
56
|
+
Point `exports` to the built output instead of raw source:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
"exports": {
|
|
60
|
+
".": {
|
|
61
|
+
"types": "./dist/index.d.ts",
|
|
62
|
+
"default": "./dist/index.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"files": ["dist"]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4. Development workflow
|
|
71
|
+
|
|
72
|
+
With a built package, `npm link` / `yalc link` alone is no longer enough — consumers need the
|
|
73
|
+
built output, not the source.
|
|
74
|
+
|
|
75
|
+
**Option A — watch build (recommended for active SDK development):**
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
# Terminal 1 — SDK
|
|
79
|
+
npm run build -- --watch
|
|
80
|
+
|
|
81
|
+
# Terminal 2 — frontoffice
|
|
82
|
+
npm run dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
frontoffice picks up changes via the symlink each time the SDK rebuild completes.
|
|
86
|
+
HMR in frontoffice will trigger on the rebuilt file, not on source edits directly.
|
|
87
|
+
|
|
88
|
+
**Option B — yalc push on change:**
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
# In octopus-sdk, after each change:
|
|
92
|
+
npm run build && yalc push
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
frontoffice receives the new build automatically if `yalc` is configured with `--watch`.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## What to revert in frontoffice
|
|
100
|
+
|
|
101
|
+
Once the SDK is built, remove from frontoffice's `vite.config.js`:
|
|
102
|
+
|
|
103
|
+
- `optimizeDeps.exclude: ['@saooti/octopus-sdk']` — no longer needed
|
|
104
|
+
- `server.watch.ignored: ['!**/node_modules/@saooti/octopus-sdk/**']` — no longer needed
|
|
105
|
+
- `server.fs.allow: ['../../../octopus-sdk']` — no longer needed (for npm link setups)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
## 41.10.
|
|
3
|
+
## 41.10.6 (27/05/2026)
|
|
4
|
+
|
|
5
|
+
**Fixes**
|
|
6
|
+
|
|
7
|
+
- **144228** - Correction lecture épisodes non publiés sur orga sécurisée
|
|
8
|
+
|
|
9
|
+
## 41.10.5 (26/05/2026)
|
|
10
|
+
|
|
11
|
+
**Features**
|
|
12
|
+
|
|
13
|
+
- Possibilité de désactiver l'affichage des podcasts dans `EmissionPlayerItem`
|
|
14
|
+
|
|
15
|
+
**Fixes**
|
|
16
|
+
|
|
17
|
+
- **14486** - Tri des saisons sur la page d'émission
|
|
18
|
+
|
|
19
|
+
## 41.10.4 (13/05/2026)
|
|
4
20
|
|
|
5
21
|
**Fixes**
|
|
6
22
|
|
package/package.json
CHANGED
|
@@ -12,9 +12,12 @@ import fetchHelper from "../../../helper/fetchHelper";
|
|
|
12
12
|
import dayjs from "dayjs";
|
|
13
13
|
import { FetchParam } from "@/stores/class/general/fetchParam";
|
|
14
14
|
import { podcastApi } from "../../../api/podcastApi";
|
|
15
|
+
import { Organisation } from "../../../stores/class/general/organisation";
|
|
16
|
+
import { organisationApi } from "../../../api/organisationApi";
|
|
17
|
+
import { OrganisationPrivacy } from "../../../stores/class/securisation/privateOrganisation";
|
|
15
18
|
|
|
16
19
|
export const usePlayerLogic = (forceHide: Ref<boolean, boolean>) => {
|
|
17
|
-
const hlsReady= ref(false);
|
|
20
|
+
const hlsReady = ref(false);
|
|
18
21
|
|
|
19
22
|
const { listenTime, onPlay, setDownloadId, onTimeUpdateProgress, playLive, endingLive, playRadio} = usePlayerLive(hlsReady);
|
|
20
23
|
const { contentEndedAdsLoader } = usePlayerStitching();
|
|
@@ -35,6 +38,10 @@ export const usePlayerLogic = (forceHide: Ref<boolean, boolean>) => {
|
|
|
35
38
|
|
|
36
39
|
watch(()=>getAudioUrl(), async () => {
|
|
37
40
|
playerError.value = false;
|
|
41
|
+
|
|
42
|
+
// In some cases, the audio does not go through normal download endpoint
|
|
43
|
+
// Mostly when the podcast is not available to users
|
|
44
|
+
let download = true;
|
|
38
45
|
if (
|
|
39
46
|
playerStore.playerMedia ||
|
|
40
47
|
!playerStore.playerPodcast ||
|
|
@@ -42,12 +49,32 @@ export const usePlayerLogic = (forceHide: Ref<boolean, boolean>) => {
|
|
|
42
49
|
!playerStore.playerPodcast.availability.visibility ||
|
|
43
50
|
listenError.value
|
|
44
51
|
) {
|
|
52
|
+
download = false;
|
|
53
|
+
|
|
54
|
+
// Not yet available podcasts in secured organisations go through
|
|
55
|
+
// download if possible (see #14228)
|
|
56
|
+
const podcast = playerStore.playerPodcast;
|
|
57
|
+
if (podcast && !podcast.availability.visibility) {
|
|
58
|
+
let organisation: Organisation|null = null;
|
|
59
|
+
if (podcast.organisation) {
|
|
60
|
+
organisation = podcast.organisation;
|
|
61
|
+
} else if ('organisationId' in podcast) {
|
|
62
|
+
// Handle case of missing organisation (for example for SimplifiedPodcast)
|
|
63
|
+
organisation = await organisationApi.get(podcast.organisationId as string);
|
|
64
|
+
}
|
|
65
|
+
if (organisation.privacy === OrganisationPrivacy.SECURED) {
|
|
66
|
+
download = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (download) {
|
|
72
|
+
const response = await podcastApi.downloadRegister(playerStore.playerPodcast.podcastId, getAudioUrlParameters());
|
|
73
|
+
setDownloadId(response.downloadId.toString());
|
|
74
|
+
audioUrlToPlay.value = response.location;
|
|
75
|
+
} else {
|
|
45
76
|
audioUrlToPlay.value = getAudioUrl();
|
|
46
|
-
return;
|
|
47
77
|
}
|
|
48
|
-
const response = await podcastApi.downloadRegister(playerStore.playerPodcast.podcastId, getAudioUrlParameters());
|
|
49
|
-
setDownloadId(response.downloadId.toString());
|
|
50
|
-
audioUrlToPlay.value = response.location;
|
|
51
78
|
});
|
|
52
79
|
|
|
53
80
|
watch(()=>playerStore.playerPodcast, async () => {
|
|
@@ -103,6 +103,7 @@ const PodcastPlayBasicButton = defineAsyncComponent(() => import("../podcasts/Po
|
|
|
103
103
|
//Props
|
|
104
104
|
const props = defineProps({
|
|
105
105
|
emission: { default: () => ({}), type: Object as () => Emission },
|
|
106
|
+
/** Number of podcasts to display (default: 2); if set to 0, no podcasts will be displayed */
|
|
106
107
|
nbPodcasts: { default: undefined, type: Number },
|
|
107
108
|
rubriqueName: { default: undefined, type: String },
|
|
108
109
|
})
|
|
@@ -125,6 +126,10 @@ onBeforeMount(()=>loadPodcasts())
|
|
|
125
126
|
|
|
126
127
|
//Methods
|
|
127
128
|
async function loadPodcasts(): Promise<void> {
|
|
129
|
+
if (props.nbPodcasts === 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
128
133
|
const nb = props.nbPodcasts ? props.nbPodcasts : 2;
|
|
129
134
|
const data = await classicApi.fetchData<ListClassicReturn<Podcast>>({
|
|
130
135
|
api: 0,
|
|
@@ -41,11 +41,15 @@
|
|
|
41
41
|
v-model:active-tab="activeSeasonTab"
|
|
42
42
|
:tab-number="emission.seasons.length"
|
|
43
43
|
>
|
|
44
|
-
<template v-for="(season, i) in
|
|
44
|
+
<template v-for="(season, i) in seasons" #[tabNameSlot(i)]>
|
|
45
45
|
{{ $t('Podcast - Season N', { season }) }}
|
|
46
46
|
</template>
|
|
47
47
|
|
|
48
|
-
<template
|
|
48
|
+
<template
|
|
49
|
+
v-for="(season, i) in seasons"
|
|
50
|
+
#[tabContentSlot(i)]
|
|
51
|
+
:key="season"
|
|
52
|
+
>
|
|
49
53
|
<PodcastList
|
|
50
54
|
class="flex-grow-1"
|
|
51
55
|
:first="dfirst"
|
|
@@ -137,6 +141,8 @@ const showSeasons = computed(() => {
|
|
|
137
141
|
return props.emission !== undefined && areSeasonsEnabled(props.emission) && (props.emission.seasons?.length ?? 0) > 0;
|
|
138
142
|
});
|
|
139
143
|
|
|
144
|
+
const seasons = computed((): Array<number> => [...props.emission.seasons].sort());
|
|
145
|
+
|
|
140
146
|
const activeSeasonTab = ref(showSeasons.value ? props.emission.seasons?.indexOf(getMaxSeason(props.emission)) ?? 0 : 0);
|
|
141
147
|
|
|
142
148
|
const sort = computed((): PodcastSort => {
|
|
@@ -11,6 +11,8 @@ import { Chaptering, ChapteringPercent } from "./class/chaptering/chaptering";
|
|
|
11
11
|
import classicApi from "../api/classicApi";
|
|
12
12
|
|
|
13
13
|
import { state as sdkParams } from "./ParamSdkStore";
|
|
14
|
+
import { Canal } from "./class/radio/canal";
|
|
15
|
+
import { Conference } from "./class/conference/conference";
|
|
14
16
|
|
|
15
17
|
interface Transcript {
|
|
16
18
|
actual: number;
|
|
@@ -92,10 +94,18 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
92
94
|
return chapteringPercent;
|
|
93
95
|
},
|
|
94
96
|
playerHeight() {
|
|
95
|
-
if ("STOPPED" === this.playerStatus)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (
|
|
97
|
+
if ("STOPPED" === this.playerStatus) {
|
|
98
|
+
return '0px';
|
|
99
|
+
}
|
|
100
|
+
if (this.playerVideo) {
|
|
101
|
+
return "0px" /* "281px" */;
|
|
102
|
+
}
|
|
103
|
+
if (this.playerLargeVersion) {
|
|
104
|
+
return "27rem";
|
|
105
|
+
}
|
|
106
|
+
if (window.innerWidth > 450) {
|
|
107
|
+
return "6rem";
|
|
108
|
+
}
|
|
99
109
|
return "3.5rem";
|
|
100
110
|
},
|
|
101
111
|
|
|
@@ -182,19 +192,27 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
182
192
|
* @param param The data
|
|
183
193
|
* @param isVideo If true, enable video mode
|
|
184
194
|
*/
|
|
185
|
-
async playerPlay(param?:
|
|
195
|
+
async playerPlay(param?: Podcast|Media|Canal|Conference, isVideo = false) {
|
|
186
196
|
if (!param) {
|
|
187
197
|
this.stop();
|
|
188
198
|
return;
|
|
189
199
|
}
|
|
190
200
|
if (
|
|
191
|
-
(
|
|
201
|
+
(
|
|
202
|
+
this.playerPodcast &&
|
|
203
|
+
'podcastId' in param &&
|
|
192
204
|
this.playerPodcast.podcastId === param.podcastId &&
|
|
193
|
-
isVideo === this.playerVideo
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
isVideo === this.playerVideo
|
|
206
|
+
) || (
|
|
207
|
+
this.playerMedia &&
|
|
208
|
+
'mediaId' in param &&
|
|
209
|
+
this.playerMedia.mediaId === param.mediaId
|
|
210
|
+
) || (
|
|
211
|
+
this.playerLive &&
|
|
212
|
+
'conferenceId' in param &&
|
|
196
213
|
this.playerLive.conferenceId === param.conferenceId &&
|
|
197
|
-
isVideo === this.playerVideo
|
|
214
|
+
isVideo === this.playerVideo
|
|
215
|
+
)
|
|
198
216
|
) {
|
|
199
217
|
//Do nothing
|
|
200
218
|
return;
|
|
@@ -215,6 +233,7 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
215
233
|
this.playerChaptering = undefined;
|
|
216
234
|
|
|
217
235
|
if (
|
|
236
|
+
'conferenceId' in param &&
|
|
218
237
|
param.conferenceId &&
|
|
219
238
|
(!param.podcastId || param.processingStatus !== "READY")
|
|
220
239
|
) {
|
|
@@ -223,24 +242,28 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
223
242
|
this.playerCurrentChange = null;
|
|
224
243
|
return;
|
|
225
244
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
|
|
246
|
+
if ('podcastId' in param && param.podcastId) {
|
|
247
|
+
const podcast = param as Podcast;
|
|
248
|
+
this.playerPodcast = podcast;
|
|
249
|
+
this.playerCurrentChange = podcast.podcastId;
|
|
250
|
+
if (podcast.annotations?.chaptering) {
|
|
230
251
|
this.playerChaptering = await classicApi.fetchData<Chaptering>({
|
|
231
|
-
api:4,
|
|
232
|
-
path:
|
|
233
|
-
isNotAuth:true
|
|
252
|
+
api: 4,
|
|
253
|
+
path: podcast.annotations.chaptering as string,
|
|
254
|
+
isNotAuth: true
|
|
234
255
|
});
|
|
235
256
|
}
|
|
236
257
|
return;
|
|
237
258
|
}
|
|
238
|
-
|
|
259
|
+
|
|
260
|
+
if ('mediaId' in param && param.mediaId) {
|
|
239
261
|
this.playerMedia = param;
|
|
240
262
|
this.playerCurrentChange = null;
|
|
241
263
|
return;
|
|
242
264
|
}
|
|
243
|
-
|
|
265
|
+
|
|
266
|
+
if ('canalId' in param && param.canalId) {
|
|
244
267
|
this.playerRadio = { ...param, isInit: false };
|
|
245
268
|
this.playerCurrentChange = -param.canalId;
|
|
246
269
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Person } from "../user/person";
|
|
2
2
|
|
|
3
|
+
export enum MonetisationOptions {
|
|
4
|
+
YES = 'YES',
|
|
5
|
+
NO = 'NO'
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
export type OrganisationAttributes = {
|
|
4
9
|
//[key: string]: string | number | boolean | undefined;
|
|
5
10
|
automation?: string;
|
|
@@ -13,6 +18,10 @@ export type OrganisationAttributes = {
|
|
|
13
18
|
'translation-config'?: string;
|
|
14
19
|
/** Language of the RSS */
|
|
15
20
|
'rss-language'?: string;
|
|
21
|
+
/** Monetisation of the organisation */
|
|
22
|
+
MONETISABLE?: MonetisationOptions;
|
|
23
|
+
/** Privacy parameters (JSON) */
|
|
24
|
+
PRIVATE?: string;
|
|
16
25
|
};
|
|
17
26
|
|
|
18
27
|
export interface Organisation {
|
|
@@ -26,7 +35,7 @@ export interface Organisation {
|
|
|
26
35
|
longitude: number;
|
|
27
36
|
latitude: number;
|
|
28
37
|
};
|
|
29
|
-
monetisable?:
|
|
38
|
+
monetisable?: MonetisationOptions;
|
|
30
39
|
name: string;
|
|
31
40
|
notSeenOnKeycloak?: number;
|
|
32
41
|
score?: number;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export enum OrganisationPrivacy {
|
|
2
|
+
SECURED = 'SECURED',
|
|
3
|
+
PRIVATE = 'PRIVATE',
|
|
4
|
+
PUBLIC = 'PUBLIC'
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
export interface PrivateOrganisation {
|
|
2
|
-
privacy:
|
|
8
|
+
privacy: OrganisationPrivacy;
|
|
3
9
|
accountCreation: string; // ENABLED DISABLED
|
|
4
10
|
referrers: Array<string>;
|
|
5
11
|
cidrs: Array<string>;
|
|
@@ -8,7 +14,7 @@ export interface PrivateOrganisation {
|
|
|
8
14
|
|
|
9
15
|
export function emptyPrivateOrganisation(): PrivateOrganisation {
|
|
10
16
|
return {
|
|
11
|
-
privacy:
|
|
17
|
+
privacy: OrganisationPrivacy.PUBLIC,
|
|
12
18
|
accountCreation: "DISABLED",
|
|
13
19
|
referrers: [],
|
|
14
20
|
cidrs: [],
|