diff options
author | Chocobozzz <me@florianbigard.com> | 2021-11-17 16:04:53 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-11-18 09:04:30 +0100 |
commit | b46cf4b920984492df598c1b61179acfc7f6f22e (patch) | |
tree | 21fda049c85be48ab3d37b537aafa98e94649ad7 /server | |
parent | 3cfa817672657df18260ece5b354efa0f3b6e317 (diff) | |
download | PeerTube-b46cf4b920984492df598c1b61179acfc7f6f22e.tar.gz PeerTube-b46cf4b920984492df598c1b61179acfc7f6f22e.tar.zst PeerTube-b46cf4b920984492df598c1b61179acfc7f6f22e.zip |
Add ability to remove hls/webtorrent files
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/videos/files.ts | 79 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 15 | ||||
-rw-r--r-- | server/controllers/api/videos/update.ts | 2 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file-import.ts | 2 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-transcoding.ts | 2 | ||||
-rw-r--r-- | server/middlewares/validators/videos/index.ts | 1 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-files.ts | 104 | ||||
-rw-r--r-- | server/models/redundancy/video-redundancy.ts | 2 | ||||
-rw-r--r-- | server/models/video/video.ts | 4 | ||||
-rw-r--r-- | server/tests/api/check-params/index.ts | 1 | ||||
-rw-r--r-- | server/tests/api/check-params/video-files.ts | 99 | ||||
-rw-r--r-- | server/tests/api/videos/index.ts | 1 | ||||
-rw-r--r-- | server/tests/api/videos/video-files.ts | 70 |
13 files changed, 363 insertions, 19 deletions
diff --git a/server/controllers/api/videos/files.ts b/server/controllers/api/videos/files.ts new file mode 100644 index 000000000..2fe4b5a3f --- /dev/null +++ b/server/controllers/api/videos/files.ts | |||
@@ -0,0 +1,79 @@ | |||
1 | import express from 'express' | ||
2 | import toInt from 'validator/lib/toInt' | ||
3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | ||
4 | import { federateVideoIfNeeded } from '@server/lib/activitypub/videos' | ||
5 | import { VideoFileModel } from '@server/models/video/video-file' | ||
6 | import { HttpStatusCode } from '@shared/models' | ||
7 | import { | ||
8 | asyncMiddleware, | ||
9 | authenticate, | ||
10 | videoFileMetadataGetValidator, | ||
11 | videoFilesDeleteHLSValidator, | ||
12 | videoFilesDeleteWebTorrentValidator | ||
13 | } from '../../../middlewares' | ||
14 | |||
15 | const lTags = loggerTagsFactory('api', 'video') | ||
16 | const filesRouter = express.Router() | ||
17 | |||
18 | filesRouter.get('/:id/metadata/:videoFileId', | ||
19 | asyncMiddleware(videoFileMetadataGetValidator), | ||
20 | asyncMiddleware(getVideoFileMetadata) | ||
21 | ) | ||
22 | |||
23 | filesRouter.delete('/:id/hls', | ||
24 | authenticate, | ||
25 | asyncMiddleware(videoFilesDeleteHLSValidator), | ||
26 | asyncMiddleware(removeHLSPlaylist) | ||
27 | ) | ||
28 | |||
29 | filesRouter.delete('/:id/webtorrent', | ||
30 | authenticate, | ||
31 | asyncMiddleware(videoFilesDeleteWebTorrentValidator), | ||
32 | asyncMiddleware(removeWebTorrentFiles) | ||
33 | ) | ||
34 | |||
35 | // --------------------------------------------------------------------------- | ||
36 | |||
37 | export { | ||
38 | filesRouter | ||
39 | } | ||
40 | |||
41 | // --------------------------------------------------------------------------- | ||
42 | |||
43 | async function getVideoFileMetadata (req: express.Request, res: express.Response) { | ||
44 | const videoFile = await VideoFileModel.loadWithMetadata(toInt(req.params.videoFileId)) | ||
45 | |||
46 | return res.json(videoFile.metadata) | ||
47 | } | ||
48 | |||
49 | async function removeHLSPlaylist (req: express.Request, res: express.Response) { | ||
50 | const video = res.locals.videoAll | ||
51 | |||
52 | logger.info('Deleting HLS playlist of %s.', video.url, lTags(video.uuid)) | ||
53 | |||
54 | const hls = video.getHLSPlaylist() | ||
55 | await video.removeStreamingPlaylistFiles(hls) | ||
56 | await hls.destroy() | ||
57 | |||
58 | video.VideoStreamingPlaylists = video.VideoStreamingPlaylists.filter(p => p.id !== hls.id) | ||
59 | |||
60 | await federateVideoIfNeeded(video, false, undefined) | ||
61 | |||
62 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
63 | } | ||
64 | |||
65 | async function removeWebTorrentFiles (req: express.Request, res: express.Response) { | ||
66 | const video = res.locals.videoAll | ||
67 | |||
68 | logger.info('Deleting WebTorrent files of %s.', video.url, lTags(video.uuid)) | ||
69 | |||
70 | for (const file of video.VideoFiles) { | ||
71 | await video.removeWebTorrentFileAndTorrent(file) | ||
72 | await file.destroy() | ||
73 | } | ||
74 | |||
75 | video.VideoFiles = [] | ||
76 | await federateVideoIfNeeded(video, false, undefined) | ||
77 | |||
78 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
79 | } | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 72b382595..2d088a73e 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import toInt from 'validator/lib/toInt' | ||
3 | import { pickCommonVideoQuery } from '@server/helpers/query' | 2 | import { pickCommonVideoQuery } from '@server/helpers/query' |
4 | import { doJSONRequest } from '@server/helpers/requests' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
5 | import { VideoViews } from '@server/lib/video-views' | 4 | import { VideoViews } from '@server/lib/video-views' |
@@ -27,17 +26,16 @@ import { | |||
27 | paginationValidator, | 26 | paginationValidator, |
28 | setDefaultPagination, | 27 | setDefaultPagination, |
29 | setDefaultVideosSort, | 28 | setDefaultVideosSort, |
30 | videoFileMetadataGetValidator, | ||
31 | videosCustomGetValidator, | 29 | videosCustomGetValidator, |
32 | videosGetValidator, | 30 | videosGetValidator, |
33 | videosRemoveValidator, | 31 | videosRemoveValidator, |
34 | videosSortValidator | 32 | videosSortValidator |
35 | } from '../../../middlewares' | 33 | } from '../../../middlewares' |
36 | import { VideoModel } from '../../../models/video/video' | 34 | import { VideoModel } from '../../../models/video/video' |
37 | import { VideoFileModel } from '../../../models/video/video-file' | ||
38 | import { blacklistRouter } from './blacklist' | 35 | import { blacklistRouter } from './blacklist' |
39 | import { videoCaptionsRouter } from './captions' | 36 | import { videoCaptionsRouter } from './captions' |
40 | import { videoCommentRouter } from './comment' | 37 | import { videoCommentRouter } from './comment' |
38 | import { filesRouter } from './files' | ||
41 | import { videoImportsRouter } from './import' | 39 | import { videoImportsRouter } from './import' |
42 | import { liveRouter } from './live' | 40 | import { liveRouter } from './live' |
43 | import { ownershipVideoRouter } from './ownership' | 41 | import { ownershipVideoRouter } from './ownership' |
@@ -59,6 +57,7 @@ videosRouter.use('/', watchingRouter) | |||
59 | videosRouter.use('/', liveRouter) | 57 | videosRouter.use('/', liveRouter) |
60 | videosRouter.use('/', uploadRouter) | 58 | videosRouter.use('/', uploadRouter) |
61 | videosRouter.use('/', updateRouter) | 59 | videosRouter.use('/', updateRouter) |
60 | videosRouter.use('/', filesRouter) | ||
62 | 61 | ||
63 | videosRouter.get('/categories', | 62 | videosRouter.get('/categories', |
64 | openapiOperationDoc({ operationId: 'getCategories' }), | 63 | openapiOperationDoc({ operationId: 'getCategories' }), |
@@ -93,10 +92,6 @@ videosRouter.get('/:id/description', | |||
93 | asyncMiddleware(videosGetValidator), | 92 | asyncMiddleware(videosGetValidator), |
94 | asyncMiddleware(getVideoDescription) | 93 | asyncMiddleware(getVideoDescription) |
95 | ) | 94 | ) |
96 | videosRouter.get('/:id/metadata/:videoFileId', | ||
97 | asyncMiddleware(videoFileMetadataGetValidator), | ||
98 | asyncMiddleware(getVideoFileMetadata) | ||
99 | ) | ||
100 | videosRouter.get('/:id', | 95 | videosRouter.get('/:id', |
101 | openapiOperationDoc({ operationId: 'getVideo' }), | 96 | openapiOperationDoc({ operationId: 'getVideo' }), |
102 | optionalAuthenticate, | 97 | optionalAuthenticate, |
@@ -177,12 +172,6 @@ async function getVideoDescription (req: express.Request, res: express.Response) | |||
177 | return res.json({ description }) | 172 | return res.json({ description }) |
178 | } | 173 | } |
179 | 174 | ||
180 | async function getVideoFileMetadata (req: express.Request, res: express.Response) { | ||
181 | const videoFile = await VideoFileModel.loadWithMetadata(toInt(req.params.videoFileId)) | ||
182 | |||
183 | return res.json(videoFile.metadata) | ||
184 | } | ||
185 | |||
186 | async function listVideos (req: express.Request, res: express.Response) { | 175 | async function listVideos (req: express.Request, res: express.Response) { |
187 | const serverActor = await getServerActor() | 176 | const serverActor = await getServerActor() |
188 | 177 | ||
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts index a0aa13d71..de5d94d55 100644 --- a/server/controllers/api/videos/update.ts +++ b/server/controllers/api/videos/update.ts | |||
@@ -51,7 +51,7 @@ export { | |||
51 | 51 | ||
52 | // --------------------------------------------------------------------------- | 52 | // --------------------------------------------------------------------------- |
53 | 53 | ||
54 | export async function updateVideo (req: express.Request, res: express.Response) { | 54 | async function updateVideo (req: express.Request, res: express.Response) { |
55 | const videoFromReq = res.locals.videoAll | 55 | const videoFromReq = res.locals.videoAll |
56 | const videoFieldsSave = videoFromReq.toJSON() | 56 | const videoFieldsSave = videoFromReq.toJSON() |
57 | const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON()) | 57 | const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON()) |
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 47ae10a66..a91c2ef80 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -55,7 +55,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { | |||
55 | 55 | ||
56 | if (currentVideoFile) { | 56 | if (currentVideoFile) { |
57 | // Remove old file and old torrent | 57 | // Remove old file and old torrent |
58 | await video.removeFileAndTorrent(currentVideoFile) | 58 | await video.removeWebTorrentFileAndTorrent(currentVideoFile) |
59 | // Remove the old video file from the array | 59 | // Remove the old video file from the array |
60 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) | 60 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) |
61 | 61 | ||
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 0143cd02a..904ef2e3c 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -138,7 +138,7 @@ async function onHlsPlaylistGeneration (video: MVideoFullLight, user: MUser, pay | |||
138 | if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { | 138 | if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { |
139 | // Remove webtorrent files if not enabled | 139 | // Remove webtorrent files if not enabled |
140 | for (const file of video.VideoFiles) { | 140 | for (const file of video.VideoFiles) { |
141 | await video.removeFileAndTorrent(file) | 141 | await video.removeWebTorrentFileAndTorrent(file) |
142 | await file.destroy() | 142 | await file.destroy() |
143 | } | 143 | } |
144 | 144 | ||
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts index 369c2c9b6..fd1d58093 100644 --- a/server/middlewares/validators/videos/index.ts +++ b/server/middlewares/validators/videos/index.ts | |||
@@ -2,6 +2,7 @@ export * from './video-blacklist' | |||
2 | export * from './video-captions' | 2 | export * from './video-captions' |
3 | export * from './video-channels' | 3 | export * from './video-channels' |
4 | export * from './video-comments' | 4 | export * from './video-comments' |
5 | export * from './video-files' | ||
5 | export * from './video-imports' | 6 | export * from './video-imports' |
6 | export * from './video-live' | 7 | export * from './video-live' |
7 | export * from './video-ownership-changes' | 8 | export * from './video-ownership-changes' |
diff --git a/server/middlewares/validators/videos/video-files.ts b/server/middlewares/validators/videos/video-files.ts new file mode 100644 index 000000000..282594ab6 --- /dev/null +++ b/server/middlewares/validators/videos/video-files.ts | |||
@@ -0,0 +1,104 @@ | |||
1 | import express from 'express' | ||
2 | import { MUser, MVideo } from '@server/types/models' | ||
3 | import { HttpStatusCode, UserRight } from '../../../../shared' | ||
4 | import { logger } from '../../../helpers/logger' | ||
5 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' | ||
6 | |||
7 | const videoFilesDeleteWebTorrentValidator = [ | ||
8 | isValidVideoIdParam('id'), | ||
9 | |||
10 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
11 | logger.debug('Checking videoFilesDeleteWebTorrent parameters', { parameters: req.params }) | ||
12 | |||
13 | if (areValidationErrors(req, res)) return | ||
14 | if (!await doesVideoExist(req.params.id, res)) return | ||
15 | |||
16 | const video = res.locals.videoAll | ||
17 | const user = res.locals.oauth.token.User | ||
18 | |||
19 | if (!checkUserCanDeleteFiles(user, res)) return | ||
20 | if (!checkLocalVideo(video, res)) return | ||
21 | |||
22 | if (!video.hasWebTorrentFiles()) { | ||
23 | return res.fail({ | ||
24 | status: HttpStatusCode.BAD_REQUEST_400, | ||
25 | message: 'This video does not have WebTorrent files' | ||
26 | }) | ||
27 | } | ||
28 | |||
29 | if (!video.getHLSPlaylist()) { | ||
30 | return res.fail({ | ||
31 | status: HttpStatusCode.BAD_REQUEST_400, | ||
32 | message: 'Cannot delete WebTorrent files since this video does not have HLS playlist' | ||
33 | }) | ||
34 | } | ||
35 | |||
36 | return next() | ||
37 | } | ||
38 | ] | ||
39 | |||
40 | const videoFilesDeleteHLSValidator = [ | ||
41 | isValidVideoIdParam('id'), | ||
42 | |||
43 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
44 | logger.debug('Checking videoFilesDeleteHLS parameters', { parameters: req.params }) | ||
45 | |||
46 | if (areValidationErrors(req, res)) return | ||
47 | if (!await doesVideoExist(req.params.id, res)) return | ||
48 | |||
49 | const video = res.locals.videoAll | ||
50 | const user = res.locals.oauth.token.User | ||
51 | |||
52 | if (!checkUserCanDeleteFiles(user, res)) return | ||
53 | if (!checkLocalVideo(video, res)) return | ||
54 | |||
55 | if (!video.getHLSPlaylist()) { | ||
56 | return res.fail({ | ||
57 | status: HttpStatusCode.BAD_REQUEST_400, | ||
58 | message: 'This video does not have HLS files' | ||
59 | }) | ||
60 | } | ||
61 | |||
62 | if (!video.hasWebTorrentFiles()) { | ||
63 | return res.fail({ | ||
64 | status: HttpStatusCode.BAD_REQUEST_400, | ||
65 | message: 'Cannot delete HLS playlist since this video does not have WebTorrent files' | ||
66 | }) | ||
67 | } | ||
68 | |||
69 | return next() | ||
70 | } | ||
71 | ] | ||
72 | |||
73 | export { | ||
74 | videoFilesDeleteWebTorrentValidator, | ||
75 | videoFilesDeleteHLSValidator | ||
76 | } | ||
77 | |||
78 | // --------------------------------------------------------------------------- | ||
79 | |||
80 | function checkLocalVideo (video: MVideo, res: express.Response) { | ||
81 | if (video.remote) { | ||
82 | res.fail({ | ||
83 | status: HttpStatusCode.BAD_REQUEST_400, | ||
84 | message: 'Cannot delete files of remote video' | ||
85 | }) | ||
86 | |||
87 | return false | ||
88 | } | ||
89 | |||
90 | return true | ||
91 | } | ||
92 | |||
93 | function checkUserCanDeleteFiles (user: MUser, res: express.Response) { | ||
94 | if (user.hasRight(UserRight.MANAGE_VIDEO_FILES) !== true) { | ||
95 | res.fail({ | ||
96 | status: HttpStatusCode.FORBIDDEN_403, | ||
97 | message: 'User cannot update video files' | ||
98 | }) | ||
99 | |||
100 | return false | ||
101 | } | ||
102 | |||
103 | return true | ||
104 | } | ||
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index 529977924..e8d79a3ab 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -160,7 +160,7 @@ export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedu | |||
160 | const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}` | 160 | const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}` |
161 | logger.info('Removing duplicated video file %s.', logIdentifier) | 161 | logger.info('Removing duplicated video file %s.', logIdentifier) |
162 | 162 | ||
163 | videoFile.Video.removeFileAndTorrent(videoFile, true) | 163 | videoFile.Video.removeWebTorrentFileAndTorrent(videoFile, true) |
164 | .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err })) | 164 | .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err })) |
165 | } | 165 | } |
166 | 166 | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 69d009e04..6eeb6b312 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -746,7 +746,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
746 | 746 | ||
747 | // Remove physical files and torrents | 747 | // Remove physical files and torrents |
748 | instance.VideoFiles.forEach(file => { | 748 | instance.VideoFiles.forEach(file => { |
749 | tasks.push(instance.removeFileAndTorrent(file)) | 749 | tasks.push(instance.removeWebTorrentFileAndTorrent(file)) |
750 | }) | 750 | }) |
751 | 751 | ||
752 | // Remove playlists file | 752 | // Remove playlists file |
@@ -1706,7 +1706,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
1706 | .concat(toAdd) | 1706 | .concat(toAdd) |
1707 | } | 1707 | } |
1708 | 1708 | ||
1709 | removeFileAndTorrent (videoFile: MVideoFile, isRedundancy = false) { | 1709 | removeWebTorrentFileAndTorrent (videoFile: MVideoFile, isRedundancy = false) { |
1710 | const filePath = isRedundancy | 1710 | const filePath = isRedundancy |
1711 | ? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile) | 1711 | ? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile) |
1712 | : VideoPathManager.Instance.getFSVideoFileOutputPath(this, videoFile) | 1712 | : VideoPathManager.Instance.getFSVideoFileOutputPath(this, videoFile) |
diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts index 0882f8176..ff7dc4abb 100644 --- a/server/tests/api/check-params/index.ts +++ b/server/tests/api/check-params/index.ts | |||
@@ -28,5 +28,6 @@ import './video-imports' | |||
28 | import './video-playlists' | 28 | import './video-playlists' |
29 | import './videos' | 29 | import './videos' |
30 | import './videos-common-filters' | 30 | import './videos-common-filters' |
31 | import './video-files' | ||
31 | import './videos-history' | 32 | import './videos-history' |
32 | import './videos-overviews' | 33 | import './videos-overviews' |
diff --git a/server/tests/api/check-params/video-files.ts b/server/tests/api/check-params/video-files.ts new file mode 100644 index 000000000..48b10d2b5 --- /dev/null +++ b/server/tests/api/check-params/video-files.ts | |||
@@ -0,0 +1,99 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import { cleanupTests, createMultipleServers, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils' | ||
5 | import { HttpStatusCode, UserRole } from '@shared/models' | ||
6 | |||
7 | describe('Test videos files', function () { | ||
8 | let servers: PeerTubeServer[] | ||
9 | let webtorrentId: string | ||
10 | let hlsId: string | ||
11 | let remoteId: string | ||
12 | let userToken: string | ||
13 | let moderatorToken: string | ||
14 | let validId1: string | ||
15 | let validId2: string | ||
16 | |||
17 | // --------------------------------------------------------------- | ||
18 | |||
19 | before(async function () { | ||
20 | this.timeout(150_000) | ||
21 | |||
22 | servers = await createMultipleServers(2) | ||
23 | await setAccessTokensToServers(servers) | ||
24 | |||
25 | userToken = await servers[0].users.generateUserAndToken('user', UserRole.USER) | ||
26 | moderatorToken = await servers[0].users.generateUserAndToken('moderator', UserRole.MODERATOR) | ||
27 | |||
28 | { | ||
29 | await servers[0].config.enableTranscoding(true, true) | ||
30 | |||
31 | { | ||
32 | const { uuid } = await servers[0].videos.quickUpload({ name: 'both 1' }) | ||
33 | validId1 = uuid | ||
34 | } | ||
35 | |||
36 | { | ||
37 | const { uuid } = await servers[0].videos.quickUpload({ name: 'both 2' }) | ||
38 | validId2 = uuid | ||
39 | } | ||
40 | } | ||
41 | |||
42 | await waitJobs(servers) | ||
43 | |||
44 | { | ||
45 | await servers[0].config.enableTranscoding(false, true) | ||
46 | const { uuid } = await servers[0].videos.quickUpload({ name: 'hls' }) | ||
47 | hlsId = uuid | ||
48 | } | ||
49 | |||
50 | await waitJobs(servers) | ||
51 | |||
52 | { | ||
53 | await servers[0].config.enableTranscoding(false, true) | ||
54 | const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' }) | ||
55 | webtorrentId = uuid | ||
56 | } | ||
57 | |||
58 | await waitJobs(servers) | ||
59 | }) | ||
60 | |||
61 | it('Should not delete files of a remote video', async function () { | ||
62 | await servers[0].videos.removeHLSFiles({ videoId: remoteId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
63 | await servers[0].videos.removeWebTorrentFiles({ videoId: remoteId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
64 | }) | ||
65 | |||
66 | it('Should not delete files by a non admin user', async function () { | ||
67 | const expectedStatus = HttpStatusCode.FORBIDDEN_403 | ||
68 | |||
69 | await servers[0].videos.removeHLSFiles({ videoId: validId1, token: userToken, expectedStatus }) | ||
70 | await servers[0].videos.removeHLSFiles({ videoId: validId1, token: moderatorToken, expectedStatus }) | ||
71 | |||
72 | await servers[0].videos.removeWebTorrentFiles({ videoId: validId1, token: userToken, expectedStatus }) | ||
73 | await servers[0].videos.removeWebTorrentFiles({ videoId: validId1, token: moderatorToken, expectedStatus }) | ||
74 | }) | ||
75 | |||
76 | it('Should not delete files if the files are not available', async function () { | ||
77 | await servers[0].videos.removeHLSFiles({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
78 | await servers[0].videos.removeWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
79 | }) | ||
80 | |||
81 | it('Should not delete files if no both versions are available', async function () { | ||
82 | await servers[0].videos.removeHLSFiles({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
83 | await servers[0].videos.removeWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
84 | }) | ||
85 | |||
86 | it('Should not delete files if no both versions are available', async function () { | ||
87 | await servers[0].videos.removeHLSFiles({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
88 | await servers[0].videos.removeWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
89 | }) | ||
90 | |||
91 | it('Should delete files if both versions are available', async function () { | ||
92 | await servers[0].videos.removeHLSFiles({ videoId: validId1 }) | ||
93 | await servers[0].videos.removeWebTorrentFiles({ videoId: validId2 }) | ||
94 | }) | ||
95 | |||
96 | after(async function () { | ||
97 | await cleanupTests(servers) | ||
98 | }) | ||
99 | }) | ||
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts index c9c678e9d..f92e339e7 100644 --- a/server/tests/api/videos/index.ts +++ b/server/tests/api/videos/index.ts | |||
@@ -7,6 +7,7 @@ import './video-change-ownership' | |||
7 | import './video-channels' | 7 | import './video-channels' |
8 | import './video-comments' | 8 | import './video-comments' |
9 | import './video-description' | 9 | import './video-description' |
10 | import './video-files' | ||
10 | import './video-hls' | 11 | import './video-hls' |
11 | import './video-imports' | 12 | import './video-imports' |
12 | import './video-nsfw' | 13 | import './video-nsfw' |
diff --git a/server/tests/api/videos/video-files.ts b/server/tests/api/videos/video-files.ts new file mode 100644 index 000000000..fcb2ca2e4 --- /dev/null +++ b/server/tests/api/videos/video-files.ts | |||
@@ -0,0 +1,70 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import { expect } from 'chai' | ||
5 | import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils' | ||
6 | |||
7 | describe('Test videos files', function () { | ||
8 | let servers: PeerTubeServer[] | ||
9 | let validId1: string | ||
10 | let validId2: string | ||
11 | |||
12 | // --------------------------------------------------------------- | ||
13 | |||
14 | before(async function () { | ||
15 | this.timeout(150_000) | ||
16 | |||
17 | servers = await createMultipleServers(2) | ||
18 | await setAccessTokensToServers(servers) | ||
19 | |||
20 | await doubleFollow(servers[0], servers[1]) | ||
21 | |||
22 | await servers[0].config.enableTranscoding(true, true) | ||
23 | |||
24 | { | ||
25 | const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' }) | ||
26 | validId1 = uuid | ||
27 | } | ||
28 | |||
29 | { | ||
30 | const { uuid } = await servers[0].videos.quickUpload({ name: 'video 2' }) | ||
31 | validId2 = uuid | ||
32 | } | ||
33 | |||
34 | await waitJobs(servers) | ||
35 | }) | ||
36 | |||
37 | it('Should delete webtorrent files', async function () { | ||
38 | this.timeout(30_000) | ||
39 | |||
40 | await servers[0].videos.removeWebTorrentFiles({ videoId: validId1 }) | ||
41 | |||
42 | await waitJobs(servers) | ||
43 | |||
44 | for (const server of servers) { | ||
45 | const video = await server.videos.get({ id: validId1 }) | ||
46 | |||
47 | expect(video.files).to.have.lengthOf(0) | ||
48 | expect(video.streamingPlaylists).to.have.lengthOf(1) | ||
49 | } | ||
50 | }) | ||
51 | |||
52 | it('Should delete HLS files', async function () { | ||
53 | this.timeout(30_000) | ||
54 | |||
55 | await servers[0].videos.removeHLSFiles({ videoId: validId2 }) | ||
56 | |||
57 | await waitJobs(servers) | ||
58 | |||
59 | for (const server of servers) { | ||
60 | const video = await server.videos.get({ id: validId2 }) | ||
61 | |||
62 | expect(video.files).to.have.length.above(0) | ||
63 | expect(video.streamingPlaylists).to.have.lengthOf(0) | ||
64 | } | ||
65 | }) | ||
66 | |||
67 | after(async function () { | ||
68 | await cleanupTests(servers) | ||
69 | }) | ||
70 | }) | ||