]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Optimize torrent URL update
authorChocobozzz <me@florianbigard.com>
Wed, 18 Aug 2021 07:14:51 +0000 (09:14 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 18 Aug 2021 07:14:51 +0000 (09:14 +0200)
.github/workflows/test.yml
package.json
scripts/update-host.ts
server/helpers/webtorrent.ts
server/lib/job-queue/handlers/move-to-object-storage.ts
server/tests/api/object-storage/videos.ts
shared/extra-utils/miscs/checks.ts
shared/extra-utils/server/servers-command.ts

index 35c91bf85bc6fc7b02b16c48c5f7541c75c9b5ed..f38b07bc696b962207611ecef6b0fbea60496e90 100644 (file)
@@ -32,7 +32,7 @@ jobs:
           - 10389:10389
 
       s3ninja:
-        image: scireum/s3-ninja
+        image: chocobozzz/s3-ninja
         ports:
           - 9444:9000
 
index 32f4e7c31adf3c62171ef837b230571b5361118d..18bce1123fb00a0a0c20d92c4d34a77513ee6cd9 100644 (file)
@@ -77,6 +77,7 @@
     "async": "^3.0.1",
     "async-lru": "^1.1.1",
     "bcrypt": "5.0.1",
+    "bencode": "^2.0.2",
     "bittorrent-tracker": "^9.0.0",
     "bluebird": "^3.5.0",
     "bull": "^3.4.2",
index 9e8dd41cabc44f88ec5035adc63cbcb0dd4fa003..5d81a8d74d5d039694b1e141349a882fc0e495dc 100755 (executable)
@@ -17,7 +17,7 @@ import { VideoCommentModel } from '../server/models/video/video-comment'
 import { AccountModel } from '../server/models/account/account'
 import { VideoChannelModel } from '../server/models/video/video-channel'
 import { initDatabaseModels } from '../server/initializers/database'
-import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
+import { updateTorrentUrls } from '@server/helpers/webtorrent'
 import { getServerActor } from '@server/models/application/application'
 
 run()
@@ -126,7 +126,7 @@ async function run () {
 
     for (const file of video.VideoFiles) {
       console.log('Updating torrent file %s of video %s.', file.resolution, video.uuid)
-      await createTorrentAndSetInfoHash(video, file)
+      await updateTorrentUrls(video, file)
 
       await file.save()
     }
@@ -135,7 +135,7 @@ async function run () {
     for (const file of (playlist?.VideoFiles || [])) {
       console.log('Updating fragmented torrent file %s of video %s.', file.resolution, video.uuid)
 
-      await createTorrentAndSetInfoHash(video, file)
+      await updateTorrentUrls(video, file)
 
       await file.save()
     }
index c84376304458d4b6336d46a0f74c94f40b22b9b4..0129184687eb6993e09b20a67fa466661f988504 100644 (file)
@@ -1,5 +1,6 @@
+import * as bencode from 'bencode'
 import * as createTorrent from 'create-torrent'
-import { createWriteStream, ensureDir, remove, writeFile } from 'fs-extra'
+import { createWriteStream, ensureDir, readFile, remove, writeFile } from 'fs-extra'
 import * as magnetUtil from 'magnet-uri'
 import * as parseTorrent from 'parse-torrent'
 import { dirname, join } from 'path'
@@ -79,43 +80,65 @@ async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName
   })
 }
 
-function createTorrentAndSetInfoHash (
-  videoOrPlaylist: MVideo | MStreamingPlaylistVideo,
-  videoFile: MVideoFile
-) {
+function createTorrentAndSetInfoHash (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) {
   const video = extractVideo(videoOrPlaylist)
 
   const options = {
     // Keep the extname, it's used by the client to stream the file inside a web browser
     name: `${video.name} ${videoFile.resolution}p${videoFile.extname}`,
     createdBy: 'PeerTube',
-    announceList: [
-      [ WEBSERVER.WS + '://' + WEBSERVER.HOSTNAME + ':' + WEBSERVER.PORT + '/tracker/socket' ],
-      [ WEBSERVER.URL + '/tracker/announce' ]
-    ],
-    urlList: [ videoFile.getFileUrl(video) ]
+    announceList: buildAnnounceList(),
+    urlList: buildUrlList(video, videoFile)
   }
 
   return VideoPathManager.Instance.makeAvailableVideoFile(videoOrPlaylist, videoFile, async videoPath => {
-    const torrent = await createTorrentPromise(videoPath, options)
+    const torrentContent = await createTorrentPromise(videoPath, options)
 
     const torrentFilename = generateTorrentFileName(videoOrPlaylist, videoFile.resolution)
     const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, torrentFilename)
     logger.info('Creating torrent %s.', torrentPath)
 
-    await writeFile(torrentPath, torrent)
+    await writeFile(torrentPath, torrentContent)
 
     // Remove old torrent file if it existed
     if (videoFile.hasTorrent()) {
       await remove(join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename))
     }
 
-    const parsedTorrent = parseTorrent(torrent)
+    const parsedTorrent = parseTorrent(torrentContent)
     videoFile.infoHash = parsedTorrent.infoHash
     videoFile.torrentFilename = torrentFilename
   })
 }
 
+async function updateTorrentUrls (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) {
+  const video = extractVideo(videoOrPlaylist)
+
+  const oldTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename)
+
+  const torrentContent = await readFile(oldTorrentPath)
+  const decoded = bencode.decode(torrentContent)
+
+  decoded['announce-list'] = buildAnnounceList()
+  decoded.announce = decoded['announce-list'][0][0]
+
+  decoded['url-list'] = buildUrlList(video, videoFile)
+
+  const newTorrentFilename = generateTorrentFileName(videoOrPlaylist, videoFile.resolution)
+  const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, newTorrentFilename)
+
+  logger.info('Updating torrent URLs %s.', newTorrentPath)
+
+  await writeFile(newTorrentPath, bencode.encode(decoded))
+
+  // Remove old torrent file if it existed
+  if (videoFile.hasTorrent()) {
+    await remove(join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename))
+  }
+
+  videoFile.torrentFilename = newTorrentFilename
+}
+
 function generateMagnetUri (
   video: MVideo,
   videoFile: MVideoFileRedundanciesOpt,
@@ -143,6 +166,7 @@ function generateMagnetUri (
 
 export {
   createTorrentPromise,
+  updateTorrentUrls,
   createTorrentAndSetInfoHash,
   generateMagnetUri,
   downloadWebTorrentVideo
@@ -186,3 +210,14 @@ function deleteDownloadedFile (downloadedFile: { directoryPath: string, filepath
   remove(toRemovePath)
     .catch(err => logger.error('Cannot remove torrent file %s in webtorrent download.', toRemovePath, { err }))
 }
+
+function buildAnnounceList () {
+  return [
+    [ WEBSERVER.WS + '://' + WEBSERVER.HOSTNAME + ':' + WEBSERVER.PORT + '/tracker/socket' ],
+    [ WEBSERVER.URL + '/tracker/announce' ]
+  ]
+}
+
+function buildUrlList (video: MVideo, videoFile: MVideoFile) {
+  return [ videoFile.getFileUrl(video) ]
+}
index a0c58d21143755553b154ff8d537011d9450c760..f3b8726eb19d6535d3f14a82438f4fe6955de75a 100644 (file)
@@ -2,7 +2,7 @@ import * as Bull from 'bull'
 import { remove } from 'fs-extra'
 import { join } from 'path'
 import { logger } from '@server/helpers/logger'
-import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
+import { updateTorrentUrls } from '@server/helpers/webtorrent'
 import { CONFIG } from '@server/initializers/config'
 import { storeHLSFile, storeWebTorrentFile } from '@server/lib/object-storage'
 import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
@@ -106,7 +106,7 @@ async function onFileMoved (options: {
   file.fileUrl = fileUrl
   file.storage = VideoStorage.OBJECT_STORAGE
 
-  await createTorrentAndSetInfoHash(videoOrPlaylist, file)
+  await updateTorrentUrls(videoOrPlaylist, file)
   await file.save()
 
   logger.debug('Removing %s because it\'s now on object storage', oldPath)
index 3958bd3d7aae2e8a2f53946cb6c1ecee9f77149f..6c9c224eb365f2b11aeaa50dc593c06f354327ad 100644 (file)
@@ -10,6 +10,7 @@ import {
   createMultipleServers,
   createSingleServer,
   doubleFollow,
+  expectLogDoesNotContain,
   expectStartWith,
   killallServers,
   makeRawRequest,
@@ -235,6 +236,12 @@ function runTestSuite (options: {
     }
   })
 
+  it('Should not have downloaded files from object storage', async function () {
+    for (const server of servers) {
+      await expectLogDoesNotContain(server, 'from object storage')
+    }
+  })
+
   after(async function () {
     mockObjectStorage.terminate()
 
index aa2c8e8fa47eea60a77cb668ffc2c51802303a62..b1be214b1848f04a9925379749338dbb5eff778c 100644 (file)
@@ -20,6 +20,12 @@ function expectStartWith (str: string, start: string) {
   expect(str.startsWith(start), `${str} does not start with ${start}`).to.be.true
 }
 
+async function expectLogDoesNotContain (server: PeerTubeServer, str: string) {
+  const content = await server.servers.getLogContent()
+
+  expect(content.toString()).to.not.contain(str)
+}
+
 async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
   const res = await makeGetRequest({
     url,
@@ -46,6 +52,7 @@ async function testFileExistsOrNot (server: PeerTubeServer, directory: string, f
 export {
   dateIsValid,
   testImage,
+  expectLogDoesNotContain,
   testFileExistsOrNot,
   expectStartWith
 }
index 40a11e8d7f160843699e4be21d88716ea9ebd236..776d2123c20aba0757080e176ca7a9f9590cc211 100644 (file)
@@ -55,7 +55,7 @@ export class ServersCommand extends AbstractCommand {
   }
 
   async waitUntilLog (str: string, count = 1, strictCount = true) {
-    const logfile = this.server.servers.buildDirectory('logs/peertube.log')
+    const logfile = this.buildDirectory('logs/peertube.log')
 
     while (true) {
       const buf = await readFile(logfile)
@@ -80,6 +80,10 @@ export class ServersCommand extends AbstractCommand {
     return this.buildDirectory(join('streaming-playlists', 'hls', videoUUID, basename(fileUrl)))
   }
 
+  getLogContent () {
+    return readFile(this.buildDirectory('logs/peertube.log'))
+  }
+
   async getServerFileSize (subPath: string) {
     const path = this.server.servers.buildDirectory(subPath)