diff --git a/client/src/app/+admin/overview/videos/video-list.component.html b/client/src/app/+admin/overview/videos/video-list.component.html index 06da2f96f..6ee4cfadc 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.html +++ b/client/src/app/+admin/overview/videos/video-list.component.html @@ -42,7 +42,7 @@ Video Info - Files + Files Published diff --git a/packages/tests/src/api/videos/multiple-servers.ts b/packages/tests/src/api/videos/multiple-servers.ts index 69d13d48e..6a38017f5 100644 --- a/packages/tests/src/api/videos/multiple-servers.ts +++ b/packages/tests/src/api/videos/multiple-servers.ts @@ -1,25 +1,25 @@ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ -import { expect } from 'chai' -import request from 'supertest' import { wait } from '@peertube/peertube-core-utils' import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@peertube/peertube-models' import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils' import { + PeerTubeServer, cleanupTests, createMultipleServers, doubleFollow, makeGetRequest, - PeerTubeServer, setAccessTokensToServers, setDefaultAccountAvatar, setDefaultChannelAvatar, waitJobs } from '@peertube/peertube-server-commands' -import { testImageGeneratedByFFmpeg, dateIsValid } from '@tests/shared/checks.js' +import { dateIsValid, testImageGeneratedByFFmpeg } from '@tests/shared/checks.js' import { checkTmpIsEmpty } from '@tests/shared/directories.js' -import { completeVideoCheck, saveVideoInServers, checkVideoFilesWereRemoved } from '@tests/shared/videos.js' +import { checkVideoFilesWereRemoved, completeVideoCheck, saveVideoInServers } from '@tests/shared/videos.js' import { checkWebTorrentWorks } from '@tests/shared/webtorrent.js' +import { expect } from 'chai' +import request from 'supertest' describe('Test multiple servers', function () { let servers: PeerTubeServer[] = [] @@ -367,7 +367,8 @@ describe('Test multiple servers', function () { }) }) - describe('It should list local videos', function () { + describe('Local videos listing', function () { + it('Should list only local videos on server 1', async function () { const { data, total } = await servers[0].videos.list({ isLocal: true }) @@ -397,6 +398,21 @@ describe('Test multiple servers', function () { }) }) + describe('All videos listing', function () { + + it('Should list and sort by "localVideoFilesSize"', async function () { + const { data, total } = await servers[2].videos.list({ sort: '-localVideoFilesSize' }) + + expect(total).to.equal(4) + expect(data).to.be.an('array') + expect(data.length).to.equal(4) + expect(data[0].name).to.equal('my super name for server 3') + expect(data[1].name).to.equal('my super name for server 3-2') + expect(data[2].isLocal).to.be.false + expect(data[3].isLocal).to.be.false + }) + }) + describe('Should seed the uploaded video', function () { it('Should add the file 1 by asking server 3', async function () { diff --git a/server/core/initializers/constants.ts b/server/core/initializers/constants.ts index d25ce70b4..03259f9d6 100644 --- a/server/core/initializers/constants.ts +++ b/server/core/initializers/constants.ts @@ -110,7 +110,19 @@ const SORTABLE_COLUMNS = { RUNNER_REGISTRATION_TOKENS: [ 'createdAt' ], RUNNER_JOBS: [ 'updatedAt', 'createdAt', 'priority', 'state', 'progress' ], - VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'trending', 'hot', 'best' ], + VIDEOS: [ + 'name', + 'duration', + 'createdAt', + 'publishedAt', + 'originallyPublishedAt', + 'views', + 'likes', + 'trending', + 'hot', + 'best', + 'localVideoFilesSize' + ], // Don't forget to update peertube-search-index with the same values VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'match' ], diff --git a/server/core/models/video/sql/video/videos-id-list-query-builder.ts b/server/core/models/video/sql/video/videos-id-list-query-builder.ts index c19cea166..b5f1e8aac 100644 --- a/server/core/models/video/sql/video/videos-id-list-query-builder.ts +++ b/server/core/models/video/sql/video/videos-id-list-query-builder.ts @@ -693,6 +693,23 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { this.attributes.push('COALESCE("video"."originallyPublishedAt", "video"."publishedAt") AS "publishedAtForOrder"') } + if (sort === '-localVideoFilesSize' || sort === 'localVideoFilesSize') { + this.attributes.push( + '(' + + 'CASE ' + + 'WHEN "video"."remote" IS TRUE THEN 0 ' + // Consider remote videos with size of 0 + 'ELSE (' + + '(SELECT COALESCE(SUM(size), 0) FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id")' + + ' + ' + + '(SELECT COALESCE(SUM(size), 0) FROM "videoFile" ' + + 'INNER JOIN "videoStreamingPlaylist" ON "videoStreamingPlaylist"."id" = "videoFile"."videoStreamingPlaylistId" ' + + 'AND "videoStreamingPlaylist"."videoId" = "video"."id"' + + ')' + + ') END' + + ') AS "localVideoFilesSize"' + ) + } + this.sort = this.buildOrder(sort) } @@ -712,6 +729,8 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { firstSort = '"similarity"' } else if (field === 'originallyPublishedAt') { firstSort = '"publishedAtForOrder"' + } else if (field === 'localVideoFilesSize') { + firstSort = '"localVideoFilesSize"' } else if (field.includes('.')) { firstSort = field } else {