diff --git a/server/lib/plugins/plugin-helpers-builder.ts b/server/lib/plugins/plugin-helpers-builder.ts index e26776f45..bea0f8959 100644 --- a/server/lib/plugins/plugin-helpers-builder.ts +++ b/server/lib/plugins/plugin-helpers-builder.ts @@ -9,15 +9,16 @@ import { AccountBlocklistModel } from '@server/models/account/account-blocklist' import { getServerActor } from '@server/models/application/application' import { ServerModel } from '@server/models/server/server' import { ServerBlocklistModel } from '@server/models/server/server-blocklist' +import { UserModel } from '@server/models/user/user' import { VideoModel } from '@server/models/video/video' import { VideoBlacklistModel } from '@server/models/video/video-blacklist' import { MPlugin } from '@server/types/models' import { PeerTubeHelpers } from '@server/types/plugins' -import { VideoBlacklistCreate } from '@shared/models' +import { VideoBlacklistCreate, VideoStorage } from '@shared/models' import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../blocklist' import { ServerConfigManager } from '../server-config-manager' import { blacklistVideo, unblacklistVideo } from '../video-blacklist' -import { UserModel } from '@server/models/user/user' +import { VideoPathManager } from '../video-path-manager' function buildPluginHelpers (pluginModel: MPlugin, npmName: string): PeerTubeHelpers { const logger = buildPluginLogger(npmName) @@ -85,6 +86,56 @@ function buildVideosHelpers () { await video.destroy({ transaction: t }) }) + }, + + getFiles: async (id: number | string) => { + const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(id) + if (!video) return undefined + + const webtorrentVideoFiles = (video.VideoFiles || []).map(f => ({ + path: f.storage === VideoStorage.FILE_SYSTEM + ? VideoPathManager.Instance.getFSVideoFileOutputPath(video, f) + : null, + url: f.getFileUrl(video), + + resolution: f.resolution, + size: f.size, + fps: f.fps + })) + + const hls = video.getHLSPlaylist() + + const hlsVideoFiles = hls + ? (video.getHLSPlaylist().VideoFiles || []).map(f => { + return { + path: f.storage === VideoStorage.FILE_SYSTEM + ? VideoPathManager.Instance.getFSVideoFileOutputPath(hls, f) + : null, + url: f.getFileUrl(video), + resolution: f.resolution, + size: f.size, + fps: f.fps + } + }) + : [] + + const thumbnails = video.Thumbnails.map(t => ({ + type: t.type, + url: t.getFileUrl(video), + path: t.getPath() + })) + + return { + webtorrent: { + videoFiles: webtorrentVideoFiles + }, + + hls: { + videoFiles: hlsVideoFiles + }, + + thumbnails + } } } } diff --git a/server/tests/fixtures/peertube-plugin-test-four/main.js b/server/tests/fixtures/peertube-plugin-test-four/main.js index b9b207b81..edbb883e2 100644 --- a/server/tests/fixtures/peertube-plugin-test-four/main.js +++ b/server/tests/fixtures/peertube-plugin-test-four/main.js @@ -104,6 +104,13 @@ async function register ({ isUser }) }) + + router.get('/video-files/:id', async (req, res) => { + const details = await peertubeHelpers.videos.getFiles(req.params.id) + if (!details) return res.sendStatus(404) + + return res.json(details) + }) } } diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts index 5d16b28a4..26f66b0b1 100644 --- a/server/tests/plugins/plugin-helpers.ts +++ b/server/tests/plugins/plugin-helpers.ts @@ -2,6 +2,7 @@ import 'mocha' import { expect } from 'chai' +import { pathExists } from 'fs-extra' import { checkVideoFilesWereRemoved, cleanupTests, @@ -9,12 +10,13 @@ import { doubleFollow, makeGetRequest, makePostBodyRequest, + makeRawRequest, PeerTubeServer, PluginsCommand, setAccessTokensToServers, waitJobs } from '@shared/extra-utils' -import { HttpStatusCode } from '@shared/models' +import { HttpStatusCode, ThumbnailType } from '@shared/models' function postCommand (server: PeerTubeServer, command: string, bodyArg?: object) { const body = { command } @@ -224,8 +226,56 @@ describe('Test plugin helpers', function () { let videoUUID: string before(async () => { + this.timeout(240000) + + await servers[0].config.enableTranscoding() + const res = await servers[0].videos.quickUpload({ name: 'video1' }) videoUUID = res.uuid + + await waitJobs(servers) + }) + + it('Should get video files', async function () { + const { body } = await makeGetRequest({ + url: servers[0].url, + path: '/plugins/test-four/router/video-files/' + videoUUID, + expectedStatus: HttpStatusCode.OK_200 + }) + + // Video files check + { + expect(body.webtorrent.videoFiles).to.be.an('array') + expect(body.hls.videoFiles).to.be.an('array') + + for (const resolution of [ 144, 240, 360, 480, 720 ]) { + for (const files of [ body.webtorrent.videoFiles, body.hls.videoFiles ]) { + const file = files.find(f => f.resolution === resolution) + expect(file).to.exist + + expect(file.size).to.be.a('number') + expect(file.fps).to.equal(25) + + expect(await pathExists(file.path)).to.be.true + await makeRawRequest(file.url, HttpStatusCode.OK_200) + } + } + } + + // Thumbnails check + { + expect(body.thumbnails).to.be.an('array') + + const miniature = body.thumbnails.find(t => t.type === ThumbnailType.MINIATURE) + expect(miniature).to.exist + expect(await pathExists(miniature.path)).to.be.true + await makeRawRequest(miniature.url, HttpStatusCode.OK_200) + + const preview = body.thumbnails.find(t => t.type === ThumbnailType.PREVIEW) + expect(preview).to.exist + expect(await pathExists(preview.path)).to.be.true + await makeRawRequest(preview.url, HttpStatusCode.OK_200) + } }) it('Should remove a video after a view', async function () { diff --git a/server/types/plugins/register-server-option.model.ts b/server/types/plugins/register-server-option.model.ts index 8774bcd8c..473990eb6 100644 --- a/server/types/plugins/register-server-option.model.ts +++ b/server/types/plugins/register-server-option.model.ts @@ -13,6 +13,7 @@ import { RegisterServerHookOptions, RegisterServerSettingOptions, ServerConfig, + ThumbnailType, UserRole, VideoBlacklistCreate } from '@shared/models' @@ -35,6 +36,33 @@ export type PeerTubeHelpers = { loadByIdOrUUID: (id: number | string) => Promise removeVideo: (videoId: number) => Promise + + getFiles: (id: number | string) => Promise<{ + webtorrent: { + videoFiles: { + path: string // Could be null if using remote storage + url: string + resolution: number + size: number + fps: number + }[] + } + + hls: { + videoFiles: { + path: string // Could be null if using remote storage + url: string + resolution: number + size: number + fps: number + }[] + } + + thumbnails: { + type: ThumbnailType + path: string + }[] + }> } config: {