Support short uuid for GET video/playlist

pull/4218/head
Chocobozzz 2021-06-28 17:30:59 +02:00 committed by Chocobozzz
parent 62ddc31a9e
commit d4a8e7a65f
94 changed files with 1029 additions and 673 deletions

View File

@ -122,7 +122,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
} }
getVideoUrl (videoBlock: VideoBlacklist) { getVideoUrl (videoBlock: VideoBlacklist) {
return Video.buildClientUrl(videoBlock.video.uuid) return Video.buildWatchUrl(videoBlock.video)
} }
toHtml (text: string) { toHtml (text: string) {

View File

@ -1,7 +1,7 @@
import { SortMeta } from 'primeng/api' import { SortMeta } from 'primeng/api'
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { Notifier, RestPagination, RestTable } from '@app/core' import { Notifier, RestPagination, RestTable } from '@app/core'
import { VideoImportService } from '@app/shared/shared-main' import { Video, VideoImportService } from '@app/shared/shared-main'
import { VideoImport, VideoImportState } from '@shared/models' import { VideoImport, VideoImportState } from '@shared/models'
@Component({ @Component({
@ -55,11 +55,11 @@ export class MyVideoImportsComponent extends RestTable implements OnInit {
} }
getVideoUrl (video: { uuid: string }) { getVideoUrl (video: { uuid: string }) {
return '/w/' + video.uuid return Video.buildWatchUrl(video)
} }
getEditVideoUrl (video: { uuid: string }) { getEditVideoUrl (video: { uuid: string }) {
return '/videos/update/' + video.uuid return Video.buildUpdateUrl(video)
} }
protected reloadData () { protected reloadData () {

View File

@ -1,7 +1,7 @@
import { forkJoin } from 'rxjs' import { forkJoin } from 'rxjs'
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { VideoChannel } from '@app/shared/shared-main' import { Video, VideoChannel } from '@app/shared/shared-main'
import { SearchService } from '@app/shared/shared-search' import { SearchService } from '@app/shared/shared-search'
@Component({ @Component({
@ -39,7 +39,7 @@ export class RemoteInteractionComponent implements OnInit {
if (videoResult.data.length !== 0) { if (videoResult.data.length !== 0) {
const video = videoResult.data[0] const video = videoResult.data[0]
redirectUrl = '/w/' + video.uuid redirectUrl = Video.buildWatchUrl(video)
} else if (channelResult.data.length !== 0) { } else if (channelResult.data.length !== 0) {
const channel = new VideoChannel(channelResult.data[0]) const channel = new VideoChannel(channelResult.data[0])

View File

@ -1,7 +1,7 @@
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { map } from 'rxjs/operators' import { map } from 'rxjs/operators'
import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router' import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'
import { ResultList } from '@shared/models/result-list.model' import { ResultList } from '@shared/models'
export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> { export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
protected router: Router protected router: Router

View File

@ -19,6 +19,6 @@ export class PlaylistLazyLoadResolver extends AbstractLazyLoadResolver<VideoPlay
} }
protected buildUrl (playlist: VideoPlaylist) { protected buildUrl (playlist: VideoPlaylist) {
return '/w/p/' + playlist.uuid return VideoPlaylist.buildWatchUrl(playlist)
} }
} }

View File

@ -19,6 +19,6 @@ export class VideoLazyLoadResolver extends AbstractLazyLoadResolver<Video> {
} }
protected buildUrl (video: Video) { protected buildUrl (video: Video) {
return '/w/' + video.uuid return Video.buildWatchUrl(video)
} }
} }

View File

@ -1,11 +1,11 @@
import { forkJoin } from 'rxjs' import { forkJoin } from 'rxjs'
import { AfterViewChecked, AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core'
import { scrollToTop } from '@app/helpers' import { scrollToTop } from '@app/helpers'
import { FormValidatorService } from '@app/shared/shared-forms' import { FormValidatorService } from '@app/shared/shared-forms'
import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LiveVideoService } from '@app/shared/shared-video-live' import { LiveVideoService } from '@app/shared/shared-video-live'
import { LoadingBarService } from '@ngx-loading-bar/core' import { LoadingBarService } from '@ngx-loading-bar/core'
import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode, VideoPrivacy } from '@shared/models' import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode, VideoPrivacy } from '@shared/models'
@ -127,7 +127,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
() => { () => {
this.notifier.success($localize`Live published.`) this.notifier.success($localize`Live published.`)
this.router.navigate(['/w', video.uuid]) this.router.navigateByUrl(Video.buildWatchUrl(video))
}, },
err => { err => {

View File

@ -1,16 +1,16 @@
import { UploadState, UploadxOptions, UploadxService } from 'ngx-uploadx'
import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http'
import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { UploadxOptions, UploadState, UploadxService } from 'ngx-uploadx'
import { UploaderXFormData } from './uploaderx-form-data'
import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService, UserService } from '@app/core' import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService, UserService } from '@app/core'
import { scrollToTop, genericUploadErrorHandler } from '@app/helpers' import { genericUploadErrorHandler, scrollToTop } from '@app/helpers'
import { FormValidatorService } from '@app/shared/shared-forms' import { FormValidatorService } from '@app/shared/shared-forms'
import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LoadingBarService } from '@ngx-loading-bar/core' import { LoadingBarService } from '@ngx-loading-bar/core'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { VideoPrivacy } from '@shared/models' import { VideoPrivacy } from '@shared/models'
import { UploaderXFormData } from './uploaderx-form-data'
import { VideoSend } from './video-send' import { VideoSend } from './video-send'
import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http'
@Component({ @Component({
selector: 'my-video-upload', selector: 'my-video-upload',
@ -243,7 +243,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
this.isUploadingVideo = false this.isUploadingVideo = false
this.notifier.success($localize`Video published.`) this.notifier.success($localize`Video published.`)
this.router.navigate([ '/w', video.uuid ]) this.router.navigateByUrl(Video.buildWatchUrl(video))
}, },
err => { err => {

View File

@ -1,7 +1,7 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div class="title-page title-page-single">
<span class="mr-1" i18n>Update</span> <span class="mr-1" i18n>Update</span>
<a [routerLink]="[ '/w', video.uuid ]">{{ video?.name }}</a> <a [routerLink]="getVideoUrl()">{{ video?.name }}</a>
</div> </div>
<form novalidate [formGroup]="form"> <form novalidate [formGroup]="form">

View File

@ -5,7 +5,7 @@ import { Component, HostListener, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LiveVideoService } from '@app/shared/shared-video-live' import { LiveVideoService } from '@app/shared/shared-video-live'
import { LoadingBarService } from '@ngx-loading-bar/core' import { LoadingBarService } from '@ngx-loading-bar/core'
import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models' import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
@ -156,7 +156,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
this.isUpdatingVideo = false this.isUpdatingVideo = false
this.loadingBar.useRef().complete() this.loadingBar.useRef().complete()
this.notifier.success($localize`Video updated.`) this.notifier.success($localize`Video updated.`)
this.router.navigate([ '/w', this.video.uuid ]) this.router.navigateByUrl(Video.buildWatchUrl(this.video))
}, },
err => { err => {
@ -175,4 +175,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
pluginData: this.video.pluginData pluginData: this.video.pluginData
}) })
} }
getVideoUrl () {
return Video.buildWatchUrl(this.videoDetails)
}
} }

View File

@ -20,7 +20,7 @@
</a> </a>
</div> </div>
<a [routerLink]="['/w', video.uuid, { 'threadId': comment.threadId }]" class="comment-date" [title]="comment.createdAt"> <a [routerLink]="['/w', video.shortUUID, { 'threadId': comment.threadId }]" class="comment-date" [title]="comment.createdAt">
{{ comment.createdAt | myFromNow }} {{ comment.createdAt | myFromNow }}
</a> </a>
</div> </div>
@ -45,7 +45,7 @@
<ng-container *ngIf="comment.isDeleted"> <ng-container *ngIf="comment.isDeleted">
<div class="comment-account-date"> <div class="comment-account-date">
<span class="comment-account" i18n>Deleted</span> <span class="comment-account" i18n>Deleted</span>
<a [routerLink]="['/w', video.uuid, { 'threadId': comment.threadId }]" <a [routerLink]="['/w', video.shortUUID, { 'threadId': comment.threadId }]"
class="comment-date">{{ comment.createdAt | myFromNow }}</a> class="comment-date">{{ comment.createdAt | myFromNow }}</a>
</div> </div>

View File

@ -247,7 +247,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
this.componentPagination.totalItems = null this.componentPagination.totalItems = null
this.totalNotDeletedComments = null this.totalNotDeletedComments = null
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video.uuid) this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
this.loadMoreThreads() this.loadMoreThreads()
} }
} }

View File

@ -312,7 +312,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
getVideoUrl () { getVideoUrl () {
if (!this.video.url) { if (!this.video.url) {
return this.video.originInstanceUrl + VideoDetails.buildClientUrl(this.video.uuid) return this.video.originInstanceUrl + VideoDetails.buildWatchUrl(this.video)
} }
return this.video.url return this.video.url
} }
@ -415,7 +415,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private loadVideo (videoId: string) { private loadVideo (videoId: string) {
// Video did not change // Video did not change
if (this.video && this.video.uuid === videoId) return if (
this.video &&
(this.video.uuid === videoId || this.video.shortUUID === videoId)
) return
if (this.player) this.player.pause() if (this.player) this.player.pause()
@ -489,7 +492,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private loadPlaylist (playlistId: string) { private loadPlaylist (playlistId: string) {
// Playlist did not change // Playlist did not change
if (this.playlist && this.playlist.uuid === playlistId) return if (
this.playlist &&
(this.playlist.uuid === playlistId || this.playlist.shortUUID === playlistId)
) return
this.playlistService.getVideoPlaylist(playlistId) this.playlistService.getVideoPlaylist(playlistId)
.pipe( .pipe(
@ -772,7 +778,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private flushPlayer () { private flushPlayer () {
// Remove player if it exists // Remove player if it exists
if (this.player) { if (!this.player) return
try { try {
this.player.dispose() this.player.dispose()
this.player = undefined this.player = undefined
@ -780,7 +787,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
console.error('Cannot dispose player.', err) console.error('Cannot dispose player.', err)
} }
} }
}
private buildPlayerManagerOptions (params: { private buildPlayerManagerOptions (params: {
video: VideoDetails, video: VideoDetails,

View File

@ -116,11 +116,11 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
} }
getVideoUrl (abuse: AdminAbuse) { getVideoUrl (abuse: AdminAbuse) {
return Video.buildClientUrl(abuse.video.uuid) return Video.buildWatchUrl(abuse.video)
} }
getCommentUrl (abuse: AdminAbuse) { getCommentUrl (abuse: AdminAbuse) {
return Video.buildClientUrl(abuse.comment.video.uuid) + ';threadId=' + abuse.comment.threadId return Video.buildWatchUrl(abuse.comment.video) + ';threadId=' + abuse.comment.threadId
} }
getAccountUrl (abuse: ProcessedAbuse) { getAccountUrl (abuse: ProcessedAbuse) {

View File

@ -6,7 +6,7 @@ import { Component, Input, ViewEncapsulation } from '@angular/core'
templateUrl: './link.component.html' templateUrl: './link.component.html'
}) })
export class LinkComponent { export class LinkComponent {
@Input() internalLink?: any[] @Input() internalLink?: string | any[]
@Input() href?: string @Input() href?: string
@Input() target?: string @Input() target?: string

View File

@ -12,6 +12,7 @@ import {
UserRight, UserRight,
VideoInfo VideoInfo
} from '@shared/models' } from '@shared/models'
import { Video } from '../video'
export class UserNotification implements UserNotificationServer { export class UserNotification implements UserNotificationServer {
id: number id: number
@ -238,7 +239,7 @@ export class UserNotification implements UserNotificationServer {
} }
private buildVideoUrl (video: { uuid: string }) { private buildVideoUrl (video: { uuid: string }) {
return '/w/' + video.uuid return Video.buildWatchUrl(video)
} }
private buildAccountUrl (account: { name: string, host: string }) { private buildAccountUrl (account: { name: string, host: string }) {

View File

@ -26,12 +26,18 @@ export class Video implements VideoServerModel {
licence: VideoConstant<number> licence: VideoConstant<number>
language: VideoConstant<string> language: VideoConstant<string>
privacy: VideoConstant<VideoPrivacy> privacy: VideoConstant<VideoPrivacy>
description: string description: string
duration: number duration: number
durationLabel: string durationLabel: string
id: number id: number
uuid: string uuid: string
shortUUID: string
isLocal: boolean isLocal: boolean
name: string name: string
serverHost: string serverHost: string
thumbnailPath: string thumbnailPath: string
@ -85,8 +91,12 @@ export class Video implements VideoServerModel {
pluginData?: any pluginData?: any
static buildClientUrl (videoUUID: string) { static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) {
return '/w/' + videoUUID return '/w/' + (video.shortUUID || video.uuid)
}
static buildUpdateUrl (video: Pick<Video, 'uuid'>) {
return '/videos/update/' + video.uuid
} }
constructor (hash: VideoServerModel, translations = {}) { constructor (hash: VideoServerModel, translations = {}) {
@ -109,6 +119,7 @@ export class Video implements VideoServerModel {
this.id = hash.id this.id = hash.id
this.uuid = hash.uuid this.uuid = hash.uuid
this.shortUUID = hash.shortUUID
this.isLocal = hash.isLocal this.isLocal = hash.isLocal
this.name = hash.name this.name = hash.name

View File

@ -1,5 +1,5 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core' import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { VideoDetails } from '@app/shared/shared-main' import { Video, VideoDetails } from '@app/shared/shared-main'
import { VideoPlaylist } from '@app/shared/shared-video-playlist' import { VideoPlaylist } from '@app/shared/shared-video-playlist'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models' import { VideoCaption } from '@shared/models'
@ -98,14 +98,15 @@ export class VideoShareComponent {
getVideoUrl () { getVideoUrl () {
let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin
baseUrl += '/w/' + this.video.uuid baseUrl += Video.buildWatchUrl(this.video)
const options = this.getVideoOptions(baseUrl) const options = this.getVideoOptions(baseUrl)
return buildVideoLink(options) return buildVideoLink(options)
} }
getPlaylistUrl () { getPlaylistUrl () {
const base = window.location.origin + '/w/p/' + this.playlist.uuid const base = window.location.origin + VideoPlaylist.buildWatchUrl(this.playlist)
if (!this.includeVideoInPlaylist) return base if (!this.includeVideoInPlaylist) return base

View File

@ -12,7 +12,7 @@ export class VideoThumbnailComponent {
@Input() video: Video @Input() video: Video
@Input() nsfw = false @Input() nsfw = false
@Input() videoRouterLink: any[] @Input() videoRouterLink: string | any[]
@Input() queryParams: { [ p: string ]: any } @Input() queryParams: { [ p: string ]: any }
@Input() videoHref: string @Input() videoHref: string
@Input() videoTarget: string @Input() videoTarget: string
@ -57,7 +57,7 @@ export class VideoThumbnailComponent {
getVideoRouterLink () { getVideoRouterLink () {
if (this.videoRouterLink) return this.videoRouterLink if (this.videoRouterLink) return this.videoRouterLink
return [ '/w', this.video.uuid ] return Video.buildWatchUrl(this.video)
} }
onWatchLaterClick (event: Event) { onWatchLaterClick (event: Event) {

View File

@ -1,5 +1,5 @@
import { getAbsoluteAPIUrl } from '@app/helpers' import { getAbsoluteAPIUrl } from '@app/helpers'
import { Account, Actor } from '@app/shared/shared-main' import { Account, Actor, Video } from '@app/shared/shared-main'
import { Account as AccountInterface, VideoComment as VideoCommentServerModel, VideoCommentAdmin as VideoCommentAdminServerModel } from '@shared/models' import { Account as AccountInterface, VideoComment as VideoCommentServerModel, VideoCommentAdmin as VideoCommentAdminServerModel } from '@shared/models'
export class VideoComment implements VideoCommentServerModel { export class VideoComment implements VideoCommentServerModel {
@ -85,7 +85,7 @@ export class VideoCommentAdmin implements VideoCommentAdminServerModel {
id: hash.video.id, id: hash.video.id,
uuid: hash.video.uuid, uuid: hash.video.uuid,
name: hash.video.name, name: hash.video.name,
localUrl: '/w/' + hash.video.uuid localUrl: Video.buildWatchUrl(hash.video)
} }
this.localUrl = this.video.localUrl + ';threadId=' + this.threadId this.localUrl = this.video.localUrl + ';threadId=' + this.threadId

View File

@ -9,6 +9,7 @@ import {
FeedFormat, FeedFormat,
ResultList, ResultList,
ThreadsResultList, ThreadsResultList,
Video,
VideoComment as VideoCommentServerModel, VideoComment as VideoCommentServerModel,
VideoCommentAdmin, VideoCommentAdmin,
VideoCommentCreate, VideoCommentCreate,
@ -127,7 +128,7 @@ export class VideoCommentService {
) )
} }
getVideoCommentsFeeds (videoUUID?: string) { getVideoCommentsFeeds (video: Pick<Video, 'uuid'>) {
const feeds = [ const feeds = [
{ {
format: FeedFormat.RSS, format: FeedFormat.RSS,
@ -146,9 +147,9 @@ export class VideoCommentService {
} }
] ]
if (videoUUID !== undefined) { if (video !== undefined) {
for (const feed of feeds) { for (const feed of feeds) {
feed.url += '?videoId=' + videoUUID feed.url += '?videoId=' + video.uuid
} }
} }

View File

@ -85,7 +85,7 @@ export class VideoMiniatureComponent implements OnInit {
playlistElementId?: number playlistElementId?: number
} }
videoRouterLink: any[] = [] videoRouterLink: string | any[] = []
videoHref: string videoHref: string
videoTarget: string videoTarget: string
@ -120,7 +120,7 @@ export class VideoMiniatureComponent implements OnInit {
buildVideoLink () { buildVideoLink () {
if (this.videoLinkType === 'internal' || !this.video.url) { if (this.videoLinkType === 'internal' || !this.video.url) {
this.videoRouterLink = [ '/w', this.video.uuid ] this.videoRouterLink = Video.buildWatchUrl(this.video)
return return
} }

View File

@ -66,7 +66,7 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit {
buildRouterLink () { buildRouterLink () {
if (!this.playlist) return null if (!this.playlist) return null
return [ '/w/p', this.playlist.uuid ] return VideoPlaylist.buildWatchUrl(this.playlist)
} }
buildRouterQuery () { buildRouterQuery () {

View File

@ -39,7 +39,7 @@ export class VideoPlaylistMiniatureComponent implements OnInit {
} }
if (this.linkType === 'internal' || !this.playlist.url) { if (this.linkType === 'internal' || !this.playlist.url) {
this.routerLink = [ '/w/p', this.playlist.uuid ] this.routerLink = VideoPlaylist.buildWatchUrl(this.playlist)
return return
} }

View File

@ -13,6 +13,8 @@ import {
export class VideoPlaylist implements ServerVideoPlaylist { export class VideoPlaylist implements ServerVideoPlaylist {
id: number id: number
uuid: string uuid: string
shortUUID: string
isLocal: boolean isLocal: boolean
url: string url: string
@ -41,11 +43,17 @@ export class VideoPlaylist implements ServerVideoPlaylist {
videoChannelBy?: string videoChannelBy?: string
static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) {
return '/w/p/' + (playlist.uuid || playlist.shortUUID)
}
constructor (hash: ServerVideoPlaylist, translations: {}) { constructor (hash: ServerVideoPlaylist, translations: {}) {
const absoluteAPIUrl = getAbsoluteAPIUrl() const absoluteAPIUrl = getAbsoluteAPIUrl()
this.id = hash.id this.id = hash.id
this.uuid = hash.uuid this.uuid = hash.uuid
this.shortUUID = hash.shortUUID
this.url = hash.url this.url = hash.url
this.isLocal = hash.isLocal this.isLocal = hash.isLocal

View File

@ -131,6 +131,7 @@
"sanitize-html": "2.x", "sanitize-html": "2.x",
"sequelize": "6.6.2", "sequelize": "6.6.2",
"sequelize-typescript": "^2.0.0-beta.1", "sequelize-typescript": "^2.0.0-beta.1",
"short-uuid": "^4.2.0",
"sitemap": "^7.0.0", "sitemap": "^7.0.0",
"socket.io": "^4.0.1", "socket.io": "^4.0.1",
"sql-formatter": "^4.0.0-beta.0", "sql-formatter": "^4.0.0-beta.0",
@ -138,7 +139,6 @@
"tsconfig-paths": "^3.9.0", "tsconfig-paths": "^3.9.0",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"useragent": "^2.3.0", "useragent": "^2.3.0",
"uuid": "^8.1.0",
"validator": "^13.0.0", "validator": "^13.0.0",
"webfinger.js": "^2.6.6", "webfinger.js": "^2.6.6",
"webtorrent": "^1.0.0", "webtorrent": "^1.0.0",

View File

@ -1,7 +1,7 @@
import * as express from 'express' import * as express from 'express'
import * as RateLimit from 'express-rate-limit' import * as RateLimit from 'express-rate-limit'
import { v4 as uuidv4 } from 'uuid'
import { logger } from '@server/helpers/logger' import { logger } from '@server/helpers/logger'
import { buildUUID } from '@server/helpers/uuid'
import { CONFIG } from '@server/initializers/config' import { CONFIG } from '@server/initializers/config'
import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth' import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth'
import { handleOAuthToken } from '@server/lib/auth/oauth' import { handleOAuthToken } from '@server/lib/auth/oauth'
@ -107,7 +107,7 @@ function getScopedTokens (req: express.Request, res: express.Response) {
async function renewScopedTokens (req: express.Request, res: express.Response) { async function renewScopedTokens (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.user const user = res.locals.oauth.token.user
user.feedToken = uuidv4() user.feedToken = buildUUID()
await user.save() await user.save()
return res.json({ return res.json({

View File

@ -1,6 +1,8 @@
import * as express from 'express' import * as express from 'express'
import { join } from 'path' import { join } from 'path'
import { uuidToShort } from '@server/helpers/uuid'
import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
import { Hooks } from '@server/lib/plugins/hooks'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
@ -43,7 +45,6 @@ import {
import { AccountModel } from '../../models/account/account' import { AccountModel } from '../../models/account/account'
import { VideoPlaylistModel } from '../../models/video/video-playlist' import { VideoPlaylistModel } from '../../models/video/video-playlist'
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
import { Hooks } from '@server/lib/plugins/hooks'
const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
@ -199,6 +200,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
return res.json({ return res.json({
videoPlaylist: { videoPlaylist: {
id: videoPlaylistCreated.id, id: videoPlaylistCreated.id,
shortUUID: uuidToShort(videoPlaylistCreated.uuid),
uuid: videoPlaylistCreated.uuid uuid: videoPlaylistCreated.uuid
} }
}) })

View File

@ -1,6 +1,6 @@
import * as express from 'express' import * as express from 'express'
import { v4 as uuidv4 } from 'uuid'
import { createReqFiles } from '@server/helpers/express-utils' import { createReqFiles } from '@server/helpers/express-utils'
import { buildUUID, uuidToShort } from '@server/helpers/uuid'
import { CONFIG } from '@server/initializers/config' import { CONFIG } from '@server/initializers/config'
import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants' import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
@ -11,12 +11,12 @@ import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator
import { VideoLiveModel } from '@server/models/video/video-live' import { VideoLiveModel } from '@server/models/video/video-live'
import { MVideoDetails, MVideoFullLight } from '@server/types/models' import { MVideoDetails, MVideoFullLight } from '@server/types/models'
import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared' import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { sequelizeTypescript } from '../../../initializers/database' import { sequelizeTypescript } from '../../../initializers/database'
import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail'
import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares'
import { VideoModel } from '../../../models/video/video' import { VideoModel } from '../../../models/video/video'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
const liveRouter = express.Router() const liveRouter = express.Router()
@ -94,7 +94,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
const videoLive = new VideoLiveModel() const videoLive = new VideoLiveModel()
videoLive.saveReplay = videoInfo.saveReplay || false videoLive.saveReplay = videoInfo.saveReplay || false
videoLive.permanentLive = videoInfo.permanentLive || false videoLive.permanentLive = videoInfo.permanentLive || false
videoLive.streamKey = uuidv4() videoLive.streamKey = buildUUID()
const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({
video, video,
@ -138,6 +138,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
return res.json({ return res.json({
video: { video: {
id: videoCreated.id, id: videoCreated.id,
shortUUID: uuidToShort(videoCreated.uuid),
uuid: videoCreated.uuid uuid: videoCreated.uuid
} }
}) })

View File

@ -2,6 +2,7 @@ import * as express from 'express'
import { move } from 'fs-extra' import { move } from 'fs-extra'
import { getLowercaseExtension } from '@server/helpers/core-utils' import { getLowercaseExtension } from '@server/helpers/core-utils'
import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload' import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload'
import { uuidToShort } from '@server/helpers/uuid'
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
@ -218,6 +219,7 @@ async function addVideo (options: {
return res.json({ return res.json({
video: { video: {
id: videoCreated.id, id: videoCreated.id,
shortUUID: uuidToShort(videoCreated.uuid),
uuid: videoCreated.uuid uuid: videoCreated.uuid
} }
}) })

View File

@ -2,6 +2,7 @@ import 'multer'
import { UploadFilesForCheck } from 'express' import { UploadFilesForCheck } from 'express'
import { sep } from 'path' import { sep } from 'path'
import validator from 'validator' import validator from 'validator'
import { isShortUUID, shortToUUID } from '../uuid'
function exists (value: any) { function exists (value: any) {
return value !== undefined && value !== null return value !== undefined && value !== null
@ -50,42 +51,7 @@ function isIntOrNull (value: any) {
return value === null || validator.isInt('' + value) return value === null || validator.isInt('' + value)
} }
function toIntOrNull (value: string) { // ---------------------------------------------------------------------------
const v = toValueOrNull(value)
if (v === null || v === undefined) return v
if (typeof v === 'number') return v
return validator.toInt('' + v)
}
function toBooleanOrNull (value: any) {
const v = toValueOrNull(value)
if (v === null || v === undefined) return v
if (typeof v === 'boolean') return v
return validator.toBoolean('' + v)
}
function toValueOrNull (value: string) {
if (value === 'null') return null
return value
}
function toArray (value: any) {
if (value && isArray(value) === false) return [ value ]
return value
}
function toIntArray (value: any) {
if (!value) return []
if (isArray(value) === false) return [ validator.toInt(value) ]
return value.map(v => validator.toInt(v))
}
function isFileFieldValid ( function isFileFieldValid (
files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
@ -160,6 +126,51 @@ function isFileValid (
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function toCompleteUUID (value: string) {
if (isShortUUID(value)) return shortToUUID(value)
return value
}
function toIntOrNull (value: string) {
const v = toValueOrNull(value)
if (v === null || v === undefined) return v
if (typeof v === 'number') return v
return validator.toInt('' + v)
}
function toBooleanOrNull (value: any) {
const v = toValueOrNull(value)
if (v === null || v === undefined) return v
if (typeof v === 'boolean') return v
return validator.toBoolean('' + v)
}
function toValueOrNull (value: string) {
if (value === 'null') return null
return value
}
function toArray (value: any) {
if (value && isArray(value) === false) return [ value ]
return value
}
function toIntArray (value: any) {
if (!value) return []
if (isArray(value) === false) return [ validator.toInt(value) ]
return value.map(v => validator.toInt(v))
}
// ---------------------------------------------------------------------------
export { export {
exists, exists,
isArrayOf, isArrayOf,
@ -169,6 +180,7 @@ export {
isIdValid, isIdValid,
isSafePath, isSafePath,
isUUIDValid, isUUIDValid,
toCompleteUUID,
isIdOrUUIDValid, isIdOrUUIDValid,
isDateValid, isDateValid,
toValueOrNull, toValueOrNull,

View File

@ -1,12 +1,12 @@
import { copy, readFile, remove, rename } from 'fs-extra' import { copy, readFile, remove, rename } from 'fs-extra'
import * as Jimp from 'jimp' import * as Jimp from 'jimp'
import { v4 as uuidv4 } from 'uuid'
import { getLowercaseExtension } from './core-utils' import { getLowercaseExtension } from './core-utils'
import { convertWebPToJPG, processGIF } from './ffmpeg-utils' import { convertWebPToJPG, processGIF } from './ffmpeg-utils'
import { logger } from './logger' import { logger } from './logger'
import { buildUUID } from './uuid'
function generateImageFilename (extension = '.jpg') { function generateImageFilename (extension = '.jpg') {
return uuidv4() + extension return buildUUID() + extension
} }
async function processImage ( async function processImage (

32
server/helpers/uuid.ts Normal file
View File

@ -0,0 +1,32 @@
import * as short from 'short-uuid'
const translator = short()
function buildUUID () {
return short.uuid()
}
function uuidToShort (uuid: string) {
if (!uuid) return uuid
return translator.fromUUID(uuid)
}
function shortToUUID (shortUUID: string) {
if (!shortUUID) return shortUUID
return translator.toUUID(shortUUID)
}
function isShortUUID (value: string) {
if (!value) return false
return value.length === translator.maxLength
}
export {
buildUUID,
uuidToShort,
shortToUUID,
isShortUUID
}

View File

@ -1,5 +1,5 @@
import { buildUUID } from '@server/helpers/uuid'
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { v4 as uuidv4 } from 'uuid'
async function up (utils: { async function up (utils: {
transaction: Sequelize.Transaction transaction: Sequelize.Transaction
@ -23,7 +23,7 @@ async function up (utils: {
{ {
const authors = await utils.db.Author.findAll() const authors = await utils.db.Author.findAll()
for (const author of authors) { for (const author of authors) {
author.uuid = uuidv4() author.uuid = buildUUID()
await author.save() await author.save()
} }
} }

View File

@ -1,6 +1,6 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { buildUUID } from '@server/helpers/uuid'
import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos' import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos'
import { v4 as uuidv4 } from 'uuid'
import { WEBSERVER } from '../constants' import { WEBSERVER } from '../constants'
async function up (utils: { async function up (utils: {
@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS "videoPlaylistElement"
const usernames = userResult.map(r => r.username) const usernames = userResult.map(r => r.username)
for (const username of usernames) { for (const username of usernames) {
const uuid = uuidv4() const uuid = buildUUID()
const baseUrl = WEBSERVER.URL + '/video-playlists/' + uuid const baseUrl = WEBSERVER.URL + '/video-playlists/' + uuid
const query = ` const query = `

View File

@ -1,5 +1,5 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { v4 as uuidv4 } from 'uuid' import { buildUUID } from '@server/helpers/uuid'
async function up (utils: { async function up (utils: {
transaction: Sequelize.Transaction transaction: Sequelize.Transaction
@ -26,7 +26,7 @@ async function up (utils: {
const users = await utils.sequelize.query<any>(query, options) const users = await utils.sequelize.query<any>(query, options)
for (const user of users) { for (const user of users) {
const queryUpdate = `UPDATE "user" SET "feedToken" = '${uuidv4()}' WHERE id = ${user.id}` const queryUpdate = `UPDATE "user" SET "feedToken" = '${buildUUID()}' WHERE id = ${user.id}`
await utils.sequelize.query(queryUpdate) await utils.sequelize.query(queryUpdate)
} }
} }

View File

@ -1,6 +1,6 @@
import { v4 as uuidv4 } from 'uuid'
import { getLowercaseExtension } from '@server/helpers/core-utils' import { getLowercaseExtension } from '@server/helpers/core-utils'
import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc' import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
import { buildUUID } from '@server/helpers/uuid'
import { MIMETYPES } from '@server/initializers/constants' import { MIMETYPES } from '@server/initializers/constants'
import { ActorModel } from '@server/models/actor/actor' import { ActorModel } from '@server/models/actor/actor'
import { FilteredModelAttributes } from '@server/types' import { FilteredModelAttributes } from '@server/types'
@ -51,7 +51,7 @@ function getImageInfoFromObject (actorObject: ActivityPubActor, type: ActorImage
if (!extension) return undefined if (!extension) return undefined
return { return {
name: uuidv4() + extension, name: buildUUID() + extension,
fileUrl: icon.url, fileUrl: icon.url,
height: icon.height, height: icon.height,
width: icon.width, width: icon.width,

View File

@ -27,6 +27,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
import { VideoPlaylistModel } from '../models/video/video-playlist' import { VideoPlaylistModel } from '../models/video/video-playlist'
import { MAccountActor, MChannelActor } from '../types/models' import { MAccountActor, MChannelActor } from '../types/models'
import { ServerConfigManager } from './server-config-manager' import { ServerConfigManager } from './server-config-manager'
import { toCompleteUUID } from '@server/helpers/custom-validators/misc'
type Tags = { type Tags = {
ogType: string ogType: string
@ -78,7 +79,9 @@ class ClientHtml {
return customHtml return customHtml
} }
static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { static async getWatchHTMLPage (videoIdArg: string, req: express.Request, res: express.Response) {
const videoId = toCompleteUUID(videoIdArg)
// Let Angular application handle errors // Let Angular application handle errors
if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) { if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) {
res.status(HttpStatusCode.NOT_FOUND_404) res.status(HttpStatusCode.NOT_FOUND_404)
@ -136,7 +139,9 @@ class ClientHtml {
return customHtml return customHtml
} }
static async getWatchPlaylistHTMLPage (videoPlaylistId: string, req: express.Request, res: express.Response) { static async getWatchPlaylistHTMLPage (videoPlaylistIdArg: string, req: express.Request, res: express.Response) {
const videoPlaylistId = toCompleteUUID(videoPlaylistIdArg)
// Let Angular application handle errors // Let Angular application handle errors
if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) { if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) {
res.status(HttpStatusCode.NOT_FOUND_404) res.status(HttpStatusCode.NOT_FOUND_404)

View File

@ -2,8 +2,8 @@ import 'multer'
import { queue } from 'async' import { queue } from 'async'
import * as LRUCache from 'lru-cache' import * as LRUCache from 'lru-cache'
import { join } from 'path' import { join } from 'path'
import { v4 as uuidv4 } from 'uuid'
import { getLowercaseExtension } from '@server/helpers/core-utils' import { getLowercaseExtension } from '@server/helpers/core-utils'
import { buildUUID } from '@server/helpers/uuid'
import { ActorModel } from '@server/models/actor/actor' import { ActorModel } from '@server/models/actor/actor'
import { ActivityPubActorType, ActorImageType } from '@shared/models' import { ActivityPubActorType, ActorImageType } from '@shared/models'
import { retryTransactionWrapper } from '../helpers/database-utils' import { retryTransactionWrapper } from '../helpers/database-utils'
@ -44,7 +44,7 @@ async function updateLocalActorImageFile (
const extension = getLowercaseExtension(imagePhysicalFile.filename) const extension = getLowercaseExtension(imagePhysicalFile.filename)
const imageName = uuidv4() + extension const imageName = buildUUID() + extension
const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName) const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName)
await processImage(imagePhysicalFile.path, destination, imageSize) await processImage(imagePhysicalFile.path, destination, imageSize)

View File

@ -1,5 +1,5 @@
import { Transaction } from 'sequelize/types' import { Transaction } from 'sequelize/types'
import { v4 as uuidv4 } from 'uuid' import { buildUUID } from '@server/helpers/uuid'
import { UserModel } from '@server/models/user/user' import { UserModel } from '@server/models/user/user'
import { MActorDefault } from '@server/types/models/actor' import { MActorDefault } from '@server/types/models/actor'
import { ActivityPubActorType } from '../../shared/models/activitypub' import { ActivityPubActorType } from '../../shared/models/activitypub'
@ -210,7 +210,7 @@ async function buildChannelAttributes (user: MUser, transaction?: Transaction, c
// Conflict, generate uuid instead // Conflict, generate uuid instead
const actor = await ActorModel.loadLocalByName(channelName, transaction) const actor = await ActorModel.loadLocalByName(channelName, transaction)
if (actor) channelName = uuidv4() if (actor) channelName = buildUUID()
const videoChannelDisplayName = `Main ${user.username} channel` const videoChannelDisplayName = `Main ${user.username} channel`

View File

@ -12,7 +12,7 @@ import {
isAbuseTimestampValid, isAbuseTimestampValid,
isAbuseVideoIsValid isAbuseVideoIsValid
} from '@server/helpers/custom-validators/abuses' } from '@server/helpers/custom-validators/abuses'
import { exists, isIdOrUUIDValid, isIdValid, toIntOrNull } from '@server/helpers/custom-validators/misc' import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID, toIntOrNull } from '@server/helpers/custom-validators/misc'
import { logger } from '@server/helpers/logger' import { logger } from '@server/helpers/logger'
import { AbuseMessageModel } from '@server/models/abuse/abuse-message' import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
import { AbuseCreate, UserRight } from '@shared/models' import { AbuseCreate, UserRight } from '@shared/models'
@ -27,6 +27,7 @@ const abuseReportValidator = [
body('video.id') body('video.id')
.optional() .optional()
.customSanitizer(toCompleteUUID)
.custom(isIdOrUUIDValid) .custom(isIdOrUUIDValid)
.withMessage('Should have a valid videoId'), .withMessage('Should have a valid videoId'),
body('video.startAt') body('video.startAt')

View File

@ -1,8 +1,9 @@
import * as express from 'express' import * as express from 'express'
import { param, query } from 'express-validator' import { param, query } from 'express-validator'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
import { exists, isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { import {
areValidationErrors, areValidationErrors,
@ -98,7 +99,10 @@ const videoSubscriptionFeedsValidator = [
] ]
const videoCommentsFeedsValidator = [ const videoCommentsFeedsValidator = [
query('videoId').optional().custom(isIdOrUUIDValid), query('videoId')
.customSanitizer(toCompleteUUID)
.optional()
.custom(isIdOrUUIDValid),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking feeds parameters', { parameters: req.query }) logger.debug('Checking feeds parameters', { parameters: req.query })

View File

@ -11,7 +11,7 @@ export * from './sort'
export * from './users' export * from './users'
export * from './user-subscriptions' export * from './user-subscriptions'
export * from './videos' export * from './videos'
export * from './webfinger'
export * from './search' export * from './search'
export * from './server' export * from './server'
export * from './user-history' export * from './user-history'
export * from './webfinger'

View File

@ -6,7 +6,7 @@ import { VideoPlaylistModel } from '@server/models/video/video-playlist'
import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { isTestInstance } from '../../helpers/core-utils' import { isTestInstance } from '../../helpers/core-utils'
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { isIdOrUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { WEBSERVER } from '../../initializers/constants' import { WEBSERVER } from '../../initializers/constants'
import { areValidationErrors } from './shared' import { areValidationErrors } from './shared'
@ -79,7 +79,7 @@ const oembedValidator = [
}) })
} }
const elementId = matches[1] const elementId = toCompleteUUID(matches[1])
if (isIdOrUUIDValid(elementId) === false) { if (isIdOrUUIDValid(elementId) === false) {
return res.fail({ message: 'Invalid video or playlist id.' }) return res.fail({ message: 'Invalid video or playlist id.' })
} }

View File

@ -2,15 +2,24 @@ import * as express from 'express'
import { body, param, query } from 'express-validator' import { body, param, query } from 'express-validator'
import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies' import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { exists, isBooleanValid, isIdOrUUIDValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' import {
exists,
isBooleanValid,
isIdOrUUIDValid,
isIdValid,
toBooleanOrNull,
toCompleteUUID,
toIntOrNull
} from '../../helpers/custom-validators/misc'
import { isHostValid } from '../../helpers/custom-validators/servers' import { isHostValid } from '../../helpers/custom-validators/servers'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
import { ServerModel } from '../../models/server/server' import { ServerModel } from '../../models/server/server'
import { areValidationErrors, doesVideoExist } from './shared' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from './shared'
const videoFileRedundancyGetValidator = [ const videoFileRedundancyGetValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), isValidVideoIdParam('videoId'),
param('resolution') param('resolution')
.customSanitizer(toIntOrNull) .customSanitizer(toIntOrNull)
.custom(exists).withMessage('Should have a valid resolution'), .custom(exists).withMessage('Should have a valid resolution'),
@ -56,9 +65,8 @@ const videoFileRedundancyGetValidator = [
] ]
const videoPlaylistRedundancyGetValidator = [ const videoPlaylistRedundancyGetValidator = [
param('videoId') isValidVideoIdParam('videoId'),
.custom(isIdOrUUIDValid)
.not().isEmpty().withMessage('Should have a valid video id'),
param('streamingPlaylistType') param('streamingPlaylistType')
.customSanitizer(toIntOrNull) .customSanitizer(toIntOrNull)
.custom(exists).withMessage('Should have a valid streaming playlist type'), .custom(exists).withMessage('Should have a valid streaming playlist type'),
@ -135,7 +143,8 @@ const listVideoRedundanciesValidator = [
const addVideoRedundancyValidator = [ const addVideoRedundancyValidator = [
body('videoId') body('videoId')
.custom(isIdValid) .customSanitizer(toCompleteUUID)
.custom(isIdOrUUIDValid)
.withMessage('Should have a valid video id'), .withMessage('Should have a valid video id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {

View File

@ -1,5 +1,6 @@
import * as express from 'express' import * as express from 'express'
import { query, validationResult } from 'express-validator' import { param, query, validationResult } from 'express-validator'
import { isIdOrUUIDValid, toCompleteUUID } from '@server/helpers/custom-validators/misc'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
function areValidationErrors (req: express.Request, res: express.Response) { function areValidationErrors (req: express.Request, res: express.Response) {
@ -41,10 +42,24 @@ function createSortableColumns (sortableColumns: string[]) {
return sortableColumns.concat(sortableColumnDesc) return sortableColumns.concat(sortableColumnDesc)
} }
function isValidVideoIdParam (paramName: string) {
return param(paramName)
.customSanitizer(toCompleteUUID)
.custom(isIdOrUUIDValid).withMessage('Should have a valid video id')
}
function isValidPlaylistIdParam (paramName: string) {
return param(paramName)
.customSanitizer(toCompleteUUID)
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id')
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
areValidationErrors, areValidationErrors,
checkSort, checkSort,
createSortableColumns createSortableColumns,
isValidVideoIdParam,
isValidPlaylistIdParam
} }

View File

@ -7,7 +7,7 @@ import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-code
import { UserRole } from '../../../shared/models/users' import { UserRole } from '../../../shared/models/users'
import { UserRegister } from '../../../shared/models/users/user-register.model' import { UserRegister } from '../../../shared/models/users/user-register.model'
import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins' import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
import { import {
isNoInstanceConfigWarningModal, isNoInstanceConfigWarningModal,
@ -35,7 +35,7 @@ import { Redis } from '../../lib/redis'
import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../lib/signup' import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../lib/signup'
import { ActorModel } from '../../models/actor/actor' import { ActorModel } from '../../models/actor/actor'
import { UserModel } from '../../models/user/user' import { UserModel } from '../../models/user/user'
import { areValidationErrors, doesVideoExist } from './shared' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from './shared'
const usersListValidator = [ const usersListValidator = [
query('blocked') query('blocked')
@ -302,7 +302,7 @@ const usersGetValidator = [
] ]
const usersVideoRatingValidator = [ const usersVideoRatingValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) logger.debug('Checking usersVideoRating parameters', { parameters: req.params })

View File

@ -1,13 +1,13 @@
import * as express from 'express' import * as express from 'express'
import { body, param, query } from 'express-validator' import { body, query } from 'express-validator'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { isBooleanValid, isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist' import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { areValidationErrors, doesVideoBlacklistExist, doesVideoExist } from '../shared' import { areValidationErrors, doesVideoBlacklistExist, doesVideoExist, isValidVideoIdParam } from '../shared'
const videosBlacklistRemoveValidator = [ const videosBlacklistRemoveValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
@ -21,7 +21,8 @@ const videosBlacklistRemoveValidator = [
] ]
const videosBlacklistAddValidator = [ const videosBlacklistAddValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
body('unfederate') body('unfederate')
.optional() .optional()
.customSanitizer(toBooleanOrNull) .customSanitizer(toBooleanOrNull)
@ -49,7 +50,8 @@ const videosBlacklistAddValidator = [
] ]
const videosBlacklistUpdateValidator = [ const videosBlacklistUpdateValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
body('reason') body('reason')
.optional() .optional()
.custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'),

View File

@ -1,16 +1,18 @@
import * as express from 'express' import * as express from 'express'
import { body, param } from 'express-validator' import { body, param } from 'express-validator'
import { UserRight } from '../../../../shared' import { UserRight } from '../../../../shared'
import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions'
import { cleanUpReqFiles } from '../../../helpers/express-utils' import { cleanUpReqFiles } from '../../../helpers/express-utils'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants' import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants'
import { areValidationErrors, checkUserCanManageVideo, doesVideoCaptionExist, doesVideoExist } from '../shared' import { areValidationErrors, checkUserCanManageVideo, doesVideoCaptionExist, doesVideoExist, isValidVideoIdParam } from '../shared'
const addVideoCaptionValidator = [ const addVideoCaptionValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), isValidVideoIdParam('videoId'),
param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'),
param('captionLanguage')
.custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'),
body('captionfile') body('captionfile')
.custom((_, { req }) => isVideoCaptionFile(req.files, 'captionfile')) .custom((_, { req }) => isVideoCaptionFile(req.files, 'captionfile'))
.withMessage( .withMessage(
@ -34,8 +36,10 @@ const addVideoCaptionValidator = [
] ]
const deleteVideoCaptionValidator = [ const deleteVideoCaptionValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), isValidVideoIdParam('videoId'),
param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'),
param('captionLanguage')
.custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params }) logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params })
@ -53,7 +57,7 @@ const deleteVideoCaptionValidator = [
] ]
const listVideoCaptionsValidator = [ const listVideoCaptionsValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking listVideoCaptions parameters', { parameters: req.params }) logger.debug('Checking listVideoCaptions parameters', { parameters: req.params })

View File

@ -3,13 +3,13 @@ import { body, param, query } from 'express-validator'
import { MUserAccountUrl } from '@server/types/models' import { MUserAccountUrl } from '@server/types/models'
import { UserRight } from '../../../../shared' import { UserRight } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { exists, isBooleanValid, isIdOrUUIDValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation' import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
import { Hooks } from '../../../lib/plugins/hooks' import { Hooks } from '../../../lib/plugins/hooks'
import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video' import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
import { areValidationErrors, doesVideoCommentExist, doesVideoCommentThreadExist, doesVideoExist } from '../shared' import { areValidationErrors, doesVideoCommentExist, doesVideoCommentThreadExist, doesVideoExist, isValidVideoIdParam } from '../shared'
const listVideoCommentsValidator = [ const listVideoCommentsValidator = [
query('isLocal') query('isLocal')
@ -40,7 +40,7 @@ const listVideoCommentsValidator = [
] ]
const listVideoCommentThreadsValidator = [ const listVideoCommentThreadsValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params }) logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
@ -53,8 +53,10 @@ const listVideoCommentThreadsValidator = [
] ]
const listVideoThreadCommentsValidator = [ const listVideoThreadCommentsValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
param('threadId')
.custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params }) logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
@ -68,8 +70,10 @@ const listVideoThreadCommentsValidator = [
] ]
const addVideoCommentThreadValidator = [ const addVideoCommentThreadValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
body('text')
.custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body }) logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
@ -84,8 +88,10 @@ const addVideoCommentThreadValidator = [
] ]
const addVideoCommentReplyValidator = [ const addVideoCommentReplyValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
@ -102,8 +108,10 @@ const addVideoCommentReplyValidator = [
] ]
const videoCommentGetValidator = [ const videoCommentGetValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
param('commentId')
.custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
@ -117,7 +125,8 @@ const videoCommentGetValidator = [
] ]
const removeVideoCommentValidator = [ const removeVideoCommentValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {

View File

@ -1,5 +1,5 @@
import * as express from 'express' import * as express from 'express'
import { body, param } from 'express-validator' import { body } from 'express-validator'
import { CONSTRAINTS_FIELDS } from '@server/initializers/constants' import { CONSTRAINTS_FIELDS } from '@server/initializers/constants'
import { isLocalLiveVideoAccepted } from '@server/lib/moderation' import { isLocalLiveVideoAccepted } from '@server/lib/moderation'
import { Hooks } from '@server/lib/plugins/hooks' import { Hooks } from '@server/lib/plugins/hooks'
@ -7,16 +7,22 @@ import { VideoModel } from '@server/models/video/video'
import { VideoLiveModel } from '@server/models/video/video-live' import { VideoLiveModel } from '@server/models/video/video-live'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { ServerErrorCode, UserRight, VideoState } from '@shared/models' import { ServerErrorCode, UserRight, VideoState } from '@shared/models'
import { isBooleanValid, isIdOrUUIDValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
import { isVideoNameValid } from '../../../helpers/custom-validators/videos' import { isVideoNameValid } from '../../../helpers/custom-validators/videos'
import { cleanUpReqFiles } from '../../../helpers/express-utils' import { cleanUpReqFiles } from '../../../helpers/express-utils'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { CONFIG } from '../../../initializers/config' import { CONFIG } from '../../../initializers/config'
import { areValidationErrors, checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../shared' import {
areValidationErrors,
checkUserCanManageVideo,
doesVideoChannelOfAccountExist,
doesVideoExist,
isValidVideoIdParam
} from '../shared'
import { getCommonVideoEditAttributes } from './videos' import { getCommonVideoEditAttributes } from './videos'
const videoLiveGetValidator = [ const videoLiveGetValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username }) logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username })

View File

@ -1,6 +1,6 @@
import * as express from 'express' import * as express from 'express'
import { param } from 'express-validator' import { param } from 'express-validator'
import { isIdOrUUIDValid } from '@server/helpers/custom-validators/misc' import { isIdValid } from '@server/helpers/custom-validators/misc'
import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership' import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership'
import { logger } from '@server/helpers/logger' import { logger } from '@server/helpers/logger'
import { isAbleToUploadVideo } from '@server/lib/user' import { isAbleToUploadVideo } from '@server/lib/user'
@ -13,11 +13,12 @@ import {
checkUserCanManageVideo, checkUserCanManageVideo,
doesChangeVideoOwnershipExist, doesChangeVideoOwnershipExist,
doesVideoChannelOfAccountExist, doesVideoChannelOfAccountExist,
doesVideoExist doesVideoExist,
isValidVideoIdParam
} from '../shared' } from '../shared'
const videosChangeOwnershipValidator = [ const videosChangeOwnershipValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking changeOwnership parameters', { parameters: req.params }) logger.debug('Checking changeOwnership parameters', { parameters: req.params })
@ -40,7 +41,8 @@ const videosChangeOwnershipValidator = [
] ]
const videosTerminateChangeOwnershipValidator = [ const videosTerminateChangeOwnershipValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id')
.custom(isIdValid).withMessage('Should have a valid id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking changeOwnership parameters', { parameters: req.params }) logger.debug('Checking changeOwnership parameters', { parameters: req.params })

View File

@ -11,6 +11,7 @@ import {
isIdOrUUIDValid, isIdOrUUIDValid,
isIdValid, isIdValid,
isUUIDValid, isUUIDValid,
toCompleteUUID,
toIntArray, toIntArray,
toIntOrNull, toIntOrNull,
toValueOrNull toValueOrNull
@ -29,7 +30,14 @@ import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element'
import { MVideoPlaylist } from '../../../types/models/video/video-playlist' import { MVideoPlaylist } from '../../../types/models/video/video-playlist'
import { authenticatePromiseIfNeeded } from '../../auth' import { authenticatePromiseIfNeeded } from '../../auth'
import { areValidationErrors, doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../shared' import {
areValidationErrors,
doesVideoChannelIdExist,
doesVideoExist,
doesVideoPlaylistExist,
isValidPlaylistIdParam,
VideoPlaylistFetchType
} from '../shared'
const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
body('displayName') body('displayName')
@ -43,10 +51,13 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
const body: VideoPlaylistCreate = req.body const body: VideoPlaylistCreate = req.body
if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
if (body.privacy === VideoPlaylistPrivacy.PUBLIC && !body.videoChannelId) { if (
!body.videoChannelId &&
(body.privacy === VideoPlaylistPrivacy.PUBLIC || body.privacy === VideoPlaylistPrivacy.UNLISTED)
) {
cleanUpReqFiles(req) cleanUpReqFiles(req)
return res.fail({ message: 'Cannot set "public" a playlist that is not assigned to a channel.' }) return res.fail({ message: 'Cannot set "public" or "unlisted" a playlist that is not assigned to a channel.' })
} }
return next() return next()
@ -54,8 +65,7 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
]) ])
const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
body('displayName') body('displayName')
.optional() .optional()
@ -101,8 +111,7 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
]) ])
const videoPlaylistsDeleteValidator = [ const videoPlaylistsDeleteValidator = [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params }) logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params })
@ -126,8 +135,7 @@ const videoPlaylistsDeleteValidator = [
const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => {
return [ return [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params })
@ -184,9 +192,10 @@ const videoPlaylistsSearchValidator = [
] ]
const videoPlaylistsAddVideoValidator = [ const videoPlaylistsAddVideoValidator = [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
body('videoId') body('videoId')
.customSanitizer(toCompleteUUID)
.custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'), .custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'),
body('startTimestamp') body('startTimestamp')
.optional() .optional()
@ -214,9 +223,9 @@ const videoPlaylistsAddVideoValidator = [
] ]
const videoPlaylistsUpdateOrRemoveVideoValidator = [ const videoPlaylistsUpdateOrRemoveVideoValidator = [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
param('playlistElementId') param('playlistElementId')
.customSanitizer(toCompleteUUID)
.custom(isIdValid).withMessage('Should have an element id/uuid'), .custom(isIdValid).withMessage('Should have an element id/uuid'),
body('startTimestamp') body('startTimestamp')
.optional() .optional()
@ -251,8 +260,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [
] ]
const videoPlaylistElementAPGetValidator = [ const videoPlaylistElementAPGetValidator = [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
param('playlistElementId') param('playlistElementId')
.custom(isIdValid).withMessage('Should have an playlist element id'), .custom(isIdValid).withMessage('Should have an playlist element id'),
@ -287,8 +295,7 @@ const videoPlaylistElementAPGetValidator = [
] ]
const videoPlaylistsReorderVideosValidator = [ const videoPlaylistsReorderVideosValidator = [
param('playlistId') isValidPlaylistIdParam('playlistId'),
.custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
body('startPosition') body('startPosition')
.isInt({ min: 1 }).withMessage('Should have a valid start position'), .isInt({ min: 1 }).withMessage('Should have a valid start position'),
body('insertAfterPosition') body('insertAfterPosition')

View File

@ -3,15 +3,16 @@ import { body, param, query } from 'express-validator'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { VideoRateType } from '../../../../shared/models/videos' import { VideoRateType } from '../../../../shared/models/videos'
import { isAccountNameValid } from '../../../helpers/custom-validators/accounts' import { isAccountNameValid } from '../../../helpers/custom-validators/accounts'
import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' import { isIdValid } from '../../../helpers/custom-validators/misc'
import { isRatingValid } from '../../../helpers/custom-validators/video-rates' import { isRatingValid } from '../../../helpers/custom-validators/video-rates'
import { isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos' import { isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { AccountVideoRateModel } from '../../../models/account/account-video-rate' import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
import { areValidationErrors, doesVideoExist } from '../shared' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
const videoUpdateRateValidator = [ const videoUpdateRateValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {

View File

@ -1,14 +1,16 @@
import * as express from 'express' import * as express from 'express'
import { param } from 'express-validator' import { param } from 'express-validator'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' import { isIdValid } from '../../../helpers/custom-validators/misc'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { VideoShareModel } from '../../../models/video/video-share' import { VideoShareModel } from '../../../models/video/video-share'
import { areValidationErrors, doesVideoExist } from '../shared' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
const videosShareValidator = [ const videosShareValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
param('actorId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid actor id'),
param('actorId')
.custom(isIdValid).not().isEmpty().withMessage('Should have a valid actor id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoShare parameters', { parameters: req.params }) logger.debug('Checking videoShare parameters', { parameters: req.params })

View File

@ -1,12 +1,13 @@
import * as express from 'express' import * as express from 'express'
import { body, param } from 'express-validator' import { body } from 'express-validator'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { isIdOrUUIDValid, toIntOrNull } from '../../../helpers/custom-validators/misc' import { toIntOrNull } from '../../../helpers/custom-validators/misc'
import { logger } from '../../../helpers/logger' import { logger } from '../../../helpers/logger'
import { areValidationErrors, doesVideoExist } from '../shared' import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
const videoWatchingValidator = [ const videoWatchingValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('videoId'),
body('currentTime') body('currentTime')
.customSanitizer(toIntOrNull) .customSanitizer(toIntOrNull)
.isInt().withMessage('Should have correct current time'), .isInt().withMessage('Should have correct current time'),

View File

@ -12,7 +12,6 @@ import {
isBooleanValid, isBooleanValid,
isDateValid, isDateValid,
isFileFieldValid, isFileFieldValid,
isIdOrUUIDValid,
isIdValid, isIdValid,
isUUIDValid, isUUIDValid,
toArray, toArray,
@ -53,7 +52,8 @@ import {
checkUserCanManageVideo, checkUserCanManageVideo,
doesVideoChannelOfAccountExist, doesVideoChannelOfAccountExist,
doesVideoExist, doesVideoExist,
doesVideoFileOfVideoExist doesVideoFileOfVideoExist,
isValidVideoIdParam
} from '../shared' } from '../shared'
const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
@ -195,7 +195,8 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([
]) ])
const videosUpdateValidator = getCommonVideoEditAttributes().concat([ const videosUpdateValidator = getCommonVideoEditAttributes().concat([
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
body('name') body('name')
.optional() .optional()
.trim() .trim()
@ -258,7 +259,7 @@ const videosCustomGetValidator = (
authenticateInQuery = false authenticateInQuery = false
) => { ) => {
return [ return [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosGet parameters', { parameters: req.params }) logger.debug('Checking videosGet parameters', { parameters: req.params })
@ -309,8 +310,10 @@ const videosGetValidator = videosCustomGetValidator('all')
const videosDownloadValidator = videosCustomGetValidator('all', true) const videosDownloadValidator = videosCustomGetValidator('all', true)
const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([ const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
param('videoFileId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid videoFileId'),
param('videoFileId')
.custom(isIdValid).not().isEmpty().withMessage('Should have a valid videoFileId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoFileMetadataGet parameters', { parameters: req.params }) logger.debug('Checking videoFileMetadataGet parameters', { parameters: req.params })
@ -323,7 +326,7 @@ const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([
]) ])
const videosRemoveValidator = [ const videosRemoveValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), isValidVideoIdParam('id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosRemove parameters', { parameters: req.params }) logger.debug('Checking videosRemove parameters', { parameters: req.params })

View File

@ -1,3 +1,4 @@
import { uuidToShort } from '@server/helpers/uuid'
import { generateMagnetUri } from '@server/helpers/webtorrent' import { generateMagnetUri } from '@server/helpers/webtorrent'
import { getLocalVideoFileMetadataUrl } from '@server/lib/video-paths' import { getLocalVideoFileMetadataUrl } from '@server/lib/video-paths'
import { VideoFile } from '@shared/models/videos/video-file.model' import { VideoFile } from '@shared/models/videos/video-file.model'
@ -47,6 +48,8 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFor
const videoObject: Video = { const videoObject: Video = {
id: video.id, id: video.id,
uuid: video.uuid, uuid: video.uuid,
shortUUID: uuidToShort(video.uuid),
name: video.name, name: video.name,
category: { category: {
id: video.category, id: video.category,

View File

@ -15,7 +15,7 @@ import {
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid' import { buildUUID } from '@server/helpers/uuid'
import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils' import { AttributesOnly } from '@shared/core-utils'
import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
@ -182,7 +182,7 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
} }
static generateCaptionName (language: string) { static generateCaptionName (language: string) {
return `${uuidv4()}-${language}.vtt` return `${buildUUID()}-${language}.vtt`
} }
isOwned () { isOwned () {

View File

@ -17,8 +17,8 @@ import {
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid'
import { setAsUpdated } from '@server/helpers/database-utils' import { setAsUpdated } from '@server/helpers/database-utils'
import { buildUUID, uuidToShort } from '@server/helpers/uuid'
import { MAccountId, MChannelId } from '@server/types/models' import { MAccountId, MChannelId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils' import { AttributesOnly } from '@shared/core-utils'
import { ActivityIconObject } from '../../../shared/models/activitypub/objects' import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
@ -545,7 +545,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
generateThumbnailName () { generateThumbnailName () {
const extension = '.jpg' const extension = '.jpg'
return 'playlist-' + uuidv4() + extension return 'playlist-' + buildUUID() + extension
} }
getThumbnailUrl () { getThumbnailUrl () {
@ -617,6 +617,8 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
return { return {
id: this.id, id: this.id,
uuid: this.uuid, uuid: this.uuid,
shortUUID: uuidToShort(this.uuid),
isLocal: this.isOwned(), isLocal: this.isOwned(),
url: this.url, url: this.url,

View File

@ -1,23 +1,65 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import * as chai from 'chai'
import 'mocha' import 'mocha'
import * as chai from 'chai'
import { VideoPlaylistPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
cleanupTests, cleanupTests,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
makeActivityPubGetRequest, makeActivityPubGetRequest,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
uploadVideo setDefaultVideoChannel,
uploadVideoAndGetId
} from '../../../../shared/extra-utils' } from '../../../../shared/extra-utils'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
const expect = chai.expect const expect = chai.expect
describe('Test activitypub', function () { describe('Test activitypub', function () {
let servers: ServerInfo[] = [] let servers: ServerInfo[] = []
let videoUUID: string let video: { id: number, uuid: string, shortUUID: string }
let playlist: { id: number, uuid: string, shortUUID: string }
async function testAccount (path: string) {
const res = await makeActivityPubGetRequest(servers[0].url, path)
const object = res.body
expect(object.type).to.equal('Person')
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/accounts/root')
expect(object.name).to.equal('root')
expect(object.preferredUsername).to.equal('root')
}
async function testChannel (path: string) {
const res = await makeActivityPubGetRequest(servers[0].url, path)
const object = res.body
expect(object.type).to.equal('Group')
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/video-channels/root_channel')
expect(object.name).to.equal('Main root channel')
expect(object.preferredUsername).to.equal('root_channel')
}
async function testVideo (path: string) {
const res = await makeActivityPubGetRequest(servers[0].url, path)
const object = res.body
expect(object.type).to.equal('Video')
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + video.uuid)
expect(object.name).to.equal('video')
}
async function testPlaylist (path: string) {
const res = await makeActivityPubGetRequest(servers[0].url, path)
const object = res.body
expect(object.type).to.equal('Playlist')
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/video-playlists/' + playlist.uuid)
expect(object.name).to.equal('playlist')
}
before(async function () { before(async function () {
this.timeout(30000) this.timeout(30000)
@ -25,38 +67,56 @@ describe('Test activitypub', function () {
servers = await flushAndRunMultipleServers(2) servers = await flushAndRunMultipleServers(2)
await setAccessTokensToServers(servers) await setAccessTokensToServers(servers)
await setDefaultVideoChannel(servers)
{ {
const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) video = await uploadVideoAndGetId({ server: servers[0], videoName: 'video' })
videoUUID = res.body.video.uuid }
{
const playlistAttrs = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id }
const resCreate = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
playlist = resCreate.body.videoPlaylist
} }
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])
}) })
it('Should return the account object', async function () { it('Should return the account object', async function () {
const res = await makeActivityPubGetRequest(servers[0].url, '/accounts/root') await testAccount('/accounts/root')
const object = res.body await testAccount('/a/root')
})
expect(object.type).to.equal('Person') it('Should return the channel object', async function () {
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/accounts/root') await testChannel('/video-channels/root_channel')
expect(object.name).to.equal('root') await testChannel('/c/root_channel')
expect(object.preferredUsername).to.equal('root')
}) })
it('Should return the video object', async function () { it('Should return the video object', async function () {
const res = await makeActivityPubGetRequest(servers[0].url, '/videos/watch/' + videoUUID) await testVideo('/videos/watch/' + video.id)
const object = res.body await testVideo('/videos/watch/' + video.uuid)
await testVideo('/videos/watch/' + video.shortUUID)
await testVideo('/w/' + video.id)
await testVideo('/w/' + video.uuid)
await testVideo('/w/' + video.shortUUID)
})
expect(object.type).to.equal('Video') it('Should return the playlist object', async function () {
expect(object.id).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) await testPlaylist('/video-playlists/' + playlist.id)
expect(object.name).to.equal('video') await testPlaylist('/video-playlists/' + playlist.uuid)
await testPlaylist('/video-playlists/' + playlist.shortUUID)
await testPlaylist('/w/p/' + playlist.id)
await testPlaylist('/w/p/' + playlist.uuid)
await testPlaylist('/w/p/' + playlist.shortUUID)
await testPlaylist('/videos/watch/playlist/' + playlist.id)
await testPlaylist('/videos/watch/playlist/' + playlist.uuid)
await testPlaylist('/videos/watch/playlist/' + playlist.shortUUID)
}) })
it('Should redirect to the origin video object', async function () { it('Should redirect to the origin video object', async function () {
const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, HttpStatusCode.FOUND_302) const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + video.uuid, HttpStatusCode.FOUND_302)
expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + video.uuid)
}) })
after(async function () { after(async function () {

View File

@ -258,7 +258,7 @@ describe('Test abuses API validators', function () {
}) })
it('Should succeed with the correct parameters (basic)', async function () { it('Should succeed with the correct parameters (basic)', async function () {
const fields: AbuseCreate = { video: { id: server.video.id }, reason: 'my super reason' } const fields: AbuseCreate = { video: { id: server.video.shortUUID }, reason: 'my super reason' }
const res = await makePostBodyRequest({ const res = await makePostBodyRequest({
url: server.url, url: server.url,

View File

@ -2,7 +2,7 @@
import 'mocha' import 'mocha'
import { omit } from 'lodash' import { omit } from 'lodash'
import { LiveVideo, VideoPrivacy } from '@shared/models' import { LiveVideo, VideoCreateResult, VideoPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
buildAbsoluteFixturePath, buildAbsoluteFixturePath,
@ -31,7 +31,7 @@ describe('Test video lives API validator', function () {
let server: ServerInfo let server: ServerInfo
let userAccessToken = '' let userAccessToken = ''
let channelId: number let channelId: number
let videoId: number let video: VideoCreateResult
let videoIdNotLive: number let videoIdNotLive: number
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -230,7 +230,7 @@ describe('Test video lives API validator', function () {
statusCodeExpected: HttpStatusCode.OK_200 statusCodeExpected: HttpStatusCode.OK_200
}) })
videoId = res.body.video.id video = res.body.video
}) })
it('Should forbid if live is disabled', async function () { it('Should forbid if live is disabled', async function () {
@ -326,15 +326,15 @@ describe('Test video lives API validator', function () {
describe('When getting live information', function () { describe('When getting live information', function () {
it('Should fail without access token', async function () { it('Should fail without access token', async function () {
await getLive(server.url, '', videoId, HttpStatusCode.UNAUTHORIZED_401) await getLive(server.url, '', video.id, HttpStatusCode.UNAUTHORIZED_401)
}) })
it('Should fail with a bad access token', async function () { it('Should fail with a bad access token', async function () {
await getLive(server.url, 'toto', videoId, HttpStatusCode.UNAUTHORIZED_401) await getLive(server.url, 'toto', video.id, HttpStatusCode.UNAUTHORIZED_401)
}) })
it('Should fail with access token of another user', async function () { it('Should fail with access token of another user', async function () {
await getLive(server.url, userAccessToken, videoId, HttpStatusCode.FORBIDDEN_403) await getLive(server.url, userAccessToken, video.id, HttpStatusCode.FORBIDDEN_403)
}) })
it('Should fail with a bad video id', async function () { it('Should fail with a bad video id', async function () {
@ -350,22 +350,23 @@ describe('Test video lives API validator', function () {
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await getLive(server.url, server.accessToken, videoId) await getLive(server.url, server.accessToken, video.id)
await getLive(server.url, server.accessToken, video.shortUUID)
}) })
}) })
describe('When updating live information', async function () { describe('When updating live information', async function () {
it('Should fail without access token', async function () { it('Should fail without access token', async function () {
await updateLive(server.url, '', videoId, {}, HttpStatusCode.UNAUTHORIZED_401) await updateLive(server.url, '', video.id, {}, HttpStatusCode.UNAUTHORIZED_401)
}) })
it('Should fail with a bad access token', async function () { it('Should fail with a bad access token', async function () {
await updateLive(server.url, 'toto', videoId, {}, HttpStatusCode.UNAUTHORIZED_401) await updateLive(server.url, 'toto', video.id, {}, HttpStatusCode.UNAUTHORIZED_401)
}) })
it('Should fail with access token of another user', async function () { it('Should fail with access token of another user', async function () {
await updateLive(server.url, userAccessToken, videoId, {}, HttpStatusCode.FORBIDDEN_403) await updateLive(server.url, userAccessToken, video.id, {}, HttpStatusCode.FORBIDDEN_403)
}) })
it('Should fail with a bad video id', async function () { it('Should fail with a bad video id', async function () {
@ -383,11 +384,12 @@ describe('Test video lives API validator', function () {
it('Should fail with save replay and permanent live set to true', async function () { it('Should fail with save replay and permanent live set to true', async function () {
const fields = { saveReplay: true, permanentLive: true } const fields = { saveReplay: true, permanentLive: true }
await updateLive(server.url, server.accessToken, videoId, fields, HttpStatusCode.BAD_REQUEST_400) await updateLive(server.url, server.accessToken, video.id, fields, HttpStatusCode.BAD_REQUEST_400)
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await updateLive(server.url, server.accessToken, videoId, { saveReplay: false }) await updateLive(server.url, server.accessToken, video.id, { saveReplay: false })
await updateLive(server.url, server.accessToken, video.shortUUID, { saveReplay: false })
}) })
it('Should fail to update replay status if replay is not allowed on the instance', async function () { it('Should fail to update replay status if replay is not allowed on the instance', async function () {
@ -398,19 +400,19 @@ describe('Test video lives API validator', function () {
} }
}) })
await updateLive(server.url, server.accessToken, videoId, { saveReplay: true }, HttpStatusCode.FORBIDDEN_403) await updateLive(server.url, server.accessToken, video.id, { saveReplay: true }, HttpStatusCode.FORBIDDEN_403)
}) })
it('Should fail to update a live if it has already started', async function () { it('Should fail to update a live if it has already started', async function () {
this.timeout(40000) this.timeout(40000)
const resLive = await getLive(server.url, server.accessToken, videoId) const resLive = await getLive(server.url, server.accessToken, video.id)
const live: LiveVideo = resLive.body const live: LiveVideo = resLive.body
const command = sendRTMPStream(live.rtmpUrl, live.streamKey) const command = sendRTMPStream(live.rtmpUrl, live.streamKey)
await waitUntilLivePublished(server.url, server.accessToken, videoId) await waitUntilLivePublished(server.url, server.accessToken, video.id)
await updateLive(server.url, server.accessToken, videoId, {}, HttpStatusCode.BAD_REQUEST_400) await updateLive(server.url, server.accessToken, video.id, {}, HttpStatusCode.BAD_REQUEST_400)
await stopFfmpeg(command) await stopFfmpeg(command)
}) })
@ -418,14 +420,14 @@ describe('Test video lives API validator', function () {
it('Should fail to stream twice in the save live', async function () { it('Should fail to stream twice in the save live', async function () {
this.timeout(40000) this.timeout(40000)
const resLive = await getLive(server.url, server.accessToken, videoId) const resLive = await getLive(server.url, server.accessToken, video.id)
const live: LiveVideo = resLive.body const live: LiveVideo = resLive.body
const command = sendRTMPStream(live.rtmpUrl, live.streamKey) const command = sendRTMPStream(live.rtmpUrl, live.streamKey)
await waitUntilLivePublished(server.url, server.accessToken, videoId) await waitUntilLivePublished(server.url, server.accessToken, video.id)
await runAndTestFfmpegStreamError(server.url, server.accessToken, videoId, true) await runAndTestFfmpegStreamError(server.url, server.accessToken, video.id, true)
await stopFfmpeg(command) await stopFfmpeg(command)
}) })

View File

@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { VideoCreateResult } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
checkBadCountPagination, checkBadCountPagination,
checkBadSortPagination, checkBadSortPagination,
@ -9,20 +10,24 @@ import {
cleanupTests, cleanupTests,
createUser, createUser,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, makeDeleteRequest, flushAndRunMultipleServers,
makeGetRequest, makePostBodyRequest, getVideo,
makeDeleteRequest,
makeGetRequest,
makePostBodyRequest,
makePutBodyRequest, makePutBodyRequest,
ServerInfo, ServerInfo,
setAccessTokensToServers, uploadVideoAndGetId, setAccessTokensToServers,
userLogin, waitJobs, getVideoIdFromUUID uploadVideoAndGetId,
userLogin,
waitJobs
} from '../../../../shared/extra-utils' } from '../../../../shared/extra-utils'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
describe('Test server redundancy API validators', function () { describe('Test server redundancy API validators', function () {
let servers: ServerInfo[] let servers: ServerInfo[]
let userAccessToken = null let userAccessToken = null
let videoIdLocal: number let videoIdLocal: number
let videoIdRemote: number let videoRemote: VideoCreateResult
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -48,7 +53,8 @@ describe('Test server redundancy API validators', function () {
await waitJobs(servers) await waitJobs(servers)
videoIdRemote = await getVideoIdFromUUID(servers[0].url, remoteUUID) const resVideo = await getVideo(servers[0].url, remoteUUID)
videoRemote = resVideo.body
}) })
describe('When listing redundancies', function () { describe('When listing redundancies', function () {
@ -131,7 +137,13 @@ describe('Test server redundancy API validators', function () {
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await makePostBodyRequest({ url, path, token, fields: { videoId: videoIdRemote }, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) await makePostBodyRequest({
url,
path,
token,
fields: { videoId: videoRemote.shortUUID },
statusCodeExpected: HttpStatusCode.NO_CONTENT_204
})
}) })
it('Should fail if the video is already duplicated', async function () { it('Should fail if the video is already duplicated', async function () {
@ -139,7 +151,13 @@ describe('Test server redundancy API validators', function () {
await waitJobs(servers) await waitJobs(servers)
await makePostBodyRequest({ url, path, token, fields: { videoId: videoIdRemote }, statusCodeExpected: HttpStatusCode.CONFLICT_409 }) await makePostBodyRequest({
url,
path,
token,
fields: { videoId: videoRemote.uuid },
statusCodeExpected: HttpStatusCode.CONFLICT_409
})
}) })
}) })

View File

@ -2,7 +2,7 @@
import 'mocha' import 'mocha'
import { omit } from 'lodash' import { omit } from 'lodash'
import { User, UserRole } from '../../../../shared' import { User, UserRole, VideoCreateResult } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
addVideoChannel, addVideoChannel,
@ -45,7 +45,7 @@ describe('Test users API validators', function () {
let userId: number let userId: number
let rootId: number let rootId: number
let moderatorId: number let moderatorId: number
let videoId: number let video: VideoCreateResult
let server: ServerInfo let server: ServerInfo
let serverWithRegistrationDisabled: ServerInfo let serverWithRegistrationDisabled: ServerInfo
let userAccessToken = '' let userAccessToken = ''
@ -126,7 +126,7 @@ describe('Test users API validators', function () {
{ {
const res = await uploadVideo(server.url, server.accessToken, {}) const res = await uploadVideo(server.url, server.accessToken, {})
videoId = res.body.video.id video = res.body.video
} }
{ {
@ -829,7 +829,7 @@ describe('Test users API validators', function () {
describe('When getting my video rating', function () { describe('When getting my video rating', function () {
it('Should fail with a non authenticated user', async function () { it('Should fail with a non authenticated user', async function () {
await getMyUserVideoRating(server.url, 'fake_token', videoId, HttpStatusCode.UNAUTHORIZED_401) await getMyUserVideoRating(server.url, 'fake_token', video.id, HttpStatusCode.UNAUTHORIZED_401)
}) })
it('Should fail with an incorrect video uuid', async function () { it('Should fail with an incorrect video uuid', async function () {
@ -841,7 +841,9 @@ describe('Test users API validators', function () {
}) })
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await getMyUserVideoRating(server.url, server.accessToken, videoId) await getMyUserVideoRating(server.url, server.accessToken, video.id)
await getMyUserVideoRating(server.url, server.accessToken, video.uuid)
await getMyUserVideoRating(server.url, server.accessToken, video.shortUUID)
}) })
}) })

View File

@ -191,7 +191,7 @@ describe('Test video blacklist API validators', function () {
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
const path = basePath + servers[0].video.uuid + '/blacklist' const path = basePath + servers[0].video.shortUUID + '/blacklist'
const fields = { reason: 'hello' } const fields = { reason: 'hello' }
await makePutBodyRequest({ await makePutBodyRequest({
@ -222,10 +222,14 @@ describe('Test video blacklist API validators', function () {
}) })
it('Should succeed with an admin', async function () { it('Should succeed with an admin', async function () {
const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, servers[0].video.uuid, HttpStatusCode.OK_200) const video = servers[0].video
for (const id of [ video.id, video.uuid, video.shortUUID ]) {
const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, id, HttpStatusCode.OK_200)
const video: VideoDetails = res.body const video: VideoDetails = res.body
expect(video.blacklisted).to.be.true expect(video.blacklisted).to.be.true
}
}) })
}) })

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { VideoCreateResult } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
buildAbsoluteFixturePath, buildAbsoluteFixturePath,
@ -23,7 +23,7 @@ describe('Test video captions API validator', function () {
let server: ServerInfo let server: ServerInfo
let userAccessToken: string let userAccessToken: string
let videoUUID: string let video: VideoCreateResult
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -36,7 +36,7 @@ describe('Test video captions API validator', function () {
{ {
const res = await uploadVideo(server.url, server.accessToken, {}) const res = await uploadVideo(server.url, server.accessToken, {})
videoUUID = res.body.video.uuid video = res.body.video
} }
{ {
@ -79,7 +79,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should fail with a missing language in path', async function () { it('Should fail with a missing language in path', async function () {
const captionPath = path + videoUUID + '/captions' const captionPath = path + video.uuid + '/captions'
await makeUploadRequest({ await makeUploadRequest({
method: 'PUT', method: 'PUT',
url: server.url, url: server.url,
@ -91,7 +91,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should fail with an unknown language', async function () { it('Should fail with an unknown language', async function () {
const captionPath = path + videoUUID + '/captions/15' const captionPath = path + video.uuid + '/captions/15'
await makeUploadRequest({ await makeUploadRequest({
method: 'PUT', method: 'PUT',
url: server.url, url: server.url,
@ -103,7 +103,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should fail without access token', async function () { it('Should fail without access token', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.uuid + '/captions/fr'
await makeUploadRequest({ await makeUploadRequest({
method: 'PUT', method: 'PUT',
url: server.url, url: server.url,
@ -115,7 +115,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should fail with a bad access token', async function () { it('Should fail with a bad access token', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.uuid + '/captions/fr'
await makeUploadRequest({ await makeUploadRequest({
method: 'PUT', method: 'PUT',
url: server.url, url: server.url,
@ -133,7 +133,7 @@ describe('Test video captions API validator', function () {
// 'captionfile': buildAbsoluteFixturePath('subtitle-bad.txt') // 'captionfile': buildAbsoluteFixturePath('subtitle-bad.txt')
// } // }
// //
// const captionPath = path + videoUUID + '/captions/fr' // const captionPath = path + video.uuid + '/captions/fr'
// await makeUploadRequest({ // await makeUploadRequest({
// method: 'PUT', // method: 'PUT',
// url: server.url, // url: server.url,
@ -151,7 +151,7 @@ describe('Test video captions API validator', function () {
// url: server.url, // url: server.url,
// accessToken: server.accessToken, // accessToken: server.accessToken,
// language: 'zh', // language: 'zh',
// videoId: videoUUID, // videoId: video.uuid,
// fixture: 'subtitle-bad.txt', // fixture: 'subtitle-bad.txt',
// mimeType: 'application/octet-stream', // mimeType: 'application/octet-stream',
// statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 // statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
@ -163,7 +163,7 @@ describe('Test video captions API validator', function () {
url: server.url, url: server.url,
accessToken: server.accessToken, accessToken: server.accessToken,
language: 'zh', language: 'zh',
videoId: videoUUID, videoId: video.uuid,
fixture: 'subtitle-good.srt', fixture: 'subtitle-good.srt',
mimeType: 'application/octet-stream' mimeType: 'application/octet-stream'
}) })
@ -175,7 +175,7 @@ describe('Test video captions API validator', function () {
// 'captionfile': buildAbsoluteFixturePath('subtitle-bad.srt') // 'captionfile': buildAbsoluteFixturePath('subtitle-bad.srt')
// } // }
// //
// const captionPath = path + videoUUID + '/captions/fr' // const captionPath = path + video.uuid + '/captions/fr'
// await makeUploadRequest({ // await makeUploadRequest({
// method: 'PUT', // method: 'PUT',
// url: server.url, // url: server.url,
@ -188,7 +188,7 @@ describe('Test video captions API validator', function () {
// }) // })
it('Should success with the correct parameters', async function () { it('Should success with the correct parameters', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.uuid + '/captions/fr'
await makeUploadRequest({ await makeUploadRequest({
method: 'PUT', method: 'PUT',
url: server.url, url: server.url,
@ -215,7 +215,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should success with the correct parameters', async function () { it('Should success with the correct parameters', async function () {
await makeGetRequest({ url: server.url, path: path + videoUUID + '/captions', statusCodeExpected: HttpStatusCode.OK_200 }) await makeGetRequest({ url: server.url, path: path + video.shortUUID + '/captions', statusCodeExpected: HttpStatusCode.OK_200 })
}) })
}) })
@ -246,27 +246,27 @@ describe('Test video captions API validator', function () {
}) })
it('Should fail with a missing language', async function () { it('Should fail with a missing language', async function () {
const captionPath = path + videoUUID + '/captions' const captionPath = path + video.shortUUID + '/captions'
await makeDeleteRequest({ url: server.url, path: captionPath, token: server.accessToken }) await makeDeleteRequest({ url: server.url, path: captionPath, token: server.accessToken })
}) })
it('Should fail with an unknown language', async function () { it('Should fail with an unknown language', async function () {
const captionPath = path + videoUUID + '/captions/15' const captionPath = path + video.shortUUID + '/captions/15'
await makeDeleteRequest({ url: server.url, path: captionPath, token: server.accessToken }) await makeDeleteRequest({ url: server.url, path: captionPath, token: server.accessToken })
}) })
it('Should fail without access token', async function () { it('Should fail without access token', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.shortUUID + '/captions/fr'
await makeDeleteRequest({ url: server.url, path: captionPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) await makeDeleteRequest({ url: server.url, path: captionPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 })
}) })
it('Should fail with a bad access token', async function () { it('Should fail with a bad access token', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.shortUUID + '/captions/fr'
await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 })
}) })
it('Should fail with another user', async function () { it('Should fail with another user', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.shortUUID + '/captions/fr'
await makeDeleteRequest({ await makeDeleteRequest({
url: server.url, url: server.url,
path: captionPath, path: captionPath,
@ -276,7 +276,7 @@ describe('Test video captions API validator', function () {
}) })
it('Should success with the correct parameters', async function () { it('Should success with the correct parameters', async function () {
const captionPath = path + videoUUID + '/captions/fr' const captionPath = path + video.shortUUID + '/captions/fr'
await makeDeleteRequest({ await makeDeleteRequest({
url: server.url, url: server.url,
path: captionPath, path: captionPath,

View File

@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import * as chai from 'chai'
import 'mocha' import 'mocha'
import * as chai from 'chai'
import { VideoCreateResult } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
cleanupTests, cleanupTests,
createUser, createUser,
@ -20,7 +22,6 @@ import {
checkBadStartPagination checkBadStartPagination
} from '../../../../shared/extra-utils/requests/check-api-params' } from '../../../../shared/extra-utils/requests/check-api-params'
import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments' import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
const expect = chai.expect const expect = chai.expect
@ -28,7 +29,7 @@ describe('Test video comments API validator', function () {
let pathThread: string let pathThread: string
let pathComment: string let pathComment: string
let server: ServerInfo let server: ServerInfo
let videoUUID: string let video: VideoCreateResult
let userAccessToken: string let userAccessToken: string
let userAccessToken2: string let userAccessToken2: string
let commentId: number let commentId: number
@ -44,14 +45,14 @@ describe('Test video comments API validator', function () {
{ {
const res = await uploadVideo(server.url, server.accessToken, {}) const res = await uploadVideo(server.url, server.accessToken, {})
videoUUID = res.body.video.uuid video = res.body.video
pathThread = '/api/v1/videos/' + videoUUID + '/comment-threads' pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
} }
{ {
const res = await addVideoCommentThread(server.url, server.accessToken, videoUUID, 'coucou') const res = await addVideoCommentThread(server.url, server.accessToken, video.uuid, 'coucou')
commentId = res.body.comment.id commentId = res.body.comment.id
pathComment = '/api/v1/videos/' + videoUUID + '/comments/' + commentId pathComment = '/api/v1/videos/' + video.uuid + '/comments/' + commentId
} }
{ {
@ -101,7 +102,7 @@ describe('Test video comments API validator', function () {
it('Should fail with an incorrect thread id', async function () { it('Should fail with an incorrect thread id', async function () {
await makeGetRequest({ await makeGetRequest({
url: server.url, url: server.url,
path: '/api/v1/videos/' + videoUUID + '/comment-threads/156', path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/156',
statusCodeExpected: HttpStatusCode.NOT_FOUND_404 statusCodeExpected: HttpStatusCode.NOT_FOUND_404
}) })
}) })
@ -109,7 +110,7 @@ describe('Test video comments API validator', function () {
it('Should success with the correct params', async function () { it('Should success with the correct params', async function () {
await makeGetRequest({ await makeGetRequest({
url: server.url, url: server.url,
path: '/api/v1/videos/' + videoUUID + '/comment-threads/' + commentId, path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/' + commentId,
statusCodeExpected: HttpStatusCode.OK_200 statusCodeExpected: HttpStatusCode.OK_200
}) })
}) })
@ -225,7 +226,7 @@ describe('Test video comments API validator', function () {
}) })
it('Should fail with an incorrect comment', async function () { it('Should fail with an incorrect comment', async function () {
const path = '/api/v1/videos/' + videoUUID + '/comments/124' const path = '/api/v1/videos/' + video.uuid + '/comments/124'
const fields = { const fields = {
text: 'super comment' text: 'super comment'
} }
@ -272,7 +273,7 @@ describe('Test video comments API validator', function () {
}) })
it('Should fail with an incorrect comment', async function () { it('Should fail with an incorrect comment', async function () {
const path = '/api/v1/videos/' + videoUUID + '/comments/124' const path = '/api/v1/videos/' + video.uuid + '/comments/124'
await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 })
}) })
@ -280,11 +281,11 @@ describe('Test video comments API validator', function () {
let commentToDelete: number let commentToDelete: number
{ {
const res = await addVideoCommentThread(server.url, userAccessToken, videoUUID, 'hello') const res = await addVideoCommentThread(server.url, userAccessToken, video.uuid, 'hello')
commentToDelete = res.body.comment.id commentToDelete = res.body.comment.id
} }
const path = '/api/v1/videos/' + videoUUID + '/comments/' + commentToDelete const path = '/api/v1/videos/' + video.uuid + '/comments/' + commentToDelete
await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 })
await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 })
@ -323,8 +324,8 @@ describe('Test video comments API validator', function () {
describe('When a video has comments disabled', function () { describe('When a video has comments disabled', function () {
before(async function () { before(async function () {
const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false }) const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false })
videoUUID = res.body.video.uuid video = res.body.video
pathThread = '/api/v1/videos/' + videoUUID + '/comment-threads' pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
}) })
it('Should return an empty thread list', async function () { it('Should return an empty thread list', async function () {

View File

@ -1,8 +1,13 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { VideoPlaylistCreateResult, VideoPlaylistPrivacy, VideoPlaylistType } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
addVideoInPlaylist, addVideoInPlaylist,
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination,
cleanupTests, cleanupTests,
createVideoPlaylist, createVideoPlaylist,
deleteVideoPlaylist, deleteVideoPlaylist,
@ -21,20 +26,14 @@ import {
updateVideoPlaylistElement, updateVideoPlaylistElement,
uploadVideoAndGetId uploadVideoAndGetId
} from '../../../../shared/extra-utils' } from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
} from '../../../../shared/extra-utils/requests/check-api-params'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
describe('Test video playlists API validator', function () { describe('Test video playlists API validator', function () {
let server: ServerInfo let server: ServerInfo
let userAccessToken: string let userAccessToken: string
let playlistUUID: string
let playlist: VideoPlaylistCreateResult
let privatePlaylistUUID: string let privatePlaylistUUID: string
let watchLaterPlaylistId: number let watchLaterPlaylistId: number
let videoId: number let videoId: number
let playlistElementId: number let playlistElementId: number
@ -67,7 +66,7 @@ describe('Test video playlists API validator', function () {
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
}) })
playlistUUID = res.body.videoPlaylist.uuid playlist = res.body.videoPlaylist
} }
{ {
@ -150,15 +149,15 @@ describe('Test video playlists API validator', function () {
const path = '/api/v1/video-playlists/' const path = '/api/v1/video-playlists/'
it('Should fail with a bad start pagination', async function () { it('Should fail with a bad start pagination', async function () {
await checkBadStartPagination(server.url, path + playlistUUID + '/videos', server.accessToken) await checkBadStartPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
}) })
it('Should fail with a bad count pagination', async function () { it('Should fail with a bad count pagination', async function () {
await checkBadCountPagination(server.url, path + playlistUUID + '/videos', server.accessToken) await checkBadCountPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
}) })
it('Should success with the correct parameters', async function () { it('Should success with the correct parameters', async function () {
await makeGetRequest({ url: server.url, path: path + playlistUUID + '/videos', statusCodeExpected: HttpStatusCode.OK_200 }) await makeGetRequest({ url: server.url, path: path + playlist.shortUUID + '/videos', statusCodeExpected: HttpStatusCode.OK_200 })
}) })
}) })
@ -177,6 +176,7 @@ describe('Test video playlists API validator', function () {
token: server.accessToken, token: server.accessToken,
playlistAttrs: { playlistAttrs: {
displayName: 'super playlist', displayName: 'super playlist',
videoChannelId: server.videoChannel.id,
privacy: VideoPlaylistPrivacy.UNLISTED privacy: VideoPlaylistPrivacy.UNLISTED
} }
}) })
@ -187,7 +187,7 @@ describe('Test video playlists API validator', function () {
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await getVideoPlaylist(server.url, playlistUUID, HttpStatusCode.OK_200) await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200)
}) })
}) })
@ -213,7 +213,7 @@ describe('Test video playlists API validator', function () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail without displayName', async function () { it('Should fail without displayName', async function () {
@ -226,42 +226,42 @@ describe('Test video playlists API validator', function () {
const params = getBase({ displayName: 's'.repeat(300) }) const params = getBase({ displayName: 's'.repeat(300) })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect description', async function () { it('Should fail with an incorrect description', async function () {
const params = getBase({ description: 't' }) const params = getBase({ description: 't' })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect privacy', async function () { it('Should fail with an incorrect privacy', async function () {
const params = getBase({ privacy: 45 }) const params = getBase({ privacy: 45 })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an unknown video channel id', async function () { it('Should fail with an unknown video channel id', async function () {
const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect thumbnail file', async function () { it('Should fail with an incorrect thumbnail file', async function () {
const params = getBase({ thumbnailfile: 'video_short.mp4' }) const params = getBase({ thumbnailfile: 'video_short.mp4' })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with a thumbnail file too big', async function () { it('Should fail with a thumbnail file too big', async function () {
const params = getBase({ thumbnailfile: 'preview-big.png' }) const params = getBase({ thumbnailfile: 'preview-big.png' })
await createVideoPlaylist(params) await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail to set "public" a playlist not assigned to a channel', async function () { it('Should fail to set "public" a playlist not assigned to a channel', async function () {
@ -272,8 +272,8 @@ describe('Test video playlists API validator', function () {
await createVideoPlaylist(params) await createVideoPlaylist(params)
await createVideoPlaylist(params2) await createVideoPlaylist(params2)
await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID)) await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID))
await updateVideoPlaylist(getUpdate(params2, playlistUUID)) await updateVideoPlaylist(getUpdate(params2, playlist.shortUUID))
await updateVideoPlaylist(getUpdate(params3, playlistUUID)) await updateVideoPlaylist(getUpdate(params3, playlist.shortUUID))
}) })
it('Should fail with an unknown playlist to update', async function () { it('Should fail with an unknown playlist to update', async function () {
@ -286,7 +286,7 @@ describe('Test video playlists API validator', function () {
it('Should fail to update a playlist of another user', async function () { it('Should fail to update a playlist of another user', async function () {
await updateVideoPlaylist(getUpdate( await updateVideoPlaylist(getUpdate(
getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }), getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }),
playlistUUID playlist.shortUUID
)) ))
}) })
@ -305,7 +305,7 @@ describe('Test video playlists API validator', function () {
{ {
const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await updateVideoPlaylist(getUpdate(params, playlistUUID)) await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
} }
}) })
}) })
@ -316,7 +316,7 @@ describe('Test video playlists API validator', function () {
expectedStatus: HttpStatusCode.BAD_REQUEST_400, expectedStatus: HttpStatusCode.BAD_REQUEST_400,
url: server.url, url: server.url,
token: server.accessToken, token: server.accessToken,
playlistId: playlistUUID, playlistId: playlist.id,
elementAttrs: Object.assign({ elementAttrs: Object.assign({
videoId, videoId,
startTimestamp: 2, startTimestamp: 2,
@ -381,7 +381,7 @@ describe('Test video playlists API validator', function () {
stopTimestamp: 2 stopTimestamp: 2
}, elementAttrs), }, elementAttrs),
playlistElementId, playlistElementId,
playlistId: playlistUUID, playlistId: playlist.id,
expectedStatus: HttpStatusCode.BAD_REQUEST_400 expectedStatus: HttpStatusCode.BAD_REQUEST_400
}, wrapper) }, wrapper)
} }
@ -451,7 +451,7 @@ describe('Test video playlists API validator', function () {
return Object.assign({ return Object.assign({
url: server.url, url: server.url,
token: server.accessToken, token: server.accessToken,
playlistId: playlistUUID, playlistId: playlist.shortUUID,
elementAttrs: Object.assign({ elementAttrs: Object.assign({
startPosition: 1, startPosition: 1,
insertAfterPosition: 2, insertAfterPosition: 2,
@ -469,7 +469,7 @@ describe('Test video playlists API validator', function () {
await addVideoInPlaylist({ await addVideoInPlaylist({
url: server.url, url: server.url,
token: server.accessToken, token: server.accessToken,
playlistId: playlistUUID, playlistId: playlist.shortUUID,
elementAttrs: { videoId: id } elementAttrs: { videoId: id }
}) })
} }
@ -606,7 +606,7 @@ describe('Test video playlists API validator', function () {
url: server.url, url: server.url,
token: server.accessToken, token: server.accessToken,
playlistElementId, playlistElementId,
playlistId: playlistUUID, playlistId: playlist.uuid,
expectedStatus: HttpStatusCode.BAD_REQUEST_400 expectedStatus: HttpStatusCode.BAD_REQUEST_400
}, wrapper) }, wrapper)
} }
@ -662,7 +662,7 @@ describe('Test video playlists API validator', function () {
}) })
it('Should fail with a playlist of another user', async function () { it('Should fail with a playlist of another user', async function () {
await deleteVideoPlaylist(server.url, userAccessToken, playlistUUID, HttpStatusCode.FORBIDDEN_403) await deleteVideoPlaylist(server.url, userAccessToken, playlist.uuid, HttpStatusCode.FORBIDDEN_403)
}) })
it('Should fail with the watch later playlist', async function () { it('Should fail with the watch later playlist', async function () {
@ -670,7 +670,7 @@ describe('Test video playlists API validator', function () {
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await deleteVideoPlaylist(server.url, server.accessToken, playlistUUID) await deleteVideoPlaylist(server.url, server.accessToken, playlist.uuid)
}) })
}) })

View File

@ -5,7 +5,7 @@ import * as chai from 'chai'
import { omit } from 'lodash' import { omit } from 'lodash'
import { join } from 'path' import { join } from 'path'
import { randomInt } from '@shared/core-utils' import { randomInt } from '@shared/core-utils'
import { PeerTubeProblemDocument } from '@shared/models' import { PeerTubeProblemDocument, VideoCreateResult } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
checkUploadVideoParam, checkUploadVideoParam,
@ -42,7 +42,7 @@ describe('Test videos API validator', function () {
let accountName: string let accountName: string
let channelId: number let channelId: number
let channelName: string let channelName: string
let videoId let video: VideoCreateResult
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -490,7 +490,7 @@ describe('Test videos API validator', function () {
before(async function () { before(async function () {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
videoId = res.body.data[0].uuid video = res.body.data[0]
}) })
it('Should fail with nothing', async function () { it('Should fail with nothing', async function () {
@ -518,79 +518,79 @@ describe('Test videos API validator', function () {
it('Should fail with a long name', async function () { it('Should fail with a long name', async function () {
const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad category', async function () { it('Should fail with a bad category', async function () {
const fields = immutableAssign(baseCorrectParams, { category: 125 }) const fields = immutableAssign(baseCorrectParams, { category: 125 })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad licence', async function () { it('Should fail with a bad licence', async function () {
const fields = immutableAssign(baseCorrectParams, { licence: 125 }) const fields = immutableAssign(baseCorrectParams, { licence: 125 })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad language', async function () { it('Should fail with a bad language', async function () {
const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a long description', async function () { it('Should fail with a long description', async function () {
const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a long support text', async function () { it('Should fail with a long support text', async function () {
const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad channel', async function () { it('Should fail with a bad channel', async function () {
const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) const fields = immutableAssign(baseCorrectParams, { channelId: 545454 })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with too many tags', async function () { it('Should fail with too many tags', async function () {
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a tag length too low', async function () { it('Should fail with a tag length too low', async function () {
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a tag length too big', async function () { it('Should fail with a tag length too big', async function () {
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad schedule update (miss updateAt)', async function () { it('Should fail with a bad schedule update (miss updateAt)', async function () {
const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }) const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad schedule update (wrong updateAt)', async function () { it('Should fail with a bad schedule update (wrong updateAt)', async function () {
const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } }) const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with a bad originally published at param', async function () { it('Should fail with a bad originally published at param', async function () {
const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' }) const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
}) })
it('Should fail with an incorrect thumbnail file', async function () { it('Should fail with an incorrect thumbnail file', async function () {
@ -602,7 +602,7 @@ describe('Test videos API validator', function () {
await makeUploadRequest({ await makeUploadRequest({
url: server.url, url: server.url,
method: 'PUT', method: 'PUT',
path: path + videoId, path: path + video.shortUUID,
token: server.accessToken, token: server.accessToken,
fields, fields,
attaches attaches
@ -618,7 +618,7 @@ describe('Test videos API validator', function () {
await makeUploadRequest({ await makeUploadRequest({
url: server.url, url: server.url,
method: 'PUT', method: 'PUT',
path: path + videoId, path: path + video.shortUUID,
token: server.accessToken, token: server.accessToken,
fields, fields,
attaches attaches
@ -634,7 +634,7 @@ describe('Test videos API validator', function () {
await makeUploadRequest({ await makeUploadRequest({
url: server.url, url: server.url,
method: 'PUT', method: 'PUT',
path: path + videoId, path: path + video.shortUUID,
token: server.accessToken, token: server.accessToken,
fields, fields,
attaches attaches
@ -650,7 +650,7 @@ describe('Test videos API validator', function () {
await makeUploadRequest({ await makeUploadRequest({
url: server.url, url: server.url,
method: 'PUT', method: 'PUT',
path: path + videoId, path: path + video.shortUUID,
token: server.accessToken, token: server.accessToken,
fields, fields,
attaches attaches
@ -662,7 +662,7 @@ describe('Test videos API validator', function () {
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
path: path + videoId, path: path + video.shortUUID,
token: userAccessToken, token: userAccessToken,
fields, fields,
statusCodeExpected: HttpStatusCode.FORBIDDEN_403 statusCodeExpected: HttpStatusCode.FORBIDDEN_403
@ -674,7 +674,7 @@ describe('Test videos API validator', function () {
it('Shoud report the appropriate error', async function () { it('Shoud report the appropriate error', async function () {
const fields = immutableAssign(baseCorrectParams, { licence: 125 }) const fields = immutableAssign(baseCorrectParams, { licence: 125 })
const res = await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) const res = await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
const error = res.body as PeerTubeProblemDocument const error = res.body as PeerTubeProblemDocument
expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/putVideo') expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/putVideo')
@ -694,7 +694,7 @@ describe('Test videos API validator', function () {
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
path: path + videoId, path: path + video.shortUUID,
token: server.accessToken, token: server.accessToken,
fields, fields,
statusCodeExpected: HttpStatusCode.NO_CONTENT_204 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
@ -739,7 +739,7 @@ describe('Test videos API validator', function () {
}) })
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await getVideo(server.url, videoId) await getVideo(server.url, video.shortUUID)
}) })
}) })
@ -810,7 +810,7 @@ describe('Test videos API validator', function () {
}) })
it('Should fail with a video of another user without the appropriate right', async function () { it('Should fail with a video of another user without the appropriate right', async function () {
await removeVideo(server.url, userAccessToken, videoId, HttpStatusCode.FORBIDDEN_403) await removeVideo(server.url, userAccessToken, video.uuid, HttpStatusCode.FORBIDDEN_403)
}) })
it('Should fail with a video of another server') it('Should fail with a video of another server')
@ -832,7 +832,7 @@ describe('Test videos API validator', function () {
}) })
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await removeVideo(server.url, server.accessToken, videoId) await removeVideo(server.url, server.accessToken, video.uuid)
}) })
}) })

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { v4 as uuidv4 } from 'uuid' import { buildUUID } from '@server/helpers/uuid'
import { AbuseState } from '@shared/models' import { AbuseState } from '@shared/models'
import { import {
addAbuseMessage, addAbuseMessage,
@ -85,7 +85,7 @@ describe('Test moderation notifications', function () {
it('Should send a notification to moderators on local video abuse', async function () { it('Should send a notification to moderators on local video abuse', async function () {
this.timeout(20000) this.timeout(20000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
@ -98,7 +98,7 @@ describe('Test moderation notifications', function () {
it('Should send a notification to moderators on remote video abuse', async function () { it('Should send a notification to moderators on remote video abuse', async function () {
this.timeout(20000) this.timeout(20000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
@ -114,10 +114,10 @@ describe('Test moderation notifications', function () {
it('Should send a notification to moderators on local comment abuse', async function () { it('Should send a notification to moderators on local comment abuse', async function () {
this.timeout(20000) this.timeout(20000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + uuidv4()) const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID())
const comment = resComment.body.comment const comment = resComment.body.comment
await waitJobs(servers) await waitJobs(servers)
@ -131,10 +131,10 @@ describe('Test moderation notifications', function () {
it('Should send a notification to moderators on remote comment abuse', async function () { it('Should send a notification to moderators on remote comment abuse', async function () {
this.timeout(20000) this.timeout(20000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + uuidv4()) await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID())
await waitJobs(servers) await waitJobs(servers)
@ -188,7 +188,7 @@ describe('Test moderation notifications', function () {
token: userAccessToken token: userAccessToken
} }
const name = 'abuse ' + uuidv4() const name = 'abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
@ -236,7 +236,7 @@ describe('Test moderation notifications', function () {
token: servers[0].accessToken token: servers[0].accessToken
} }
const name = 'abuse ' + uuidv4() const name = 'abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video const video = resVideo.body.video
@ -307,7 +307,7 @@ describe('Test moderation notifications', function () {
it('Should send a notification to video owner on blacklist', async function () { it('Should send a notification to video owner on blacklist', async function () {
this.timeout(10000) this.timeout(10000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const uuid = resVideo.body.video.uuid const uuid = resVideo.body.video.uuid
@ -320,7 +320,7 @@ describe('Test moderation notifications', function () {
it('Should send a notification to video owner on unblacklist', async function () { it('Should send a notification to video owner on unblacklist', async function () {
this.timeout(10000) this.timeout(10000)
const name = 'video for abuse ' + uuidv4() const name = 'video for abuse ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const uuid = resVideo.body.video.uuid const uuid = resVideo.body.video.uuid
@ -507,7 +507,7 @@ describe('Test moderation notifications', function () {
it('Should send notification to moderators on new video with auto-blacklist', async function () { it('Should send notification to moderators on new video with auto-blacklist', async function () {
this.timeout(40000) this.timeout(40000)
videoName = 'video with auto-blacklist ' + uuidv4() videoName = 'video with auto-blacklist ' + buildUUID()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
videoUUID = resVideo.body.video.uuid videoUUID = resVideo.body.video.uuid
@ -553,7 +553,7 @@ describe('Test moderation notifications', function () {
const updateAt = new Date(new Date().getTime() + 1000000) const updateAt = new Date(new Date().getTime() + 1000000)
const name = 'video with auto-blacklist and future schedule ' + uuidv4() const name = 'video with auto-blacklist and future schedule ' + buildUUID()
const data = { const data = {
name, name,
@ -586,7 +586,7 @@ describe('Test moderation notifications', function () {
// In 2 seconds // In 2 seconds
const updateAt = new Date(new Date().getTime() + 2000) const updateAt = new Date(new Date().getTime() + 2000)
const name = 'video with schedule done and still auto-blacklisted ' + uuidv4() const name = 'video with schedule done and still auto-blacklisted ' + buildUUID()
const data = { const data = {
name, name,
@ -609,7 +609,7 @@ describe('Test moderation notifications', function () {
it('Should not send a notification to moderators on new video without auto-blacklist', async function () { it('Should not send a notification to moderators on new video without auto-blacklist', async function () {
this.timeout(60000) this.timeout(60000)
const name = 'video without auto-blacklist ' + uuidv4() const name = 'video without auto-blacklist ' + buildUUID()
// admin with blacklist right will not be auto-blacklisted // admin with blacklist right will not be auto-blacklisted
const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name }) const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name })

View File

@ -2,7 +2,7 @@
import 'mocha' import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { v4 as uuidv4 } from 'uuid' import { buildUUID } from '@server/helpers/uuid'
import { import {
cleanupTests, cleanupTests,
updateMyUser, updateMyUser,
@ -207,7 +207,7 @@ describe('Test user notifications', function () {
it('Should send a new video notification after a video import', async function () { it('Should send a new video notification after a video import', async function () {
this.timeout(100000) this.timeout(100000)
const name = 'video import ' + uuidv4() const name = 'video import ' + buildUUID()
const attributes = { const attributes = {
name, name,
@ -278,7 +278,7 @@ describe('Test user notifications', function () {
it('Should send a notification when an imported video is transcoded', async function () { it('Should send a notification when an imported video is transcoded', async function () {
this.timeout(50000) this.timeout(50000)
const name = 'video import ' + uuidv4() const name = 'video import ' + buildUUID()
const attributes = { const attributes = {
name, name,
@ -347,7 +347,7 @@ describe('Test user notifications', function () {
it('Should send a notification when the video import failed', async function () { it('Should send a notification when the video import failed', async function () {
this.timeout(70000) this.timeout(70000)
const name = 'video import ' + uuidv4() const name = 'video import ' + buildUUID()
const attributes = { const attributes = {
name, name,
@ -365,7 +365,7 @@ describe('Test user notifications', function () {
it('Should send a notification when the video import succeeded', async function () { it('Should send a notification when the video import succeeded', async function () {
this.timeout(70000) this.timeout(70000)
const name = 'video import ' + uuidv4() const name = 'video import ' + buildUUID()
const attributes = { const attributes = {
name, name,

View File

@ -47,7 +47,7 @@ describe('Test handle downs', function () {
let missedVideo2: Video let missedVideo2: Video
let unlistedVideo: Video let unlistedVideo: Video
const videoIdsServer1: number[] = [] const videoIdsServer1: string[] = []
const videoAttributes = { const videoAttributes = {
name: 'my super name for server 1', name: 'my super name for server 1',

View File

@ -56,7 +56,7 @@ import {
removeServerFromServerBlocklist removeServerFromServerBlocklist
} from '../../../../shared/extra-utils/users/blocklist' } from '../../../../shared/extra-utils/users/blocklist'
import { User } from '../../../../shared/models/users' import { User } from '../../../../shared/models/users'
import { VideoPrivacy } from '../../../../shared/models/videos' import { VideoPlaylistCreateResult, VideoPrivacy } from '../../../../shared/models/videos'
import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model' import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model' import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
@ -427,20 +427,30 @@ describe('Test video playlists', function () {
expect(data).to.have.lengthOf(0) expect(data).to.have.lengthOf(0)
} }
}) })
})
it('Should not list unlisted or private playlists', async function () { describe('Playlist rights', function () {
let unlistedPlaylist: VideoPlaylistCreateResult
let privatePlaylist: VideoPlaylistCreateResult
before(async function () {
this.timeout(30000) this.timeout(30000)
await createVideoPlaylist({ {
const res = await createVideoPlaylist({
url: servers[1].url, url: servers[1].url,
token: servers[1].accessToken, token: servers[1].accessToken,
playlistAttrs: { playlistAttrs: {
displayName: 'playlist unlisted', displayName: 'playlist unlisted',
privacy: VideoPlaylistPrivacy.UNLISTED privacy: VideoPlaylistPrivacy.UNLISTED,
videoChannelId: servers[1].videoChannel.id
} }
}) })
unlistedPlaylist = res.body.videoPlaylist
}
await createVideoPlaylist({ {
const res = await createVideoPlaylist({
url: servers[1].url, url: servers[1].url,
token: servers[1].accessToken, token: servers[1].accessToken,
playlistAttrs: { playlistAttrs: {
@ -448,10 +458,14 @@ describe('Test video playlists', function () {
privacy: VideoPlaylistPrivacy.PRIVATE privacy: VideoPlaylistPrivacy.PRIVATE
} }
}) })
privatePlaylist = res.body.videoPlaylist
}
await waitJobs(servers) await waitJobs(servers)
await wait(3000) await wait(3000)
})
it('Should not list unlisted or private playlists', async function () {
for (const server of servers) { for (const server of servers) {
const results = [ const results = [
await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'), await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
@ -469,6 +483,27 @@ describe('Test video playlists', function () {
} }
} }
}) })
it('Should not get unlisted playlist using only the id', async function () {
await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404)
})
it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid)
await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID)
})
it('Should not get private playlist without token', async function () {
for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
await getVideoPlaylist(servers[1].url, id, 401)
}
})
it('Should get private playlist with a token', async function () {
for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id)
}
})
}) })
describe('Update playlists', function () { describe('Update playlists', function () {

View File

@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import * as chai from 'chai'
import 'mocha' import 'mocha'
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum' import * as chai from 'chai'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { Video, VideoCreateResult } from '@shared/models'
import { import {
cleanupTests, cleanupTests,
flushAndRunServer, flushAndRunServer,
@ -13,12 +14,11 @@ import {
uploadVideo uploadVideo
} from '../../../../shared/extra-utils/index' } from '../../../../shared/extra-utils/index'
import { doubleFollow } from '../../../../shared/extra-utils/server/follows' import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { userLogin } from '../../../../shared/extra-utils/users/login' import { userLogin } from '../../../../shared/extra-utils/users/login'
import { createUser } from '../../../../shared/extra-utils/users/users' import { createUser } from '../../../../shared/extra-utils/users/users'
import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos' import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos'
import { waitJobs } from '../../../../shared/extra-utils/server/jobs' import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
import { Video } from '@shared/models'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
const expect = chai.expect const expect = chai.expect
@ -32,7 +32,7 @@ describe('Test video privacy', function () {
let internalVideoId: number let internalVideoId: number
let internalVideoUUID: string let internalVideoUUID: string
let unlistedVideoUUID: string let unlistedVideo: VideoCreateResult
let nonFederatedUnlistedVideoUUID: string let nonFederatedUnlistedVideoUUID: string
let now: number let now: number
@ -59,6 +59,8 @@ describe('Test video privacy', function () {
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])
}) })
describe('Private and internal videos', function () {
it('Should upload a private and internal videos on server 1', async function () { it('Should upload a private and internal videos on server 1', async function () {
this.timeout(10000) this.timeout(10000)
@ -135,6 +137,9 @@ describe('Test video privacy', function () {
it('Should be able to watch the private video with the correct user', async function () { it('Should be able to watch the private video with the correct user', async function () {
await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, HttpStatusCode.OK_200) await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, HttpStatusCode.OK_200)
}) })
})
describe('Unlisted videos', function () {
it('Should upload an unlisted video on server 2', async function () { it('Should upload an unlisted video on server 2', async function () {
this.timeout(60000) this.timeout(60000)
@ -164,15 +169,21 @@ describe('Test video privacy', function () {
expect(res.body.total).to.equal(1) expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1) expect(res.body.data).to.have.lengthOf(1)
unlistedVideoUUID = res.body.data[0].uuid unlistedVideo = res.body.data[0]
}) })
it('Should be able to get this unlisted video', async function () { it('Should not be able to get this unlisted video using its id', async function () {
await getVideo(servers[1].url, unlistedVideo.id, 404)
})
it('Should be able to get this unlisted video using its uuid/shortUUID', async function () {
for (const server of servers) { for (const server of servers) {
const res = await getVideo(server.url, unlistedVideoUUID) for (const id of [ unlistedVideo.uuid, unlistedVideo.shortUUID ]) {
const res = await getVideo(server.url, id)
expect(res.body.name).to.equal('unlisted video') expect(res.body.name).to.equal('unlisted video')
} }
}
}) })
it('Should upload a non-federating unlisted video to server 1', async function () { it('Should upload a non-federating unlisted video to server 1', async function () {
@ -205,6 +216,9 @@ describe('Test video privacy', function () {
it('Should not be able to get non-federated unlisted video from federated server', async function () { it('Should not be able to get non-federated unlisted video from federated server', async function () {
await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, HttpStatusCode.NOT_FOUND_404) await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, HttpStatusCode.NOT_FOUND_404)
}) })
})
describe('Privacy update', function () {
it('Should update the private and internal videos to public on server 1', async function () { it('Should update the private and internal videos to public on server 1', async function () {
this.timeout(10000) this.timeout(10000)
@ -285,6 +299,7 @@ describe('Test video privacy', function () {
expect(internalVideo.privacy.id).to.equal(VideoPrivacy.PRIVATE) expect(internalVideo.privacy.id).to.equal(VideoPrivacy.PRIVATE)
} }
}) })
})
after(async function () { after(async function () {
await cleanupTests(servers) await cleanupTests(servers)

View File

@ -2,7 +2,10 @@
import 'mocha' import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { waitJobs } from '../../../shared/extra-utils/server/jobs' import { createFile, readdir } from 'fs-extra'
import { join } from 'path'
import { buildUUID } from '@server/helpers/uuid'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { import {
buildServerDirectory, buildServerDirectory,
cleanupTests, cleanupTests,
@ -21,11 +24,8 @@ import {
uploadVideo, uploadVideo,
wait wait
} from '../../../shared/extra-utils' } from '../../../shared/extra-utils'
import { waitJobs } from '../../../shared/extra-utils/server/jobs'
import { Account, VideoPlaylistPrivacy } from '../../../shared/models' import { Account, VideoPlaylistPrivacy } from '../../../shared/models'
import { createFile, readdir } from 'fs-extra'
import { v4 as uuidv4 } from 'uuid'
import { join } from 'path'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
const expect = chai.expect const expect = chai.expect
@ -131,8 +131,8 @@ describe('Test prune storage scripts', function () {
{ {
const base = buildServerDirectory(servers[0], 'videos') const base = buildServerDirectory(servers[0], 'videos')
const n1 = uuidv4() + '.mp4' const n1 = buildUUID() + '.mp4'
const n2 = uuidv4() + '.webm' const n2 = buildUUID() + '.webm'
await createFile(join(base, n1)) await createFile(join(base, n1))
await createFile(join(base, n2)) await createFile(join(base, n2))
@ -143,8 +143,8 @@ describe('Test prune storage scripts', function () {
{ {
const base = buildServerDirectory(servers[0], 'torrents') const base = buildServerDirectory(servers[0], 'torrents')
const n1 = uuidv4() + '-240.torrent' const n1 = buildUUID() + '-240.torrent'
const n2 = uuidv4() + '-480.torrent' const n2 = buildUUID() + '-480.torrent'
await createFile(join(base, n1)) await createFile(join(base, n1))
await createFile(join(base, n2)) await createFile(join(base, n2))
@ -155,8 +155,8 @@ describe('Test prune storage scripts', function () {
{ {
const base = buildServerDirectory(servers[0], 'thumbnails') const base = buildServerDirectory(servers[0], 'thumbnails')
const n1 = uuidv4() + '.jpg' const n1 = buildUUID() + '.jpg'
const n2 = uuidv4() + '.jpg' const n2 = buildUUID() + '.jpg'
await createFile(join(base, n1)) await createFile(join(base, n1))
await createFile(join(base, n2)) await createFile(join(base, n2))
@ -167,8 +167,8 @@ describe('Test prune storage scripts', function () {
{ {
const base = buildServerDirectory(servers[0], 'previews') const base = buildServerDirectory(servers[0], 'previews')
const n1 = uuidv4() + '.jpg' const n1 = buildUUID() + '.jpg'
const n2 = uuidv4() + '.jpg' const n2 = buildUUID() + '.jpg'
await createFile(join(base, n1)) await createFile(join(base, n1))
await createFile(join(base, n2)) await createFile(join(base, n2))
@ -179,8 +179,8 @@ describe('Test prune storage scripts', function () {
{ {
const base = buildServerDirectory(servers[0], 'avatars') const base = buildServerDirectory(servers[0], 'avatars')
const n1 = uuidv4() + '.png' const n1 = buildUUID() + '.png'
const n2 = uuidv4() + '.jpg' const n2 = buildUUID() + '.jpg'
await createFile(join(base, n1)) await createFile(join(base, n1))
await createFile(join(base, n2)) await createFile(join(base, n2))

View File

@ -3,9 +3,8 @@
import 'mocha' import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { omit } from 'lodash' import { omit } from 'lodash'
import * as request from 'supertest'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { Account, CustomConfig, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models' import { Account, CustomConfig, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
import { import {
addVideoInPlaylist, addVideoInPlaylist,
cleanupTests, cleanupTests,
@ -50,13 +49,16 @@ describe('Test a client controllers', function () {
const playlistName = 'super playlist name' const playlistName = 'super playlist name'
const playlistDescription = 'super playlist description' const playlistDescription = 'super playlist description'
let playlistUUID: string let playlist: VideoPlaylistCreateResult
const channelDescription = 'my super channel description' const channelDescription = 'my super channel description'
const watchVideoBasePaths = [ '/videos/watch/', '/w/' ] const watchVideoBasePaths = [ '/videos/watch/', '/w/' ]
const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ] const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ]
let videoIds: (string | number)[] = []
let playlistIds: (string | number)[] = []
before(async function () { before(async function () {
this.timeout(120000) this.timeout(120000)
@ -79,7 +81,9 @@ describe('Test a client controllers', function () {
const videos = resVideosRequest.body.data const videos = resVideosRequest.body.data
expect(videos.length).to.equal(1) expect(videos.length).to.equal(1)
servers[0].video = videos[0] const video = videos[0]
servers[0].video = video
videoIds = [ video.id, video.uuid, video.shortUUID ]
// Playlist // Playlist
@ -91,16 +95,14 @@ describe('Test a client controllers', function () {
} }
const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
playlist = resVideoPlaylistRequest.body.videoPlaylist
const playlist = resVideoPlaylistRequest.body.videoPlaylist playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
const playlistId = playlist.id
playlistUUID = playlist.uuid
await addVideoInPlaylist({ await addVideoInPlaylist({
url: servers[0].url, url: servers[0].url,
token: servers[0].accessToken, token: servers[0].accessToken,
playlistId, playlistId: playlist.shortUUID,
elementAttrs: { videoId: servers[0].video.id } elementAttrs: { videoId: video.id }
}) })
// Account // Account
@ -117,11 +119,13 @@ describe('Test a client controllers', function () {
it('Should have valid oEmbed discovery tags for videos', async function () { it('Should have valid oEmbed discovery tags for videos', async function () {
for (const basePath of watchVideoBasePaths) { for (const basePath of watchVideoBasePaths) {
const path = basePath + servers[0].video.uuid for (const id of videoIds) {
const res = await request(servers[0].url) const res = await makeGetRequest({
.get(path) url: servers[0].url,
.set('Accept', 'text/html') path: basePath + id,
.expect(HttpStatusCode.OK_200) accept: 'text/html',
statusCodeExpected: HttpStatusCode.OK_200
})
const port = servers[0].port const port = servers[0].port
@ -131,23 +135,28 @@ describe('Test a client controllers', function () {
expect(res.text).to.contain(expectedLink) expect(res.text).to.contain(expectedLink)
} }
}
}) })
it('Should have valid oEmbed discovery tags for a playlist', async function () { it('Should have valid oEmbed discovery tags for a playlist', async function () {
for (const basePath of watchPlaylistBasePaths) { for (const basePath of watchPlaylistBasePaths) {
const res = await request(servers[0].url) for (const id of playlistIds) {
.get(basePath + playlistUUID) const res = await makeGetRequest({
.set('Accept', 'text/html') url: servers[0].url,
.expect(HttpStatusCode.OK_200) path: basePath + id,
accept: 'text/html',
statusCodeExpected: HttpStatusCode.OK_200
})
const port = servers[0].port const port = servers[0].port
const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' + const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
`url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlistUUID}" ` + `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.uuid}" ` +
`title="${playlistName}" />` `title="${playlistName}" />`
expect(res.text).to.contain(expectedLink) expect(res.text).to.contain(expectedLink)
} }
}
}) })
}) })
@ -190,7 +199,7 @@ describe('Test a client controllers', function () {
expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`) expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`) expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
expect(text).to.contain('<meta property="og:type" content="video" />') expect(text).to.contain('<meta property="og:type" content="video" />')
expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlistUUID}" />`) expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.uuid}" />`)
} }
it('Should have valid Open Graph tags on the account page', async function () { it('Should have valid Open Graph tags on the account page', async function () {
@ -206,15 +215,19 @@ describe('Test a client controllers', function () {
}) })
it('Should have valid Open Graph tags on the watch page', async function () { it('Should have valid Open Graph tags on the watch page', async function () {
await watchVideoPageTest('/videos/watch/' + servers[0].video.id) for (const path of watchVideoBasePaths) {
await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) for (const id of videoIds) {
await watchVideoPageTest('/w/' + servers[0].video.uuid) await watchVideoPageTest(path + id)
await watchVideoPageTest('/w/' + servers[0].video.id) }
}
}) })
it('Should have valid Open Graph tags on the watch playlist page', async function () { it('Should have valid Open Graph tags on the watch playlist page', async function () {
await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) for (const path of watchPlaylistBasePaths) {
await watchPlaylistPageTest('/w/p/' + playlistUUID) for (const id of playlistIds) {
await watchPlaylistPageTest(path + id)
}
}
}) })
}) })
@ -263,15 +276,19 @@ describe('Test a client controllers', function () {
} }
it('Should have valid twitter card on the watch video page', async function () { it('Should have valid twitter card on the watch video page', async function () {
await watchVideoPageTest('/videos/watch/' + servers[0].video.id) for (const path of watchVideoBasePaths) {
await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) for (const id of videoIds) {
await watchVideoPageTest('/w/' + servers[0].video.uuid) await watchVideoPageTest(path + id)
await watchVideoPageTest('/w/' + servers[0].video.id) }
}
}) })
it('Should have valid twitter card on the watch playlist page', async function () { it('Should have valid twitter card on the watch playlist page', async function () {
await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) for (const path of watchPlaylistBasePaths) {
await watchPlaylistPageTest('/w/p/' + playlistUUID) for (const id of playlistIds) {
await watchPlaylistPageTest(path + id)
}
}
}) })
it('Should have valid twitter card on the account page', async function () { it('Should have valid twitter card on the account page', async function () {
@ -333,15 +350,19 @@ describe('Test a client controllers', function () {
} }
it('Should have valid twitter card on the watch video page', async function () { it('Should have valid twitter card on the watch video page', async function () {
await watchVideoPageTest('/videos/watch/' + servers[0].video.id) for (const path of watchVideoBasePaths) {
await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) for (const id of videoIds) {
await watchVideoPageTest('/w/' + servers[0].video.uuid) await watchVideoPageTest(path + id)
await watchVideoPageTest('/w/' + servers[0].video.id) }
}
}) })
it('Should have valid twitter card on the watch playlist page', async function () { it('Should have valid twitter card on the watch playlist page', async function () {
await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) for (const path of watchPlaylistBasePaths) {
await watchPlaylistPageTest('/w/p/' + playlistUUID) for (const id of playlistIds) {
await watchPlaylistPageTest(path + id)
}
}
}) })
it('Should have valid twitter card on the account page', async function () { it('Should have valid twitter card on the account page', async function () {
@ -399,9 +420,11 @@ describe('Test a client controllers', function () {
it('Should use the original video URL for the canonical tag', async function () { it('Should use the original video URL for the canonical tag', async function () {
for (const basePath of watchVideoBasePaths) { for (const basePath of watchVideoBasePaths) {
const res = await makeHTMLRequest(servers[1].url, basePath + servers[0].video.uuid) for (const id of videoIds) {
const res = await makeHTMLRequest(servers[1].url, basePath + id)
expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`) expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
} }
}
}) })
it('Should use the original account URL for the canonical tag', async function () { it('Should use the original account URL for the canonical tag', async function () {
@ -426,8 +449,10 @@ describe('Test a client controllers', function () {
it('Should use the original playlist URL for the canonical tag', async function () { it('Should use the original playlist URL for the canonical tag', async function () {
for (const basePath of watchPlaylistBasePaths) { for (const basePath of watchPlaylistBasePaths) {
const res = await makeHTMLRequest(servers[1].url, basePath + playlistUUID) for (const id of playlistIds) {
expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlistUUID}" />`) const res = await makeHTMLRequest(servers[1].url, basePath + id)
expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlist.uuid}" />`)
}
} }
}) })
}) })

View File

@ -4,7 +4,6 @@ registerTSPaths()
import * as repl from 'repl' import * as repl from 'repl'
import * as path from 'path' import * as path from 'path'
import * as _ from 'lodash' import * as _ from 'lodash'
import { uuidv1, uuidv3, uuidv4, uuidv5 } from 'uuid'
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import * as YoutubeDL from 'youtube-dl' import * as YoutubeDL from 'youtube-dl'
import { initDatabaseModels, sequelizeTypescript } from '../initializers/database' import { initDatabaseModels, sequelizeTypescript } from '../initializers/database'
@ -31,10 +30,6 @@ const start = async () => {
env: process.env, env: process.env,
lodash: _, lodash: _,
path, path,
uuidv1,
uuidv3,
uuidv4,
uuidv5,
cli, cli,
logger, logger,
constants, constants,

View File

@ -43,6 +43,7 @@ interface ServerInfo {
video?: { video?: {
id: number id: number
uuid: string uuid: string
shortUUID: string
name?: string name?: string
url?: string url?: string

View File

@ -6,9 +6,9 @@ import got, { Response as GotResponse } from 'got/dist/source'
import * as parseTorrent from 'parse-torrent' import * as parseTorrent from 'parse-torrent'
import { join } from 'path' import { join } from 'path'
import * as request from 'supertest' import * as request from 'supertest'
import { v4 as uuidv4 } from 'uuid'
import validator from 'validator' import validator from 'validator'
import { getLowercaseExtension } from '@server/helpers/core-utils' import { getLowercaseExtension } from '@server/helpers/core-utils'
import { buildUUID } from '@server/helpers/uuid'
import { HttpStatusCode } from '@shared/core-utils' import { HttpStatusCode } from '@shared/core-utils'
import { VideosCommonQuery } from '@shared/models' import { VideosCommonQuery } from '@shared/models'
import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
@ -806,7 +806,7 @@ async function uploadVideoAndGetId (options: {
const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs) const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
return { id: res.body.video.id, uuid: res.body.video.uuid } return res.body.video as { id: number, uuid: string, shortUUID: string }
} }
async function getLocalIdByUUID (url: string, uuid: string) { async function getLocalIdByUUID (url: string, uuid: string) {
@ -827,7 +827,7 @@ async function uploadRandomVideoOnServers (servers: ServerInfo[], serverNumber:
async function uploadRandomVideo (server: ServerInfo, wait = true, additionalParams: any = {}) { async function uploadRandomVideo (server: ServerInfo, wait = true, additionalParams: any = {}) {
const prefixName = additionalParams.prefixName || '' const prefixName = additionalParams.prefixName || ''
const name = prefixName + uuidv4() const name = prefixName + buildUUID()
const data = Object.assign({ name }, additionalParams) const data = Object.assign({ name }, additionalParams)
const res = await uploadVideo(server.url, server.accessToken, data) const res = await uploadVideo(server.url, server.accessToken, data)

View File

@ -0,0 +1 @@
export * from './result-list.model'

View File

@ -1,16 +1,16 @@
export * from './activitypub' export * from './activitypub'
export * from './actors' export * from './actors'
export * from './moderation'
export * from './custom-markup'
export * from './bulk' export * from './bulk'
export * from './redundancy' export * from './common'
export * from './users' export * from './custom-markup'
export * from './videos'
export * from './feeds' export * from './feeds'
export * from './joinpeertube' export * from './joinpeertube'
export * from './moderation'
export * from './overviews' export * from './overviews'
export * from './plugins' export * from './plugins'
export * from './redundancy'
export * from './search' export * from './search'
export * from './server' export * from './server'
export * from './oauth-client-local.model' export * from './tokens'
export * from './result-list.model' export * from './users'
export * from './videos'

View File

@ -10,7 +10,7 @@ export interface AbuseCreate {
} }
video?: { video?: {
id: number id: number | string
startAt?: number startAt?: number
endAt?: number endAt?: number
} }

View File

@ -0,0 +1 @@
export * from './oauth-client-local.model'

View File

@ -35,3 +35,4 @@ export * from './video-transcoding-fps.model'
export * from './video-update.model' export * from './video-update.model'
export * from './video.model' export * from './video.model'
export * from './video-create-result.model'

View File

@ -1,4 +1,5 @@
export * from './video-exist-in-playlist.model' export * from './video-exist-in-playlist.model'
export * from './video-playlist-create-result.model'
export * from './video-playlist-create.model' export * from './video-playlist-create.model'
export * from './video-playlist-element-create.model' export * from './video-playlist-element-create.model'
export * from './video-playlist-element-update.model' export * from './video-playlist-element-update.model'

View File

@ -0,0 +1,5 @@
export interface VideoPlaylistCreateResult {
id: number
uuid: string
shortUUID: string
}

View File

@ -6,6 +6,8 @@ import { VideoPlaylistType } from './video-playlist-type.model'
export interface VideoPlaylist { export interface VideoPlaylist {
id: number id: number
uuid: string uuid: string
shortUUID: string
isLocal: boolean isLocal: boolean
url: string url: string

View File

@ -0,0 +1,5 @@
export interface VideoCreateResult {
id: number
uuid: string
shortUUID: string
}

View File

@ -10,6 +10,8 @@ import { VideoStreamingPlaylist } from './video-streaming-playlist.model'
export interface Video { export interface Video {
id: number id: number
uuid: string uuid: string
shortUUID: string
createdAt: Date | string createdAt: Date | string
updatedAt: Date | string updatedAt: Date | string
publishedAt: Date | string publishedAt: Date | string

View File

@ -3003,6 +3003,8 @@ paths:
$ref: '#/components/schemas/VideoPlaylist/properties/id' $ref: '#/components/schemas/VideoPlaylist/properties/id'
uuid: uuid:
$ref: '#/components/schemas/VideoPlaylist/properties/uuid' $ref: '#/components/schemas/VideoPlaylist/properties/uuid'
shortUUID:
$ref: '#/components/schemas/VideoPlaylist/properties/shortUUID'
requestBody: requestBody:
content: content:
multipart/form-data: multipart/form-data:
@ -4543,11 +4545,12 @@ components:
name: id name: id
in: path in: path
required: true required: true
description: The object id or uuid description: The object id, uuid or short uuid
schema: schema:
oneOf: oneOf:
- $ref: '#/components/schemas/id' - $ref: '#/components/schemas/id'
- $ref: '#/components/schemas/UUIDv4' - $ref: '#/components/schemas/UUIDv4'
- $ref: '#/components/schemas/shortUUID'
playlistId: playlistId:
name: playlistId name: playlistId
in: path in: path
@ -4812,6 +4815,10 @@ components:
pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
minLength: 36 minLength: 36
maxLength: 36 maxLength: 36
shortUUID:
type: string
description: translation of a uuid v4 with a bigger alphabet to have a shorter uuid
example: 2y84q2MQUMWPbiEcxNXMgC
username: username:
type: string type: string
description: immutable name of the user, used to find or mention its actor description: immutable name of the user, used to find or mention its actor
@ -5141,6 +5148,9 @@ components:
description: universal identifier for the video, that can be used across instances description: universal identifier for the video, that can be used across instances
allOf: allOf:
- $ref: '#/components/schemas/UUIDv4' - $ref: '#/components/schemas/UUIDv4'
shortUUID:
allOf:
- $ref: '#/components/schemas/shortUUID'
isLive: isLive:
type: boolean type: boolean
createdAt: createdAt:
@ -5520,6 +5530,9 @@ components:
$ref: '#/components/schemas/id' $ref: '#/components/schemas/id'
uuid: uuid:
$ref: '#/components/schemas/UUIDv4' $ref: '#/components/schemas/UUIDv4'
shortUUID:
allOf:
- $ref: '#/components/schemas/shortUUID'
createdAt: createdAt:
type: string type: string
format: date-time format: date-time
@ -6295,6 +6308,8 @@ components:
$ref: '#/components/schemas/Video/properties/id' $ref: '#/components/schemas/Video/properties/id'
uuid: uuid:
$ref: '#/components/schemas/Video/properties/uuid' $ref: '#/components/schemas/Video/properties/uuid'
shortUUID:
$ref: '#/components/schemas/Video/properties/shortUUID'
CommentThreadResponse: CommentThreadResponse:
properties: properties:
total: total:

View File

@ -7260,6 +7260,14 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
short-uuid@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/short-uuid/-/short-uuid-4.2.0.tgz#3706d9e7287ac589dc5ffe324d3e34817a07540b"
integrity sha512-r3cxuPPZSuF0QkKsK9bBR7u+7cwuCRzWzgjPh07F5N2iIUNgblnMHepBY16xgj5t1lG9iOP9k/TEafY1qhRzaw==
dependencies:
any-base "^1.1.0"
uuid "^8.3.2"
side-channel@^1.0.4: side-channel@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"