Add total video file size column in users list
This commit is contained in:
parent
db69d9491e
commit
0648d57870
7 changed files with 56 additions and 12 deletions
|
@ -68,6 +68,7 @@
|
||||||
<th scope="col" *ngIf="isSelected('email')">{{ getColumn('email').label }}</th>
|
<th scope="col" *ngIf="isSelected('email')">{{ getColumn('email').label }}</th>
|
||||||
<th scope="col" *ngIf="isSelected('quota')" style="width: 160px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
|
<th scope="col" *ngIf="isSelected('quota')" style="width: 160px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
|
||||||
<th scope="col" *ngIf="isSelected('quotaDaily')" style="width: 160px;">{{ getColumn('quotaDaily').label }}</th>
|
<th scope="col" *ngIf="isSelected('quotaDaily')" style="width: 160px;">{{ getColumn('quotaDaily').label }}</th>
|
||||||
|
<th scope="col" *ngIf="isSelected('totalVideoFileSize')" style="width: 100px;">{{ getColumn('totalVideoFileSize').label }}</th>
|
||||||
<th scope="col" *ngIf="isSelected('pluginAuth')" style="width: 140px;" pResizableColumn >{{ getColumn('pluginAuth').label }}</th>
|
<th scope="col" *ngIf="isSelected('pluginAuth')" style="width: 140px;" pResizableColumn >{{ getColumn('pluginAuth').label }}</th>
|
||||||
<th scope="col" *ngIf="isSelected('createdAt')" style="width: 150px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th>
|
<th scope="col" *ngIf="isSelected('createdAt')" style="width: 150px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||||
<th scope="col" *ngIf="isSelected('lastLoginDate')" style="width: 150px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th>
|
<th scope="col" *ngIf="isSelected('lastLoginDate')" style="width: 150px;" [ngbTooltip]="sortTooltip" container="body" pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th>
|
||||||
|
@ -140,6 +141,10 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td *ngIf="isSelected('totalVideoFileSize')">
|
||||||
|
{{ user.totalVideoFileSize | bytes }}
|
||||||
|
</td>
|
||||||
|
|
||||||
<td *ngIf="isSelected('pluginAuth')">
|
<td *ngIf="isSelected('pluginAuth')">
|
||||||
<span *ngIf="user.pluginAuth" [ngbTooltip]="user.pluginAuth">{{ user.pluginAuth }}</span>
|
<span *ngIf="user.pluginAuth" [ngbTooltip]="user.pluginAuth">{{ user.pluginAuth }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -131,6 +131,7 @@ export class UserListComponent extends RestTable <User> implements OnInit {
|
||||||
{ id: 'role', label: $localize`Role` },
|
{ id: 'role', label: $localize`Role` },
|
||||||
{ id: 'email', label: $localize`Email` },
|
{ id: 'email', label: $localize`Email` },
|
||||||
{ id: 'quota', label: $localize`Video quota` },
|
{ id: 'quota', label: $localize`Video quota` },
|
||||||
|
{ id: 'totalVideoFileSize', label: $localize`Total size` },
|
||||||
{ id: 'createdAt', label: $localize`Created` },
|
{ id: 'createdAt', label: $localize`Created` },
|
||||||
{ id: 'lastLoginDate', label: $localize`Last login` },
|
{ id: 'lastLoginDate', label: $localize`Last login` },
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ export class UserListComponent extends RestTable <User> implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default behaviour
|
// Default behaviour
|
||||||
this.selectedColumns = [ 'username', 'role', 'email', 'quota', 'createdAt', 'lastLoginDate' ]
|
this.selectedColumns = [ 'username', 'role', 'email', 'quota', 'totalVideoFileSize', 'createdAt', 'lastLoginDate' ]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ export interface User {
|
||||||
videoQuotaUsed?: number
|
videoQuotaUsed?: number
|
||||||
videoQuotaUsedDaily?: number
|
videoQuotaUsedDaily?: number
|
||||||
|
|
||||||
|
totalVideoFileSize?: number
|
||||||
|
|
||||||
videosCount?: number
|
videosCount?: number
|
||||||
|
|
||||||
abusesCount?: number
|
abusesCount?: number
|
||||||
|
|
|
@ -90,6 +90,8 @@ describe('Test users', function () {
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.nsfwPolicy).to.equal('display')
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
|
|
||||||
|
expect(user.totalVideoFileSize).to.equal(0)
|
||||||
|
|
||||||
const rootUser = data[1]
|
const rootUser = data[1]
|
||||||
expect(rootUser.username).to.equal('root')
|
expect(rootUser.username).to.equal('root')
|
||||||
expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
|
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.abusesCount).to.equal(0)
|
||||||
expect(user.abusesCreatedCount).to.equal(0)
|
expect(user.abusesCreatedCount).to.equal(0)
|
||||||
expect(user.abusesAcceptedCount).to.equal(0)
|
expect(user.abusesAcceptedCount).to.equal(0)
|
||||||
|
expect(user.totalVideoFileSize).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should report correct videos count', async function () {
|
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 })
|
const user = await server.users.get({ userId: user17Id, withStats: true })
|
||||||
expect(user.videosCount).to.equal(1)
|
expect(user.videosCount).to.equal(1)
|
||||||
|
expect(user.totalVideoFileSize).to.not.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should report correct video comments for user', async function () {
|
it('Should report correct video comments for user', async function () {
|
||||||
|
|
|
@ -31,7 +31,7 @@ class LiveQuotaStore {
|
||||||
live.size += size
|
live.size += size
|
||||||
}
|
}
|
||||||
|
|
||||||
getLiveQuotaOf (userId: number) {
|
getLiveQuotaOfUser (userId: number) {
|
||||||
const currentLives = this.livesPerUser.get(userId)
|
const currentLives = this.livesPerUser.get(userId)
|
||||||
if (!currentLives) return 0
|
if (!currentLives) return 0
|
||||||
|
|
||||||
|
|
|
@ -198,14 +198,14 @@ async function sendVerifyRegistrationEmail (registration: MRegistration) {
|
||||||
async function getOriginalVideoFileTotalFromUser (user: MUserId) {
|
async function getOriginalVideoFileTotalFromUser (user: MUserId) {
|
||||||
const base = await UserModel.getUserQuota({ userId: user.id, daily: false })
|
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.
|
// Returns cumulative size of all video files uploaded in the last 24 hours.
|
||||||
async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
|
async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
|
||||||
const base = await UserModel.getUserQuota({ userId: user.id, daily: true })
|
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: {
|
async function isUserQuotaValid (options: {
|
||||||
|
|
|
@ -83,6 +83,7 @@ enum ScopeNames {
|
||||||
FOR_ME_API = 'FOR_ME_API',
|
FOR_ME_API = 'FOR_ME_API',
|
||||||
WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS',
|
WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS',
|
||||||
WITH_QUOTA = 'WITH_QUOTA',
|
WITH_QUOTA = 'WITH_QUOTA',
|
||||||
|
WITH_TOTAL_FILE_SIZES = 'WITH_TOTAL_FILE_SIZES',
|
||||||
WITH_STATS = 'WITH_STATS'
|
WITH_STATS = 'WITH_STATS'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +169,8 @@ enum ScopeNames {
|
||||||
'(' +
|
'(' +
|
||||||
UserModel.generateUserQuotaBaseSQL({
|
UserModel.generateUserQuotaBaseSQL({
|
||||||
whereUserId: '"UserModel"."id"',
|
whereUserId: '"UserModel"."id"',
|
||||||
daily: false
|
daily: false,
|
||||||
|
onlyMaxResolution: true
|
||||||
}) +
|
}) +
|
||||||
')'
|
')'
|
||||||
),
|
),
|
||||||
|
@ -179,7 +181,8 @@ enum ScopeNames {
|
||||||
'(' +
|
'(' +
|
||||||
UserModel.generateUserQuotaBaseSQL({
|
UserModel.generateUserQuotaBaseSQL({
|
||||||
whereUserId: '"UserModel"."id"',
|
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]: {
|
[ScopeNames.WITH_STATS]: {
|
||||||
attributes: {
|
attributes: {
|
||||||
include: [
|
include: [
|
||||||
|
@ -521,7 +542,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
UserModel.unscoped().count(query),
|
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 }))
|
]).then(([ total, data ]) => ({ total, data }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,6 +628,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
if (withStats) {
|
if (withStats) {
|
||||||
scopes.push(ScopeNames.WITH_QUOTA)
|
scopes.push(ScopeNames.WITH_QUOTA)
|
||||||
scopes.push(ScopeNames.WITH_STATS)
|
scopes.push(ScopeNames.WITH_STATS)
|
||||||
|
scopes.push(ScopeNames.WITH_TOTAL_FILE_SIZES)
|
||||||
}
|
}
|
||||||
|
|
||||||
return UserModel.scope(scopes).findByPk(id)
|
return UserModel.scope(scopes).findByPk(id)
|
||||||
|
@ -805,8 +827,9 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
static generateUserQuotaBaseSQL (options: {
|
static generateUserQuotaBaseSQL (options: {
|
||||||
daily: boolean
|
daily: boolean
|
||||||
whereUserId: '$userId' | '"UserModel"."id"'
|
whereUserId: '$userId' | '"UserModel"."id"'
|
||||||
|
onlyMaxResolution: boolean
|
||||||
}) {
|
}) {
|
||||||
const { daily, whereUserId } = options
|
const { daily, whereUserId, onlyMaxResolution } = options
|
||||||
|
|
||||||
const andWhere = daily === true
|
const andWhere = daily === true
|
||||||
? 'AND "video"."createdAt" > now() - interval \'24 hours\''
|
? 'AND "video"."createdAt" > now() - interval \'24 hours\''
|
||||||
|
@ -825,9 +848,13 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
||||||
videoChannelJoin
|
videoChannelJoin
|
||||||
|
|
||||||
|
const sizeSelect = onlyMaxResolution
|
||||||
|
? 'MAX("t1"."size")'
|
||||||
|
: 'SUM("t1"."size")'
|
||||||
|
|
||||||
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
||||||
'FROM (' +
|
'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"' +
|
'GROUP BY "t1"."videoId"' +
|
||||||
') t2'
|
') t2'
|
||||||
}
|
}
|
||||||
|
@ -838,7 +865,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
}) {
|
}) {
|
||||||
const { daily, userId } = options
|
const { daily, userId } = options
|
||||||
|
|
||||||
const sql = this.generateUserQuotaBaseSQL({ daily, whereUserId: '$userId' })
|
const sql = this.generateUserQuotaBaseSQL({ daily, whereUserId: '$userId', onlyMaxResolution: true })
|
||||||
|
|
||||||
const queryOptions = {
|
const queryOptions = {
|
||||||
bind: { userId },
|
bind: { userId },
|
||||||
|
@ -914,6 +941,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
const [ abusesCount, abusesAcceptedCount ] = (this.get('abusesCount') as string || ':').split(':')
|
const [ abusesCount, abusesAcceptedCount ] = (this.get('abusesCount') as string || ':').split(':')
|
||||||
const abusesCreatedCount = this.get('abusesCreatedCount')
|
const abusesCreatedCount = this.get('abusesCreatedCount')
|
||||||
const videoCommentsCount = this.get('videoCommentsCount')
|
const videoCommentsCount = this.get('videoCommentsCount')
|
||||||
|
const totalVideoFileSize = this.get('totalVideoFileSize')
|
||||||
|
|
||||||
const json: User = {
|
const json: User = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
@ -943,12 +971,16 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
videoQuota: this.videoQuota,
|
videoQuota: this.videoQuota,
|
||||||
videoQuotaDaily: this.videoQuotaDaily,
|
videoQuotaDaily: this.videoQuotaDaily,
|
||||||
|
|
||||||
|
totalVideoFileSize: totalVideoFileSize !== undefined
|
||||||
|
? forceNumber(totalVideoFileSize)
|
||||||
|
: undefined,
|
||||||
|
|
||||||
videoQuotaUsed: videoQuotaUsed !== undefined
|
videoQuotaUsed: videoQuotaUsed !== undefined
|
||||||
? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
|
? forceNumber(videoQuotaUsed) + LiveQuotaStore.Instance.getLiveQuotaOfUser(this.id)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
||||||
videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
|
videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
|
||||||
? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
|
? forceNumber(videoQuotaUsedDaily) + LiveQuotaStore.Instance.getLiveQuotaOfUser(this.id)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
||||||
videosCount: videosCount !== undefined
|
videosCount: videosCount !== undefined
|
||||||
|
|
Loading…
Reference in a new issue