Add federation to ownership change
[oweals/peertube.git] / server / controllers / api / search.ts
1 import * as express from 'express'
2 import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
3 import { getFormattedObjects, getServerActor } from '../../helpers/utils'
4 import { VideoModel } from '../../models/video/video'
5 import {
6   asyncMiddleware,
7   commonVideosFiltersValidator,
8   optionalAuthenticate,
9   paginationValidator,
10   setDefaultPagination,
11   setDefaultSearchSort,
12   videoChannelsSearchSortValidator,
13   videoChannelsSearchValidator,
14   videosSearchSortValidator,
15   videosSearchValidator
16 } from '../../middlewares'
17 import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search'
18 import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel } from '../../lib/activitypub'
19 import { logger } from '../../helpers/logger'
20 import { User } from '../../../shared/models/users'
21 import { CONFIG } from '../../initializers/constants'
22 import { VideoChannelModel } from '../../models/video/video-channel'
23 import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
24
25 const searchRouter = express.Router()
26
27 searchRouter.get('/videos',
28   paginationValidator,
29   setDefaultPagination,
30   videosSearchSortValidator,
31   setDefaultSearchSort,
32   optionalAuthenticate,
33   commonVideosFiltersValidator,
34   videosSearchValidator,
35   asyncMiddleware(searchVideos)
36 )
37
38 searchRouter.get('/video-channels',
39   paginationValidator,
40   setDefaultPagination,
41   videoChannelsSearchSortValidator,
42   setDefaultSearchSort,
43   optionalAuthenticate,
44   videoChannelsSearchValidator,
45   asyncMiddleware(searchVideoChannels)
46 )
47
48 // ---------------------------------------------------------------------------
49
50 export { searchRouter }
51
52 // ---------------------------------------------------------------------------
53
54 function searchVideoChannels (req: express.Request, res: express.Response) {
55   const query: VideoChannelsSearchQuery = req.query
56   const search = query.search
57
58   const isURISearch = search.startsWith('http://') || search.startsWith('https://')
59
60   const parts = search.split('@')
61   const isWebfingerSearch = parts.length === 2 && parts.every(p => p.indexOf(' ') === -1)
62
63   if (isURISearch || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res)
64
65   return searchVideoChannelsDB(query, res)
66 }
67
68 async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) {
69   const serverActor = await getServerActor()
70
71   const options = {
72     actorId: serverActor.id,
73     search: query.search,
74     start: query.start,
75     count: query.count,
76     sort: query.sort
77   }
78   const resultList = await VideoChannelModel.searchForApi(options)
79
80   return res.json(getFormattedObjects(resultList.data, resultList.total))
81 }
82
83 async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
84   let videoChannel: VideoChannelModel
85   let uri = search
86
87   if (isWebfingerSearch) uri = await loadActorUrlOrGetFromWebfinger(search)
88
89   if (isUserAbleToSearchRemoteURI(res)) {
90     try {
91       const actor = await getOrCreateActorAndServerAndModel(uri, true, true)
92       videoChannel = actor.VideoChannel
93     } catch (err) {
94       logger.info('Cannot search remote video channel %s.', uri, { err })
95     }
96   } else {
97     videoChannel = await VideoChannelModel.loadByUrlAndPopulateAccount(uri)
98   }
99
100   return res.json({
101     total: videoChannel ? 1 : 0,
102     data: videoChannel ? [ videoChannel.toFormattedJSON() ] : []
103   })
104 }
105
106 function searchVideos (req: express.Request, res: express.Response) {
107   const query: VideosSearchQuery = req.query
108   const search = query.search
109   if (search && (search.startsWith('http://') || search.startsWith('https://'))) {
110     return searchVideoURI(search, res)
111   }
112
113   return searchVideosDB(query, res)
114 }
115
116 async function searchVideosDB (query: VideosSearchQuery, res: express.Response) {
117   const options = Object.assign(query, {
118     includeLocalVideos: true,
119     nsfw: buildNSFWFilter(res, query.nsfw)
120   })
121   const resultList = await VideoModel.searchAndPopulateAccountAndServer(options)
122
123   return res.json(getFormattedObjects(resultList.data, resultList.total))
124 }
125
126 async function searchVideoURI (url: string, res: express.Response) {
127   let video: VideoModel
128
129   // Check if we can fetch a remote video with the URL
130   if (isUserAbleToSearchRemoteURI(res)) {
131     try {
132       const syncParam = {
133         likes: false,
134         dislikes: false,
135         shares: false,
136         comments: false,
137         thumbnail: true,
138         refreshVideo: false
139       }
140
141       const result = await getOrCreateVideoAndAccountAndChannel(url, syncParam)
142       video = result ? result.video : undefined
143     } catch (err) {
144       logger.info('Cannot search remote video %s.', url, { err })
145     }
146   } else {
147     video = await VideoModel.loadByUrlAndPopulateAccount(url)
148   }
149
150   return res.json({
151     total: video ? 1 : 0,
152     data: video ? [ video.toFormattedJSON() ] : []
153   })
154 }