diff options
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/account/user.ts | 159 | ||||
-rw-r--r-- | server/models/video/video-live.ts | 10 |
2 files changed, 91 insertions, 78 deletions
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 22e6715b4..e850d1e6d 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -23,6 +23,7 @@ import { | |||
23 | } from 'sequelize-typescript' | 23 | } from 'sequelize-typescript' |
24 | import { | 24 | import { |
25 | MMyUserFormattable, | 25 | MMyUserFormattable, |
26 | MUser, | ||
26 | MUserDefault, | 27 | MUserDefault, |
27 | MUserFormattable, | 28 | MUserFormattable, |
28 | MUserId, | 29 | MUserId, |
@@ -70,6 +71,7 @@ import { VideoImportModel } from '../video/video-import' | |||
70 | import { VideoPlaylistModel } from '../video/video-playlist' | 71 | import { VideoPlaylistModel } from '../video/video-playlist' |
71 | import { AccountModel } from './account' | 72 | import { AccountModel } from './account' |
72 | import { UserNotificationSettingModel } from './user-notification-setting' | 73 | import { UserNotificationSettingModel } from './user-notification-setting' |
74 | import { VideoLiveModel } from '../video/video-live' | ||
73 | 75 | ||
74 | enum ScopeNames { | 76 | enum ScopeNames { |
75 | FOR_ME_API = 'FOR_ME_API', | 77 | FOR_ME_API = 'FOR_ME_API', |
@@ -540,7 +542,11 @@ export class UserModel extends Model<UserModel> { | |||
540 | return UserModel.findAll(query) | 542 | return UserModel.findAll(query) |
541 | } | 543 | } |
542 | 544 | ||
543 | static loadById (id: number, withStats = false): Bluebird<MUserDefault> { | 545 | static loadById (id: number): Bluebird<MUser> { |
546 | return UserModel.unscoped().findByPk(id) | ||
547 | } | ||
548 | |||
549 | static loadByIdWithChannels (id: number, withStats = false): Bluebird<MUserDefault> { | ||
544 | const scopes = [ | 550 | const scopes = [ |
545 | ScopeNames.WITH_VIDEOCHANNELS | 551 | ScopeNames.WITH_VIDEOCHANNELS |
546 | ] | 552 | ] |
@@ -685,26 +691,85 @@ export class UserModel extends Model<UserModel> { | |||
685 | return UserModel.findOne(query) | 691 | return UserModel.findOne(query) |
686 | } | 692 | } |
687 | 693 | ||
688 | static getOriginalVideoFileTotalFromUser (user: MUserId) { | 694 | static loadByLiveId (liveId: number): Bluebird<MUser> { |
689 | // Don't use sequelize because we need to use a sub query | 695 | const query = { |
690 | const query = UserModel.generateUserQuotaBaseSQL({ | 696 | include: [ |
691 | withSelect: true, | 697 | { |
692 | whereUserId: '$userId' | 698 | attributes: [ 'id' ], |
693 | }) | 699 | model: AccountModel.unscoped(), |
700 | required: true, | ||
701 | include: [ | ||
702 | { | ||
703 | attributes: [ 'id' ], | ||
704 | model: VideoChannelModel.unscoped(), | ||
705 | required: true, | ||
706 | include: [ | ||
707 | { | ||
708 | attributes: [ 'id' ], | ||
709 | model: VideoModel.unscoped(), | ||
710 | required: true, | ||
711 | include: [ | ||
712 | { | ||
713 | attributes: [ 'id', 'videoId' ], | ||
714 | model: VideoLiveModel.unscoped(), | ||
715 | required: true, | ||
716 | where: { | ||
717 | id: liveId | ||
718 | } | ||
719 | } | ||
720 | ] | ||
721 | } | ||
722 | ] | ||
723 | } | ||
724 | ] | ||
725 | } | ||
726 | ] | ||
727 | } | ||
728 | |||
729 | return UserModel.findOne(query) | ||
730 | } | ||
731 | |||
732 | static generateUserQuotaBaseSQL (options: { | ||
733 | whereUserId: '$userId' | '"UserModel"."id"' | ||
734 | withSelect: boolean | ||
735 | where?: string | ||
736 | }) { | ||
737 | const andWhere = options.where | ||
738 | ? 'AND ' + options.where | ||
739 | : '' | ||
740 | |||
741 | const videoChannelJoin = 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
742 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + | ||
743 | `WHERE "account"."userId" = ${options.whereUserId} ${andWhere}` | ||
744 | |||
745 | const webtorrentFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' + | ||
746 | 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + | ||
747 | videoChannelJoin | ||
748 | |||
749 | const hlsFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' + | ||
750 | 'INNER JOIN "videoStreamingPlaylist" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id ' + | ||
751 | 'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" ' + | ||
752 | videoChannelJoin | ||
694 | 753 | ||
695 | return UserModel.getTotalRawQuery(query, user.id) | 754 | return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' + |
755 | 'FROM (' + | ||
756 | `SELECT MAX("t1"."size") AS "size" FROM (${webtorrentFiles} UNION ${hlsFiles}) t1 ` + | ||
757 | 'GROUP BY "t1"."videoId"' + | ||
758 | ') t2' | ||
696 | } | 759 | } |
697 | 760 | ||
698 | // Returns cumulative size of all video files uploaded in the last 24 hours. | 761 | static getTotalRawQuery (query: string, userId: number) { |
699 | static getOriginalVideoFileTotalDailyFromUser (user: MUserId) { | 762 | const options = { |
700 | // Don't use sequelize because we need to use a sub query | 763 | bind: { userId }, |
701 | const query = UserModel.generateUserQuotaBaseSQL({ | 764 | type: QueryTypes.SELECT as QueryTypes.SELECT |
702 | withSelect: true, | 765 | } |
703 | whereUserId: '$userId', | 766 | |
704 | where: '"video"."createdAt" > now() - interval \'24 hours\'' | 767 | return UserModel.sequelize.query<{ total: string }>(query, options) |
705 | }) | 768 | .then(([ { total } ]) => { |
769 | if (total === null) return 0 | ||
706 | 770 | ||
707 | return UserModel.getTotalRawQuery(query, user.id) | 771 | return parseInt(total, 10) |
772 | }) | ||
708 | } | 773 | } |
709 | 774 | ||
710 | static async getStats () { | 775 | static async getStats () { |
@@ -874,64 +939,4 @@ export class UserModel extends Model<UserModel> { | |||
874 | 939 | ||
875 | return Object.assign(formatted, { specialPlaylists }) | 940 | return Object.assign(formatted, { specialPlaylists }) |
876 | } | 941 | } |
877 | |||
878 | async isAbleToUploadVideo (videoFile: { size: number }) { | ||
879 | if (this.videoQuota === -1 && this.videoQuotaDaily === -1) return Promise.resolve(true) | ||
880 | |||
881 | const [ totalBytes, totalBytesDaily ] = await Promise.all([ | ||
882 | UserModel.getOriginalVideoFileTotalFromUser(this), | ||
883 | UserModel.getOriginalVideoFileTotalDailyFromUser(this) | ||
884 | ]) | ||
885 | |||
886 | const uploadedTotal = videoFile.size + totalBytes | ||
887 | const uploadedDaily = videoFile.size + totalBytesDaily | ||
888 | |||
889 | if (this.videoQuotaDaily === -1) return uploadedTotal < this.videoQuota | ||
890 | if (this.videoQuota === -1) return uploadedDaily < this.videoQuotaDaily | ||
891 | |||
892 | return uploadedTotal < this.videoQuota && uploadedDaily < this.videoQuotaDaily | ||
893 | } | ||
894 | |||
895 | private static generateUserQuotaBaseSQL (options: { | ||
896 | whereUserId: '$userId' | '"UserModel"."id"' | ||
897 | withSelect: boolean | ||
898 | where?: string | ||
899 | }) { | ||
900 | const andWhere = options.where | ||
901 | ? 'AND ' + options.where | ||
902 | : '' | ||
903 | |||
904 | const videoChannelJoin = 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
905 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + | ||
906 | `WHERE "account"."userId" = ${options.whereUserId} ${andWhere}` | ||
907 | |||
908 | const webtorrentFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' + | ||
909 | 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + | ||
910 | videoChannelJoin | ||
911 | |||
912 | const hlsFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' + | ||
913 | 'INNER JOIN "videoStreamingPlaylist" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id ' + | ||
914 | 'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" ' + | ||
915 | videoChannelJoin | ||
916 | |||
917 | return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' + | ||
918 | 'FROM (' + | ||
919 | `SELECT MAX("t1"."size") AS "size" FROM (${webtorrentFiles} UNION ${hlsFiles}) t1 ` + | ||
920 | 'GROUP BY "t1"."videoId"' + | ||
921 | ') t2' | ||
922 | } | ||
923 | |||
924 | private static getTotalRawQuery (query: string, userId: number) { | ||
925 | const options = { | ||
926 | bind: { userId }, | ||
927 | type: QueryTypes.SELECT as QueryTypes.SELECT | ||
928 | } | ||
929 | |||
930 | return UserModel.sequelize.query<{ total: string }>(query, options) | ||
931 | .then(([ { total } ]) => { | ||
932 | if (total === null) return 0 | ||
933 | |||
934 | return parseInt(total, 10) | ||
935 | }) | ||
936 | } | ||
937 | } | 942 | } |
diff --git a/server/models/video/video-live.ts b/server/models/video/video-live.ts index 8608bc84c..a1dd80d3c 100644 --- a/server/models/video/video-live.ts +++ b/server/models/video/video-live.ts | |||
@@ -30,10 +30,18 @@ import { VideoBlacklistModel } from './video-blacklist' | |||
30 | }) | 30 | }) |
31 | export class VideoLiveModel extends Model<VideoLiveModel> { | 31 | export class VideoLiveModel extends Model<VideoLiveModel> { |
32 | 32 | ||
33 | @AllowNull(false) | 33 | @AllowNull(true) |
34 | @Column(DataType.STRING) | 34 | @Column(DataType.STRING) |
35 | streamKey: string | 35 | streamKey: string |
36 | 36 | ||
37 | @AllowNull(false) | ||
38 | @Column | ||
39 | perpetualLive: boolean | ||
40 | |||
41 | @AllowNull(false) | ||
42 | @Column | ||
43 | saveReplay: boolean | ||
44 | |||
37 | @CreatedAt | 45 | @CreatedAt |
38 | createdAt: Date | 46 | createdAt: Date |
39 | 47 | ||