Dirty webtorrent fix to wait FS sync
[oweals/peertube.git] / server / helpers / webtorrent.ts
1 import { logger } from './logger'
2 import * as WebTorrent from 'webtorrent'
3 import { remove } from 'fs-extra'
4 import { CONFIG } from '../initializers'
5 import { join } from 'path'
6
7 function downloadWebTorrentVideo (target: { magnetUri: string, torrentName?: string }, timeout?: number) {
8   const id = target.magnetUri || target.torrentName
9   let timer
10
11   logger.info('Importing torrent video %s', id)
12
13   return new Promise<string>((res, rej) => {
14     const webtorrent = new WebTorrent()
15     let file: WebTorrent.TorrentFile
16
17     const torrentId = target.magnetUri || join(CONFIG.STORAGE.TORRENTS_DIR, target.torrentName)
18
19     const options = { path: CONFIG.STORAGE.VIDEOS_DIR }
20     const torrent = webtorrent.add(torrentId, options, torrent => {
21       if (torrent.files.length !== 1) {
22         if (timer) clearTimeout(timer)
23
24         return safeWebtorrentDestroy(webtorrent, torrentId, file.name, target.torrentName)
25           .then(() => rej(new Error('Cannot import torrent ' + torrentId + ': there are multiple files in it')))
26       }
27
28       torrent.on('done', () => {
29         // FIXME: Dirty fix, we need to wait the FS sync but webtorrent does not provide such method
30         setTimeout(() => res(join(CONFIG.STORAGE.VIDEOS_DIR, torrent.files[ 0 ].name)), 1000)
31       })
32     })
33
34     torrent.on('error', err => rej(err))
35
36     if (timeout) {
37       timer = setTimeout(async () => {
38         return safeWebtorrentDestroy(webtorrent, torrentId, file ? file.name : undefined, target.torrentName)
39           .then(() => rej(new Error('Webtorrent download timeout.')))
40       }, timeout)
41     }
42   })
43 }
44
45 // ---------------------------------------------------------------------------
46
47 export {
48   downloadWebTorrentVideo
49 }
50
51 // ---------------------------------------------------------------------------
52
53 function safeWebtorrentDestroy (webtorrent: WebTorrent.Instance, torrentId: string, filename?: string, torrentName?: string) {
54   return new Promise(res => {
55     webtorrent.destroy(err => {
56       // Delete torrent file
57       if (torrentName) {
58         remove(torrentId)
59           .catch(err => logger.error('Cannot remove torrent %s in webtorrent download.', torrentId, { err }))
60       }
61
62       // Delete downloaded file
63       if (filename) {
64         remove(join(CONFIG.STORAGE.VIDEOS_DIR, filename))
65           .catch(err => logger.error('Cannot remove torrent file %s in webtorrent download.', filename, { err }))
66       }
67
68       if (err) {
69         logger.warn('Cannot destroy webtorrent in timeout.', { err })
70       }
71
72       return res()
73     })
74   })
75 }