diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.html b/client/src/app/+admin/overview/users/user-list/user-list.component.html
index 142057141..2954a681d 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.html
@@ -68,6 +68,7 @@
{{ getColumn('email').label }} |
{{ getColumn('quota').label }} |
{{ getColumn('quotaDaily').label }} |
+ {{ getColumn('totalVideoFileSize').label }} |
{{ getColumn('pluginAuth').label }} |
{{ getColumn('createdAt').label }} |
{{ getColumn('lastLoginDate').label }} |
@@ -140,6 +141,10 @@
+
+ {{ user.totalVideoFileSize | bytes }}
+ |
+
{{ user.pluginAuth }}
|
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.ts b/client/src/app/+admin/overview/users/user-list/user-list.component.ts
index a5a1552da..36849011d 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.ts
@@ -131,6 +131,7 @@ export class UserListComponent extends RestTable implements OnInit {
{ id: 'role', label: $localize`Role` },
{ id: 'email', label: $localize`Email` },
{ id: 'quota', label: $localize`Video quota` },
+ { id: 'totalVideoFileSize', label: $localize`Total size` },
{ id: 'createdAt', label: $localize`Created` },
{ id: 'lastLoginDate', label: $localize`Last login` },
@@ -154,7 +155,7 @@ export class UserListComponent extends RestTable implements OnInit {
}
// Default behaviour
- this.selectedColumns = [ 'username', 'role', 'email', 'quota', 'createdAt', 'lastLoginDate' ]
+ this.selectedColumns = [ 'username', 'role', 'email', 'quota', 'totalVideoFileSize', 'createdAt', 'lastLoginDate' ]
return
}
diff --git a/packages/models/src/users/user.model.ts b/packages/models/src/users/user.model.ts
index 57b4c1aab..ea03e08ca 100644
--- a/packages/models/src/users/user.model.ts
+++ b/packages/models/src/users/user.model.ts
@@ -37,6 +37,8 @@ export interface User {
videoQuotaUsed?: number
videoQuotaUsedDaily?: number
+ totalVideoFileSize?: number
+
videosCount?: number
abusesCount?: number
diff --git a/packages/tests/src/api/users/users.ts b/packages/tests/src/api/users/users.ts
index a0090a463..cf26f444c 100644
--- a/packages/tests/src/api/users/users.ts
+++ b/packages/tests/src/api/users/users.ts
@@ -90,6 +90,8 @@ describe('Test users', function () {
expect(user.email).to.equal('user_1@example.com')
expect(user.nsfwPolicy).to.equal('display')
+ expect(user.totalVideoFileSize).to.equal(0)
+
const rootUser = data[1]
expect(rootUser.username).to.equal('root')
expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
@@ -484,6 +486,7 @@ describe('Test users', function () {
expect(user.abusesCount).to.equal(0)
expect(user.abusesCreatedCount).to.equal(0)
expect(user.abusesAcceptedCount).to.equal(0)
+ expect(user.totalVideoFileSize).to.equal(0)
})
it('Should report correct videos count', async function () {
@@ -495,6 +498,7 @@ describe('Test users', function () {
const user = await server.users.get({ userId: user17Id, withStats: true })
expect(user.videosCount).to.equal(1)
+ expect(user.totalVideoFileSize).to.not.equal(0)
})
it('Should report correct video comments for user', async function () {
diff --git a/server/core/lib/live/live-quota-store.ts b/server/core/lib/live/live-quota-store.ts
index 44539faaa..464bc6b03 100644
--- a/server/core/lib/live/live-quota-store.ts
+++ b/server/core/lib/live/live-quota-store.ts
@@ -31,7 +31,7 @@ class LiveQuotaStore {
live.size += size
}
- getLiveQuotaOf (userId: number) {
+ getLiveQuotaOfUser (userId: number) {
const currentLives = this.livesPerUser.get(userId)
if (!currentLives) return 0
diff --git a/server/core/lib/user.ts b/server/core/lib/user.ts
index 68a64d4fe..3c3b0de0f 100644
--- a/server/core/lib/user.ts
+++ b/server/core/lib/user.ts
@@ -198,14 +198,14 @@ async function sendVerifyRegistrationEmail (registration: MRegistration) {
async function getOriginalVideoFileTotalFromUser (user: MUserId) {
const base = await UserModel.getUserQuota({ userId: user.id, daily: false })
- return base + LiveQuotaStore.Instance.getLiveQuotaOf(user.id)
+ return base + LiveQuotaStore.Instance.getLiveQuotaOfUser(user.id)
}
// Returns cumulative size of all video files uploaded in the last 24 hours.
async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
const base = await UserModel.getUserQuota({ userId: user.id, daily: true })
- return base + LiveQuotaStore.Instance.getLiveQuotaOf(user.id)
+ return base + LiveQuotaStore.Instance.getLiveQuotaOfUser(user.id)
}
async function isUserQuotaValid (options: {
diff --git a/server/core/models/user/user.ts b/server/core/models/user/user.ts
index 8a7c1cf6b..8a1e201c4 100644
--- a/server/core/models/user/user.ts
+++ b/server/core/models/user/user.ts
@@ -83,6 +83,7 @@ enum ScopeNames {
FOR_ME_API = 'FOR_ME_API',
WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS',
WITH_QUOTA = 'WITH_QUOTA',
+ WITH_TOTAL_FILE_SIZES = 'WITH_TOTAL_FILE_SIZES',
WITH_STATS = 'WITH_STATS'
}
@@ -168,7 +169,8 @@ enum ScopeNames {
'(' +
UserModel.generateUserQuotaBaseSQL({
whereUserId: '"UserModel"."id"',
- daily: false
+ daily: false,
+ onlyMaxResolution: true
}) +
')'
),
@@ -179,7 +181,8 @@ enum ScopeNames {
'(' +
UserModel.generateUserQuotaBaseSQL({
whereUserId: '"UserModel"."id"',
- daily: true
+ daily: true,
+ onlyMaxResolution: true
}) +
')'
),
@@ -188,6 +191,24 @@ enum ScopeNames {
]
}
},
+ [ScopeNames.WITH_TOTAL_FILE_SIZES]: {
+ attributes: {
+ include: [
+ [
+ literal(
+ '(' +
+ UserModel.generateUserQuotaBaseSQL({
+ whereUserId: '"UserModel"."id"',
+ daily: false,
+ onlyMaxResolution: false
+ }) +
+ ')'
+ ),
+ 'totalVideoFileSize'
+ ]
+ ]
+ }
+ },
[ScopeNames.WITH_STATS]: {
attributes: {
include: [
@@ -521,7 +542,7 @@ export class UserModel extends Model>> {
return Promise.all([
UserModel.unscoped().count(query),
- UserModel.scope([ 'defaultScope', ScopeNames.WITH_QUOTA ]).findAll(query)
+ UserModel.scope([ 'defaultScope', ScopeNames.WITH_QUOTA, ScopeNames.WITH_TOTAL_FILE_SIZES ]).findAll(query)
]).then(([ total, data ]) => ({ total, data }))
}
@@ -607,6 +628,7 @@ export class UserModel extends Model>> {
if (withStats) {
scopes.push(ScopeNames.WITH_QUOTA)
scopes.push(ScopeNames.WITH_STATS)
+ scopes.push(ScopeNames.WITH_TOTAL_FILE_SIZES)
}
return UserModel.scope(scopes).findByPk(id)
@@ -805,8 +827,9 @@ export class UserModel extends Model>> {
static generateUserQuotaBaseSQL (options: {
daily: boolean
whereUserId: '$userId' | '"UserModel"."id"'
+ onlyMaxResolution: boolean
}) {
- const { daily, whereUserId } = options
+ const { daily, whereUserId, onlyMaxResolution } = options
const andWhere = daily === true
? 'AND "video"."createdAt" > now() - interval \'24 hours\''
@@ -825,9 +848,13 @@ export class UserModel extends Model>> {
'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
videoChannelJoin
+ const sizeSelect = onlyMaxResolution
+ ? 'MAX("t1"."size")'
+ : 'SUM("t1"."size")'
+
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
'FROM (' +
- `SELECT MAX("t1"."size") AS "size" FROM (${webVideoFiles} UNION ${hlsFiles}) t1 ` +
+ `SELECT ${sizeSelect} AS "size" FROM (${webVideoFiles} UNION ${hlsFiles}) t1 ` +
'GROUP BY "t1"."videoId"' +
') t2'
}
@@ -838,7 +865,7 @@ export class UserModel extends Model>> {
}) {
const { daily, userId } = options
- const sql = this.generateUserQuotaBaseSQL({ daily, whereUserId: '$userId' })
+ const sql = this.generateUserQuotaBaseSQL({ daily, whereUserId: '$userId', onlyMaxResolution: true })
const queryOptions = {
bind: { userId },
@@ -914,6 +941,7 @@ export class UserModel extends Model>> {
const [ abusesCount, abusesAcceptedCount ] = (this.get('abusesCount') as string || ':').split(':')
const abusesCreatedCount = this.get('abusesCreatedCount')
const videoCommentsCount = this.get('videoCommentsCount')
+ const totalVideoFileSize = this.get('totalVideoFileSize')
const json: User = {
id: this.id,
@@ -943,12 +971,16 @@ export class UserModel extends Model>> {
videoQuota: this.videoQuota,
videoQuotaDaily: this.videoQuotaDaily,
+ totalVideoFileSize: totalVideoFileSize !== undefined
+ ? forceNumber(totalVideoFileSize)
+ : undefined,
+
videoQuotaUsed: videoQuotaUsed !== undefined
- ? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
+ ? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOfUser(this.id)
: undefined,
videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
- ? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
+ ? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOfUser(this.id)
: undefined,
videosCount: videosCount !== undefined