search.ts 5.3 KB

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