aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-17 15:45:42 +0200
committerChocobozzz <me@florianbigard.com>2018-08-27 09:41:54 +0200
commit8a19bee1a1ee39f973bb37429e4f73c3f2873cdb (patch)
tree33c93ef19451d7e46d4be74ce0681359d2dcc70e
parent965c4b22d0e4d2f853501e844e6ebbb861bd389d (diff)
downloadPeerTube-8a19bee1a1ee39f973bb37429e4f73c3f2873cdb.tar.gz
PeerTube-8a19bee1a1ee39f973bb37429e4f73c3f2873cdb.tar.zst
PeerTube-8a19bee1a1ee39f973bb37429e4f73c3f2873cdb.zip
Add ability to set a name to a channel
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.html2
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.ts2
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts6
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html16
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss9
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html9
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss3
-rw-r--r--client/src/app/+video-channels/video-channels.component.html1
-rw-r--r--client/src/app/shared/forms/form-validators/video-channel-validators.service.ts16
-rw-r--r--client/src/app/shared/video-channel/video-channel.model.ts2
-rw-r--r--client/src/app/shared/video-channel/video-channel.service.ts4
-rw-r--r--client/src/app/shared/video/video.service.ts2
-rw-r--r--client/src/app/signup/signup.component.html15
-rw-r--r--client/src/app/signup/signup.component.scss9
-rw-r--r--client/src/app/signup/signup.component.ts4
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/sass/include/_mixins.scss11
-rw-r--r--server/controllers/activitypub/client.ts16
-rw-r--r--server/controllers/api/accounts.ts2
-rw-r--r--server/controllers/api/video-channel.ts19
-rw-r--r--server/helpers/custom-validators/accounts.ts2
-rw-r--r--server/helpers/custom-validators/activitypub/actor.ts2
-rw-r--r--server/helpers/custom-validators/video-channels.ts22
-rw-r--r--server/lib/activitypub/url.ts4
-rw-r--r--server/lib/user.ts11
-rw-r--r--server/lib/video-channel.ts5
-rw-r--r--server/middlewares/validators/feeds.ts11
-rw-r--r--server/middlewares/validators/video-channels.ts23
-rw-r--r--server/models/account/account.ts2
-rw-r--r--server/models/activitypub/actor.ts5
-rw-r--r--server/models/video/video-channel.ts56
-rw-r--r--server/tests/api/check-params/user-subscriptions.ts19
-rw-r--r--server/tests/api/check-params/video-channels.ts47
-rw-r--r--server/tests/api/server/follows.ts2
-rw-r--r--server/tests/api/server/handle-down.ts2
-rw-r--r--server/tests/api/users/user-subscriptions.ts37
-rw-r--r--server/tests/api/users/users-multiple-servers.ts9
-rw-r--r--server/tests/api/videos/multiple-servers.ts12
-rw-r--r--server/tests/api/videos/single-server.ts4
-rw-r--r--server/tests/api/videos/video-channels.ts61
-rw-r--r--server/tests/cli/update-host.ts1
-rw-r--r--server/tests/real-world/populate-database.ts1
-rw-r--r--server/tests/utils/videos/video-channels.ts4
-rw-r--r--server/tests/utils/videos/videos.ts4
-rw-r--r--shared/models/videos/channel/video-channel-create.model.ts1
45 files changed, 317 insertions, 180 deletions
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
index bcd3beaf0..114a9e517 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
@@ -1,6 +1,6 @@
1<div *ngIf="account" class="row"> 1<div *ngIf="account" class="row">
2 <a 2 <a
3 *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]" 3 *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.name ]"
4 class="video-channel" i18n-title title="See this video channel" 4 class="video-channel" i18n-title title="See this video channel"
5 > 5 >
6 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 6 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
index ebc671113..44f5626bb 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
@@ -2,10 +2,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute } from '@angular/router' 2import { ActivatedRoute } from '@angular/router'
3import { Account } from '@app/shared/account/account.model' 3import { Account } from '@app/shared/account/account.model'
4import { AccountService } from '@app/shared/account/account.service' 4import { AccountService } from '@app/shared/account/account.service'
5import { VideoChannel } from '../../../../../shared/models/videos'
6import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 5import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
7import { flatMap, map, tap } from 'rxjs/operators' 6import { flatMap, map, tap } from 'rxjs/operators'
8import { Subscription } from 'rxjs' 7import { Subscription } from 'rxjs'
8import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
9 9
10@Component({ 10@Component({
11 selector: 'my-account-video-channels', 11 selector: 'my-account-video-channels',
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
index c0eaa4763..79ac07c93 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
@@ -29,8 +29,13 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
29 super() 29 super()
30 } 30 }
31 31
32 get instanceHost () {
33 return window.location.host
34 }
35
32 ngOnInit () { 36 ngOnInit () {
33 this.buildForm({ 37 this.buildForm({
38 name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME,
34 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME, 39 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME,
35 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION, 40 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION,
36 support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT 41 support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT
@@ -42,6 +47,7 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
42 47
43 const body = this.form.value 48 const body = this.form.value
44 const videoChannelCreate: VideoChannelCreate = { 49 const videoChannelCreate: VideoChannelCreate = {
50 name: body.name,
45 displayName: body['display-name'], 51 displayName: body['display-name'],
46 description: body.description || null, 52 description: body.description || null,
47 support: body.support || null 53 support: body.support || null
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
index f7ca2ec43..81fb11f45 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
@@ -8,6 +8,22 @@
8<div *ngIf="error" class="alert alert-danger">{{ error }}</div> 8<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
9 9
10<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> 10<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
11 <div class="form-group" *ngIf="isCreation() === true">
12 <label i18n for="name">Name</label>
13 <div class="input-group">
14 <input
15 type="text" id="name" i18n-placeholder placeholder="Example: my_channel"
16 formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }"
17 >
18 <div class="input-group-append">
19 <span class="input-group-text">@{{ instanceHost }}</span>
20 </div>
21 </div>
22 <div *ngIf="formErrors['name']" class="form-error">
23 {{ formErrors['name'] }}
24 </div>
25 </div>
26
11 <div class="form-group"> 27 <div class="form-group">
12 <label i18n for="display-name">Display name</label> 28 <label i18n for="display-name">Display name</label>
13 <input 29 <input
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss
index 86c2598b7..833fda450 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss
@@ -10,10 +10,19 @@ my-actor-avatar-info {
10 margin-bottom: 20px; 10 margin-bottom: 20px;
11} 11}
12 12
13.input-group {
14 @include peertube-input-group(340px);
15}
16
13input[type=text] { 17input[type=text] {
14 @include peertube-input-text(340px); 18 @include peertube-input-text(340px);
15 19
16 display: block; 20 display: block;
21
22 &#name {
23 width: auto;
24 flex-grow: 1;
25 }
17} 26}
18 27
19textarea { 28textarea {
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
index d27c3b4ec..548645a76 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
@@ -7,15 +7,14 @@
7 7
8<div class="video-channels"> 8<div class="video-channels">
9 <div *ngFor="let videoChannel of videoChannels" class="video-channel"> 9 <div *ngFor="let videoChannel of videoChannels" class="video-channel">
10 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]"> 10 <a [routerLink]="[ '/video-channels', videoChannel.name ]">
11 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 11 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
12 </a> 12 </a>
13 13
14 <div class="video-channel-info"> 14 <div class="video-channel-info">
15 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" i18n-title title="Go to the channel"> 15 <a [routerLink]="[ '/video-channels', videoChannel.name ]" class="video-channel-names" i18n-title title="Go to the channel">
16 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div> 16 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
17 <!-- Hide the name for now, because it's an UUID not very friendly --> 17 <div class="video-channel-name">{{ videoChannel.name }}</div>
18 <!--<div class="video-channel-name">{{ videoChannel.name }}</div>-->
19 </a> 18 </a>
20 19
21 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 20 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
@@ -24,7 +23,7 @@
24 <div class="video-channel-buttons"> 23 <div class="video-channel-buttons">
25 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button> 24 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
26 25
27 <my-edit-button [routerLink]="[ 'update', videoChannel.uuid ]"></my-edit-button> 26 <my-edit-button [routerLink]="[ 'update', videoChannel.name ]"></my-edit-button>
28 </div> 27 </div>
29 </div> 28 </div>
30</div> 29</div>
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
index f047bb411..f8fd2684e 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
@@ -30,7 +30,9 @@
30 a.video-channel-names { 30 a.video-channel-names {
31 @include disable-default-a-behaviour; 31 @include disable-default-a-behaviour;
32 32
33 width: fit-content;
33 display: flex; 34 display: flex;
35 align-items: baseline;
34 color: #000; 36 color: #000;
35 37
36 .video-channel-display-name { 38 .video-channel-display-name {
@@ -41,6 +43,7 @@
41 .video-channel-name { 43 .video-channel-name {
42 font-size: 14px; 44 font-size: 14px;
43 color: #777272; 45 color: #777272;
46 margin-left: 5px;
44 } 47 }
45 } 48 }
46 } 49 }
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index a52894cac..5a69a82a0 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -7,6 +7,7 @@
7 <div class="actor-info"> 7 <div class="actor-info">
8 <div class="actor-names"> 8 <div class="actor-names">
9 <div class="actor-display-name">{{ videoChannel.displayName }}</div> 9 <div class="actor-display-name">{{ videoChannel.displayName }}</div>
10 <div class="actor-name">{{ videoChannel.nameWithHost }}</div>
10 </div> 11 </div>
11 <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div> 12 <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
12 13
diff --git a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
index 28b063f89..1ce3a0dca 100644
--- a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
@@ -5,11 +5,27 @@ import { BuildFormValidator } from '@app/shared'
5 5
6@Injectable() 6@Injectable()
7export class VideoChannelValidatorsService { 7export class VideoChannelValidatorsService {
8 readonly VIDEO_CHANNEL_NAME: BuildFormValidator
8 readonly VIDEO_CHANNEL_DISPLAY_NAME: BuildFormValidator 9 readonly VIDEO_CHANNEL_DISPLAY_NAME: BuildFormValidator
9 readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator 10 readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator
10 readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator 11 readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator
11 12
12 constructor (private i18n: I18n) { 13 constructor (private i18n: I18n) {
14 this.VIDEO_CHANNEL_NAME = {
15 VALIDATORS: [
16 Validators.required,
17 Validators.minLength(3),
18 Validators.maxLength(20),
19 Validators.pattern(/^[a-z0-9._]+$/)
20 ],
21 MESSAGES: {
22 'required': this.i18n('Name is required.'),
23 'minlength': this.i18n('Name must be at least 3 characters long.'),
24 'maxlength': this.i18n('Name cannot be more than 20 characters long.'),
25 'pattern': this.i18n('Name should be only lowercase alphanumeric characters.')
26 }
27 }
28
13 this.VIDEO_CHANNEL_DISPLAY_NAME = { 29 this.VIDEO_CHANNEL_DISPLAY_NAME = {
14 VALIDATORS: [ 30 VALIDATORS: [
15 Validators.required, 31 Validators.required,
diff --git a/client/src/app/shared/video-channel/video-channel.model.ts b/client/src/app/shared/video-channel/video-channel.model.ts
index b6862b681..309b614ae 100644
--- a/client/src/app/shared/video-channel/video-channel.model.ts
+++ b/client/src/app/shared/video-channel/video-channel.model.ts
@@ -7,6 +7,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
7 description: string 7 description: string
8 support: string 8 support: string
9 isLocal: boolean 9 isLocal: boolean
10 nameWithHost: string
10 ownerAccount?: Account 11 ownerAccount?: Account
11 ownerBy?: string 12 ownerBy?: string
12 ownerAvatarUrl?: string 13 ownerAvatarUrl?: string
@@ -18,6 +19,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
18 this.description = hash.description 19 this.description = hash.description
19 this.support = hash.support 20 this.support = hash.support
20 this.isLocal = hash.isLocal 21 this.isLocal = hash.isLocal
22 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host)
21 23
22 if (hash.ownerAccount) { 24 if (hash.ownerAccount) {
23 this.ownerAccount = hash.ownerAccount 25 this.ownerAccount = hash.ownerAccount
diff --git a/client/src/app/shared/video-channel/video-channel.service.ts b/client/src/app/shared/video-channel/video-channel.service.ts
index 8c000665f..510dc9c3d 100644
--- a/client/src/app/shared/video-channel/video-channel.service.ts
+++ b/client/src/app/shared/video-channel/video-channel.service.ts
@@ -22,8 +22,8 @@ export class VideoChannelService {
22 private restExtractor: RestExtractor 22 private restExtractor: RestExtractor
23 ) {} 23 ) {}
24 24
25 getVideoChannel (videoChannelUUID: string) { 25 getVideoChannel (videoChannelName: string) {
26 return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelUUID) 26 return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName)
27 .pipe( 27 .pipe(
28 map(videoChannelHash => new VideoChannel(videoChannelHash)), 28 map(videoChannelHash => new VideoChannel(videoChannelHash)),
29 tap(videoChannel => this.videoChannelLoaded.next(videoChannel)), 29 tap(videoChannel => this.videoChannelLoaded.next(videoChannel)),
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index e2a62c701..e44f1ee65 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -150,7 +150,7 @@ export class VideoService {
150 params = this.restService.addRestGetParams(params, pagination, sort) 150 params = this.restService.addRestGetParams(params, pagination, sort)
151 151
152 return this.authHttp 152 return this.authHttp
153 .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) 153 .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.name + '/videos', { params })
154 .pipe( 154 .pipe(
155 switchMap(res => this.extractVideos(res)), 155 switchMap(res => this.extractVideos(res)),
156 catchError(err => this.restExtractor.handleError(err)) 156 catchError(err => this.restExtractor.handleError(err))
diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html
index 565b695d9..5fd630b09 100644
--- a/client/src/app/signup/signup.component.html
+++ b/client/src/app/signup/signup.component.html
@@ -23,10 +23,17 @@
23 <form role="form" (ngSubmit)="signup()" [formGroup]="form"> 23 <form role="form" (ngSubmit)="signup()" [formGroup]="form">
24 <div class="form-group"> 24 <div class="form-group">
25 <label for="username" i18n>Username</label> 25 <label for="username" i18n>Username</label>
26 <input 26
27 type="text" id="username" i18n-placeholder placeholder="Username" 27 <div class="input-group">
28 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" 28 <input
29 > 29 type="text" id="username" i18n-placeholder placeholder="Example: neil_amstrong"
30 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
31 >
32 <div class="input-group-append">
33 <span class="input-group-text">@{{ instanceHost }}</span>
34 </div>
35 </div>
36
30 <div *ngIf="formErrors.username" class="form-error"> 37 <div *ngIf="formErrors.username" class="form-error">
31 {{ formErrors.username }} 38 {{ formErrors.username }}
32 </div> 39 </div>
diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/signup/signup.component.scss
index e6d484297..1c992faf5 100644
--- a/client/src/app/signup/signup.component.scss
+++ b/client/src/app/signup/signup.component.scss
@@ -14,9 +14,18 @@
14 margin: 30px 0; 14 margin: 30px 0;
15} 15}
16 16
17.input-group {
18 @include peertube-input-group(340px);
19}
20
17input:not([type=submit]) { 21input:not([type=submit]) {
18 @include peertube-input-text(340px); 22 @include peertube-input-text(340px);
19 display: block; 23 display: block;
24
25 &#username {
26 width: auto;
27 flex-grow: 1;
28 }
20} 29}
21 30
22input[type=submit] { 31input[type=submit] {
diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts
index 076dac454..ed68487ae 100644
--- a/client/src/app/signup/signup.component.ts
+++ b/client/src/app/signup/signup.component.ts
@@ -34,6 +34,10 @@ export class SignupComponent extends FormReactive implements OnInit {
34 return this.serverService.getConfig().user.videoQuota 34 return this.serverService.getConfig().user.videoQuota
35 } 35 }
36 36
37 get instanceHost () {
38 return window.location.host
39 }
40
37 ngOnInit () { 41 ngOnInit () {
38 this.buildForm({ 42 this.buildForm({
39 username: this.userValidatorsService.USER_USERNAME, 43 username: this.userValidatorsService.USER_USERNAME,
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index 8d4a4a5ca..c275258ef 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -37,7 +37,7 @@
37 </div> 37 </div>
38 38
39 <div class="video-info-channel"> 39 <div class="video-info-channel">
40 <a [routerLink]="[ '/video-channels', video.channel.uuid ]" i18n-title title="Go the channel page"> 40 <a [routerLink]="[ '/video-channels', video.channel.name ]" i18n-title title="Go the channel page">
41 {{ video.channel.displayName }} 41 {{ video.channel.displayName }}
42 42
43 <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" /> 43 <img [src]="video.videoChannelAvatarUrl" alt="Video channel avatar" />
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 3d518394a..b0b0f544c 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -36,9 +36,16 @@
36 border-radius: 3px; 36 border-radius: 3px;
37 padding-left: 15px; 37 padding-left: 15px;
38 padding-right: 15px; 38 padding-right: 15px;
39}
39 40
40 &::placeholder { 41@mixin peertube-input-group($width) {
41 color: #585858; 42 width: $width;
43 height: $button-height;
44 padding-top: 0;
45 padding-bottom: 0;
46
47 .input-group-text{
48 font-size: 14px;
42 } 49 }
43} 50}
44 51
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index c90c3f931..54cf44419 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -6,8 +6,8 @@ import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers'
6import { buildVideoAnnounce } from '../../lib/activitypub/send' 6import { buildVideoAnnounce } from '../../lib/activitypub/send'
7import { audiencify, getAudience } from '../../lib/activitypub/audience' 7import { audiencify, getAudience } from '../../lib/activitypub/audience'
8import { createActivityData } from '../../lib/activitypub/send/send-create' 8import { createActivityData } from '../../lib/activitypub/send/send-create'
9import { asyncMiddleware, executeIfActivityPub, localAccountValidator } from '../../middlewares' 9import { asyncMiddleware, executeIfActivityPub, localAccountValidator, localVideoChannelValidator } from '../../middlewares'
10import { videoChannelsGetValidator, videosGetValidator, videosShareValidator } from '../../middlewares/validators' 10import { videosGetValidator, videosShareValidator } from '../../middlewares/validators'
11import { videoCommentGetValidator } from '../../middlewares/validators/video-comments' 11import { videoCommentGetValidator } from '../../middlewares/validators/video-comments'
12import { AccountModel } from '../../models/account/account' 12import { AccountModel } from '../../models/account/account'
13import { ActorModel } from '../../models/activitypub/actor' 13import { ActorModel } from '../../models/activitypub/actor'
@@ -80,16 +80,16 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity
80 executeIfActivityPub(asyncMiddleware(videoCommentController)) 80 executeIfActivityPub(asyncMiddleware(videoCommentController))
81) 81)
82 82
83activityPubClientRouter.get('/video-channels/:id', 83activityPubClientRouter.get('/video-channels/:name',
84 executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), 84 executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)),
85 executeIfActivityPub(asyncMiddleware(videoChannelController)) 85 executeIfActivityPub(asyncMiddleware(videoChannelController))
86) 86)
87activityPubClientRouter.get('/video-channels/:id/followers', 87activityPubClientRouter.get('/video-channels/:name/followers',
88 executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), 88 executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)),
89 executeIfActivityPub(asyncMiddleware(videoChannelFollowersController)) 89 executeIfActivityPub(asyncMiddleware(videoChannelFollowersController))
90) 90)
91activityPubClientRouter.get('/video-channels/:id/following', 91activityPubClientRouter.get('/video-channels/:name/following',
92 executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), 92 executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)),
93 executeIfActivityPub(asyncMiddleware(videoChannelFollowingController)) 93 executeIfActivityPub(asyncMiddleware(videoChannelFollowingController))
94) 94)
95 95
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 308970abc..7b7e5e740 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -78,7 +78,7 @@ async function listAccountVideos (req: express.Request, res: express.Response, n
78 start: req.query.start, 78 start: req.query.start,
79 count: req.query.count, 79 count: req.query.count,
80 sort: req.query.sort, 80 sort: req.query.sort,
81 includeLocalVideos: false, 81 includeLocalVideos: true,
82 categoryOneOf: req.query.categoryOneOf, 82 categoryOneOf: req.query.categoryOneOf,
83 licenceOneOf: req.query.licenceOneOf, 83 licenceOneOf: req.query.licenceOneOf,
84 languageOneOf: req.query.languageOneOf, 84 languageOneOf: req.query.languageOneOf,
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 6ffc09f87..3f51f03f4 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -10,13 +10,12 @@ import {
10 setDefaultPagination, 10 setDefaultPagination,
11 setDefaultSort, 11 setDefaultSort,
12 videoChannelsAddValidator, 12 videoChannelsAddValidator,
13 videoChannelsGetValidator,
14 videoChannelsRemoveValidator, 13 videoChannelsRemoveValidator,
15 videoChannelsSortValidator, 14 videoChannelsSortValidator,
16 videoChannelsUpdateValidator 15 videoChannelsUpdateValidator
17} from '../../middlewares' 16} from '../../middlewares'
18import { VideoChannelModel } from '../../models/video/video-channel' 17import { VideoChannelModel } from '../../models/video/video-channel'
19import { videosSortValidator } from '../../middlewares/validators' 18import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
20import { sendUpdateActor } from '../../lib/activitypub/send' 19import { sendUpdateActor } from '../../lib/activitypub/send'
21import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' 20import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
22import { createVideoChannel } from '../../lib/video-channel' 21import { createVideoChannel } from '../../lib/video-channel'
@@ -50,7 +49,7 @@ videoChannelRouter.post('/',
50 asyncRetryTransactionMiddleware(addVideoChannel) 49 asyncRetryTransactionMiddleware(addVideoChannel)
51) 50)
52 51
53videoChannelRouter.post('/:id/avatar/pick', 52videoChannelRouter.post('/:nameWithHost/avatar/pick',
54 authenticate, 53 authenticate,
55 reqAvatarFile, 54 reqAvatarFile,
56 // Check the rights 55 // Check the rights
@@ -59,25 +58,25 @@ videoChannelRouter.post('/:id/avatar/pick',
59 asyncMiddleware(updateVideoChannelAvatar) 58 asyncMiddleware(updateVideoChannelAvatar)
60) 59)
61 60
62videoChannelRouter.put('/:id', 61videoChannelRouter.put('/:nameWithHost',
63 authenticate, 62 authenticate,
64 asyncMiddleware(videoChannelsUpdateValidator), 63 asyncMiddleware(videoChannelsUpdateValidator),
65 asyncRetryTransactionMiddleware(updateVideoChannel) 64 asyncRetryTransactionMiddleware(updateVideoChannel)
66) 65)
67 66
68videoChannelRouter.delete('/:id', 67videoChannelRouter.delete('/:nameWithHost',
69 authenticate, 68 authenticate,
70 asyncMiddleware(videoChannelsRemoveValidator), 69 asyncMiddleware(videoChannelsRemoveValidator),
71 asyncRetryTransactionMiddleware(removeVideoChannel) 70 asyncRetryTransactionMiddleware(removeVideoChannel)
72) 71)
73 72
74videoChannelRouter.get('/:id', 73videoChannelRouter.get('/:nameWithHost',
75 asyncMiddleware(videoChannelsGetValidator), 74 asyncMiddleware(videoChannelsNameWithHostValidator),
76 asyncMiddleware(getVideoChannel) 75 asyncMiddleware(getVideoChannel)
77) 76)
78 77
79videoChannelRouter.get('/:id/videos', 78videoChannelRouter.get('/:nameWithHost/videos',
80 asyncMiddleware(videoChannelsGetValidator), 79 asyncMiddleware(videoChannelsNameWithHostValidator),
81 paginationValidator, 80 paginationValidator,
82 videosSortValidator, 81 videosSortValidator,
83 setDefaultSort, 82 setDefaultSort,
@@ -215,7 +214,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
215 start: req.query.start, 214 start: req.query.start,
216 count: req.query.count, 215 count: req.query.count,
217 sort: req.query.sort, 216 sort: req.query.sort,
218 includeLocalVideos: false, 217 includeLocalVideos: true,
219 categoryOneOf: req.query.categoryOneOf, 218 categoryOneOf: req.query.categoryOneOf,
220 licenceOneOf: req.query.licenceOneOf, 219 licenceOneOf: req.query.licenceOneOf,
221 languageOneOf: req.query.languageOneOf, 220 languageOneOf: req.query.languageOneOf,
diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts
index 0607d661c..191de1496 100644
--- a/server/helpers/custom-validators/accounts.ts
+++ b/server/helpers/custom-validators/accounts.ts
@@ -42,7 +42,7 @@ function isAccountNameWithHostExist (nameWithDomain: string, res: Response, send
42 42
43 let promise: Bluebird<AccountModel> 43 let promise: Bluebird<AccountModel>
44 if (!host || host === CONFIG.WEBSERVER.HOST) promise = AccountModel.loadLocalByName(accountName) 44 if (!host || host === CONFIG.WEBSERVER.HOST) promise = AccountModel.loadLocalByName(accountName)
45 else promise = AccountModel.loadLocalByNameAndHost(accountName, host) 45 else promise = AccountModel.loadByNameAndHost(accountName, host)
46 46
47 return isAccountExist(promise, res, sendNotFound) 47 return isAccountExist(promise, res, sendNotFound)
48} 48}
diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts
index ae5014f8f..c3a62c12d 100644
--- a/server/helpers/custom-validators/activitypub/actor.ts
+++ b/server/helpers/custom-validators/activitypub/actor.ts
@@ -27,7 +27,7 @@ function isActorPublicKeyValid (publicKey: string) {
27 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY) 27 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY)
28} 28}
29 29
30const actorNameRegExp = new RegExp('[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_]+') 30const actorNameRegExp = new RegExp('^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_]+$')
31function isActorPreferredUsernameValid (preferredUsername: string) { 31function isActorPreferredUsernameValid (preferredUsername: string) {
32 return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp) 32 return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
33} 33}
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index 32faf36f7..f13519c1d 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -2,10 +2,9 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import 'multer' 3import 'multer'
4import * as validator from 'validator' 4import * as validator from 'validator'
5import { CONSTRAINTS_FIELDS } from '../../initializers' 5import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
6import { VideoChannelModel } from '../../models/video/video-channel' 6import { VideoChannelModel } from '../../models/video/video-channel'
7import { exists } from './misc' 7import { exists } from './misc'
8import { Response } from 'express'
9 8
10const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS 9const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
11 10
@@ -21,13 +20,13 @@ function isVideoChannelSupportValid (value: string) {
21 return value === null || (exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.SUPPORT)) 20 return value === null || (exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.SUPPORT))
22} 21}
23 22
24async function isLocalVideoChannelNameExist (name: string, res: Response) { 23async function isLocalVideoChannelNameExist (name: string, res: express.Response) {
25 const videoChannel = await VideoChannelModel.loadLocalByName(name) 24 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
26 25
27 return processVideoChannelExist(videoChannel, res) 26 return processVideoChannelExist(videoChannel, res)
28} 27}
29 28
30async function isVideoChannelExist (id: string, res: express.Response) { 29async function isVideoChannelIdExist (id: string, res: express.Response) {
31 let videoChannel: VideoChannelModel 30 let videoChannel: VideoChannelModel
32 if (validator.isInt(id)) { 31 if (validator.isInt(id)) {
33 videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) 32 videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
@@ -38,14 +37,25 @@ async function isVideoChannelExist (id: string, res: express.Response) {
38 return processVideoChannelExist(videoChannel, res) 37 return processVideoChannelExist(videoChannel, res)
39} 38}
40 39
40async function isVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
41 const [ name, host ] = nameWithDomain.split('@')
42 let videoChannel: VideoChannelModel
43
44 if (!host || host === CONFIG.WEBSERVER.HOST) videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
45 else videoChannel = await VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host)
46
47 return processVideoChannelExist(videoChannel, res)
48}
49
41// --------------------------------------------------------------------------- 50// ---------------------------------------------------------------------------
42 51
43export { 52export {
53 isVideoChannelNameWithHostExist,
44 isLocalVideoChannelNameExist, 54 isLocalVideoChannelNameExist,
45 isVideoChannelDescriptionValid, 55 isVideoChannelDescriptionValid,
46 isVideoChannelNameValid, 56 isVideoChannelNameValid,
47 isVideoChannelSupportValid, 57 isVideoChannelSupportValid,
48 isVideoChannelExist 58 isVideoChannelIdExist
49} 59}
50 60
51function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { 61function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index ba3bf688d..262463310 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -13,8 +13,8 @@ function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCo
13 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id 13 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
14} 14}
15 15
16function getVideoChannelActivityPubUrl (videoChannelUUID: string) { 16function getVideoChannelActivityPubUrl (videoChannelName: string) {
17 return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelUUID 17 return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelName
18} 18}
19 19
20function getAccountActivityPubUrl (accountName: string) { 20function getAccountActivityPubUrl (accountName: string) {
diff --git a/server/lib/user.ts b/server/lib/user.ts
index e7a45f5aa..db29469eb 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -1,4 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as uuidv4 from 'uuid/v4'
2import { ActivityPubActorType } from '../../shared/models/activitypub' 3import { ActivityPubActorType } from '../../shared/models/activitypub'
3import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../initializers' 4import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../initializers'
4import { AccountModel } from '../models/account/account' 5import { AccountModel } from '../models/account/account'
@@ -7,6 +8,7 @@ import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from
7import { createVideoChannel } from './video-channel' 8import { createVideoChannel } from './video-channel'
8import { VideoChannelModel } from '../models/video/video-channel' 9import { VideoChannelModel } from '../models/video/video-channel'
9import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' 10import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
11import { ActorModel } from '../models/activitypub/actor'
10 12
11async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) { 13async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) {
12 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { 14 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@@ -19,8 +21,15 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
19 const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t) 21 const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t)
20 userCreated.Account = accountCreated 22 userCreated.Account = accountCreated
21 23
22 const videoChannelDisplayName = `Default ${userCreated.username} channel` 24 let channelName = userCreated.username + '_channel'
25
26 // Conflict, generate uuid instead
27 const actor = await ActorModel.loadLocalByName(channelName)
28 if (actor) channelName = uuidv4()
29
30 const videoChannelDisplayName = `Main ${userCreated.username} channel`
23 const videoChannelInfo = { 31 const videoChannelInfo = {
32 name: channelName,
24 displayName: videoChannelDisplayName 33 displayName: videoChannelDisplayName
25 } 34 }
26 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t) 35 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 600316cda..0fe95ca09 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -7,9 +7,8 @@ import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub
7 7
8async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { 8async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
9 const uuid = uuidv4() 9 const uuid = uuidv4()
10 const url = getVideoChannelActivityPubUrl(uuid) 10 const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
11 // We use the name as uuid 11 const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
12 const actorInstance = buildActorInstance('Group', url, uuid, uuid)
13 12
14 const actorInstanceCreated = await actorInstance.save({ transaction: t }) 13 const actorInstanceCreated = await actorInstance.save({ transaction: t })
15 14
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index 3c8532bd9..c1054ad9b 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param, query } from 'express-validator/check' 2import { param, query } from 'express-validator/check'
3import { isAccountIdExist, isAccountNameValid } from '../../helpers/custom-validators/accounts' 3import { isAccountIdExist, isAccountNameValid, isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
4import { join } from 'path'
5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
6import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
7import { areValidationErrors } from './utils' 6import { areValidationErrors } from './utils'
8import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' 7import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
9import { isVideoChannelExist } from '../../helpers/custom-validators/video-channels' 8import { isVideoChannelIdExist, isVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels'
10import { isVideoExist } from '../../helpers/custom-validators/videos' 9import { isVideoExist } from '../../helpers/custom-validators/videos'
10import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
11 11
12const videoFeedsValidator = [ 12const videoFeedsValidator = [
13 param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'), 13 param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'),
@@ -15,6 +15,7 @@ const videoFeedsValidator = [
15 query('accountId').optional().custom(isIdOrUUIDValid), 15 query('accountId').optional().custom(isIdOrUUIDValid),
16 query('accountName').optional().custom(isAccountNameValid), 16 query('accountName').optional().custom(isAccountNameValid),
17 query('videoChannelId').optional().custom(isIdOrUUIDValid), 17 query('videoChannelId').optional().custom(isIdOrUUIDValid),
18 query('videoChannelName').optional().custom(isActorPreferredUsernameValid),
18 19
19 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 20 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
20 logger.debug('Checking feeds parameters', { parameters: req.query }) 21 logger.debug('Checking feeds parameters', { parameters: req.query })
@@ -22,7 +23,9 @@ const videoFeedsValidator = [
22 if (areValidationErrors(req, res)) return 23 if (areValidationErrors(req, res)) return
23 24
24 if (req.query.accountId && !await isAccountIdExist(req.query.accountId, res)) return 25 if (req.query.accountId && !await isAccountIdExist(req.query.accountId, res)) return
25 if (req.query.videoChannelId && !await isVideoChannelExist(req.query.videoChannelId, res)) return 26 if (req.query.videoChannelName && !await isVideoChannelIdExist(req.query.videoChannelName, res)) return
27 if (req.query.accountName && !await isAccountNameWithHostExist(req.query.accountName, res)) return
28 if (req.query.videoChannelName && !await isVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return
26 29
27 return next() 30 return next()
28 } 31 }
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts
index d354c7e05..79587b028 100644
--- a/server/middlewares/validators/video-channels.ts
+++ b/server/middlewares/validators/video-channels.ts
@@ -2,18 +2,18 @@ import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param } from 'express-validator/check'
3import { UserRight } from '../../../shared' 3import { UserRight } from '../../../shared'
4import { isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' 4import { isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
6import { 5import {
7 isLocalVideoChannelNameExist, 6 isLocalVideoChannelNameExist,
8 isVideoChannelDescriptionValid, 7 isVideoChannelDescriptionValid,
9 isVideoChannelExist,
10 isVideoChannelNameValid, 8 isVideoChannelNameValid,
9 isVideoChannelNameWithHostExist,
11 isVideoChannelSupportValid 10 isVideoChannelSupportValid
12} from '../../helpers/custom-validators/video-channels' 11} from '../../helpers/custom-validators/video-channels'
13import { logger } from '../../helpers/logger' 12import { logger } from '../../helpers/logger'
14import { UserModel } from '../../models/account/user' 13import { UserModel } from '../../models/account/user'
15import { VideoChannelModel } from '../../models/video/video-channel' 14import { VideoChannelModel } from '../../models/video/video-channel'
16import { areValidationErrors } from './utils' 15import { areValidationErrors } from './utils'
16import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
17 17
18const listVideoAccountChannelsValidator = [ 18const listVideoAccountChannelsValidator = [
19 param('accountName').exists().withMessage('Should have a valid account name'), 19 param('accountName').exists().withMessage('Should have a valid account name'),
@@ -29,6 +29,7 @@ const listVideoAccountChannelsValidator = [
29] 29]
30 30
31const videoChannelsAddValidator = [ 31const videoChannelsAddValidator = [
32 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
32 body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 33 body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
33 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 34 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
34 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 35 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@@ -43,7 +44,7 @@ const videoChannelsAddValidator = [
43] 44]
44 45
45const videoChannelsUpdateValidator = [ 46const videoChannelsUpdateValidator = [
46 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 47 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
47 body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 48 body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
48 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 49 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
49 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 50 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@@ -52,7 +53,7 @@ const videoChannelsUpdateValidator = [
52 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 53 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
53 54
54 if (areValidationErrors(req, res)) return 55 if (areValidationErrors(req, res)) return
55 if (!await isVideoChannelExist(req.params.id, res)) return 56 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
56 57
57 // We need to make additional checks 58 // We need to make additional checks
58 if (res.locals.videoChannel.Actor.isOwned() === false) { 59 if (res.locals.videoChannel.Actor.isOwned() === false) {
@@ -72,13 +73,13 @@ const videoChannelsUpdateValidator = [
72] 73]
73 74
74const videoChannelsRemoveValidator = [ 75const videoChannelsRemoveValidator = [
75 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 76 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
76 77
77 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 78 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
78 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 79 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
79 80
80 if (areValidationErrors(req, res)) return 81 if (areValidationErrors(req, res)) return
81 if (!await isVideoChannelExist(req.params.id, res)) return 82 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
82 83
83 if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return 84 if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
84 if (!await checkVideoChannelIsNotTheLastOne(res)) return 85 if (!await checkVideoChannelIsNotTheLastOne(res)) return
@@ -87,15 +88,15 @@ const videoChannelsRemoveValidator = [
87 } 88 }
88] 89]
89 90
90const videoChannelsGetValidator = [ 91const videoChannelsNameWithHostValidator = [
91 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 92 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
92 93
93 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 94 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
94 logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) 95 logger.debug('Checking videoChannelsNameWithHostValidator parameters', { parameters: req.params })
95 96
96 if (areValidationErrors(req, res)) return 97 if (areValidationErrors(req, res)) return
97 98
98 if (!await isVideoChannelExist(req.params.id, res)) return 99 if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return
99 100
100 return next() 101 return next()
101 } 102 }
@@ -121,7 +122,7 @@ export {
121 videoChannelsAddValidator, 122 videoChannelsAddValidator,
122 videoChannelsUpdateValidator, 123 videoChannelsUpdateValidator,
123 videoChannelsRemoveValidator, 124 videoChannelsRemoveValidator,
124 videoChannelsGetValidator, 125 videoChannelsNameWithHostValidator,
125 localVideoChannelValidator 126 localVideoChannelValidator
126} 127}
127 128
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 66f5dcf2e..07539a04e 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -194,7 +194,7 @@ export class AccountModel extends Model<AccountModel> {
194 return AccountModel.findOne(query) 194 return AccountModel.findOne(query)
195 } 195 }
196 196
197 static loadLocalByNameAndHost (name: string, host: string) { 197 static loadByNameAndHost (name: string, host: string) {
198 const query = { 198 const query = {
199 include: [ 199 include: [
200 { 200 {
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 35d7c35e8..2abf40713 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -260,12 +260,13 @@ export class ActorModel extends Model<ActorModel> {
260 return ActorModel.scope(ScopeNames.FULL).findAll(query) 260 return ActorModel.scope(ScopeNames.FULL).findAll(query)
261 } 261 }
262 262
263 static loadLocalByName (preferredUsername: string) { 263 static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) {
264 const query = { 264 const query = {
265 where: { 265 where: {
266 preferredUsername, 266 preferredUsername,
267 serverId: null 267 serverId: null
268 } 268 },
269 transaction
269 } 270 }
270 271
271 return ActorModel.scope(ScopeNames.FULL).findOne(query) 272 return ActorModel.scope(ScopeNames.FULL).findOne(query)
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 0273fab13..9f80e0b8d 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -29,6 +29,7 @@ import { getSort, throwIfNotValid } from '../utils'
29import { VideoModel } from './video' 29import { VideoModel } from './video'
30import { CONSTRAINTS_FIELDS } from '../../initializers' 30import { CONSTRAINTS_FIELDS } from '../../initializers'
31import { AvatarModel } from '../avatar/avatar' 31import { AvatarModel } from '../avatar/avatar'
32import { ServerModel } from '../server/server'
32 33
33enum ScopeNames { 34enum ScopeNames {
34 WITH_ACCOUNT = 'WITH_ACCOUNT', 35 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -206,7 +207,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
206 } 207 }
207 208
208 static loadByIdAndAccount (id: number, accountId: number) { 209 static loadByIdAndAccount (id: number, accountId: number) {
209 const options = { 210 const query = {
210 where: { 211 where: {
211 id, 212 id,
212 accountId 213 accountId
@@ -215,7 +216,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
215 216
216 return VideoChannelModel 217 return VideoChannelModel
217 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 218 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
218 .findOne(options) 219 .findOne(query)
219 } 220 }
220 221
221 static loadAndPopulateAccount (id: number) { 222 static loadAndPopulateAccount (id: number) {
@@ -225,7 +226,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
225 } 226 }
226 227
227 static loadByUUIDAndPopulateAccount (uuid: string) { 228 static loadByUUIDAndPopulateAccount (uuid: string) {
228 const options = { 229 const query = {
229 include: [ 230 include: [
230 { 231 {
231 model: ActorModel, 232 model: ActorModel,
@@ -239,36 +240,63 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
239 240
240 return VideoChannelModel 241 return VideoChannelModel
241 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 242 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
242 .findOne(options) 243 .findOne(query)
243 } 244 }
244 245
245 static loadAndPopulateAccountAndVideos (id: number) { 246 static loadLocalByNameAndPopulateAccount (name: string) {
246 const options = { 247 const query = {
247 include: [ 248 include: [
248 VideoModel 249 {
250 model: ActorModel,
251 required: true,
252 where: {
253 preferredUsername: name,
254 serverId: null
255 }
256 }
249 ] 257 ]
250 } 258 }
251 259
252 return VideoChannelModel 260 return VideoChannelModel
253 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) 261 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
254 .findById(id, options) 262 .findOne(query)
255 } 263 }
256 264
257 static loadLocalByName (name: string) { 265 static loadByNameAndHostAndPopulateAccount (name: string, host: string) {
258 const query = { 266 const query = {
259 include: [ 267 include: [
260 { 268 {
261 model: ActorModel, 269 model: ActorModel,
262 required: true, 270 required: true,
263 where: { 271 where: {
264 preferredUsername: name, 272 preferredUsername: name
265 serverId: null 273 },
266 } 274 include: [
275 {
276 model: ServerModel,
277 required: true,
278 where: { host }
279 }
280 ]
267 } 281 }
268 ] 282 ]
269 } 283 }
270 284
271 return VideoChannelModel.findOne(query) 285 return VideoChannelModel
286 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
287 .findOne(query)
288 }
289
290 static loadAndPopulateAccountAndVideos (id: number) {
291 const options = {
292 include: [
293 VideoModel
294 ]
295 }
296
297 return VideoChannelModel
298 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ])
299 .findById(id, options)
272 } 300 }
273 301
274 toFormattedJSON (): VideoChannel { 302 toFormattedJSON (): VideoChannel {
diff --git a/server/tests/api/check-params/user-subscriptions.ts b/server/tests/api/check-params/user-subscriptions.ts
index 9f7d15b27..628a74476 100644
--- a/server/tests/api/check-params/user-subscriptions.ts
+++ b/server/tests/api/check-params/user-subscriptions.ts
@@ -5,7 +5,6 @@ import 'mocha'
5import { 5import {
6 createUser, 6 createUser,
7 flushTests, 7 flushTests,
8 getMyUserInformation,
9 killallServers, 8 killallServers,
10 makeDeleteRequest, 9 makeDeleteRequest,
11 makeGetRequest, 10 makeGetRequest,
@@ -21,7 +20,6 @@ describe('Test user subscriptions API validators', function () {
21 const path = '/api/v1/users/me/subscriptions' 20 const path = '/api/v1/users/me/subscriptions'
22 let server: ServerInfo 21 let server: ServerInfo
23 let userAccessToken = '' 22 let userAccessToken = ''
24 let userChannelUUID: string
25 23
26 // --------------------------------------------------------------- 24 // ---------------------------------------------------------------
27 25
@@ -40,11 +38,6 @@ describe('Test user subscriptions API validators', function () {
40 } 38 }
41 await createUser(server.url, server.accessToken, user.username, user.password) 39 await createUser(server.url, server.accessToken, user.username, user.password)
42 userAccessToken = await userLogin(server, user) 40 userAccessToken = await userLogin(server, user)
43
44 {
45 const res = await getMyUserInformation(server.url, server.accessToken)
46 userChannelUUID = res.body.videoChannels[ 0 ].uuid
47 }
48 }) 41 })
49 42
50 describe('When listing my subscriptions', function () { 43 describe('When listing my subscriptions', function () {
@@ -69,7 +62,7 @@ describe('Test user subscriptions API validators', function () {
69 }) 62 })
70 63
71 it('Should success with the correct parameters', async function () { 64 it('Should success with the correct parameters', async function () {
72 await await makeGetRequest({ 65 await makeGetRequest({
73 url: server.url, 66 url: server.url,
74 path, 67 path,
75 token: userAccessToken, 68 token: userAccessToken,
@@ -102,7 +95,7 @@ describe('Test user subscriptions API validators', function () {
102 }) 95 })
103 96
104 it('Should success with the correct parameters', async function () { 97 it('Should success with the correct parameters', async function () {
105 await await makeGetRequest({ 98 await makeGetRequest({
106 url: server.url, 99 url: server.url,
107 path, 100 path,
108 token: userAccessToken, 101 token: userAccessToken,
@@ -116,7 +109,7 @@ describe('Test user subscriptions API validators', function () {
116 await makePostBodyRequest({ 109 await makePostBodyRequest({
117 url: server.url, 110 url: server.url,
118 path, 111 path,
119 fields: { uri: userChannelUUID + '@localhost:9001' }, 112 fields: { uri: 'user1_channel@localhost:9001' },
120 statusCodeExpected: 401 113 statusCodeExpected: 401
121 }) 114 })
122 }) 115 })
@@ -152,7 +145,7 @@ describe('Test user subscriptions API validators', function () {
152 url: server.url, 145 url: server.url,
153 path, 146 path,
154 token: server.accessToken, 147 token: server.accessToken,
155 fields: { uri: userChannelUUID + '@localhost:9001' }, 148 fields: { uri: 'user1_channel@localhost:9001' },
156 statusCodeExpected: 204 149 statusCodeExpected: 204
157 }) 150 })
158 }) 151 })
@@ -162,7 +155,7 @@ describe('Test user subscriptions API validators', function () {
162 it('Should fail with a non authenticated user', async function () { 155 it('Should fail with a non authenticated user', async function () {
163 await makeDeleteRequest({ 156 await makeDeleteRequest({
164 url: server.url, 157 url: server.url,
165 path: path + '/' + userChannelUUID + '@localhost:9001', 158 path: path + '/user1_channel@localhost:9001',
166 statusCodeExpected: 401 159 statusCodeExpected: 401
167 }) 160 })
168 }) 161 })
@@ -202,7 +195,7 @@ describe('Test user subscriptions API validators', function () {
202 it('Should success with the correct parameters', async function () { 195 it('Should success with the correct parameters', async function () {
203 await makeDeleteRequest({ 196 await makeDeleteRequest({
204 url: server.url, 197 url: server.url,
205 path: path + '/' + userChannelUUID + '@localhost:9001', 198 path: path + '/user1_channel@localhost:9001',
206 token: server.accessToken, 199 token: server.accessToken,
207 statusCodeExpected: 204 200 statusCodeExpected: 204
208 }) 201 })
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 0980de73b..bcf4b7473 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -31,7 +31,6 @@ describe('Test video channels API validator', function () {
31 const videoChannelPath = '/api/v1/video-channels' 31 const videoChannelPath = '/api/v1/video-channels'
32 let server: ServerInfo 32 let server: ServerInfo
33 let accessTokenUser: string 33 let accessTokenUser: string
34 let videoChannelUUID: string
35 34
36 // --------------------------------------------------------------- 35 // ---------------------------------------------------------------
37 36
@@ -53,12 +52,6 @@ describe('Test video channels API validator', function () {
53 await createUser(server.url, server.accessToken, user.username, user.password) 52 await createUser(server.url, server.accessToken, user.username, user.password)
54 accessTokenUser = await userLogin(server, user) 53 accessTokenUser = await userLogin(server, user)
55 } 54 }
56
57 {
58 const res = await getMyUserInformation(server.url, server.accessToken)
59 const user: User = res.body
60 videoChannelUUID = user.videoChannels[0].uuid
61 }
62 }) 55 })
63 56
64 describe('When listing a video channels', function () { 57 describe('When listing a video channels', function () {
@@ -83,6 +76,7 @@ describe('Test video channels API validator', function () {
83 76
84 describe('When adding a video channel', function () { 77 describe('When adding a video channel', function () {
85 const baseCorrectParams = { 78 const baseCorrectParams = {
79 name: 'super_channel',
86 displayName: 'hello', 80 displayName: 'hello',
87 description: 'super description', 81 description: 'super description',
88 support: 'super support text' 82 support: 'super support text'
@@ -104,6 +98,16 @@ describe('Test video channels API validator', function () {
104 }) 98 })
105 99
106 it('Should fail without a name', async function () { 100 it('Should fail without a name', async function () {
101 const fields = omit(baseCorrectParams, 'name')
102 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
103 })
104
105 it('Should fail with a bad name', async function () {
106 const fields = immutableAssign(baseCorrectParams, { name: 'super name' })
107 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
108 })
109
110 it('Should fail without a name', async function () {
107 const fields = omit(baseCorrectParams, 'displayName') 111 const fields = omit(baseCorrectParams, 'displayName')
108 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 112 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
109 }) 113 })
@@ -142,7 +146,7 @@ describe('Test video channels API validator', function () {
142 let path: string 146 let path: string
143 147
144 before(async function () { 148 before(async function () {
145 path = videoChannelPath + '/' + videoChannelUUID 149 path = videoChannelPath + '/super_channel'
146 }) 150 })
147 151
148 it('Should fail with a non authenticated user', async function () { 152 it('Should fail with a non authenticated user', async function () {
@@ -195,7 +199,7 @@ describe('Test video channels API validator', function () {
195 let path: string 199 let path: string
196 200
197 before(async function () { 201 before(async function () {
198 path = videoChannelPath + '/' + videoChannelUUID 202 path = videoChannelPath + '/super_channel'
199 }) 203 })
200 204
201 it('Should fail with an incorrect input file', async function () { 205 it('Should fail with an incorrect input file', async function () {
@@ -255,18 +259,10 @@ describe('Test video channels API validator', function () {
255 expect(res.body.data).to.be.an('array') 259 expect(res.body.data).to.be.an('array')
256 }) 260 })
257 261
258 it('Should fail without a correct uuid', async function () {
259 await makeGetRequest({
260 url: server.url,
261 path: videoChannelPath + '/coucou',
262 statusCodeExpected: 400
263 })
264 })
265
266 it('Should return 404 with an incorrect video channel', async function () { 262 it('Should return 404 with an incorrect video channel', async function () {
267 await makeGetRequest({ 263 await makeGetRequest({
268 url: server.url, 264 url: server.url,
269 path: videoChannelPath + '/4da6fde3-88f7-4d16-b119-108df5630b06', 265 path: videoChannelPath + '/super_channel2',
270 statusCodeExpected: 404 266 statusCodeExpected: 404
271 }) 267 })
272 }) 268 })
@@ -274,7 +270,7 @@ describe('Test video channels API validator', function () {
274 it('Should succeed with the correct parameters', async function () { 270 it('Should succeed with the correct parameters', async function () {
275 await makeGetRequest({ 271 await makeGetRequest({
276 url: server.url, 272 url: server.url,
277 path: videoChannelPath + '/' + videoChannelUUID, 273 path: videoChannelPath + '/super_channel',
278 statusCodeExpected: 200 274 statusCodeExpected: 200
279 }) 275 })
280 }) 276 })
@@ -282,26 +278,23 @@ describe('Test video channels API validator', function () {
282 278
283 describe('When deleting a video channel', function () { 279 describe('When deleting a video channel', function () {
284 it('Should fail with a non authenticated user', async function () { 280 it('Should fail with a non authenticated user', async function () {
285 await deleteVideoChannel(server.url, 'coucou', videoChannelUUID, 401) 281 await deleteVideoChannel(server.url, 'coucou', 'super_channel', 401)
286 }) 282 })
287 283
288 it('Should fail with another authenticated user', async function () { 284 it('Should fail with another authenticated user', async function () {
289 await deleteVideoChannel(server.url, accessTokenUser, videoChannelUUID, 403) 285 await deleteVideoChannel(server.url, accessTokenUser, 'super_channel', 403)
290 }) 286 })
291 287
292 it('Should fail with an unknown video channel id', async function () { 288 it('Should fail with an unknown video channel id', async function () {
293 await deleteVideoChannel(server.url, server.accessToken,454554, 404) 289 await deleteVideoChannel(server.url, server.accessToken,'super_channel2', 404)
294 }) 290 })
295 291
296 it('Should succeed with the correct parameters', async function () { 292 it('Should succeed with the correct parameters', async function () {
297 await deleteVideoChannel(server.url, server.accessToken, videoChannelUUID) 293 await deleteVideoChannel(server.url, server.accessToken, 'super_channel')
298 }) 294 })
299 295
300 it('Should fail to delete the last user video channel', async function () { 296 it('Should fail to delete the last user video channel', async function () {
301 const res = await getVideoChannelsList(server.url, 0, 1) 297 await deleteVideoChannel(server.url, server.accessToken, 'root_channel', 409)
302 const lastVideoChannelUUID = res.body.data[0].uuid
303
304 await deleteVideoChannel(server.url, server.accessToken, lastVideoChannelUUID, 409)
305 }) 298 })
306 }) 299 })
307 300
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 1cad8998c..243fcd4e7 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -311,7 +311,7 @@ describe('Test follows', function () {
311 likes: 1, 311 likes: 1,
312 dislikes: 1, 312 dislikes: 1,
313 channel: { 313 channel: {
314 name: 'Default root channel', 314 name: 'Main root channel',
315 description: '', 315 description: '',
316 isLocal 316 isLocal
317 }, 317 },
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index 18a0d9ce3..df35b36eb 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -71,7 +71,7 @@ describe('Test handle downs', function () {
71 privacy: VideoPrivacy.PUBLIC, 71 privacy: VideoPrivacy.PUBLIC,
72 commentsEnabled: true, 72 commentsEnabled: true,
73 channel: { 73 channel: {
74 name: 'Default root channel', 74 name: 'Main root channel',
75 description: '', 75 description: '',
76 isLocal: false 76 isLocal: false
77 }, 77 },
diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts
index 2ba6cdfaf..ba59a9a60 100644
--- a/server/tests/api/users/user-subscriptions.ts
+++ b/server/tests/api/users/user-subscriptions.ts
@@ -3,7 +3,7 @@
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { createUser, doubleFollow, flushAndRunMultipleServers, follow, getVideosList, unfollow, userLogin } from '../../utils' 5import { createUser, doubleFollow, flushAndRunMultipleServers, follow, getVideosList, unfollow, userLogin } from '../../utils'
6import { getMyUserInformation, killallServers, ServerInfo, uploadVideo } from '../../utils/index' 6import { killallServers, ServerInfo, uploadVideo } from '../../utils/index'
7import { setAccessTokensToServers } from '../../utils/users/login' 7import { setAccessTokensToServers } from '../../utils/users/login'
8import { Video, VideoChannel } from '../../../../shared/models/videos' 8import { Video, VideoChannel } from '../../../../shared/models/videos'
9import { waitJobs } from '../../utils/server/jobs' 9import { waitJobs } from '../../utils/server/jobs'
@@ -18,8 +18,7 @@ const expect = chai.expect
18 18
19describe('Test users subscriptions', function () { 19describe('Test users subscriptions', function () {
20 let servers: ServerInfo[] = [] 20 let servers: ServerInfo[] = []
21 const users: { accessToken: string, videoChannelName: string }[] = [] 21 const users: { accessToken: string }[] = []
22 let rootChannelNameServer1: string
23 22
24 before(async function () { 23 before(async function () {
25 this.timeout(120000) 24 this.timeout(120000)
@@ -32,19 +31,13 @@ describe('Test users subscriptions', function () {
32 // Server 1 and server 2 follow each other 31 // Server 1 and server 2 follow each other
33 await doubleFollow(servers[0], servers[1]) 32 await doubleFollow(servers[0], servers[1])
34 33
35 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
36 rootChannelNameServer1 = res.body.videoChannels[0].name
37
38 { 34 {
39 for (const server of servers) { 35 for (const server of servers) {
40 const user = { username: 'user' + server.serverNumber, password: 'password' } 36 const user = { username: 'user' + server.serverNumber, password: 'password' }
41 await createUser(server.url, server.accessToken, user.username, user.password) 37 await createUser(server.url, server.accessToken, user.username, user.password)
42 38
43 const accessToken = await userLogin(server, user) 39 const accessToken = await userLogin(server, user)
44 const res = await getMyUserInformation(server.url, accessToken) 40 users.push({ accessToken })
45 const videoChannels: VideoChannel[] = res.body.videoChannels
46
47 users.push({ accessToken, videoChannelName: videoChannels[0].name })
48 41
49 const videoName1 = 'video 1-' + server.serverNumber 42 const videoName1 = 'video 1-' + server.serverNumber
50 await uploadVideo(server.url, accessToken, { name: videoName1 }) 43 await uploadVideo(server.url, accessToken, { name: videoName1 })
@@ -64,10 +57,10 @@ describe('Test users subscriptions', function () {
64 }) 57 })
65 58
66 it('User of server 1 should follow user of server 3 and root of server 1', async function () { 59 it('User of server 1 should follow user of server 3 and root of server 1', async function () {
67 this.timeout(30000) 60 this.timeout(60000)
68 61
69 await addUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') 62 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003')
70 await addUserSubscription(servers[0].url, users[0].accessToken, rootChannelNameServer1 + '@localhost:9001') 63 await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001')
71 64
72 await waitJobs(servers) 65 await waitJobs(servers)
73 66
@@ -103,8 +96,8 @@ describe('Test users subscriptions', function () {
103 expect(subscriptions).to.be.an('array') 96 expect(subscriptions).to.be.an('array')
104 expect(subscriptions).to.have.lengthOf(2) 97 expect(subscriptions).to.have.lengthOf(2)
105 98
106 expect(subscriptions[0].name).to.equal(users[2].videoChannelName) 99 expect(subscriptions[0].name).to.equal('user3_channel')
107 expect(subscriptions[1].name).to.equal(rootChannelNameServer1) 100 expect(subscriptions[1].name).to.equal('root_channel')
108 } 101 }
109 }) 102 })
110 103
@@ -131,7 +124,7 @@ describe('Test users subscriptions', function () {
131 }) 124 })
132 125
133 it('Should upload a video by root on server 1 and see it in the subscription videos', async function () { 126 it('Should upload a video by root on server 1 and see it in the subscription videos', async function () {
134 this.timeout(30000) 127 this.timeout(60000)
135 128
136 const videoName = 'video server 1 added after follow' 129 const videoName = 'video server 1 added after follow'
137 await uploadVideo(servers[0].url, servers[0].accessToken, { name: videoName }) 130 await uploadVideo(servers[0].url, servers[0].accessToken, { name: videoName })
@@ -172,7 +165,7 @@ describe('Test users subscriptions', function () {
172 }) 165 })
173 166
174 it('Should have server 1 follow server 3 and display server 3 videos', async function () { 167 it('Should have server 1 follow server 3 and display server 3 videos', async function () {
175 this.timeout(30000) 168 this.timeout(60000)
176 169
177 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 170 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken)
178 171
@@ -190,7 +183,7 @@ describe('Test users subscriptions', function () {
190 }) 183 })
191 184
192 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () { 185 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () {
193 this.timeout(30000) 186 this.timeout(60000)
194 187
195 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 188 await unfollow(servers[0].url, servers[0].accessToken, servers[2])
196 189
@@ -230,7 +223,7 @@ describe('Test users subscriptions', function () {
230 }) 223 })
231 224
232 it('Should remove user of server 3 subscription', async function () { 225 it('Should remove user of server 3 subscription', async function () {
233 await removeUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') 226 await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003')
234 227
235 await waitJobs(servers) 228 await waitJobs(servers)
236 }) 229 })
@@ -249,7 +242,7 @@ describe('Test users subscriptions', function () {
249 }) 242 })
250 243
251 it('Should remove the root subscription and not display the videos anymore', async function () { 244 it('Should remove the root subscription and not display the videos anymore', async function () {
252 await removeUserSubscription(servers[0].url, users[0].accessToken, rootChannelNameServer1 + '@localhost:9001') 245 await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001')
253 246
254 await waitJobs(servers) 247 await waitJobs(servers)
255 248
@@ -275,9 +268,9 @@ describe('Test users subscriptions', function () {
275 }) 268 })
276 269
277 it('Should follow user of server 3 again', async function () { 270 it('Should follow user of server 3 again', async function () {
278 this.timeout(30000) 271 this.timeout(60000)
279 272
280 await addUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') 273 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003')
281 274
282 await waitJobs(servers) 275 await waitJobs(servers)
283 276
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index 81489021b..575e04546 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -12,10 +12,9 @@ import {
12 getVideoChannelsList, 12 getVideoChannelsList,
13 removeUser, 13 removeUser,
14 updateMyUser, 14 updateMyUser,
15 userLogin, 15 userLogin
16 wait
17} from '../../utils' 16} from '../../utils'
18import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index' 17import { getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index'
19import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts' 18import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts'
20import { setAccessTokensToServers } from '../../utils/users/login' 19import { setAccessTokensToServers } from '../../utils/users/login'
21import { User } from '../../../../shared/models/users' 20import { User } from '../../../../shared/models/users'
@@ -172,7 +171,7 @@ describe('Test users with multiple servers', function () {
172 171
173 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 172 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10)
174 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 173 const videoChannelDeleted = resVideoChannels.body.data.find(a => {
175 return a.displayName === 'Default user1 channel' && a.host === 'localhost:9001' 174 return a.displayName === 'Main user1 channel' && a.host === 'localhost:9001'
176 }) as VideoChannel 175 }) as VideoChannel
177 expect(videoChannelDeleted).not.to.be.undefined 176 expect(videoChannelDeleted).not.to.be.undefined
178 } 177 }
@@ -189,7 +188,7 @@ describe('Test users with multiple servers', function () {
189 188
190 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 189 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10)
191 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 190 const videoChannelDeleted = resVideoChannels.body.data.find(a => {
192 return a.name === 'Default user1 channel' && a.host === 'localhost:9001' 191 return a.name === 'Main user1 channel' && a.host === 'localhost:9001'
193 }) as VideoChannel 192 }) as VideoChannel
194 expect(videoChannelDeleted).to.be.undefined 193 expect(videoChannelDeleted).to.be.undefined
195 } 194 }
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index cab096a12..3c3839338 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -6,7 +6,6 @@ import { join } from 'path'
6import * as request from 'supertest' 6import * as request from 'supertest'
7import { VideoPrivacy } from '../../../../shared/models/videos' 7import { VideoPrivacy } from '../../../../shared/models/videos'
8import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' 8import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
9
10import { 9import {
11 addVideoChannel, 10 addVideoChannel,
12 checkVideoFilesWereRemoved, 11 checkVideoFilesWereRemoved,
@@ -60,6 +59,7 @@ describe('Test multiple servers', function () {
60 59
61 { 60 {
62 const videoChannel = { 61 const videoChannel = {
62 name: 'super_channel_name',
63 displayName: 'my channel', 63 displayName: 'my channel',
64 description: 'super channel' 64 description: 'super channel'
65 } 65 }
@@ -201,7 +201,7 @@ describe('Test multiple servers', function () {
201 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], 201 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
202 privacy: VideoPrivacy.PUBLIC, 202 privacy: VideoPrivacy.PUBLIC,
203 channel: { 203 channel: {
204 name: 'Default user1 channel', 204 name: 'Main user1 channel',
205 description: 'super channel', 205 description: 'super channel',
206 isLocal 206 isLocal
207 }, 207 },
@@ -307,7 +307,7 @@ describe('Test multiple servers', function () {
307 tags: [ 'tag1p3' ], 307 tags: [ 'tag1p3' ],
308 privacy: VideoPrivacy.PUBLIC, 308 privacy: VideoPrivacy.PUBLIC,
309 channel: { 309 channel: {
310 name: 'Default root channel', 310 name: 'Main root channel',
311 description: '', 311 description: '',
312 isLocal 312 isLocal
313 }, 313 },
@@ -339,7 +339,7 @@ describe('Test multiple servers', function () {
339 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 339 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
340 privacy: VideoPrivacy.PUBLIC, 340 privacy: VideoPrivacy.PUBLIC,
341 channel: { 341 channel: {
342 name: 'Default root channel', 342 name: 'Main root channel',
343 description: '', 343 description: '',
344 isLocal 344 isLocal
345 }, 345 },
@@ -647,7 +647,7 @@ describe('Test multiple servers', function () {
647 tags: [ 'tag_up_1', 'tag_up_2' ], 647 tags: [ 'tag_up_1', 'tag_up_2' ],
648 privacy: VideoPrivacy.PUBLIC, 648 privacy: VideoPrivacy.PUBLIC,
649 channel: { 649 channel: {
650 name: 'Default root channel', 650 name: 'Main root channel',
651 description: '', 651 description: '',
652 isLocal 652 isLocal
653 }, 653 },
@@ -967,7 +967,7 @@ describe('Test multiple servers', function () {
967 tags: [ ], 967 tags: [ ],
968 privacy: VideoPrivacy.PUBLIC, 968 privacy: VideoPrivacy.PUBLIC,
969 channel: { 969 channel: {
970 name: 'Default root channel', 970 name: 'Main root channel',
971 description: '', 971 description: '',
972 isLocal 972 isLocal
973 }, 973 },
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index ba4920d1b..12181ad67 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -56,7 +56,7 @@ describe('Test a single server', function () {
56 privacy: VideoPrivacy.PUBLIC, 56 privacy: VideoPrivacy.PUBLIC,
57 commentsEnabled: true, 57 commentsEnabled: true,
58 channel: { 58 channel: {
59 name: 'Default root channel', 59 name: 'Main root channel',
60 description: '', 60 description: '',
61 isLocal: true 61 isLocal: true
62 }, 62 },
@@ -87,7 +87,7 @@ describe('Test a single server', function () {
87 duration: 5, 87 duration: 5,
88 commentsEnabled: false, 88 commentsEnabled: false,
89 channel: { 89 channel: {
90 name: 'Default root channel', 90 name: 'Main root channel',
91 description: '', 91 description: '',
92 isLocal: true 92 isLocal: true
93 }, 93 },
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index e4e3ce9d9..8138c65d6 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -4,12 +4,13 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { User, Video } from '../../../../shared/index' 5import { User, Video } from '../../../../shared/index'
6import { 6import {
7 createUser,
7 doubleFollow, 8 doubleFollow,
8 flushAndRunMultipleServers, 9 flushAndRunMultipleServers,
9 getVideoChannelVideos, testImage, 10 getVideoChannelVideos, serverLogin, testImage,
10 updateVideo, 11 updateVideo,
11 updateVideoChannelAvatar, 12 updateVideoChannelAvatar,
12 uploadVideo, wait 13 uploadVideo, wait, userLogin
13} from '../../utils' 14} from '../../utils'
14import { 15import {
15 addVideoChannel, 16 addVideoChannel,
@@ -33,9 +34,7 @@ describe('Test video channels', function () {
33 let userInfo: User 34 let userInfo: User
34 let accountUUID: string 35 let accountUUID: string
35 let firstVideoChannelId: number 36 let firstVideoChannelId: number
36 let firstVideoChannelUUID: string
37 let secondVideoChannelId: number 37 let secondVideoChannelId: number
38 let secondVideoChannelUUID: string
39 let videoUUID: string 38 let videoUUID: string
40 39
41 before(async function () { 40 before(async function () {
@@ -54,7 +53,6 @@ describe('Test video channels', function () {
54 accountUUID = user.account.uuid 53 accountUUID = user.account.uuid
55 54
56 firstVideoChannelId = user.videoChannels[0].id 55 firstVideoChannelId = user.videoChannels[0].id
57 firstVideoChannelUUID = user.videoChannels[0].uuid
58 } 56 }
59 57
60 await waitJobs(servers) 58 await waitJobs(servers)
@@ -73,13 +71,13 @@ describe('Test video channels', function () {
73 71
74 { 72 {
75 const videoChannel = { 73 const videoChannel = {
74 name: 'second_video_channel',
76 displayName: 'second video channel', 75 displayName: 'second video channel',
77 description: 'super video channel description', 76 description: 'super video channel description',
78 support: 'super video channel support text' 77 support: 'super video channel support text'
79 } 78 }
80 const res = await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel) 79 const res = await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel)
81 secondVideoChannelId = res.body.videoChannel.id 80 secondVideoChannelId = res.body.videoChannel.id
82 secondVideoChannelUUID = res.body.videoChannel.uuid
83 } 81 }
84 82
85 // The channel is 1 is propagated to servers 2 83 // The channel is 1 is propagated to servers 2
@@ -99,7 +97,10 @@ describe('Test video channels', function () {
99 expect(userInfo.videoChannels).to.have.lengthOf(2) 97 expect(userInfo.videoChannels).to.have.lengthOf(2)
100 98
101 const videoChannels = userInfo.videoChannels 99 const videoChannels = userInfo.videoChannels
102 expect(videoChannels[0].displayName).to.equal('Default root channel') 100 expect(videoChannels[0].name).to.equal('root_channel')
101 expect(videoChannels[0].displayName).to.equal('Main root channel')
102
103 expect(videoChannels[1].name).to.equal('second_video_channel')
103 expect(videoChannels[1].displayName).to.equal('second video channel') 104 expect(videoChannels[1].displayName).to.equal('second video channel')
104 expect(videoChannels[1].description).to.equal('super video channel description') 105 expect(videoChannels[1].description).to.equal('super video channel description')
105 expect(videoChannels[1].support).to.equal('super video channel support text') 106 expect(videoChannels[1].support).to.equal('super video channel support text')
@@ -112,7 +113,10 @@ describe('Test video channels', function () {
112 expect(res.body.data).to.have.lengthOf(2) 113 expect(res.body.data).to.have.lengthOf(2)
113 114
114 const videoChannels = res.body.data 115 const videoChannels = res.body.data
115 expect(videoChannels[0].displayName).to.equal('Default root channel') 116 expect(videoChannels[0].name).to.equal('root_channel')
117 expect(videoChannels[0].displayName).to.equal('Main root channel')
118
119 expect(videoChannels[1].name).to.equal('second_video_channel')
116 expect(videoChannels[1].displayName).to.equal('second video channel') 120 expect(videoChannels[1].displayName).to.equal('second video channel')
117 expect(videoChannels[1].description).to.equal('super video channel description') 121 expect(videoChannels[1].description).to.equal('super video channel description')
118 expect(videoChannels[1].support).to.equal('super video channel support text') 122 expect(videoChannels[1].support).to.equal('super video channel support text')
@@ -125,6 +129,7 @@ describe('Test video channels', function () {
125 expect(res.body.data).to.have.lengthOf(1) 129 expect(res.body.data).to.have.lengthOf(1)
126 130
127 const videoChannels = res.body.data 131 const videoChannels = res.body.data
132 expect(videoChannels[0].name).to.equal('second_video_channel')
128 expect(videoChannels[0].displayName).to.equal('second video channel') 133 expect(videoChannels[0].displayName).to.equal('second video channel')
129 expect(videoChannels[0].description).to.equal('super video channel description') 134 expect(videoChannels[0].description).to.equal('super video channel description')
130 expect(videoChannels[0].support).to.equal('super video channel support text') 135 expect(videoChannels[0].support).to.equal('super video channel support text')
@@ -136,7 +141,8 @@ describe('Test video channels', function () {
136 expect(res.body.total).to.equal(2) 141 expect(res.body.total).to.equal(2)
137 expect(res.body.data).to.be.an('array') 142 expect(res.body.data).to.be.an('array')
138 expect(res.body.data).to.have.lengthOf(1) 143 expect(res.body.data).to.have.lengthOf(1)
139 expect(res.body.data[0].displayName).to.equal('Default root channel') 144 expect(res.body.data[0].name).to.equal('root_channel')
145 expect(res.body.data[0].displayName).to.equal('Main root channel')
140 }) 146 })
141 147
142 it('Should update video channel', async function () { 148 it('Should update video channel', async function () {
@@ -148,7 +154,7 @@ describe('Test video channels', function () {
148 support: 'video channel support text updated' 154 support: 'video channel support text updated'
149 } 155 }
150 156
151 await updateVideoChannel(servers[0].url, servers[0].accessToken, secondVideoChannelId, videoChannelAttributes) 157 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
152 158
153 await waitJobs(servers) 159 await waitJobs(servers)
154 }) 160 })
@@ -160,6 +166,7 @@ describe('Test video channels', function () {
160 expect(res.body.total).to.equal(2) 166 expect(res.body.total).to.equal(2)
161 expect(res.body.data).to.be.an('array') 167 expect(res.body.data).to.be.an('array')
162 expect(res.body.data).to.have.lengthOf(1) 168 expect(res.body.data).to.have.lengthOf(1)
169 expect(res.body.data[0].name).to.equal('second_video_channel')
163 expect(res.body.data[0].displayName).to.equal('video channel updated') 170 expect(res.body.data[0].displayName).to.equal('video channel updated')
164 expect(res.body.data[0].description).to.equal('video channel description updated') 171 expect(res.body.data[0].description).to.equal('video channel description updated')
165 expect(res.body.data[0].support).to.equal('video channel support text updated') 172 expect(res.body.data[0].support).to.equal('video channel support text updated')
@@ -174,7 +181,7 @@ describe('Test video channels', function () {
174 await updateVideoChannelAvatar({ 181 await updateVideoChannelAvatar({
175 url: servers[0].url, 182 url: servers[0].url,
176 accessToken: servers[0].accessToken, 183 accessToken: servers[0].accessToken,
177 videoChannelId: secondVideoChannelId, 184 videoChannelName: 'second_video_channel',
178 fixture 185 fixture
179 }) 186 })
180 187
@@ -192,9 +199,10 @@ describe('Test video channels', function () {
192 }) 199 })
193 200
194 it('Should get video channel', async function () { 201 it('Should get video channel', async function () {
195 const res = await getVideoChannel(servers[0].url, secondVideoChannelId) 202 const res = await getVideoChannel(servers[0].url, 'second_video_channel')
196 203
197 const videoChannel = res.body 204 const videoChannel = res.body
205 expect(videoChannel.name).to.equal('second_video_channel')
198 expect(videoChannel.displayName).to.equal('video channel updated') 206 expect(videoChannel.displayName).to.equal('video channel updated')
199 expect(videoChannel.description).to.equal('video channel description updated') 207 expect(videoChannel.description).to.equal('video channel description updated')
200 expect(videoChannel.support).to.equal('video channel support text updated') 208 expect(videoChannel.support).to.equal('video channel support text updated')
@@ -204,7 +212,8 @@ describe('Test video channels', function () {
204 this.timeout(10000) 212 this.timeout(10000)
205 213
206 for (const server of servers) { 214 for (const server of servers) {
207 const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondVideoChannelUUID, 0, 5) 215 const channelURI = 'second_video_channel@localhost:9001'
216 const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5)
208 expect(res1.body.total).to.equal(1) 217 expect(res1.body.total).to.equal(1)
209 expect(res1.body.data).to.be.an('array') 218 expect(res1.body.data).to.be.an('array')
210 expect(res1.body.data).to.have.lengthOf(1) 219 expect(res1.body.data).to.have.lengthOf(1)
@@ -224,10 +233,12 @@ describe('Test video channels', function () {
224 this.timeout(10000) 233 this.timeout(10000)
225 234
226 for (const server of servers) { 235 for (const server of servers) {
227 const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondVideoChannelUUID, 0, 5) 236 const secondChannelURI = 'second_video_channel@localhost:9001'
237 const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5)
228 expect(res1.body.total).to.equal(0) 238 expect(res1.body.total).to.equal(0)
229 239
230 const res2 = await getVideoChannelVideos(server.url, server.accessToken, firstVideoChannelUUID, 0, 5) 240 const channelURI = 'root_channel@localhost:9001'
241 const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5)
231 expect(res2.body.total).to.equal(1) 242 expect(res2.body.total).to.equal(1)
232 243
233 const videos: Video[] = res2.body.data 244 const videos: Video[] = res2.body.data
@@ -238,7 +249,7 @@ describe('Test video channels', function () {
238 }) 249 })
239 250
240 it('Should delete video channel', async function () { 251 it('Should delete video channel', async function () {
241 await deleteVideoChannel(servers[0].url, servers[0].accessToken, secondVideoChannelId) 252 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel')
242 }) 253 })
243 254
244 it('Should have video channel deleted', async function () { 255 it('Should have video channel deleted', async function () {
@@ -247,7 +258,23 @@ describe('Test video channels', function () {
247 expect(res.body.total).to.equal(1) 258 expect(res.body.total).to.equal(1)
248 expect(res.body.data).to.be.an('array') 259 expect(res.body.data).to.be.an('array')
249 expect(res.body.data).to.have.lengthOf(1) 260 expect(res.body.data).to.have.lengthOf(1)
250 expect(res.body.data[0].displayName).to.equal('Default root channel') 261 expect(res.body.data[0].displayName).to.equal('Main root channel')
262 })
263
264 it('Should create the main channel with an uuid if there is a conflict', async function () {
265 {
266 const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' }
267 await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel)
268 }
269
270 {
271 await createUser(servers[ 0 ].url, servers[ 0 ].accessToken, 'toto', 'password')
272 const accessToken = await userLogin(servers[ 0 ], { username: 'toto', password: 'password' })
273
274 const res = await getMyUserInformation(servers[ 0 ].url, accessToken)
275 const videoChannel = res.body.videoChannels[ 0 ]
276 expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
277 }
251 }) 278 })
252 279
253 after(async function () { 280 after(async function () {
diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts
index 968b7bd7f..7f54c0e70 100644
--- a/server/tests/cli/update-host.ts
+++ b/server/tests/cli/update-host.ts
@@ -54,6 +54,7 @@ describe('Test update host scripts', function () {
54 54
55 // Create channel 55 // Create channel
56 const videoChannel = { 56 const videoChannel = {
57 name: 'second_channel',
57 displayName: 'second video channel', 58 displayName: 'second video channel',
58 description: 'super video channel description' 59 description: 'super video channel description'
59 } 60 }
diff --git a/server/tests/real-world/populate-database.ts b/server/tests/real-world/populate-database.ts
index d41ac8d36..a7fdbd1dc 100644
--- a/server/tests/real-world/populate-database.ts
+++ b/server/tests/real-world/populate-database.ts
@@ -66,6 +66,7 @@ function getRandomInt (min, max) {
66 66
67function createCustomChannel (server: ServerInfo) { 67function createCustomChannel (server: ServerInfo) {
68 const videoChannel = { 68 const videoChannel = {
69 name: Date.now().toString(),
69 displayName: Date.now().toString(), 70 displayName: Date.now().toString(),
70 description: Date.now().toString() 71 description: Date.now().toString()
71 } 72 }
diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts
index 3ca39469c..1eea22b31 100644
--- a/server/tests/utils/videos/video-channels.ts
+++ b/server/tests/utils/videos/video-channels.ts
@@ -97,10 +97,10 @@ function updateVideoChannelAvatar (options: {
97 url: string, 97 url: string,
98 accessToken: string, 98 accessToken: string,
99 fixture: string, 99 fixture: string,
100 videoChannelId: string | number 100 videoChannelName: string | number
101}) { 101}) {
102 102
103 const path = '/api/v1/video-channels/' + options.videoChannelId + '/avatar/pick' 103 const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick'
104 104
105 return updateAvatarRequest(Object.assign(options, { path })) 105 return updateAvatarRequest(Object.assign(options, { path }))
106} 106}
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts
index b280cccda..592248144 100644
--- a/server/tests/utils/videos/videos.ts
+++ b/server/tests/utils/videos/videos.ts
@@ -199,13 +199,13 @@ function getAccountVideos (
199function getVideoChannelVideos ( 199function getVideoChannelVideos (
200 url: string, 200 url: string,
201 accessToken: string, 201 accessToken: string,
202 videoChannelId: number | string, 202 videoChannelName: string,
203 start: number, 203 start: number,
204 count: number, 204 count: number,
205 sort?: string, 205 sort?: string,
206 query: { nsfw?: boolean } = {} 206 query: { nsfw?: boolean } = {}
207) { 207) {
208 const path = '/api/v1/video-channels/' + videoChannelId + '/videos' 208 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
209 209
210 return makeGetRequest({ 210 return makeGetRequest({
211 url, 211 url,
diff --git a/shared/models/videos/channel/video-channel-create.model.ts b/shared/models/videos/channel/video-channel-create.model.ts
index 08cd5fb84..da8ce620c 100644
--- a/shared/models/videos/channel/video-channel-create.model.ts
+++ b/shared/models/videos/channel/video-channel-create.model.ts
@@ -1,4 +1,5 @@
1export interface VideoChannelCreate { 1export interface VideoChannelCreate {
2 name: string
2 displayName: string 3 displayName: string
3 description?: string 4 description?: string
4 support?: string 5 support?: string