diff options
Diffstat (limited to 'server/controllers')
-rw-r--r-- | server/controllers/api/index.ts | 2 | ||||
-rw-r--r-- | server/controllers/api/overviews.ts | 97 |
2 files changed, 99 insertions, 0 deletions
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts index e928a7478..8a58b5466 100644 --- a/server/controllers/api/index.ts +++ b/server/controllers/api/index.ts | |||
@@ -10,6 +10,7 @@ import { badRequest } from '../../helpers/express-utils' | |||
10 | import { videoChannelRouter } from './video-channel' | 10 | import { videoChannelRouter } from './video-channel' |
11 | import * as cors from 'cors' | 11 | import * as cors from 'cors' |
12 | import { searchRouter } from './search' | 12 | import { searchRouter } from './search' |
13 | import { overviewsRouter } from './overviews' | ||
13 | 14 | ||
14 | const apiRouter = express.Router() | 15 | const apiRouter = express.Router() |
15 | 16 | ||
@@ -28,6 +29,7 @@ apiRouter.use('/video-channels', videoChannelRouter) | |||
28 | apiRouter.use('/videos', videosRouter) | 29 | apiRouter.use('/videos', videosRouter) |
29 | apiRouter.use('/jobs', jobsRouter) | 30 | apiRouter.use('/jobs', jobsRouter) |
30 | apiRouter.use('/search', searchRouter) | 31 | apiRouter.use('/search', searchRouter) |
32 | apiRouter.use('/overviews', overviewsRouter) | ||
31 | apiRouter.use('/ping', pong) | 33 | apiRouter.use('/ping', pong) |
32 | apiRouter.use('/*', badRequest) | 34 | apiRouter.use('/*', badRequest) |
33 | 35 | ||
diff --git a/server/controllers/api/overviews.ts b/server/controllers/api/overviews.ts new file mode 100644 index 000000000..56f921ce5 --- /dev/null +++ b/server/controllers/api/overviews.ts | |||
@@ -0,0 +1,97 @@ | |||
1 | import * as express from 'express' | ||
2 | import { buildNSFWFilter } from '../../helpers/express-utils' | ||
3 | import { VideoModel } from '../../models/video/video' | ||
4 | import { asyncMiddleware, executeIfActivityPub } from '../../middlewares' | ||
5 | import { TagModel } from '../../models/video/tag' | ||
6 | import { VideosOverview } from '../../../shared/models/overviews' | ||
7 | import { OVERVIEWS, ROUTE_CACHE_LIFETIME } from '../../initializers' | ||
8 | import { cacheRoute } from '../../middlewares/cache' | ||
9 | |||
10 | const overviewsRouter = express.Router() | ||
11 | |||
12 | overviewsRouter.get('/videos', | ||
13 | executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS))), | ||
14 | asyncMiddleware(getVideosOverview) | ||
15 | ) | ||
16 | |||
17 | // --------------------------------------------------------------------------- | ||
18 | |||
19 | export { overviewsRouter } | ||
20 | |||
21 | // --------------------------------------------------------------------------- | ||
22 | |||
23 | // This endpoint could be quite long, but we cache it | ||
24 | async function getVideosOverview (req: express.Request, res: express.Response) { | ||
25 | const attributes = await buildSamples() | ||
26 | const result: VideosOverview = { | ||
27 | categories: await Promise.all(attributes.categories.map(c => getVideosByCategory(c, res))), | ||
28 | channels: await Promise.all(attributes.channels.map(c => getVideosByChannel(c, res))), | ||
29 | tags: await Promise.all(attributes.tags.map(t => getVideosByTag(t, res))) | ||
30 | } | ||
31 | |||
32 | // Cleanup our object | ||
33 | for (const key of Object.keys(result)) { | ||
34 | result[key] = result[key].filter(v => v !== undefined) | ||
35 | } | ||
36 | |||
37 | return res.json(result) | ||
38 | } | ||
39 | |||
40 | async function buildSamples () { | ||
41 | const [ categories, channels, tags ] = await Promise.all([ | ||
42 | VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), | ||
43 | VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD ,OVERVIEWS.VIDEOS.SAMPLES_COUNT), | ||
44 | TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT) | ||
45 | ]) | ||
46 | |||
47 | return { categories, channels, tags } | ||
48 | } | ||
49 | |||
50 | async function getVideosByTag (tag: string, res: express.Response) { | ||
51 | const videos = await getVideos(res, { tagsOneOf: [ tag ] }) | ||
52 | |||
53 | if (videos.length === 0) return undefined | ||
54 | |||
55 | return { | ||
56 | tag, | ||
57 | videos | ||
58 | } | ||
59 | } | ||
60 | |||
61 | async function getVideosByCategory (category: number, res: express.Response) { | ||
62 | const videos = await getVideos(res, { categoryOneOf: [ category ] }) | ||
63 | |||
64 | if (videos.length === 0) return undefined | ||
65 | |||
66 | return { | ||
67 | category: videos[0].category, | ||
68 | videos | ||
69 | } | ||
70 | } | ||
71 | |||
72 | async function getVideosByChannel (channelId: number, res: express.Response) { | ||
73 | const videos = await getVideos(res, { videoChannelId: channelId }) | ||
74 | |||
75 | if (videos.length === 0) return undefined | ||
76 | |||
77 | return { | ||
78 | channel: videos[0].channel, | ||
79 | videos | ||
80 | } | ||
81 | } | ||
82 | |||
83 | async function getVideos ( | ||
84 | res: express.Response, | ||
85 | where: { videoChannelId?: number, tagsOneOf?: string[], categoryOneOf?: number[] } | ||
86 | ) { | ||
87 | const { data } = await VideoModel.listForApi(Object.assign({ | ||
88 | start: 0, | ||
89 | count: 10, | ||
90 | sort: '-createdAt', | ||
91 | includeLocalVideos: true, | ||
92 | nsfw: buildNSFWFilter(res), | ||
93 | withFiles: false | ||
94 | }, where)) | ||
95 | |||
96 | return data.map(d => d.toFormattedJSON()) | ||
97 | } | ||