ニジカ投稿局 https://tv.nizika.tv
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

static.ts 4.0 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import cors from 'cors'
  2. import express from 'express'
  3. import { readFile } from 'fs/promises'
  4. import { join } from 'path'
  5. import { injectQueryToPlaylistUrls } from '@server/lib/hls.js'
  6. import {
  7. asyncMiddleware,
  8. ensureCanAccessPrivateVideoHLSFiles,
  9. ensureCanAccessVideoPrivateWebVideoFiles,
  10. handleStaticError,
  11. optionalAuthenticate
  12. } from '@server/middlewares/index.js'
  13. import { HttpStatusCode } from '@peertube/peertube-models'
  14. import { CONFIG } from '../initializers/config.js'
  15. import { DIRECTORIES, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers/constants.js'
  16. import { buildReinjectVideoFileTokenQuery, doReinjectVideoFileToken } from './shared/m3u8-playlist.js'
  17. const staticRouter = express.Router()
  18. // Cors is very important to let other servers access torrent and video files
  19. staticRouter.use(cors())
  20. // ---------------------------------------------------------------------------
  21. // Web videos/Classic videos
  22. // ---------------------------------------------------------------------------
  23. const privateWebVideoStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true
  24. ? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessVideoPrivateWebVideoFiles) ]
  25. : []
  26. staticRouter.use(
  27. [ STATIC_PATHS.PRIVATE_WEB_VIDEOS, STATIC_PATHS.LEGACY_PRIVATE_WEB_VIDEOS ],
  28. ...privateWebVideoStaticMiddlewares,
  29. express.static(DIRECTORIES.WEB_VIDEOS.PRIVATE, { fallthrough: false }),
  30. handleStaticError
  31. )
  32. staticRouter.use(
  33. [ STATIC_PATHS.WEB_VIDEOS, STATIC_PATHS.LEGACY_WEB_VIDEOS ],
  34. express.static(DIRECTORIES.WEB_VIDEOS.PUBLIC, { fallthrough: false }),
  35. handleStaticError
  36. )
  37. staticRouter.use(
  38. STATIC_PATHS.REDUNDANCY,
  39. express.static(CONFIG.STORAGE.REDUNDANCY_DIR, { fallthrough: false }),
  40. handleStaticError
  41. )
  42. // ---------------------------------------------------------------------------
  43. // HLS
  44. // ---------------------------------------------------------------------------
  45. const privateHLSStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true
  46. ? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessPrivateVideoHLSFiles) ]
  47. : []
  48. staticRouter.use(
  49. STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + ':videoUUID/:playlistName.m3u8',
  50. ...privateHLSStaticMiddlewares,
  51. asyncMiddleware(servePrivateM3U8)
  52. )
  53. staticRouter.use(
  54. STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS,
  55. ...privateHLSStaticMiddlewares,
  56. express.static(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, { fallthrough: false }),
  57. handleStaticError
  58. )
  59. staticRouter.use(
  60. STATIC_PATHS.STREAMING_PLAYLISTS.HLS,
  61. express.static(DIRECTORIES.HLS_STREAMING_PLAYLIST.PUBLIC, { fallthrough: false }),
  62. handleStaticError
  63. )
  64. // FIXME: deprecated in v6, to remove
  65. const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR
  66. staticRouter.use(
  67. STATIC_PATHS.THUMBNAILS,
  68. express.static(thumbnailsPhysicalPath, { maxAge: STATIC_MAX_AGE.SERVER, fallthrough: false }),
  69. handleStaticError
  70. )
  71. // ---------------------------------------------------------------------------
  72. export {
  73. staticRouter
  74. }
  75. // ---------------------------------------------------------------------------
  76. async function servePrivateM3U8 (req: express.Request, res: express.Response) {
  77. const path = join(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, req.params.videoUUID, req.params.playlistName + '.m3u8')
  78. const filename = req.params.playlistName + '.m3u8'
  79. let playlistContent: string
  80. try {
  81. playlistContent = await readFile(path, 'utf-8')
  82. } catch (err) {
  83. if (err.message.includes('ENOENT')) {
  84. return res.fail({
  85. status: HttpStatusCode.NOT_FOUND_404,
  86. message: 'File not found'
  87. })
  88. }
  89. throw err
  90. }
  91. // Inject token in playlist so players that cannot alter the HTTP request can still watch the video
  92. const transformedContent = doReinjectVideoFileToken(req)
  93. ? injectQueryToPlaylistUrls(playlistContent, buildReinjectVideoFileTokenQuery(req, filename.endsWith('master.m3u8')))
  94. : playlistContent
  95. return res.set('content-type', 'application/vnd.apple.mpegurl').send(transformedContent).end()
  96. }