-
Notifications
You must be signed in to change notification settings - Fork 202
/
media-service.ts
109 lines (99 loc) · 3.13 KB
/
media-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { decodeMediaData } from "~/utils/decode-media-data"
import type { ApiQueryParams } from "~/utils/search-query-transform"
import type { ApiService } from "~/data/api-service"
import type { DetailFromMediaType, Media } from "~/types/media"
import { AUDIO, type SupportedMediaType } from "~/constants/media"
import type { AxiosResponse } from "axios"
export interface MediaResult<
T extends Media | Media[] | Record<string, Media>
> {
result_count: number
page_count: number
page_size: number
page: number
results: T
}
class MediaService<T extends Media> {
private readonly apiService: ApiService
private readonly mediaType: T["frontendMediaType"]
constructor(apiService: ApiService, mediaType: T["frontendMediaType"]) {
this.apiService = apiService
this.mediaType = mediaType
}
/**
* Decodes the text data to avoid encoding problems.
* Also, converts the results from an array of media
* objects into an object with media id as keys.
* @param data - search result data
*/
transformResults(data: MediaResult<T[]>): MediaResult<Record<string, T>> {
const mediaResults = <T[]>data.results
return {
...data,
results: mediaResults.reduce((acc, item) => {
acc[item.id] = decodeMediaData(item, this.mediaType)
return acc
}, {} as Record<string, T>),
}
}
/**
* Search for media items by keyword.
* @param params - API search query parameters
*/
async search(
params: ApiQueryParams
): Promise<MediaResult<Record<string, Media>>> {
// Add the `peaks` param to all audio searches automatically
if (this.mediaType === AUDIO) {
params.peaks = "true"
}
const res = await this.apiService.query<MediaResult<T[]>>(
this.mediaType,
params as unknown as Record<string, string>
)
return this.transformResults(res.data)
}
/**
* Retrieve media details by its id.
* SSR-called
* @param id - the media id to fetch
*/
async getMediaDetail(id: string): Promise<T> {
if (!id) {
throw new Error(
`MediaService.getMediaDetail() id parameter required to retrieve ${this.mediaType} details.`
)
}
const res = await this.apiService.get<T>(this.mediaType, id)
return decodeMediaData(res.data, this.mediaType)
}
/**
* Retrieve related media.
* @param id - object with id of the main media, for which to fetch related media
*/
async getRelatedMedia<T extends SupportedMediaType>(
id: string
): Promise<MediaResult<DetailFromMediaType<T>[]>> {
if (!id) {
throw new Error(
`MediaService.getRelatedMedia() id parameter required to retrieve related media.`
)
}
const params: ApiQueryParams = {}
if (this.mediaType === AUDIO) {
params.peaks = "true"
}
const res = (await this.apiService.get(
this.mediaType,
`${id}/related`,
params as unknown as Record<string, string>
)) as AxiosResponse<MediaResult<DetailFromMediaType<T>[]>>
return {
...res.data,
results: res.data.results.map((item) =>
decodeMediaData(item, this.mediaType)
) as DetailFromMediaType<T>[],
}
}
}
export default MediaService