diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-07-12 11:56:02 +0200 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-07-12 11:56:02 +0200 |
commit | f981dae8617271a2dc713bb683951730b306e0c5 (patch) | |
tree | ecee631766bc1b98c20a7836479fed40850c5a56 | |
parent | 075f16caac5236cb04c98ae7b3a989766d764bb3 (diff) | |
download | PeerTube-f981dae8617271a2dc713bb683951730b306e0c5.tar.gz PeerTube-f981dae8617271a2dc713bb683951730b306e0c5.tar.zst PeerTube-f981dae8617271a2dc713bb683951730b306e0c5.zip |
Add previews cache system between pods
27 files changed, 202 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore index 6caee2e4c..169027c36 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -12,6 +12,7 @@ | |||
12 | /certs/ | 12 | /certs/ |
13 | /logs/ | 13 | /logs/ |
14 | /torrents/ | 14 | /torrents/ |
15 | /cache/ | ||
15 | /config/production.yaml | 16 | /config/production.yaml |
16 | /ffmpeg/ | 17 | /ffmpeg/ |
17 | /*.sublime-project | 18 | /*.sublime-project |
diff --git a/config/default.yaml b/config/default.yaml index e03bf1aea..b4e7606cf 100644 --- a/config/default.yaml +++ b/config/default.yaml | |||
@@ -22,6 +22,11 @@ storage: | |||
22 | previews: 'previews/' | 22 | previews: 'previews/' |
23 | thumbnails: 'thumbnails/' | 23 | thumbnails: 'thumbnails/' |
24 | torrents: 'torrents/' | 24 | torrents: 'torrents/' |
25 | cache: 'cache/' | ||
26 | |||
27 | cache: | ||
28 | previews: | ||
29 | size: 1 # Max number of previews you want to cache | ||
25 | 30 | ||
26 | admin: | 31 | admin: |
27 | email: 'admin@example.com' | 32 | email: 'admin@example.com' |
diff --git a/config/production.yaml.example b/config/production.yaml.example index c18457df6..0857aa3ca 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example | |||
@@ -23,6 +23,7 @@ storage: | |||
23 | previews: 'previews/' | 23 | previews: 'previews/' |
24 | thumbnails: 'thumbnails/' | 24 | thumbnails: 'thumbnails/' |
25 | torrents: 'torrents/' | 25 | torrents: 'torrents/' |
26 | cache: 'cache/' | ||
26 | 27 | ||
27 | admin: | 28 | admin: |
28 | email: 'admin@example.com' | 29 | email: 'admin@example.com' |
diff --git a/config/test-1.yaml b/config/test-1.yaml index dbe408a8c..e244a8797 100644 --- a/config/test-1.yaml +++ b/config/test-1.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test1/certs/' | 13 | certs: 'test1/certs/' |
14 | videos: 'test1/videos/' | 14 | videos: 'test1/videos/' |
15 | logs: 'test1/logs/' | 15 | logs: 'test1/logs/' |
16 | previews: 'test1/previews/' | ||
16 | thumbnails: 'test1/thumbnails/' | 17 | thumbnails: 'test1/thumbnails/' |
17 | torrents: 'test1/torrents/' | 18 | torrents: 'test1/torrents/' |
19 | cache: 'test1/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin1@example.com' | 22 | email: 'admin1@example.com' |
diff --git a/config/test-2.yaml b/config/test-2.yaml index c95b9c229..236dcb10d 100644 --- a/config/test-2.yaml +++ b/config/test-2.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test2/certs/' | 13 | certs: 'test2/certs/' |
14 | videos: 'test2/videos/' | 14 | videos: 'test2/videos/' |
15 | logs: 'test2/logs/' | 15 | logs: 'test2/logs/' |
16 | previews: 'test2/previews/' | ||
16 | thumbnails: 'test2/thumbnails/' | 17 | thumbnails: 'test2/thumbnails/' |
17 | torrents: 'test2/torrents/' | 18 | torrents: 'test2/torrents/' |
19 | cache: 'test2/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin2@example.com' | 22 | email: 'admin2@example.com' |
diff --git a/config/test-3.yaml b/config/test-3.yaml index 2eb984692..a29225a44 100644 --- a/config/test-3.yaml +++ b/config/test-3.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test3/certs/' | 13 | certs: 'test3/certs/' |
14 | videos: 'test3/videos/' | 14 | videos: 'test3/videos/' |
15 | logs: 'test3/logs/' | 15 | logs: 'test3/logs/' |
16 | previews: 'test3/previews/' | ||
16 | thumbnails: 'test3/thumbnails/' | 17 | thumbnails: 'test3/thumbnails/' |
17 | torrents: 'test3/torrents/' | 18 | torrents: 'test3/torrents/' |
19 | cache: 'test3/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin3@example.com' | 22 | email: 'admin3@example.com' |
diff --git a/config/test-4.yaml b/config/test-4.yaml index a0a9bde21..da93e128d 100644 --- a/config/test-4.yaml +++ b/config/test-4.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test4/certs/' | 13 | certs: 'test4/certs/' |
14 | videos: 'test4/videos/' | 14 | videos: 'test4/videos/' |
15 | logs: 'test4/logs/' | 15 | logs: 'test4/logs/' |
16 | previews: 'test4/previews/' | ||
16 | thumbnails: 'test4/thumbnails/' | 17 | thumbnails: 'test4/thumbnails/' |
17 | torrents: 'test4/torrents/' | 18 | torrents: 'test4/torrents/' |
19 | cache: 'test4/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin4@example.com' | 22 | email: 'admin4@example.com' |
diff --git a/config/test-5.yaml b/config/test-5.yaml index af8654e14..f95e25eb8 100644 --- a/config/test-5.yaml +++ b/config/test-5.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test5/certs/' | 13 | certs: 'test5/certs/' |
14 | videos: 'test5/videos/' | 14 | videos: 'test5/videos/' |
15 | logs: 'test5/logs/' | 15 | logs: 'test5/logs/' |
16 | previews: 'test5/previews/' | ||
16 | thumbnails: 'test5/thumbnails/' | 17 | thumbnails: 'test5/thumbnails/' |
17 | torrents: 'test5/torrents/' | 18 | torrents: 'test5/torrents/' |
19 | cache: 'test5/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin5@example.com' | 22 | email: 'admin5@example.com' |
diff --git a/config/test-6.yaml b/config/test-6.yaml index d74d3b052..87d054439 100644 --- a/config/test-6.yaml +++ b/config/test-6.yaml | |||
@@ -13,8 +13,10 @@ storage: | |||
13 | certs: 'test6/certs/' | 13 | certs: 'test6/certs/' |
14 | videos: 'test6/videos/' | 14 | videos: 'test6/videos/' |
15 | logs: 'test6/logs/' | 15 | logs: 'test6/logs/' |
16 | previews: 'test6/previews/' | ||
16 | thumbnails: 'test6/thumbnails/' | 17 | thumbnails: 'test6/thumbnails/' |
17 | torrents: 'test6/torrents/' | 18 | torrents: 'test6/torrents/' |
19 | cache: 'test6/cache/' | ||
18 | 20 | ||
19 | admin: | 21 | admin: |
20 | email: 'admin6@example.com' | 22 | email: 'admin6@example.com' |
diff --git a/package.json b/package.json index b875f5c26..d6da61975 100644 --- a/package.json +++ b/package.json | |||
@@ -47,6 +47,7 @@ | |||
47 | }, | 47 | }, |
48 | "dependencies": { | 48 | "dependencies": { |
49 | "async": "^2.0.0", | 49 | "async": "^2.0.0", |
50 | "async-lru": "^1.1.1", | ||
50 | "bcrypt": "^1.0.2", | 51 | "bcrypt": "^1.0.2", |
51 | "bittorrent-tracker": "^9.0.0", | 52 | "bittorrent-tracker": "^9.0.0", |
52 | "bluebird": "^3.5.0", | 53 | "bluebird": "^3.5.0", |
@@ -47,7 +47,7 @@ if (errorMessage !== null) { | |||
47 | 47 | ||
48 | // ----------- PeerTube modules ----------- | 48 | // ----------- PeerTube modules ----------- |
49 | import { migrate, installApplication } from './server/initializers' | 49 | import { migrate, installApplication } from './server/initializers' |
50 | import { JobScheduler, activateSchedulers } from './server/lib' | 50 | import { JobScheduler, activateSchedulers, VideosPreviewCache } from './server/lib' |
51 | import * as customValidators from './server/helpers/custom-validators' | 51 | import * as customValidators from './server/helpers/custom-validators' |
52 | import { apiRouter, clientsRouter, staticRouter } from './server/controllers' | 52 | import { apiRouter, clientsRouter, staticRouter } from './server/controllers' |
53 | 53 | ||
@@ -147,6 +147,8 @@ function onDatabaseInitDone () { | |||
147 | // Activate job scheduler | 147 | // Activate job scheduler |
148 | JobScheduler.Instance.activate() | 148 | JobScheduler.Instance.activate() |
149 | 149 | ||
150 | VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE) | ||
151 | |||
150 | logger.info('Server listening on port %d', port) | 152 | logger.info('Server listening on port %d', port) |
151 | logger.info('Webserver: %s', CONFIG.WEBSERVER.URL) | 153 | logger.info('Webserver: %s', CONFIG.WEBSERVER.URL) |
152 | }) | 154 | }) |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index e65282339..2fd14131e 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -6,6 +6,7 @@ import { | |||
6 | STATIC_MAX_AGE, | 6 | STATIC_MAX_AGE, |
7 | STATIC_PATHS | 7 | STATIC_PATHS |
8 | } from '../initializers' | 8 | } from '../initializers' |
9 | import { VideosPreviewCache } from '../lib' | ||
9 | 10 | ||
10 | const staticRouter = express.Router() | 11 | const staticRouter = express.Router() |
11 | 12 | ||
@@ -38,8 +39,8 @@ staticRouter.use( | |||
38 | // Video previews path for express | 39 | // Video previews path for express |
39 | const previewsPhysicalPath = CONFIG.STORAGE.PREVIEWS_DIR | 40 | const previewsPhysicalPath = CONFIG.STORAGE.PREVIEWS_DIR |
40 | staticRouter.use( | 41 | staticRouter.use( |
41 | STATIC_PATHS.PREVIEWS, | 42 | STATIC_PATHS.PREVIEWS + ':uuid.jpg', |
42 | express.static(previewsPhysicalPath, { maxAge: STATIC_MAX_AGE }) | 43 | getPreview |
43 | ) | 44 | ) |
44 | 45 | ||
45 | // --------------------------------------------------------------------------- | 46 | // --------------------------------------------------------------------------- |
@@ -47,3 +48,14 @@ staticRouter.use( | |||
47 | export { | 48 | export { |
48 | staticRouter | 49 | staticRouter |
49 | } | 50 | } |
51 | |||
52 | // --------------------------------------------------------------------------- | ||
53 | |||
54 | function getPreview (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
55 | VideosPreviewCache.Instance.getPreviewPath(req.params.uuid) | ||
56 | .then(path => { | ||
57 | if (!path) return res.sendStatus(404) | ||
58 | |||
59 | return res.sendFile(path, { maxAge: STATIC_MAX_AGE }) | ||
60 | }) | ||
61 | } | ||
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts index 1e92049f1..d28c97f09 100644 --- a/server/helpers/core-utils.ts +++ b/server/helpers/core-utils.ts | |||
@@ -16,6 +16,7 @@ import { | |||
16 | import * as mkdirp from 'mkdirp' | 16 | import * as mkdirp from 'mkdirp' |
17 | import * as bcrypt from 'bcrypt' | 17 | import * as bcrypt from 'bcrypt' |
18 | import * as createTorrent from 'create-torrent' | 18 | import * as createTorrent from 'create-torrent' |
19 | import * as rimraf from 'rimraf' | ||
19 | import * as openssl from 'openssl-wrapper' | 20 | import * as openssl from 'openssl-wrapper' |
20 | import * as Promise from 'bluebird' | 21 | import * as Promise from 'bluebird' |
21 | 22 | ||
@@ -83,6 +84,7 @@ const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare) | |||
83 | const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) | 84 | const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) |
84 | const bcryptHashPromise = promisify2<any, string|number, string>(bcrypt.hash) | 85 | const bcryptHashPromise = promisify2<any, string|number, string>(bcrypt.hash) |
85 | const createTorrentPromise = promisify2<string, any, any>(createTorrent) | 86 | const createTorrentPromise = promisify2<string, any, any>(createTorrent) |
87 | const rimrafPromise = promisify1WithVoid<string>(rimraf) | ||
86 | 88 | ||
87 | // --------------------------------------------------------------------------- | 89 | // --------------------------------------------------------------------------- |
88 | 90 | ||
@@ -105,5 +107,6 @@ export { | |||
105 | bcryptComparePromise, | 107 | bcryptComparePromise, |
106 | bcryptGenSaltPromise, | 108 | bcryptGenSaltPromise, |
107 | bcryptHashPromise, | 109 | bcryptHashPromise, |
108 | createTorrentPromise | 110 | createTorrentPromise, |
111 | rimrafPromise | ||
109 | } | 112 | } |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index f087b7476..928a3f570 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -61,7 +61,8 @@ const CONFIG = { | |||
61 | VIDEOS_DIR: join(root(), config.get<string>('storage.videos')), | 61 | VIDEOS_DIR: join(root(), config.get<string>('storage.videos')), |
62 | THUMBNAILS_DIR: join(root(), config.get<string>('storage.thumbnails')), | 62 | THUMBNAILS_DIR: join(root(), config.get<string>('storage.thumbnails')), |
63 | PREVIEWS_DIR: join(root(), config.get<string>('storage.previews')), | 63 | PREVIEWS_DIR: join(root(), config.get<string>('storage.previews')), |
64 | TORRENTS_DIR: join(root(), config.get<string>('storage.torrents')) | 64 | TORRENTS_DIR: join(root(), config.get<string>('storage.torrents')), |
65 | CACHE_DIR: join(root(), config.get<string>('storage.cache')) | ||
65 | }, | 66 | }, |
66 | WEBSERVER: { | 67 | WEBSERVER: { |
67 | SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http', | 68 | SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http', |
@@ -80,6 +81,11 @@ const CONFIG = { | |||
80 | TRANSCODING: { | 81 | TRANSCODING: { |
81 | ENABLED: config.get<boolean>('transcoding.enabled'), | 82 | ENABLED: config.get<boolean>('transcoding.enabled'), |
82 | THREADS: config.get<number>('transcoding.threads') | 83 | THREADS: config.get<number>('transcoding.threads') |
84 | }, | ||
85 | CACHE: { | ||
86 | PREVIEWS: { | ||
87 | SIZE: config.get<number>('cache.previews.size') | ||
88 | } | ||
83 | } | 89 | } |
84 | } | 90 | } |
85 | CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | 91 | CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT |
@@ -278,6 +284,13 @@ let STATIC_MAX_AGE = '30d' | |||
278 | const THUMBNAILS_SIZE = '200x110' | 284 | const THUMBNAILS_SIZE = '200x110' |
279 | const PREVIEWS_SIZE = '640x480' | 285 | const PREVIEWS_SIZE = '640x480' |
280 | 286 | ||
287 | // Subfolders of cache directory | ||
288 | const CACHE = { | ||
289 | DIRECTORIES: { | ||
290 | PREVIEWS: join(CONFIG.STORAGE.CACHE_DIR, 'previews') | ||
291 | } | ||
292 | } | ||
293 | |||
281 | // --------------------------------------------------------------------------- | 294 | // --------------------------------------------------------------------------- |
282 | 295 | ||
283 | const USER_ROLES: { [ id: string ]: UserRole } = { | 296 | const USER_ROLES: { [ id: string ]: UserRole } = { |
@@ -307,6 +320,7 @@ if (isTestInstance() === true) { | |||
307 | export { | 320 | export { |
308 | API_VERSION, | 321 | API_VERSION, |
309 | BCRYPT_SALT_SIZE, | 322 | BCRYPT_SALT_SIZE, |
323 | CACHE, | ||
310 | CONFIG, | 324 | CONFIG, |
311 | CONSTRAINTS_FIELDS, | 325 | CONSTRAINTS_FIELDS, |
312 | FRIEND_SCORE, | 326 | FRIEND_SCORE, |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 1ec24c4ad..3c5a77df9 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -4,12 +4,13 @@ import * as passwordGenerator from 'password-generator' | |||
4 | import * as Promise from 'bluebird' | 4 | import * as Promise from 'bluebird' |
5 | 5 | ||
6 | import { database as db } from './database' | 6 | import { database as db } from './database' |
7 | import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION } from './constants' | 7 | import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION, CACHE } from './constants' |
8 | import { clientsExist, usersExist } from './checker' | 8 | import { clientsExist, usersExist } from './checker' |
9 | import { logger, createCertsIfNotExist, root, mkdirpPromise } from '../helpers' | 9 | import { logger, createCertsIfNotExist, root, mkdirpPromise, rimrafPromise } from '../helpers' |
10 | 10 | ||
11 | function installApplication () { | 11 | function installApplication () { |
12 | return db.sequelize.sync() | 12 | return db.sequelize.sync() |
13 | .then(() => removeCacheDirectories()) | ||
13 | .then(() => createDirectoriesIfNotExist()) | 14 | .then(() => createDirectoriesIfNotExist()) |
14 | .then(() => createCertsIfNotExist()) | 15 | .then(() => createCertsIfNotExist()) |
15 | .then(() => createOAuthClientIfNotExist()) | 16 | .then(() => createOAuthClientIfNotExist()) |
@@ -24,13 +25,34 @@ export { | |||
24 | 25 | ||
25 | // --------------------------------------------------------------------------- | 26 | // --------------------------------------------------------------------------- |
26 | 27 | ||
28 | function removeCacheDirectories () { | ||
29 | const cacheDirectories = CACHE.DIRECTORIES | ||
30 | |||
31 | const tasks = [] | ||
32 | |||
33 | // Cache directories | ||
34 | Object.keys(cacheDirectories).forEach(key => { | ||
35 | const dir = cacheDirectories[key] | ||
36 | tasks.push(rimrafPromise(dir)) | ||
37 | }) | ||
38 | |||
39 | return Promise.all(tasks) | ||
40 | } | ||
41 | |||
27 | function createDirectoriesIfNotExist () { | 42 | function createDirectoriesIfNotExist () { |
28 | const storages = config.get('storage') | 43 | const storages = CONFIG.STORAGE |
44 | const cacheDirectories = CACHE.DIRECTORIES | ||
29 | 45 | ||
30 | const tasks = [] | 46 | const tasks = [] |
31 | Object.keys(storages).forEach(key => { | 47 | Object.keys(storages).forEach(key => { |
32 | const dir = storages[key] | 48 | const dir = storages[key] |
33 | tasks.push(mkdirpPromise(join(root(), dir))) | 49 | tasks.push(mkdirpPromise(dir)) |
50 | }) | ||
51 | |||
52 | // Cache directories | ||
53 | Object.keys(cacheDirectories).forEach(key => { | ||
54 | const dir = cacheDirectories[key] | ||
55 | tasks.push(mkdirpPromise(dir)) | ||
34 | }) | 56 | }) |
35 | 57 | ||
36 | return Promise.all(tasks) | 58 | return Promise.all(tasks) |
diff --git a/server/lib/cache/index.ts b/server/lib/cache/index.ts new file mode 100644 index 000000000..7bf63790a --- /dev/null +++ b/server/lib/cache/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './videos-preview-cache' | |||
diff --git a/server/lib/cache/videos-preview-cache.ts b/server/lib/cache/videos-preview-cache.ts new file mode 100644 index 000000000..9d365e496 --- /dev/null +++ b/server/lib/cache/videos-preview-cache.ts | |||
@@ -0,0 +1,74 @@ | |||
1 | import * as request from 'request' | ||
2 | import * as asyncLRU from 'async-lru' | ||
3 | import { join } from 'path' | ||
4 | import { createWriteStream } from 'fs' | ||
5 | import * as Promise from 'bluebird' | ||
6 | |||
7 | import { database as db, CONFIG, CACHE } from '../../initializers' | ||
8 | import { logger, writeFilePromise, unlinkPromise } from '../../helpers' | ||
9 | import { VideoInstance } from '../../models' | ||
10 | import { fetchRemotePreview } from '../../lib' | ||
11 | |||
12 | class VideosPreviewCache { | ||
13 | |||
14 | private static instance: VideosPreviewCache | ||
15 | |||
16 | private lru | ||
17 | |||
18 | private constructor () { } | ||
19 | |||
20 | static get Instance () { | ||
21 | return this.instance || (this.instance = new this()) | ||
22 | } | ||
23 | |||
24 | init (max: number) { | ||
25 | this.lru = new asyncLRU({ | ||
26 | max, | ||
27 | load: (key, cb) => { | ||
28 | this.loadPreviews(key) | ||
29 | .then(res => cb(null, res)) | ||
30 | .catch(err => cb(err)) | ||
31 | } | ||
32 | }) | ||
33 | |||
34 | this.lru.on('evict', (obj: { key: string, value: string }) => { | ||
35 | unlinkPromise(obj.value).then(() => logger.debug('%s evicted from VideosPreviewCache', obj.value)) | ||
36 | }) | ||
37 | } | ||
38 | |||
39 | getPreviewPath (key: string) { | ||
40 | return new Promise<string>((res, rej) => { | ||
41 | this.lru.get(key, (err, value) => { | ||
42 | err ? rej(err) : res(value) | ||
43 | }) | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | private loadPreviews (key: string) { | ||
48 | return db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(key) | ||
49 | .then(video => { | ||
50 | if (!video) return undefined | ||
51 | |||
52 | if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()) | ||
53 | |||
54 | return this.saveRemotePreviewAndReturnPath(video) | ||
55 | }) | ||
56 | } | ||
57 | |||
58 | private saveRemotePreviewAndReturnPath (video: VideoInstance) { | ||
59 | const req = fetchRemotePreview(video.Author.Pod, video) | ||
60 | |||
61 | return new Promise<string>((res, rej) => { | ||
62 | const path = join(CACHE.DIRECTORIES.PREVIEWS, video.getPreviewName()) | ||
63 | const stream = createWriteStream(path) | ||
64 | |||
65 | req.pipe(stream) | ||
66 | .on('finish', () => res(path)) | ||
67 | .on('error', (err) => rej(err)) | ||
68 | }) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | export { | ||
73 | VideosPreviewCache | ||
74 | } | ||
diff --git a/server/lib/friends.ts b/server/lib/friends.ts index 6ed0da013..50355d5d1 100644 --- a/server/lib/friends.ts +++ b/server/lib/friends.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as request from 'request' | 1 | import * as request from 'request' |
2 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | import * as Promise from 'bluebird' | 3 | import * as Promise from 'bluebird' |
4 | import { join } from 'path' | ||
4 | 5 | ||
5 | import { database as db } from '../initializers/database' | 6 | import { database as db } from '../initializers/database' |
6 | import { | 7 | import { |
@@ -9,7 +10,8 @@ import { | |||
9 | REQUESTS_IN_PARALLEL, | 10 | REQUESTS_IN_PARALLEL, |
10 | REQUEST_ENDPOINTS, | 11 | REQUEST_ENDPOINTS, |
11 | REQUEST_ENDPOINT_ACTIONS, | 12 | REQUEST_ENDPOINT_ACTIONS, |
12 | REMOTE_SCHEME | 13 | REMOTE_SCHEME, |
14 | STATIC_PATHS | ||
13 | } from '../initializers' | 15 | } from '../initializers' |
14 | import { | 16 | import { |
15 | logger, | 17 | logger, |
@@ -233,6 +235,13 @@ function sendOwnedVideosToPod (podId: number) { | |||
233 | }) | 235 | }) |
234 | } | 236 | } |
235 | 237 | ||
238 | function fetchRemotePreview (pod: PodInstance, video: VideoInstance) { | ||
239 | const host = video.Author.Pod.host | ||
240 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | ||
241 | |||
242 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | ||
243 | } | ||
244 | |||
236 | function getRequestScheduler () { | 245 | function getRequestScheduler () { |
237 | return requestScheduler | 246 | return requestScheduler |
238 | } | 247 | } |
@@ -263,7 +272,8 @@ export { | |||
263 | sendOwnedVideosToPod, | 272 | sendOwnedVideosToPod, |
264 | getRequestScheduler, | 273 | getRequestScheduler, |
265 | getRequestVideoQaduScheduler, | 274 | getRequestVideoQaduScheduler, |
266 | getRequestVideoEventScheduler | 275 | getRequestVideoEventScheduler, |
276 | fetchRemotePreview | ||
267 | } | 277 | } |
268 | 278 | ||
269 | // --------------------------------------------------------------------------- | 279 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/index.ts b/server/lib/index.ts index b8697fb96..8628da4dd 100644 --- a/server/lib/index.ts +++ b/server/lib/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './cache' | ||
1 | export * from './jobs' | 2 | export * from './jobs' |
2 | export * from './request' | 3 | export * from './request' |
3 | export * from './friends' | 4 | export * from './friends' |
diff --git a/server/models/oauth/oauth-token-interface.ts b/server/models/oauth/oauth-token-interface.ts index f2ddafa54..97af3c815 100644 --- a/server/models/oauth/oauth-token-interface.ts +++ b/server/models/oauth/oauth-token-interface.ts | |||
@@ -35,6 +35,8 @@ export interface OAuthTokenAttributes { | |||
35 | refreshToken: string | 35 | refreshToken: string |
36 | refreshTokenExpiresAt: Date | 36 | refreshTokenExpiresAt: Date |
37 | 37 | ||
38 | userId?: number | ||
39 | oAuthClientId?: number | ||
38 | User?: UserModel | 40 | User?: UserModel |
39 | } | 41 | } |
40 | 42 | ||
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 5c3781394..e3de9468e 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts | |||
@@ -106,10 +106,10 @@ getByRefreshTokenAndPopulateClient = function (refreshToken: string) { | |||
106 | refreshToken: token.refreshToken, | 106 | refreshToken: token.refreshToken, |
107 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, | 107 | refreshTokenExpiresAt: token.refreshTokenExpiresAt, |
108 | client: { | 108 | client: { |
109 | id: token['client'].id | 109 | id: token.oAuthClientId |
110 | }, | 110 | }, |
111 | user: { | 111 | user: { |
112 | id: token['user'] | 112 | id: token.userId |
113 | } | 113 | } |
114 | } | 114 | } |
115 | 115 | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 650025205..b7eb24c4a 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -451,6 +451,7 @@ toFormatedJSON = function (this: VideoInstance) { | |||
451 | dislikes: this.dislikes, | 451 | dislikes: this.dislikes, |
452 | tags: map<TagInstance, string>(this.Tags, 'name'), | 452 | tags: map<TagInstance, string>(this.Tags, 'name'), |
453 | thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()), | 453 | thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()), |
454 | previewPath: join(STATIC_PATHS.PREVIEWS, this.getPreviewName()), | ||
454 | createdAt: this.createdAt, | 455 | createdAt: this.createdAt, |
455 | updatedAt: this.updatedAt | 456 | updatedAt: this.updatedAt |
456 | } | 457 | } |
diff --git a/server/tests/api/fixtures/video_short1-preview.webm.jpg b/server/tests/api/fixtures/video_short1-preview.webm.jpg new file mode 100644 index 000000000..69c100c4e --- /dev/null +++ b/server/tests/api/fixtures/video_short1-preview.webm.jpg | |||
Binary files differ | |||
diff --git a/server/tests/api/multiple-pods.js b/server/tests/api/multiple-pods.js index 1bc6157e8..7753e6f2d 100644 --- a/server/tests/api/multiple-pods.js +++ b/server/tests/api/multiple-pods.js | |||
@@ -747,7 +747,7 @@ describe('Test multiple pods', function () { | |||
747 | expect(videos[0].name).not.to.equal(toRemove[1].name) | 747 | expect(videos[0].name).not.to.equal(toRemove[1].name) |
748 | expect(videos[1].name).not.to.equal(toRemove[1].name) | 748 | expect(videos[1].name).not.to.equal(toRemove[1].name) |
749 | 749 | ||
750 | videoUUID = videos[0].uuid | 750 | videoUUID = videos.find(video => video.name === 'my super name for pod 1').uuid |
751 | 751 | ||
752 | callback() | 752 | callback() |
753 | }) | 753 | }) |
@@ -781,6 +781,23 @@ describe('Test multiple pods', function () { | |||
781 | }) | 781 | }) |
782 | }, done) | 782 | }, done) |
783 | }) | 783 | }) |
784 | |||
785 | it('Should get the preview from each pod', function (done) { | ||
786 | each(servers, function (server, callback) { | ||
787 | videosUtils.getVideo(server.url, videoUUID, function (err, res) { | ||
788 | if (err) throw err | ||
789 | |||
790 | const video = res.body | ||
791 | |||
792 | videosUtils.testVideoImage(server.url, 'video_short1-preview.webm', video.previewPath, function (err, test) { | ||
793 | if (err) throw err | ||
794 | expect(test).to.equal(true) | ||
795 | |||
796 | callback() | ||
797 | }) | ||
798 | }) | ||
799 | }, done) | ||
800 | }) | ||
784 | }) | 801 | }) |
785 | 802 | ||
786 | after(function (done) { | 803 | after(function (done) { |
diff --git a/server/tests/utils/videos.js b/server/tests/utils/videos.js index 6e7aabc5d..cb3be6897 100644 --- a/server/tests/utils/videos.js +++ b/server/tests/utils/videos.js | |||
@@ -195,7 +195,7 @@ function searchVideoWithSort (url, search, sort, end) { | |||
195 | .end(end) | 195 | .end(end) |
196 | } | 196 | } |
197 | 197 | ||
198 | function testVideoImage (url, videoName, imagePath, callback) { | 198 | function testVideoImage (url, imageName, imagePath, callback) { |
199 | // Don't test images if the node env is not set | 199 | // Don't test images if the node env is not set |
200 | // Because we need a special ffmpeg version for this test | 200 | // Because we need a special ffmpeg version for this test |
201 | if (process.env.NODE_TEST_IMAGE) { | 201 | if (process.env.NODE_TEST_IMAGE) { |
@@ -205,7 +205,7 @@ function testVideoImage (url, videoName, imagePath, callback) { | |||
205 | .end(function (err, res) { | 205 | .end(function (err, res) { |
206 | if (err) return callback(err) | 206 | if (err) return callback(err) |
207 | 207 | ||
208 | fs.readFile(pathUtils.join(__dirname, '..', 'api', 'fixtures', videoName + '.jpg'), function (err, data) { | 208 | fs.readFile(pathUtils.join(__dirname, '..', 'api', 'fixtures', imageName + '.jpg'), function (err, data) { |
209 | if (err) return callback(err) | 209 | if (err) return callback(err) |
210 | 210 | ||
211 | callback(null, data.equals(res.body)) | 211 | callback(null, data.equals(res.body)) |
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts index d472cc8fb..8aa8ee448 100644 --- a/shared/models/videos/video.model.ts +++ b/shared/models/videos/video.model.ts | |||
@@ -17,6 +17,7 @@ export interface Video { | |||
17 | podHost: string | 17 | podHost: string |
18 | tags: string[] | 18 | tags: string[] |
19 | thumbnailPath: string | 19 | thumbnailPath: string |
20 | previewPath: string | ||
20 | views: number | 21 | views: number |
21 | likes: number | 22 | likes: number |
22 | dislikes: number | 23 | dislikes: number |
@@ -285,6 +285,12 @@ async-each@^1.0.0: | |||
285 | version "1.0.1" | 285 | version "1.0.1" |
286 | resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" | 286 | resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" |
287 | 287 | ||
288 | async-lru@^1.1.1: | ||
289 | version "1.1.1" | ||
290 | resolved "https://registry.yarnpkg.com/async-lru/-/async-lru-1.1.1.tgz#3edbf7e96484d5c2dd852a8bf9794fc07f5e7274" | ||
291 | dependencies: | ||
292 | lru "^3.1.0" | ||
293 | |||
288 | async@>=0.2.9, async@^2.0.0: | 294 | async@>=0.2.9, async@^2.0.0: |
289 | version "2.4.1" | 295 | version "2.4.1" |
290 | resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" | 296 | resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" |