import { Podcast, PodcastProcessingStatus, PodcastType, SimplifiedPodcast } from '../stores/class/general/podcast'; import { ListClassicReturn } from '../stores/class/general/listReturn'; import classicApi from './classicApi'; import { ModuleApi } from './apiConnection'; import { unique } from '../helper/arrayHelper'; import { organisationApi } from './organisationApi'; import { emissionApi } from './emissionApi'; import { FetchParam } from '@/stores/class/general/fetchParam'; import { Paginable } from './types'; import { participantApi } from './participantApi'; import { useNotificationStore } from '@/stores/NotificationStore'; import { AxiosError } from 'axios'; import { getI18n } from '@/i18n'; export enum PodcastSort { DATE = 'DATE', DATE_ASC = 'DATE_ASC', NAME = 'NAME', LAST_PODCAST_DESC = 'LAST_PODCAST_DESC', POPULARITY = 'POPULARITY', SCORE = 'SCORE', UPDATE_ASC = 'UPDATE_ASC', UPDATE_DESC = 'UPDATE_DESC', /** Smart sort using seasons settings */ SEASONAL = 'SEASONAL' } export enum PodcastMonetisation { YES = "YES", NO = "NO", UNDEFINED = "UNDEFINED" } /** * The options available to search for podcasts. * * ** /!\ This list is not exhaustive ** */ export interface PodcastSearchOptions extends Paginable { /** Filter by emission ID */ emissionId?: number|number[]; /** Filter by emission groups */ groupId?: number[]; /** Filter by organisation ID */ organisationId?: string[]; /** Filter by title containing */ query?: string; /** Filter by IAB category */ iabId?: number; /** Filter by participant */ participantId?: number; /** Fitler by monetisation */ monetisable?: PodcastMonetisation; /** Filter by pub date before */ pubDateBefore?: string; /** Filter by pub date after */ pubDateAfter?: string; /** Filter by rubriquage exclusion ? */ noRubriquageId?: Array; /** Filter by rubrique */ rubriqueId?: Array; /** Filter by rubriquage */ rubriquageId?: Array; /** Also show hidden podcasts */ includeHidden?: boolean; /** Filter by validity */ validity?: boolean; /** Filter by status */ processingStatus?: Array; /** Filter by podcast having a video */ withVideo?: boolean; /** Filter by tags */ tags?: Array; /** Filter by beneficiaries/rights holder reference */ beneficiaries?: Array; /** Filter by seasons */ season?: Array; /** Filter by season episode number */ seasonEpisode?: Array; /** Filter by episode type */ episodeType?: PodcastType; } async function downloadRegister(podcastId: number, parameters?: Record): Promise<{ location: string; downloadId: number }> { try { return await classicApi.fetchData<{ location: string; downloadId: number; }>({ api: ModuleApi.DEFAULT, path:"podcast/download/register/" + podcastId + ".mp3", parameters, headers: {'X-Extra-UA':'Saooti Player'} }); } catch(e) { if (e instanceof AxiosError && e.status === 403) { const error = e.response.data; // #14620 - match "Country FR is not whitelisted" or "Country FR is blacklisted" if (error.includes('is blacklisted') || error.includes('is not whitelisted')) { const { addNotification } = useNotificationStore(); const t = getI18n().global.t as (key: string) => string; addNotification({ type: 'error', title: t('Generic - Content unavailable - Country blocked - Title'), message: t('Generic - Content unavailable - Country blocked - Message') }); } } return Promise.reject(e); } } /** * Get a podcast by its ID */ function get(podcastId: number): Promise { return classicApi.fetchData({ api: ModuleApi.DEFAULT, path: 'podcast/' + podcastId }); } /** * Convert easy to use PodcastSearchOptions to the actual FetchParams used by * the endpoint. */ function processSearchParameters(search: PodcastSearchOptions): FetchParam { const parameters: FetchParam = {}; Object.keys(search).forEach(key => { const value = search[key]; if (value === undefined || value === null) { return; } if (key === 'beneficiaries') { parameters.beneficiary = value; } else if (key === 'processingStatus') { parameters.includeStatus = value; } else if (key === 'tags') { parameters.includeTag = value; } else if (key === 'pubDateBefore') { parameters.before = value; } else if (key === 'pubDateAfter') { parameters.after = value; } else if (key === 'pageSize') { parameters.size = value; } else { parameters[key] = value; } }); return parameters; } /** * Search for podcasts. Retrieved podcasts are 'incomplete', some of their * properties are only IDs. * Use `searchFull` to get full podcasts, but more queries will be made * @param options The search criterias * @param adaptParameters If true, some adjustments will be made to the parameters * @return A list of simplified podcasts */ async function search(options: PodcastSearchOptions, adaptParameters?: boolean): Promise> { return classicApi.fetchData>({ api: ModuleApi.DEFAULT, path: 'v2/podcast/search', parameters: processSearchParameters(options), specialTreatement: adaptParameters }); } /** * Count podcasts matching the filters * @param options The search criterias * @param adaptParameters If true, some adjustments will be made to the parameters * @return A list of simplified podcasts */ async function count(options: PodcastSearchOptions, adaptParameters?: boolean): Promise { const result = await search({ ...options, size: 0 }, adaptParameters); return result.count; } /** * Retrieve the podcasts matching the search criterias, with all their data. * This query is longer, because it also needs to retrieve organisations & * emissions. * @param options The search criterias * @param adaptParameters If true, some adjustments will be made to the parameters * @return A list of podcasts */ async function searchFull(options:PodcastSearchOptions, adaptParameters?: boolean): Promise> { const podcasts = await search(options, adaptParameters); const full: ListClassicReturn = { count: podcasts.count, sort:podcasts.sort, result: [] }; const organisationIds = podcasts.result.map((p: SimplifiedPodcast) => p.organisationId) .filter(unique); const emissionIds = podcasts.result.map((p: SimplifiedPodcast) => p.emissionId) .filter(unique); const participantIds = podcasts.result.map((p: SimplifiedPodcast) => p.animatorId ?? []) .flat() .filter(unique); const organisationsPromise = organisationApi.getAllById(organisationIds); const emissionsPromise = emissionApi.getAllById(emissionIds); const participantsPromise = participantApi.getAllById(participantIds); const [organisations, emissions, participants] = await Promise.all([ organisationsPromise, emissionsPromise, participantsPromise ]) podcasts.result.forEach((s: SimplifiedPodcast) => { const organisation = organisations[s.organisationId]; const emission = emissions[s.emissionId]; const animators = s.animatorId?.map(id => participants[id]); full.result.push({ ...s, organisation, emission, animators }); }); return full; } export const podcastApi = { count, downloadRegister, get, search, searchFull }