search.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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 { VideoChannelModel } from '../../models/video/video-channel'
  21. import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
  22. const searchRouter = express.Router()
  23. searchRouter.get('/videos',
  24. paginationValidator,
  25. setDefaultPagination,
  26. videosSearchSortValidator,
  27. setDefaultSearchSort,
  28. optionalAuthenticate,
  29. commonVideosFiltersValidator,
  30. videosSearchValidator,
  31. asyncMiddleware(searchVideos)
  32. )
  33. searchRouter.get('/video-channels',
  34. paginationValidator,
  35. setDefaultPagination,
  36. videoChannelsSearchSortValidator,
  37. setDefaultSearchSort,
  38. optionalAuthenticate,
  39. videoChannelsSearchValidator,
  40. asyncMiddleware(searchVideoChannels)
  41. )
  42. // ---------------------------------------------------------------------------
  43. export { searchRouter }
  44. // ---------------------------------------------------------------------------
  45. function searchVideoChannels (req: express.Request, res: express.Response) {
  46. const query: VideoChannelsSearchQuery = req.query
  47. const search = query.search
  48. const isURISearch = search.startsWith('http://') || search.startsWith('https://')
  49. const parts = search.split('@')
  50. // Handle strings like @toto@example.com
  51. if (parts.length === 3 && parts[0].length === 0) parts.shift()
  52. const isWebfingerSearch = parts.length === 2 && parts.every(p => p && p.indexOf(' ') === -1)
  53. if (isURISearch || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res)
  54. // @username -> username to search in DB
  55. if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '')
  56. return searchVideoChannelsDB(query, res)
  57. }
  58. async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) {
  59. const serverActor = await getServerActor()
  60. const options = {
  61. actorId: serverActor.id,
  62. search: query.search,
  63. start: query.start,
  64. count: query.count,
  65. sort: query.sort
  66. }
  67. const resultList = await VideoChannelModel.searchForApi(options)
  68. return res.json(getFormattedObjects(resultList.data, resultList.total))
  69. }
  70. async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
  71. let videoChannel: VideoChannelModel
  72. let uri = search
  73. if (isWebfingerSearch) {
  74. try {
  75. uri = await loadActorUrlOrGetFromWebfinger(search)
  76. } catch (err) {
  77. logger.warn('Cannot load actor URL or get from webfinger.', { search, err })
  78. return res.json({ total: 0, data: [] })
  79. }
  80. }
  81. if (isUserAbleToSearchRemoteURI(res)) {
  82. try {
  83. const actor = await getOrCreateActorAndServerAndModel(uri, 'all', true, true)
  84. videoChannel = actor.VideoChannel
  85. } catch (err) {
  86. logger.info('Cannot search remote video channel %s.', uri, { err })
  87. }
  88. } else {
  89. videoChannel = await VideoChannelModel.loadByUrlAndPopulateAccount(uri)
  90. }
  91. return res.json({
  92. total: videoChannel ? 1 : 0,
  93. data: videoChannel ? [ videoChannel.toFormattedJSON() ] : []
  94. })
  95. }
  96. function searchVideos (req: express.Request, res: express.Response) {
  97. const query: VideosSearchQuery = req.query
  98. const search = query.search
  99. if (search && (search.startsWith('http://') || search.startsWith('https://'))) {
  100. return searchVideoURI(search, res)
  101. }
  102. return searchVideosDB(query, res)
  103. }
  104. async function searchVideosDB (query: VideosSearchQuery, res: express.Response) {
  105. const options = Object.assign(query, {
  106. includeLocalVideos: true,
  107. nsfw: buildNSFWFilter(res, query.nsfw),
  108. filter: query.filter,
  109. user: res.locals.oauth ? res.locals.oauth.token.User : undefined
  110. })
  111. const resultList = await VideoModel.searchAndPopulateAccountAndServer(options)
  112. return res.json(getFormattedObjects(resultList.data, resultList.total))
  113. }
  114. async function searchVideoURI (url: string, res: express.Response) {
  115. let video: VideoModel
  116. // Check if we can fetch a remote video with the URL
  117. if (isUserAbleToSearchRemoteURI(res)) {
  118. try {
  119. const syncParam = {
  120. likes: false,
  121. dislikes: false,
  122. shares: false,
  123. comments: false,
  124. thumbnail: true,
  125. refreshVideo: false
  126. }
  127. const result = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam })
  128. video = result ? result.video : undefined
  129. } catch (err) {
  130. logger.info('Cannot search remote video %s.', url, { err })
  131. }
  132. } else {
  133. video = await VideoModel.loadByUrlAndPopulateAccount(url)
  134. }
  135. return res.json({
  136. total: video ? 1 : 0,
  137. data: video ? [ video.toFormattedJSON() ] : []
  138. })
  139. }