aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/lazy-static.ts
blob: b082e41f64ebaadeab9c87854f7061cd5c66e32f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import cors from 'cors'
import express from 'express'
import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
import { MActorImage } from '@server/types/models'
import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
import { logger } from '../helpers/logger'
import { ACTOR_IMAGES_SIZE, LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants'
import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache'
import { actorImagePathUnsafeCache, downloadActorImageFromWorker } from '../lib/local-actor'
import { asyncMiddleware, handleStaticError } from '../middlewares'
import { ActorImageModel } from '../models/actor/actor-image'

const lazyStaticRouter = express.Router()

lazyStaticRouter.use(cors())

lazyStaticRouter.use(
  LAZY_STATIC_PATHS.AVATARS + ':filename',
  asyncMiddleware(getActorImage),
  handleStaticError
)

lazyStaticRouter.use(
  LAZY_STATIC_PATHS.BANNERS + ':filename',
  asyncMiddleware(getActorImage),
  handleStaticError
)

lazyStaticRouter.use(
  LAZY_STATIC_PATHS.PREVIEWS + ':filename',
  asyncMiddleware(getPreview),
  handleStaticError
)

lazyStaticRouter.use(
  LAZY_STATIC_PATHS.VIDEO_CAPTIONS + ':filename',
  asyncMiddleware(getVideoCaption),
  handleStaticError
)

lazyStaticRouter.use(
  LAZY_STATIC_PATHS.TORRENTS + ':filename',
  asyncMiddleware(getTorrent),
  handleStaticError
)

// ---------------------------------------------------------------------------

export {
  lazyStaticRouter,
  getPreview,
  getVideoCaption
}

// ---------------------------------------------------------------------------

async function getActorImage (req: express.Request, res: express.Response, next: express.NextFunction) {
  const filename = req.params.filename

  if (actorImagePathUnsafeCache.has(filename)) {
    return res.sendFile(actorImagePathUnsafeCache.get(filename), { maxAge: STATIC_MAX_AGE.SERVER })
  }

  const image = await ActorImageModel.loadByName(filename)
  if (!image) return res.status(HttpStatusCode.NOT_FOUND_404).end()

  if (image.onDisk === false) {
    if (!image.fileUrl) return res.status(HttpStatusCode.NOT_FOUND_404).end()

    logger.info('Lazy serve remote actor image %s.', image.fileUrl)

    try {
      await downloadActorImageFromWorker({
        filename: image.filename,
        fileUrl: image.fileUrl,
        size: getActorImageSize(image),
        type: image.type
      })
    } catch (err) {
      logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err })
      return res.status(HttpStatusCode.NOT_FOUND_404).end()
    }

    image.onDisk = true
    image.save()
      .catch(err => logger.error('Cannot save new actor image disk state.', { err }))
  }

  const path = image.getPath()

  actorImagePathUnsafeCache.set(filename, path)

  return res.sendFile(path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }, (err: any) => {
    if (!err) return

    // It seems this actor image is not on the disk anymore
    if (err.status === HttpStatusCode.NOT_FOUND_404 && !image.isOwned()) {
      logger.error('Cannot lazy serve actor image %s.', filename, { err })

      actorImagePathUnsafeCache.delete(filename)

      image.onDisk = false
      image.save()
       .catch(err => logger.error('Cannot save new actor image disk state.', { err }))
    }

    return next(err)
  })
}

function getActorImageSize (image: MActorImage): { width: number, height: number } {
  if (image.width && image.height) {
    return {
      height: image.height,
      width: image.width
    }
  }

  return ACTOR_IMAGES_SIZE[image.type][0]
}

async function getPreview (req: express.Request, res: express.Response) {
  const result = await VideosPreviewCache.Instance.getFilePath(req.params.filename)
  if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end()

  return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER })
}

async function getVideoCaption (req: express.Request, res: express.Response) {
  const result = await VideosCaptionCache.Instance.getFilePath(req.params.filename)
  if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end()

  return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER })
}

async function getTorrent (req: express.Request, res: express.Response) {
  const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename)
  if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end()

  // Torrents still use the old naming convention (video uuid + .torrent)
  return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.SERVER })
}