mirror of https://github.com/Chocobozzz/PeerTube
Added support for uploading short videos
parent
eca039b97d
commit
b2fa2e84f7
|
@ -67,6 +67,8 @@ export class Video implements VideoServerModel {
|
|||
dislikes: number
|
||||
nsfw: boolean
|
||||
|
||||
shortVideo: boolean
|
||||
|
||||
originInstanceUrl: string
|
||||
originInstanceHost: string
|
||||
|
||||
|
@ -166,6 +168,8 @@ export class Video implements VideoServerModel {
|
|||
|
||||
this.nsfw = hash.nsfw
|
||||
|
||||
this.shortVideo = hash.nsfw
|
||||
|
||||
this.account = hash.account
|
||||
this.channel = hash.channel
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ smtp:
|
|||
password: null
|
||||
|
||||
log:
|
||||
level: 'debug'
|
||||
level: 'error'
|
||||
|
||||
open_telemetry:
|
||||
metrics:
|
||||
|
|
|
@ -36,6 +36,9 @@ export interface VideosCommonQuery {
|
|||
search?: string
|
||||
|
||||
excludeAlreadyWatched?: boolean
|
||||
|
||||
durationMax?: number /*EDITED*/
|
||||
shortVideo?: boolean
|
||||
}
|
||||
|
||||
export interface VideosCommonQueryAfterSanitize extends VideosCommonQuery {
|
||||
|
|
|
@ -22,4 +22,6 @@ export interface VideoCreate {
|
|||
|
||||
thumbnailfile?: Blob | string
|
||||
previewfile?: Blob | string
|
||||
|
||||
shortVideo?: boolean
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ export interface Video extends Partial<VideoAdditionalAttributes> {
|
|||
}
|
||||
|
||||
pluginData?: any
|
||||
|
||||
shortVideo: boolean
|
||||
}
|
||||
|
||||
// Not included by default, needs query params
|
||||
|
|
|
@ -176,9 +176,11 @@ async function getVideoDescription (req: express.Request, res: express.Response)
|
|||
}
|
||||
|
||||
async function listVideos (req: express.Request, res: express.Response) {
|
||||
|
||||
const serverActor = await getServerActor()
|
||||
|
||||
const query = pickCommonVideoQuery(req.query)
|
||||
|
||||
const countVideos = getCountVideos(req)
|
||||
|
||||
const apiOptions = await Hooks.wrapObject({
|
||||
|
|
|
@ -100,6 +100,9 @@ export {
|
|||
async function addVideoLegacy (req: express.Request, res: express.Response) {
|
||||
// Uploading the video could be long
|
||||
// Set timeout to 10 minutes, as Express's default is 2 minutes
|
||||
|
||||
|
||||
|
||||
req.setTimeout(1000 * 60 * 10, () => {
|
||||
logger.error('Video upload has timed out.')
|
||||
return res.fail({
|
||||
|
@ -142,6 +145,7 @@ async function addVideo (options: {
|
|||
let videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id)
|
||||
videoData = await Hooks.wrapObject(videoData, 'filter:api.video.upload.video-attribute.result')
|
||||
|
||||
|
||||
videoData.state = buildNextVideoState()
|
||||
videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware
|
||||
|
||||
|
|
|
@ -185,6 +185,12 @@ function isValidPasswordProtectedPrivacy (req: Request, res: Response) {
|
|||
return true
|
||||
}
|
||||
|
||||
|
||||
function isDurationValid(currDuration : number, requiredDuration: number){
|
||||
|
||||
return currDuration<= requiredDuration;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
|
@ -214,5 +220,6 @@ export {
|
|||
isVideoImageValid,
|
||||
isVideoSupportValid,
|
||||
isPasswordValid,
|
||||
isValidPasswordProtectedPrivacy
|
||||
isValidPasswordProtectedPrivacy,
|
||||
isDurationValid
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ function pickCommonVideoQuery (query: VideosCommonQueryAfterSanitize) {
|
|||
'hasWebtorrentFiles', // TODO: Remove in v7
|
||||
'hasWebVideoFiles',
|
||||
'search',
|
||||
'excludeAlreadyWatched'
|
||||
'excludeAlreadyWatched',
|
||||
'durationMax',
|
||||
'shortVideo'
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import { cpus } from 'os'
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const LAST_MIGRATION_VERSION = 805
|
||||
const LAST_MIGRATION_VERSION = 810
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
async function up (utils: {
|
||||
transaction: Sequelize.Transaction
|
||||
queryInterface: Sequelize.QueryInterface
|
||||
sequelize: Sequelize.Sequelize
|
||||
}): Promise<void> {
|
||||
|
||||
const data = {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: true,
|
||||
defaultValue: false
|
||||
}
|
||||
|
||||
await utils.queryInterface.addColumn('video', 'shortVideo', data)
|
||||
}
|
||||
|
||||
function down (options) {
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
export {
|
||||
up,
|
||||
down
|
||||
}
|
|
@ -38,7 +38,9 @@ export function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: numbe
|
|||
channelId,
|
||||
originallyPublishedAt: videoInfo.originallyPublishedAt
|
||||
? new Date(videoInfo.originallyPublishedAt)
|
||||
: null
|
||||
: null,
|
||||
|
||||
shortVideo: videoInfo.shortVideo || false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ import {
|
|||
isVideoNameValid,
|
||||
isVideoOriginallyPublishedAtValid,
|
||||
isVideoPrivacyValid,
|
||||
isVideoSupportValid
|
||||
isVideoSupportValid,
|
||||
isDurationValid
|
||||
} from '../../../helpers/custom-validators/videos.js'
|
||||
import { cleanUpReqFiles } from '../../../helpers/express-utils.js'
|
||||
import { logger } from '../../../helpers/logger.js'
|
||||
|
@ -54,6 +55,9 @@ import {
|
|||
} from '../shared/index.js'
|
||||
import { addDurationToVideoFileIfNeeded, commonVideoFileChecks, isVideoFileAccepted } from './shared/index.js'
|
||||
|
||||
|
||||
// let duration: number
|
||||
|
||||
const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
|
||||
body('videofile')
|
||||
.custom((_, { req }) => isFileValid({ files: req.files, field: 'videofile', mimeTypeRegex: null, maxSize: null }))
|
||||
|
@ -71,6 +75,8 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
|
|||
.isArray()
|
||||
.withMessage('Video passwords should be an array.'),
|
||||
|
||||
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
|
||||
|
||||
|
@ -81,13 +87,15 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
|
|||
!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files }) ||
|
||||
!isValidPasswordProtectedPrivacy(req, res) ||
|
||||
!await addDurationToVideoFileIfNeeded({ videoFile, res, middlewareName: 'videosAddvideosAddLegacyValidatorResumableValidator' }) ||
|
||||
!await isVideoFileAccepted({ req, res, videoFile, hook: 'filter:api.video.upload.accept.result' })
|
||||
) {
|
||||
!await isVideoFileAccepted({ req, res, videoFile, hook: 'filter:api.video.upload.accept.result' }) ) {
|
||||
return cleanUpReqFiles(req)
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
])
|
||||
|
||||
/**
|
||||
|
@ -353,8 +361,46 @@ const videosOverviewValidator = [
|
|||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
function getCommonVideoEditAttributes () {
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return cleanUpReqFiles(req);
|
||||
|
||||
const videoFile: express.VideoUploadFile = req.files['videofile'][0];
|
||||
const user = res.locals.oauth.token.User;
|
||||
|
||||
if (
|
||||
!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files }) ||
|
||||
!isValidPasswordProtectedPrivacy(req, res) ||
|
||||
!await addDurationToVideoFileIfNeeded({ videoFile, res, middlewareName: 'videosAddvideosAddLegacyValidatorResumableValidator' }) ||
|
||||
!await isVideoFileAccepted({ req, res, videoFile, hook: 'filter:api.video.upload.accept.result' })
|
||||
) {
|
||||
return cleanUpReqFiles(req);
|
||||
}
|
||||
|
||||
};
|
||||
return [
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return cleanUpReqFiles(req);
|
||||
|
||||
const videoFile: express.VideoUploadFile = req.files['videofile'][0];
|
||||
const user = res.locals.oauth.token.User;
|
||||
|
||||
if (
|
||||
!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files }) ||
|
||||
!isValidPasswordProtectedPrivacy(req, res) ||
|
||||
!await addDurationToVideoFileIfNeeded({ videoFile, res, middlewareName: 'videosAddvideosAddLegacyValidatorResumableValidator' }) ||
|
||||
!await isVideoFileAccepted({ req, res, videoFile, hook: 'filter:api.video.upload.accept.result' })
|
||||
) {
|
||||
return cleanUpReqFiles(req);
|
||||
}
|
||||
|
||||
return next();
|
||||
},
|
||||
|
||||
body('thumbnailfile')
|
||||
.custom((value, { req }) => isVideoImageValid(req.files, 'thumbnailfile')).withMessage(
|
||||
'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' +
|
||||
|
@ -427,7 +473,29 @@ function getCommonVideoEditAttributes () {
|
|||
body('scheduleUpdate.privacy')
|
||||
.optional()
|
||||
.customSanitizer(toIntOrNull)
|
||||
.custom(isScheduleVideoUpdatePrivacyValid)
|
||||
.custom(isScheduleVideoUpdatePrivacyValid),
|
||||
|
||||
|
||||
body('shortVideo')
|
||||
.optional()
|
||||
.customSanitizer(toBooleanOrNull)
|
||||
.custom(() => true).withMessage('Should have shortVideo boolean'),
|
||||
|
||||
body('shortVideo')
|
||||
.optional()
|
||||
.customSanitizer(toBooleanOrNull)
|
||||
|
||||
.custom((value, { req }) => {
|
||||
if (value) {
|
||||
const videoFile: express.VideoUploadFile = req.files['videofile'][0]
|
||||
|
||||
return isDurationValid(videoFile.duration, 20);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.withMessage('Video duration must be less than 60 seconds for short videos'),
|
||||
|
||||
|
||||
] as (ValidationChain | ExpressPromiseHandler)[]
|
||||
}
|
||||
|
||||
|
@ -459,6 +527,14 @@ const commonVideosFiltersValidator = [
|
|||
query('nsfw')
|
||||
.optional()
|
||||
.custom(isBooleanBothQueryValid),
|
||||
|
||||
|
||||
|
||||
query('shortVideo')
|
||||
.optional()
|
||||
.customSanitizer(toBooleanOrNull)
|
||||
.custom(isBooleanValid).withMessage('Should have a valid shortVideo boolean'),
|
||||
|
||||
query('isLive')
|
||||
.optional()
|
||||
.customSanitizer(toBooleanOrNull)
|
||||
|
|
|
@ -114,6 +114,8 @@ export function videoModelToFormattedJSON (video: MVideoFormattable, options: Vi
|
|||
// Can be added by external plugins
|
||||
pluginData: (video as any).pluginData,
|
||||
|
||||
shortVideo : video.shortVideo,
|
||||
|
||||
...buildAdditionalAttributes(video, options)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ export type BuildVideosListQueryOptions = {
|
|||
sort: string
|
||||
|
||||
nsfw?: boolean
|
||||
|
||||
shortVideo?: boolean
|
||||
|
||||
host?: string
|
||||
isLive?: boolean
|
||||
isLocal?: boolean
|
||||
|
@ -211,6 +214,14 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
|
|||
this.whereSFW()
|
||||
}
|
||||
|
||||
if(options.shortVideo === true){
|
||||
this.whereShortVideo()
|
||||
}
|
||||
else if(options.shortVideo == false){
|
||||
this.whereLongVideo()
|
||||
}
|
||||
|
||||
|
||||
if (options.isLive === true) {
|
||||
this.whereLive()
|
||||
} else if (options.isLive === false) {
|
||||
|
@ -305,6 +316,7 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
|
|||
this.sort + ' ' +
|
||||
this.limit + ' ' +
|
||||
this.offset
|
||||
|
||||
}
|
||||
|
||||
private setCountAttribute () {
|
||||
|
@ -513,6 +525,14 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
|
|||
this.and.push('"video"."nsfw" IS FALSE')
|
||||
}
|
||||
|
||||
private whereShortVideo(){
|
||||
this.and.push('"video"."shortVideo" IS TRUE')
|
||||
}
|
||||
|
||||
private whereLongVideo(){
|
||||
this.and.push('"video"."shortVideo" IS FALSE')
|
||||
}
|
||||
|
||||
private whereLive () {
|
||||
this.and.push('"video"."isLive" IS TRUE')
|
||||
}
|
||||
|
|
|
@ -430,6 +430,15 @@ export type ForAPIOptions = {
|
|||
nsfw: true
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
fields: [ 'shortVideo' ],
|
||||
where: {
|
||||
shortVideo: false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
fields: [ 'isLive' ], // Most of the videos are VOD
|
||||
where: {
|
||||
|
@ -550,6 +559,10 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
@Column
|
||||
downloadEnabled: boolean
|
||||
|
||||
@AllowNull(true)
|
||||
@Column
|
||||
shortVideo: boolean
|
||||
|
||||
@AllowNull(false)
|
||||
@Column
|
||||
waitTranscoding: boolean
|
||||
|
@ -1157,6 +1170,10 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
search?: string
|
||||
|
||||
excludeAlreadyWatched?: boolean
|
||||
|
||||
durationMax?:number
|
||||
|
||||
shortVideo? : boolean
|
||||
}) {
|
||||
VideoModel.throwIfPrivateIncludeWithoutUser(options.include, options.user)
|
||||
VideoModel.throwIfPrivacyOneOfWithoutUser(options.privacyOneOf, options.user)
|
||||
|
@ -1197,7 +1214,9 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
'hasWebtorrentFiles',
|
||||
'hasWebVideoFiles',
|
||||
'search',
|
||||
'excludeAlreadyWatched'
|
||||
'excludeAlreadyWatched',
|
||||
'durationMax',
|
||||
'shortVideo'
|
||||
]),
|
||||
|
||||
serverAccountIdForBlock: serverActor.Account.id,
|
||||
|
@ -1249,6 +1268,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
excludeAlreadyWatched?: boolean
|
||||
|
||||
countVideos?: boolean
|
||||
|
||||
shortVideo?: boolean
|
||||
}) {
|
||||
VideoModel.throwIfPrivateIncludeWithoutUser(options.include, options.user)
|
||||
VideoModel.throwIfPrivacyOneOfWithoutUser(options.privacyOneOf, options.user)
|
||||
|
@ -1284,7 +1305,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
'uuids',
|
||||
'search',
|
||||
'displayOnlyForFollower',
|
||||
'excludeAlreadyWatched'
|
||||
'excludeAlreadyWatched',
|
||||
'shortVideo'
|
||||
]),
|
||||
serverAccountIdForBlock: serverActor.Account.id
|
||||
}
|
||||
|
@ -1624,6 +1646,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
options: BuildVideosListQueryOptions,
|
||||
countVideos = true
|
||||
): Promise<ResultList<VideoModel>> {
|
||||
|
||||
const span = tracer.startSpan('peertube.VideoModel.getAvailableForApi')
|
||||
|
||||
function getCount () {
|
||||
|
|
Loading…
Reference in New Issue