@saooti/octopus-sdk 41.1.3 → 41.1.5

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,5 +1,19 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 41.1.5 (10/12/2025)
4
+
5
+ **Misc**
6
+
7
+ - Ajout API playlist, emission, et organisation
8
+
9
+ ## 41.1.4 (04/12/2025)
10
+
11
+ **Misc**
12
+
13
+ - Passage de `pinia` à version `>=2.3.0`
14
+ - *Podcastmaker* utilise la version `^2.3.0`
15
+ - *Frontoffice* utilise la version `^3.0.3`
16
+
3
17
  ## 41.1.3 (04/12/2025)
4
18
 
5
19
  **Misc**
package/index.ts CHANGED
@@ -137,6 +137,10 @@ import {useAuthStore} from "./src/stores/AuthStore.ts";
137
137
  import {getApiUrl, ModuleApi} from "./src/api/apiConnection.ts";
138
138
  import classicApi from "./src/api/classicApi.ts";
139
139
 
140
+ export { emissionApi } from "./src/api/emissionApi.ts";
141
+ export { organisationApi } from "./src/api/organisationApi.ts";
142
+ export { playlistApi } from "./src/api/playlistApi.ts";
143
+
140
144
  //Icons
141
145
  export const getAmazonMusicIcon = () => import("./src/components/icons/AmazonMusicIcon.vue");
142
146
  export const getApplePodcastIcon = () => import("./src/components/icons/ApplePodcastIcon.vue");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "41.1.3",
3
+ "version": "41.1.5",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -89,6 +89,6 @@
89
89
  "url": "git+https://github.com/saooti/octopus-sdk.git"
90
90
  },
91
91
  "peerDependencies": {
92
- "pinia": "^2.3.0"
92
+ "pinia": ">=2.3.0"
93
93
  }
94
94
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Utility function to easily convert a list of IDs to a map of id => element
3
+ * @param ID The type of ID for the element
4
+ * @param T The type of element to retrieve
5
+ * @param ids The list of ids
6
+ * @param getter A function that given an ID, returns a promise of the matching element
7
+ * @param key The key for the ID
8
+ * @return A promise containing a map of ID => element
9
+ */
10
+ export async function mapFromGetAll<ID extends string|number, T extends object >(ids: Array<ID>, getter: (id: ID) => Promise<T>, key: keyof T): Promise<Record<string, T>> {
11
+ const results: Record<string, T> = {};
12
+ const promises: Array<Promise<T>> = [];
13
+
14
+ // Retrieve data for each id
15
+ ids.forEach((id: ID) => {
16
+ const promise = getter(id).then(elt => results['' + elt[key]] = elt);
17
+ promises.push(promise);
18
+ });
19
+
20
+ // Wait for all promises to finish
21
+ await Promise.all(promises);
22
+
23
+ return results;
24
+ }
25
+
@@ -0,0 +1,31 @@
1
+ import classicApi from "./classicApi";
2
+ import { ModuleApi } from "./apiConnection";
3
+ import { mapFromGetAll } from "./apiUtils";
4
+ import { Emission } from "@/stores/class/general/emission";
5
+
6
+ /**
7
+ * Retrieve an emission by ID
8
+ * @param emissionId The ID of the emission
9
+ * @return An emission
10
+ */
11
+ async function get(emissionId: number): Promise<Emission> {
12
+ return classicApi.fetchData<Emission>({
13
+ api: ModuleApi.DEFAULT,
14
+ path: 'emission/' + emissionId
15
+ });
16
+ }
17
+
18
+ /**
19
+ * Retrieve all emissions specified by their IDs
20
+ * @param emissionIds A list of emission IDs. For better result, they
21
+ should be unique.
22
+ * @return The matching emissions
23
+ */
24
+ async function getAllById(emissionIds: Array<number>): Promise<Record<string, Emission>> {
25
+ return mapFromGetAll(emissionIds, get, 'emissionId');
26
+ }
27
+
28
+ export const emissionApi = {
29
+ get,
30
+ getAllById
31
+ };
@@ -0,0 +1,31 @@
1
+ import classicApi from "./classicApi";
2
+ import { ModuleApi } from "./apiConnection";
3
+ import { Organisation } from "../stores/class/general/organisation";
4
+ import { mapFromGetAll } from "./apiUtils";
5
+
6
+ /**
7
+ * Retrieve an organisation by ID
8
+ * @param organisationId The ID of the organisation
9
+ * @return An organisation
10
+ */
11
+ async function get(organisationId: string): Promise<Organisation> {
12
+ return classicApi.fetchData<Organisation>({
13
+ api: ModuleApi.DEFAULT,
14
+ path: 'organisation/' + organisationId
15
+ });
16
+ }
17
+
18
+ /**
19
+ * Retrieve all organisations specified by their IDs
20
+ * @param organisationIds A list of organisation IDs. For better result, they
21
+ should be unique.
22
+ * @return The matching organisations
23
+ */
24
+ async function getAllById(organisationIds: Array<string>): Promise<Record<string, Organisation>> {
25
+ return mapFromGetAll(organisationIds, get, 'id');
26
+ }
27
+
28
+ export const organisationApi = {
29
+ get,
30
+ getAllById
31
+ };
@@ -0,0 +1,72 @@
1
+ import classicApi from "./classicApi";
2
+ import { ModuleApi } from "./apiConnection";
3
+ import { Podcast, SimplifiedPodcast } from "../stores/class/general/podcast";
4
+ import { Playlist } from "../stores/class/general/playlist";
5
+ import { organisationApi } from "./organisationApi";
6
+ import { emissionApi } from "./emissionApi";
7
+
8
+ /**
9
+ * Retrieve playlist data
10
+ * @param playlistId The ID of the playlist
11
+ * @return The playlist
12
+ */
13
+ async function get(playlistId: number): Promise<Playlist> {
14
+ return classicApi.fetchData<Playlist>({
15
+ api: ModuleApi.DEFAULT,
16
+ path: 'playlist/' + playlistId
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Retrieve the podcasts defined in the playlist
22
+ * @param playlistId The ID of the playlist
23
+ * @return A list of simplified podcasts
24
+ */
25
+ async function getContent(playlistId: number): Promise<Array<SimplifiedPodcast>> {
26
+ return classicApi.fetchData<Array<SimplifiedPodcast>>({
27
+ api: ModuleApi.DEFAULT,
28
+ path: 'v2/playlist/' + playlistId + '/content'
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Retrieve the podcasts defined in the playlist, with all their data
34
+ * This query is longer, because it also needs to retrieve organisations &
35
+ * emissions.
36
+ * @param playlistId The ID of the playlist
37
+ * @return A list of podcasts
38
+ */
39
+ async function getContentFull(playlistId: number): Promise<Array<Podcast>> {
40
+ const simplified = await this.getContent(playlistId);
41
+ const full: Array<Podcast> = [];
42
+
43
+ // TODO unique
44
+ const organisationIds = simplified.map((p: SimplifiedPodcast) => p.organisationId);
45
+ // TODO unique
46
+ const emissionIds = simplified.map((p: SimplifiedPodcast) => p.emissionId);
47
+
48
+ const organisations = await organisationApi.getAllById(organisationIds);
49
+ const emissions = await emissionApi.getAllById(emissionIds);
50
+
51
+ simplified.forEach((s: SimplifiedPodcast) => {
52
+ const organisation = organisations[s.organisationId];
53
+ const emission = emissions[s.emissionId];
54
+
55
+ full.push({
56
+ ...s,
57
+ organisation,
58
+ emission
59
+ });
60
+ })
61
+
62
+ return full;
63
+ }
64
+
65
+ /**
66
+ * API to manage playlists
67
+ */
68
+ export const playlistApi = {
69
+ get,
70
+ getContent,
71
+ getContentFull
72
+ }
@@ -80,6 +80,7 @@ import { Playlist } from "@/stores/class/general/playlist";
80
80
  import { computed, onBeforeMount, Ref, ref, watch } from "vue";
81
81
  import { AxiosError } from "axios";
82
82
  import { useI18n } from "vue-i18n";
83
+ import { playlistApi } from "../../../api/playlistApi";
83
84
 
84
85
  //Props
85
86
  const props = defineProps({
@@ -137,7 +138,7 @@ watch(searchPattern,() => {
137
138
  }
138
139
  });
139
140
 
140
- onBeforeMount(()=>fetchContent());
141
+ onBeforeMount(fetchContent);
141
142
 
142
143
 
143
144
  //Methods
@@ -146,10 +147,7 @@ async function fetchContent(): Promise<void> {
146
147
  podcasts.value.length = 0;
147
148
  loading.value = true;
148
149
  try {
149
- podcasts.value = await classicApi.fetchData<Array<Podcast>>({
150
- api: 0,
151
- path: "playlist/" + props.playlist.playlistId + "/content",
152
- });
150
+ podcasts.value = await playlistApi.getContentFull(props.playlist.playlistId);
153
151
  if (!editRight.value) {
154
152
  podcasts.value = podcasts.value.filter((p: Podcast | null) => {
155
153
  return (
@@ -40,6 +40,7 @@ import { Podcast } from "@/stores/class/general/podcast";
40
40
  import { Playlist } from "@/stores/class/general/playlist";
41
41
  import { onMounted, Ref, ref, watch } from "vue";
42
42
  import { useI18n } from "vue-i18n";
43
+ import { playlistApi } from "../../../api/playlistApi";
43
44
 
44
45
  //Props
45
46
  const props = defineProps<{
@@ -72,14 +73,17 @@ onMounted(()=>fetchContent())
72
73
  async function fetchContent(): Promise<void> {
73
74
  allPodcasts.value.length = 0;
74
75
  loading.value = true;
75
- playlist.value = await classicApi.fetchData<Playlist>({
76
- api: 0,
77
- path: "playlist/" + props.playlistId,
78
- });
79
- allPodcasts.value = await classicApi.fetchData<Array<Podcast>>({
80
- api: 0,
81
- path: "playlist/" + props.playlistId + "/content",
82
- });
76
+
77
+ // Retrieve both playlist & content at the same time
78
+ const [playlistData, content] = await Promise.all([
79
+ playlistApi.get(props.playlistId),
80
+ playlistApi.getContentFull(props.playlistId)
81
+ ]);
82
+
83
+ // Update data
84
+ playlist.value = playlistData;
85
+ allPodcasts.value = content;
86
+
83
87
  if (
84
88
  !(
85
89
  (undefined !== authStore.authOrgaId &&
@@ -42,15 +42,15 @@ import debounce from '../../../helper/debounceHelper';
42
42
  import PodcastItemInfo from "./PodcastItemInfo.vue";
43
43
  import PodcastImage from "./PodcastImage.vue";
44
44
  import dayjs from "dayjs";
45
- import { Podcast } from "@/stores/class/general/podcast";
45
+ import { Podcast } from "../../../stores/class/general/podcast";
46
46
  import { computed, nextTick, onBeforeMount, ref, Ref, useTemplateRef } from "vue";
47
47
  import { Conference } from "@/stores/class/conference/conference";
48
48
 
49
49
  //Props
50
- const props = defineProps({
51
- podcast: { default: () => ({}), type: Object as () => Podcast },
52
- fetchConference: { default: undefined, type: Object as () => Conference }
53
- });
50
+ const props = defineProps<{
51
+ podcast: Podcast;
52
+ fetchConference?: Conference;
53
+ }>();
54
54
 
55
55
  //Data
56
56
  const isMobile = ref(false);
@@ -17,54 +17,95 @@ export enum ProcessingStatus {
17
17
  All = "ALL"
18
18
  }
19
19
 
20
+ /** Describe the availability of the podcast */
21
+ export interface PodcastAvailability {
22
+ date?: number | null;
23
+ visibility?: boolean;
24
+ immediate?: boolean;
25
+ }
26
+
20
27
  /**
21
28
  * Data about a podcast/episode
22
29
  */
23
- export interface Podcast {
30
+ export interface Podcast extends BasePodcast {
31
+ /** ID of the podcast */
32
+ podcastId: number;
33
+ /** Emission the podcast belongs to */
34
+ emission: Emission;
35
+ /** Organisation the podcast belongs to */
36
+ organisation: Organisation;
37
+ /** URL to the image */
24
38
  imageUrl?: string;
25
- animators?: Array<Participant>;
26
- annotations?: { [key: string]: string | number | boolean | undefined };
27
- audioStorageUrl: string;
39
+ /** URL to the audio file (for downloading) */
28
40
  audioUrl: string;
41
+ /** URL to the audio file (file on bucket) */
42
+ audioStorageUrl: string;
43
+ /** Title of the podcast */
44
+ title: string;
45
+ /** The availability of the podcast */
46
+ availability: PodcastAvailability;
47
+ /** Description of the podcast */
48
+ description?: string;
49
+ /** Publishing date */
50
+ pubDate?: string;
51
+ /** The status of the processing of the audio file */
52
+ processingStatus?: ProcessingStatus;
53
+ /** An optional list of tags */
54
+ tags?: Array<string>;
55
+ /** An optional list of tags for OuestFrance */
56
+ ofTags?: Array<string>;
57
+
58
+ createdAt?: string;
59
+ createdByUserId?: string;
60
+ annotations?: { [key: string]: string | number | boolean | undefined };
29
61
  article?: string;
30
- availability: {
31
- date?: number | null;
32
- visibility?: boolean;
33
- immediate?: boolean;
34
- };
35
62
  comments?: string;
36
63
  conferenceId?: number;
37
- createdAt?: string;
38
- createdByUserId?: string;
39
64
  valid?: boolean;
40
- description?: string;
41
65
  downloadCount?: number;
66
+ weekDownloadCount?: number;
42
67
  duration: number;
43
68
  email?: string;
44
- emission: Emission;
45
- guests?: Array<Participant>;
46
69
  monetisable?: string;
47
- organisation: Organisation;
48
- podcastId: number;
49
- /** The status of the processing of the audio file */
50
- processingStatus?: ProcessingStatus;
70
+
71
+ animators?: Array<Participant>;
72
+ guests?: Array<Participant>;
51
73
  processorId?: string;
52
- pubDate?: string;
53
74
  publisher?: Person;
54
75
  rubriqueIds?: Array<number>;
55
76
  rssEpisode?:string;
56
77
  score?: number;
57
78
  size?: number;
58
- /** An optional list of tags */
59
- tags?: Array<string>;
60
- /** An optional list of tags for OuestFrance */
61
- ofTags?: Array<string>;
62
- title: string;
63
- weekDownloadCount?: number;
64
79
  order?: number;
65
80
  video?: Video;
66
81
  }
67
82
 
83
+ /**
84
+ * A podcast with incomplete data
85
+ */
86
+ export interface SimplifiedPodcast extends
87
+ Omit<Podcast, 'emission'|'organisation'|'animators'|'guests'|'ofTags'|'comments'|'email'|'processorId'|'publisher'|'order'|'video'> {
88
+
89
+ /** The ID of the emission */
90
+ emissionId: number;
91
+ /** The ID of the organisation */
92
+ organisationId: string;
93
+ }
94
+
95
+ /**
96
+ * Convert a simplified podcast to a complete one
97
+ * @param simplified The incomplete podcast
98
+ * @param organisation The organisation the podcast belongs to
99
+ * @param emission The emission the podcast belongs to
100
+ */
101
+ export function simplifiedToFull(simplified: SimplifiedPodcast, organisation: Organisation, emission: Emission): Podcast {
102
+ return {
103
+ ...simplified,
104
+ organisation,
105
+ emission
106
+ };
107
+ }
108
+
68
109
  export function emptyPodcastData(): Podcast {
69
110
  return {
70
111
  podcastId: 0,