X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo.ts;h=b0fff65268fd8542153fb16649ab6f915cfd42a7;hb=9d3ef9fe052ed29bd67566754cb28662bd122234;hp=2a1226f6dcf8576921da4ce22af1999ecf8a266d;hpb=2922e048de95738b3319054ce0778f873a34a0ee;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 2a1226f6d..b0fff6526 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -28,7 +28,7 @@ import { } from 'sequelize-typescript' import { VideoPrivacy, VideoResolution } from '../../../shared' import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' -import { Video, VideoDetails } from '../../../shared/models/videos' +import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' import { VideoFilter } from '../../../shared/models/videos/video-query.type' import { activityPubCollection } from '../../helpers/activitypub' import { @@ -95,14 +95,15 @@ enum ScopeNames { } @Scopes({ - [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, filter?: VideoFilter) => ({ - where: { - id: { - [Sequelize.Op.notIn]: Sequelize.literal( - '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' - ), - [ Sequelize.Op.in ]: Sequelize.literal( - '(' + + [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean) => { + const query: IFindOptions = { + where: { + id: { + [Sequelize.Op.notIn]: Sequelize.literal( + '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' + ), + [ Sequelize.Op.in ]: Sequelize.literal( + '(' + 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 'WHERE "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + @@ -113,45 +114,60 @@ enum ScopeNames { 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + 'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' + 'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + - ')' - ) + ')' + ) + }, + privacy: VideoPrivacy.PUBLIC }, - privacy: VideoPrivacy.PUBLIC - }, - include: [ - { - attributes: [ 'name', 'description' ], - model: VideoChannelModel.unscoped(), - required: true, - include: [ - { - attributes: [ 'name' ], - model: AccountModel.unscoped(), - required: true, - include: [ - { - attributes: [ 'preferredUsername', 'url', 'serverId' ], - model: ActorModel.unscoped(), - required: true, - where: VideoModel.buildActorWhereWithFilter(filter), - include: [ - { - attributes: [ 'host' ], - model: ServerModel.unscoped(), - required: false - }, - { - model: AvatarModel.unscoped(), - required: false - } - ] - } - ] - } - ] - } - ] - }), + include: [ + { + attributes: [ 'name', 'description' ], + model: VideoChannelModel.unscoped(), + required: true, + include: [ + { + attributes: [ 'name' ], + model: AccountModel.unscoped(), + required: true, + include: [ + { + attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], + model: ActorModel.unscoped(), + required: true, + where: VideoModel.buildActorWhereWithFilter(filter), + include: [ + { + attributes: [ 'host' ], + model: ServerModel.unscoped(), + required: false + }, + { + model: AvatarModel.unscoped(), + required: false + } + ] + } + ] + } + ] + } + ] + } + + if (withFiles === true) { + query.include.push({ + model: VideoFileModel.unscoped(), + required: true + }) + } + + // Hide nsfw videos? + if (hideNSFW === true) { + query.where['nsfw'] = false + } + + return query + }, [ScopeNames.WITH_ACCOUNT_DETAILS]: { include: [ { @@ -306,8 +322,8 @@ export class VideoModel extends Model { @AllowNull(true) @Default(null) @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language')) - @Column - language: number + @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max)) + language: string @AllowNull(false) @Is('VideoPrivacy', value => throwIfNotValid(value, isVideoPrivacyValid, 'privacy')) @@ -629,8 +645,8 @@ export class VideoModel extends Model { }) } - static listUserVideosForApi (userId: number, start: number, count: number, sort: string) { - const query = { + static listAccountVideosForApi (accountId: number, start: number, count: number, sort: string, hideNSFW: boolean, withFiles = false) { + const query: IFindOptions = { offset: start, limit: count, order: getSort(sort), @@ -642,7 +658,7 @@ export class VideoModel extends Model { { model: AccountModel, where: { - userId + id: accountId }, required: true } @@ -651,6 +667,19 @@ export class VideoModel extends Model { ] } + if (withFiles === true) { + query.include.push({ + model: VideoFileModel.unscoped(), + required: true + }) + } + + if (hideNSFW === true) { + query.where = { + nsfw: false + } + } + return VideoModel.findAndCountAll(query).then(({ rows, count }) => { return { data: rows, @@ -659,7 +688,7 @@ export class VideoModel extends Model { }) } - static async listForApi (start: number, count: number, sort: string, filter?: VideoFilter) { + static async listForApi (start: number, count: number, sort: string, hideNSFW: boolean, filter?: VideoFilter, withFiles = false) { const query = { offset: start, limit: count, @@ -667,8 +696,7 @@ export class VideoModel extends Model { } const serverActor = await getServerActor() - - return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, filter ] }) + return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles ] }) .findAndCountAll(query) .then(({ rows, count }) => { return { @@ -678,7 +706,7 @@ export class VideoModel extends Model { }) } - static async searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) { + static async searchAndPopulateAccountAndServer (value: string, start: number, count: number, sort: string, hideNSFW: boolean) { const query: IFindOptions = { offset: start, limit: count, @@ -706,8 +734,9 @@ export class VideoModel extends Model { const serverActor = await getServerActor() - return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] }) - .findAndCountAll(query).then(({ rows, count }) => { + return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW ] }) + .findAndCountAll(query) + .then(({ rows, count }) => { return { data: rows, total: count @@ -848,13 +877,22 @@ export class VideoModel extends Model { return licenceLabel } - private static getLanguageLabel (id: number) { + private static getLanguageLabel (id: string) { let languageLabel = VIDEO_LANGUAGES[id] + console.log(VIDEO_LANGUAGES) + console.log(id) if (!languageLabel) languageLabel = 'Unknown' return languageLabel } + private static getPrivacyLabel (id: number) { + let privacyLabel = VIDEO_PRIVACIES[id] + if (!privacyLabel) privacyLabel = 'Unknown' + + return privacyLabel + } + getOriginalFile () { if (Array.isArray(this.VideoFiles) === false) return undefined @@ -908,8 +946,11 @@ export class VideoModel extends Model { return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) } - createTorrentAndSetInfoHash = async function (videoFile: VideoFileModel) { + async createTorrentAndSetInfoHash (videoFile: VideoFileModel) { const options = { + // Keep the extname, it's used by the client to stream the file inside a web browser + name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, + createdBy: 'PeerTube', announceList: [ [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ], [ CONFIG.WEBSERVER.URL + '/tracker/announce' ] @@ -961,6 +1002,10 @@ export class VideoModel extends Model { id: this.language, label: VideoModel.getLanguageLabel(this.language) }, + privacy: { + id: this.privacy, + label: VideoModel.getPrivacyLabel(this.privacy) + }, nsfw: this.nsfw, description: this.getTruncatedDescription(), isLocal: this.isOwned(), @@ -987,48 +1032,45 @@ export class VideoModel extends Model { toFormattedDetailsJSON (): VideoDetails { const formattedJson = this.toFormattedJSON() - // Maybe our server is not up to date and there are new privacy settings since our version - let privacyLabel = VIDEO_PRIVACIES[this.privacy] - if (!privacyLabel) privacyLabel = 'Unknown' - const detailsJson = { - privacy: { - id: this.privacy, - label: privacyLabel - }, support: this.support, descriptionPath: this.getDescriptionPath(), channel: this.VideoChannel.toFormattedJSON(), account: this.VideoChannel.Account.toFormattedJSON(), - tags: map(this.Tags, 'name'), + tags: map(this.Tags, 'name'), commentsEnabled: this.commentsEnabled, files: [] } // Format and sort video files + detailsJson.files = this.getFormattedVideoFilesJSON() + + return Object.assign(formattedJson, detailsJson) + } + + getFormattedVideoFilesJSON (): VideoFile[] { const { baseUrlHttp, baseUrlWs } = this.getBaseUrls() - detailsJson.files = this.VideoFiles - .map(videoFile => { - let resolutionLabel = videoFile.resolution + 'p' - return { - resolution: { - id: videoFile.resolution, - label: resolutionLabel - }, - magnetUri: this.generateMagnetUri(videoFile, baseUrlHttp, baseUrlWs), - size: videoFile.size, - torrentUrl: this.getTorrentUrl(videoFile, baseUrlHttp), - fileUrl: this.getVideoFileUrl(videoFile, baseUrlHttp) - } - }) - .sort((a, b) => { - if (a.resolution.id < b.resolution.id) return 1 - if (a.resolution.id === b.resolution.id) return 0 - return -1 - }) + return this.VideoFiles + .map(videoFile => { + let resolutionLabel = videoFile.resolution + 'p' - return Object.assign(formattedJson, detailsJson) + return { + resolution: { + id: videoFile.resolution, + label: resolutionLabel + }, + magnetUri: this.generateMagnetUri(videoFile, baseUrlHttp, baseUrlWs), + size: videoFile.size, + torrentUrl: this.getTorrentUrl(videoFile, baseUrlHttp), + fileUrl: this.getVideoFileUrl(videoFile, baseUrlHttp) + } as VideoFile + }) + .sort((a, b) => { + if (a.resolution.id < b.resolution.id) return 1 + if (a.resolution.id === b.resolution.id) return 0 + return -1 + }) } toActivityPubObject (): VideoTorrentObject { @@ -1043,7 +1085,7 @@ export class VideoModel extends Model { let language if (this.language) { language = { - identifier: this.language + '', + identifier: this.language, name: VideoModel.getLanguageLabel(this.language) } } @@ -1203,7 +1245,7 @@ export class VideoModel extends Model { return peertubeTruncate(this.description, maxLength) } - optimizeOriginalVideofile = async function () { + async optimizeOriginalVideofile () { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR const newExtname = '.mp4' const inputVideoFile = this.getOriginalFile() @@ -1240,7 +1282,7 @@ export class VideoModel extends Model { } } - transcodeOriginalVideofile = async function (resolution: VideoResolution, isPortraitMode: boolean) { + async transcodeOriginalVideofile (resolution: VideoResolution, isPortraitMode: boolean) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR const extname = '.mp4'