aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-04-24 15:10:54 +0200
committerChocobozzz <me@florianbigard.com>2018-04-24 15:13:19 +0200
commit0626e7af82e02f8a5bd1e74a7d4d8c916d073ceb (patch)
tree79b5befbc6a04c007e5919805f1514d065b30e11
parentb4d1af3dd8cdab2d58927e671d62194ca383cd75 (diff)
downloadPeerTube-0626e7af82e02f8a5bd1e74a7d4d8c916d073ceb.tar.gz
PeerTube-0626e7af82e02f8a5bd1e74a7d4d8c916d073ceb.tar.zst
PeerTube-0626e7af82e02f8a5bd1e74a7d4d8c916d073ceb.zip
Add account view
-rw-r--r--client/src/app/+account/account-about/account-about.component.html12
-rw-r--r--client/src/app/+account/account-about/account-about.component.scss8
-rw-r--r--client/src/app/+account/account-about/account-about.component.ts39
-rw-r--r--client/src/app/+account/account-routing.module.ts45
-rw-r--r--client/src/app/+account/account-videos/account-videos.component.scss3
-rw-r--r--client/src/app/+account/account-videos/account-videos.component.ts71
-rw-r--r--client/src/app/+account/account.component.html23
-rw-r--r--client/src/app/+account/account.component.scss46
-rw-r--r--client/src/app/+account/account.component.ts29
-rw-r--r--client/src/app/+account/account.module.ts26
-rw-r--r--client/src/app/+account/index.ts3
-rw-r--r--client/src/app/app-routing.module.ts4
-rw-r--r--client/src/app/my-account/my-account-settings/my-account-settings.component.scss6
-rw-r--r--client/src/app/my-account/my-account.component.ts2
-rw-r--r--client/src/app/shared/account/account.model.ts15
-rw-r--r--client/src/app/shared/account/account.service.ts31
-rw-r--r--client/src/app/shared/shared.module.ts2
-rw-r--r--client/src/app/shared/video/abstract-video-list.html4
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts1
-rw-r--r--client/src/app/shared/video/video.service.ts18
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html4
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts7
-rw-r--r--client/src/sass/include/_mixins.scss7
-rw-r--r--server/controllers/api/accounts.ts37
-rw-r--r--server/controllers/api/index.ts2
-rw-r--r--server/controllers/api/users.ts3
-rw-r--r--server/controllers/api/videos/index.ts19
-rw-r--r--server/controllers/feeds.ts29
-rw-r--r--server/helpers/express-utils.ts77
-rw-r--r--server/helpers/utils.ts62
-rw-r--r--server/middlewares/servers.ts2
-rw-r--r--server/middlewares/validators/webfinger.ts2
-rw-r--r--server/models/video/video.ts73
33 files changed, 563 insertions, 149 deletions
diff --git a/client/src/app/+account/account-about/account-about.component.html b/client/src/app/+account/account-about/account-about.component.html
new file mode 100644
index 000000000..003a8045e
--- /dev/null
+++ b/client/src/app/+account/account-about/account-about.component.html
@@ -0,0 +1,12 @@
1<div *ngIf="account" class="row">
2 <div class="description col-md-6 col-sm-12">
3 <div class="small-title">Description</div>
4 <div class="content">{{ getAccountDescription() }}</div>
5 </div>
6
7 <div class="stats col-md-6 col-sm-12">
8 <div class="small-title">Stats</div>
9
10 <div class="content">Joined {{ account.createdAt | date }}</div>
11 </div>
12</div> \ No newline at end of file
diff --git a/client/src/app/+account/account-about/account-about.component.scss b/client/src/app/+account/account-about/account-about.component.scss
new file mode 100644
index 000000000..b1be7d4ed
--- /dev/null
+++ b/client/src/app/+account/account-about/account-about.component.scss
@@ -0,0 +1,8 @@
1@import '_variables';
2@import '_mixins';
3
4.small-title {
5 @include in-content-small-title;
6
7 margin-bottom: 20px;
8}
diff --git a/client/src/app/+account/account-about/account-about.component.ts b/client/src/app/+account/account-about/account-about.component.ts
new file mode 100644
index 000000000..0772b844e
--- /dev/null
+++ b/client/src/app/+account/account-about/account-about.component.ts
@@ -0,0 +1,39 @@
1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { Location } from '@angular/common'
4import { getParameterByName, immutableAssign } from '@app/shared/misc/utils'
5import { NotificationsService } from 'angular2-notifications'
6import 'rxjs/add/observable/from'
7import 'rxjs/add/operator/concatAll'
8import { AuthService } from '../../core/auth'
9import { ConfirmService } from '../../core/confirm'
10import { AbstractVideoList } from '../../shared/video/abstract-video-list'
11import { VideoService } from '../../shared/video/video.service'
12import { Account } from '@app/shared/account/account.model'
13import { AccountService } from '@app/shared/account/account.service'
14
15@Component({
16 selector: 'my-account-about',
17 templateUrl: './account-about.component.html',
18 styleUrls: [ './account-about.component.scss' ]
19})
20export class AccountAboutComponent implements OnInit {
21 private account: Account
22
23 constructor (
24 protected route: ActivatedRoute,
25 private accountService: AccountService
26 ) { }
27
28 ngOnInit () {
29 // Parent get the account for us
30 this.accountService.accountLoaded
31 .subscribe(account => this.account = account)
32 }
33
34 getAccountDescription () {
35 if (this.account.description) return this.account.description
36
37 return 'No description'
38 }
39}
diff --git a/client/src/app/+account/account-routing.module.ts b/client/src/app/+account/account-routing.module.ts
new file mode 100644
index 000000000..534102121
--- /dev/null
+++ b/client/src/app/+account/account-routing.module.ts
@@ -0,0 +1,45 @@
1import { NgModule } from '@angular/core'
2import { RouterModule, Routes } from '@angular/router'
3import { MetaGuard } from '@ngx-meta/core'
4import { AccountComponent } from './account.component'
5import { AccountVideosComponent } from './account-videos/account-videos.component'
6import { AccountAboutComponent } from '@app/+account/account-about/account-about.component'
7
8const accountRoutes: Routes = [
9 {
10 path: ':accountId',
11 component: AccountComponent,
12 canActivateChild: [ MetaGuard ],
13 children: [
14 {
15 path: '',
16 redirectTo: 'videos',
17 pathMatch: 'full'
18 },
19 {
20 path: 'videos',
21 component: AccountVideosComponent,
22 data: {
23 meta: {
24 title: 'Account videos'
25 }
26 }
27 },
28 {
29 path: 'about',
30 component: AccountAboutComponent,
31 data: {
32 meta: {
33 title: 'About account'
34 }
35 }
36 }
37 ]
38 }
39]
40
41@NgModule({
42 imports: [ RouterModule.forChild(accountRoutes) ],
43 exports: [ RouterModule ]
44})
45export class AccountRoutingModule {}
diff --git a/client/src/app/+account/account-videos/account-videos.component.scss b/client/src/app/+account/account-videos/account-videos.component.scss
new file mode 100644
index 000000000..2ba85c031
--- /dev/null
+++ b/client/src/app/+account/account-videos/account-videos.component.scss
@@ -0,0 +1,3 @@
1.title-page-single {
2 margin-top: 0;
3} \ No newline at end of file
diff --git a/client/src/app/+account/account-videos/account-videos.component.ts b/client/src/app/+account/account-videos/account-videos.component.ts
new file mode 100644
index 000000000..6c0f0bb52
--- /dev/null
+++ b/client/src/app/+account/account-videos/account-videos.component.ts
@@ -0,0 +1,71 @@
1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { Location } from '@angular/common'
4import { immutableAssign } from '@app/shared/misc/utils'
5import { NotificationsService } from 'angular2-notifications'
6import 'rxjs/add/observable/from'
7import 'rxjs/add/operator/concatAll'
8import { AuthService } from '../../core/auth'
9import { ConfirmService } from '../../core/confirm'
10import { AbstractVideoList } from '../../shared/video/abstract-video-list'
11import { VideoService } from '../../shared/video/video.service'
12import { Account } from '@app/shared/account/account.model'
13import { AccountService } from '@app/shared/account/account.service'
14
15@Component({
16 selector: 'my-account-videos',
17 templateUrl: '../../shared/video/abstract-video-list.html',
18 styleUrls: [
19 '../../shared/video/abstract-video-list.scss',
20 './account-videos.component.scss'
21 ]
22})
23export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
24 titlePage = 'Published videos'
25 marginContent = false // Disable margin
26 currentRoute = '/account/videos'
27 loadOnInit = false
28
29 private account: Account
30
31 constructor (
32 protected router: Router,
33 protected route: ActivatedRoute,
34 protected authService: AuthService,
35 protected notificationsService: NotificationsService,
36 protected confirmService: ConfirmService,
37 protected location: Location,
38 private accountService: AccountService,
39 private videoService: VideoService
40 ) {
41 super()
42 }
43
44 ngOnInit () {
45 super.ngOnInit()
46
47 // Parent get the account for us
48 this.accountService.accountLoaded
49 .subscribe(account => {
50 this.account = account
51 this.currentRoute = '/account/' + this.account.id + '/videos'
52
53 this.loadMoreVideos(this.pagination.currentPage)
54 this.generateSyndicationList()
55 })
56 }
57
58 ngOnDestroy () {
59 super.ngOnDestroy()
60 }
61
62 getVideosObservable (page: number) {
63 const newPagination = immutableAssign(this.pagination, { currentPage: page })
64
65 return this.videoService.getAccountVideos(this.account, newPagination, this.sort)
66 }
67
68 generateSyndicationList () {
69 this.syndicationItems = this.videoService.getAccountFeedUrls(this.account.id)
70 }
71}
diff --git a/client/src/app/+account/account.component.html b/client/src/app/+account/account.component.html
new file mode 100644
index 000000000..f875b37a4
--- /dev/null
+++ b/client/src/app/+account/account.component.html
@@ -0,0 +1,23 @@
1<div *ngIf="account" class="row">
2 <div class="sub-menu">
3
4 <div class="account">
5 <img [src]="getAvatarUrl()" alt="Avatar" />
6
7 <div class="account-info">
8 <div class="account-display-name">{{ account.displayName }}</div>
9 <div class="account-followers">{{ account.followersCount }} subscribers</div>
10 </div>
11 </div>
12
13 <div class="links">
14 <a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
15
16 <a routerLink="about" routerLinkActive="active" class="title-page">About</a>
17 </div>
18 </div>
19
20 <div class="margin-content">
21 <router-outlet></router-outlet>
22 </div>
23</div>
diff --git a/client/src/app/+account/account.component.scss b/client/src/app/+account/account.component.scss
new file mode 100644
index 000000000..c7b8f038f
--- /dev/null
+++ b/client/src/app/+account/account.component.scss
@@ -0,0 +1,46 @@
1@import '_variables';
2@import '_mixins';
3
4.sub-menu {
5 height: 160px;
6 display: flex;
7 flex-direction: column;
8 align-items: start;
9
10 .account {
11 display: flex;
12 margin-top: 20px;
13 margin-bottom: 20px;
14
15 img {
16 @include avatar(80px);
17
18 margin-right: 20px;
19 }
20
21 .account-info {
22 display: flex;
23 flex-direction: column;
24 justify-content: center;
25
26 .account-display-name {
27 font-size: 23px;
28 font-weight: $font-bold;
29 }
30
31 .account-followers {
32 font-size: 15px;
33 }
34 }
35 }
36
37 .links {
38 margin-top: 0;
39 margin-bottom: 10px;
40
41 a {
42 margin-top: 0;
43 margin-bottom: 0;
44 }
45 }
46} \ No newline at end of file
diff --git a/client/src/app/+account/account.component.ts b/client/src/app/+account/account.component.ts
new file mode 100644
index 000000000..1c3e528a7
--- /dev/null
+++ b/client/src/app/+account/account.component.ts
@@ -0,0 +1,29 @@
1import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute } from '@angular/router'
3import { AccountService } from '@app/shared/account/account.service'
4import { Account } from '@app/shared/account/account.model'
5
6@Component({
7 selector: 'my-account',
8 templateUrl: './account.component.html',
9 styleUrls: [ './account.component.scss' ]
10})
11export class AccountComponent implements OnInit {
12 private account: Account
13
14 constructor (
15 private route: ActivatedRoute,
16 private accountService: AccountService
17 ) {}
18
19 ngOnInit () {
20 const accountId = parseInt(this.route.snapshot.params['accountId'], 10)
21
22 this.accountService.getAccount(accountId)
23 .subscribe(account => this.account = account)
24 }
25
26 getAvatarUrl () {
27 return Account.GET_ACCOUNT_AVATAR_URL(this.account)
28 }
29}
diff --git a/client/src/app/+account/account.module.ts b/client/src/app/+account/account.module.ts
new file mode 100644
index 000000000..2fe67f3c5
--- /dev/null
+++ b/client/src/app/+account/account.module.ts
@@ -0,0 +1,26 @@
1import { NgModule } from '@angular/core'
2import { SharedModule } from '../shared'
3import { AccountRoutingModule } from './account-routing.module'
4import { AccountComponent } from './account.component'
5import { AccountVideosComponent } from './account-videos/account-videos.component'
6import { AccountAboutComponent } from './account-about/account-about.component'
7
8@NgModule({
9 imports: [
10 AccountRoutingModule,
11 SharedModule
12 ],
13
14 declarations: [
15 AccountComponent,
16 AccountVideosComponent,
17 AccountAboutComponent
18 ],
19
20 exports: [
21 AccountComponent
22 ],
23
24 providers: []
25})
26export class AccountModule { }
diff --git a/client/src/app/+account/index.ts b/client/src/app/+account/index.ts
new file mode 100644
index 000000000..dc56ffdbd
--- /dev/null
+++ b/client/src/app/+account/index.ts
@@ -0,0 +1,3 @@
1export * from './account-routing.module'
2export * from './account.component'
3export * from './account.module'
diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts
index 2ee3cf974..1d55b4cea 100644
--- a/client/src/app/app-routing.module.ts
+++ b/client/src/app/app-routing.module.ts
@@ -7,6 +7,10 @@ const routes: Routes = [
7 { 7 {
8 path: 'admin', 8 path: 'admin',
9 loadChildren: './+admin/admin.module#AdminModule' 9 loadChildren: './+admin/admin.module#AdminModule'
10 },
11 {
12 path: 'account',
13 loadChildren: './+account/account.module#AccountModule'
10 } 14 }
11] 15]
12 16
diff --git a/client/src/app/my-account/my-account-settings/my-account-settings.component.scss b/client/src/app/my-account/my-account-settings/my-account-settings.component.scss
index 1cc00ca49..85079d620 100644
--- a/client/src/app/my-account/my-account-settings/my-account-settings.component.scss
+++ b/client/src/app/my-account/my-account-settings/my-account-settings.component.scss
@@ -47,10 +47,8 @@
47} 47}
48 48
49.account-title { 49.account-title {
50 text-transform: uppercase; 50 @include in-content-small-title;
51 color: $orange-color; 51
52 font-weight: $font-bold;
53 font-size: 13px;
54 margin-top: 55px; 52 margin-top: 55px;
55 margin-bottom: 30px; 53 margin-bottom: 30px;
56} 54}
diff --git a/client/src/app/my-account/my-account.component.ts b/client/src/app/my-account/my-account.component.ts
index 0955e2b7b..7bb461d3c 100644
--- a/client/src/app/my-account/my-account.component.ts
+++ b/client/src/app/my-account/my-account.component.ts
@@ -1,7 +1,7 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2 2
3@Component({ 3@Component({
4 selector: 'my-account', 4 selector: 'my-my-account',
5 templateUrl: './my-account.component.html' 5 templateUrl: './my-account.component.html'
6}) 6})
7export class MyAccountComponent {} 7export class MyAccountComponent {}
diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts
index 0bdc76478..3d5176bdd 100644
--- a/client/src/app/shared/account/account.model.ts
+++ b/client/src/app/shared/account/account.model.ts
@@ -16,6 +16,21 @@ export class Account implements ServerAccount {
16 updatedAt: Date 16 updatedAt: Date
17 avatar: Avatar 17 avatar: Avatar
18 18
19 constructor (hash: ServerAccount) {
20 this.id = hash.id
21 this.uuid = hash.uuid
22 this.url = hash.url
23 this.name = hash.name
24 this.displayName = hash.displayName
25 this.description = hash.description
26 this.host = hash.host
27 this.followingCount = hash.followingCount
28 this.followersCount = hash.followersCount
29 this.createdAt = new Date(hash.createdAt.toString())
30 this.updatedAt = new Date(hash.updatedAt.toString())
31 this.avatar = hash.avatar
32 }
33
19 static GET_ACCOUNT_AVATAR_URL (account: Account) { 34 static GET_ACCOUNT_AVATAR_URL (account: Account) {
20 const absoluteAPIUrl = getAbsoluteAPIUrl() 35 const absoluteAPIUrl = getAbsoluteAPIUrl()
21 36
diff --git a/client/src/app/shared/account/account.service.ts b/client/src/app/shared/account/account.service.ts
new file mode 100644
index 000000000..8c66ae04a
--- /dev/null
+++ b/client/src/app/shared/account/account.service.ts
@@ -0,0 +1,31 @@
1import { Injectable } from '@angular/core'
2import 'rxjs/add/operator/catch'
3import 'rxjs/add/operator/map'
4import { environment } from '../../../environments/environment'
5import { Observable } from 'rxjs/Observable'
6import { Account } from '@app/shared/account/account.model'
7import { RestExtractor } from '@app/shared/rest/rest-extractor.service'
8import { RestService } from '@app/shared/rest/rest.service'
9import { HttpClient } from '@angular/common/http'
10import { Account as ServerAccount } from '../../../../../shared/models/actors/account.model'
11import { ReplaySubject } from 'rxjs/ReplaySubject'
12
13@Injectable()
14export class AccountService {
15 static BASE_ACCOUNT_URL = environment.apiUrl + '/api/v1/accounts/'
16
17 accountLoaded = new ReplaySubject<Account>(1)
18
19 constructor (
20 private authHttp: HttpClient,
21 private restExtractor: RestExtractor,
22 private restService: RestService
23 ) {}
24
25 getAccount (id: number): Observable<Account> {
26 return this.authHttp.get<ServerAccount>(AccountService.BASE_ACCOUNT_URL + id)
27 .map(accountHash => new Account(accountHash))
28 .do(account => this.accountLoaded.next(account))
29 .catch((res) => this.restExtractor.handleError(res))
30 }
31}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 74730e2aa..2178eebc8 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -31,6 +31,7 @@ import { VideoMiniatureComponent } from './video/video-miniature.component'
31import { VideoFeedComponent } from './video/video-feed.component' 31import { VideoFeedComponent } from './video/video-feed.component'
32import { VideoThumbnailComponent } from './video/video-thumbnail.component' 32import { VideoThumbnailComponent } from './video/video-thumbnail.component'
33import { VideoService } from './video/video.service' 33import { VideoService } from './video/video.service'
34import { AccountService } from '@app/shared/account/account.service'
34 35
35@NgModule({ 36@NgModule({
36 imports: [ 37 imports: [
@@ -104,6 +105,7 @@ import { VideoService } from './video/video.service'
104 VideoBlacklistService, 105 VideoBlacklistService,
105 UserService, 106 UserService,
106 VideoService, 107 VideoService,
108 AccountService,
107 MarkdownService 109 MarkdownService
108 ] 110 ]
109}) 111})
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index cb04e07b4..690529dcf 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -1,5 +1,5 @@
1<div class="margin-content"> 1<div [ngClass]="{ 'margin-content': marginContent }">
2 <div class="title-page title-page-single"> 2 <div *ngIf="titlePage" class="title-page title-page-single">
3 {{ titlePage }} 3 {{ titlePage }}
4 </div> 4 </div>
5 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed> 5 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 728c864e9..642a85f65 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -29,6 +29,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
29 syndicationItems = [] 29 syndicationItems = []
30 30
31 loadOnInit = true 31 loadOnInit = true
32 marginContent = true
32 pageHeight: number 33 pageHeight: number
33 videoWidth: number 34 videoWidth: number
34 videoHeight: number 35 videoHeight: number
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index ef8babd55..f82aa7389 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -21,6 +21,8 @@ import { VideoDetails } from './video-details.model'
21import { VideoEdit } from './video-edit.model' 21import { VideoEdit } from './video-edit.model'
22import { Video } from './video.model' 22import { Video } from './video.model'
23import { objectToFormData } from '@app/shared/misc/utils' 23import { objectToFormData } from '@app/shared/misc/utils'
24import { Account } from '@app/shared/account/account.model'
25import { AccountService } from '@app/shared/account/account.service'
24 26
25@Injectable() 27@Injectable()
26export class VideoService { 28export class VideoService {
@@ -97,6 +99,22 @@ export class VideoService {
97 .catch((res) => this.restExtractor.handleError(res)) 99 .catch((res) => this.restExtractor.handleError(res))
98 } 100 }
99 101
102 getAccountVideos (
103 account: Account,
104 videoPagination: ComponentPagination,
105 sort: VideoSortField
106 ): Observable<{ videos: Video[], totalVideos: number}> {
107 const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
108
109 let params = new HttpParams()
110 params = this.restService.addRestGetParams(params, pagination, sort)
111
112 return this.authHttp
113 .get(AccountService.BASE_ACCOUNT_URL + account.id + '/videos', { params })
114 .map(this.extractVideos)
115 .catch((res) => this.restExtractor.handleError(res))
116 }
117
100 getVideos ( 118 getVideos (
101 videoPagination: ComponentPagination, 119 videoPagination: ComponentPagination,
102 sort: VideoSortField, 120 sort: VideoSortField,
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 91e590094..abda5043e 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -22,12 +22,10 @@
22 </div> 22 </div>
23 23
24 <div class="video-info-by"> 24 <div class="video-info-by">
25 <a [routerLink]="[ '/videos', 'search' ]" [queryParams]="{ search: video.account.name }" title="Search videos of this account"> 25 <a [routerLink]="[ '/account', video.account.id ]" title="Go the account page">
26 By {{ video.by }} 26 By {{ video.by }}
27 <img [src]="getAvatarPath()" alt="Account avatar" /> 27 <img [src]="getAvatarPath()" alt="Account avatar" />
28 </a> 28 </a>
29
30 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
31 </div> 29 </div>
32 </div> 30 </div>
33 31
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts
index 6f6f02378..4b0c49583 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -39,8 +39,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
39 39
40 otherVideosDisplayed: Video[] = [] 40 otherVideosDisplayed: Video[] = []
41 41
42 syndicationItems = {}
43
44 player: videojs.Player 42 player: videojs.Player
45 playerElement: HTMLVideoElement 43 playerElement: HTMLVideoElement
46 userRating: UserVideoRateType = null 44 userRating: UserVideoRateType = null
@@ -110,7 +108,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
110 const startTime = this.route.snapshot.queryParams.start 108 const startTime = this.route.snapshot.queryParams.start
111 this.onVideoFetched(video, startTime) 109 this.onVideoFetched(video, startTime)
112 .catch(err => this.handleError(err)) 110 .catch(err => this.handleError(err))
113 this.generateSyndicationList()
114 }, 111 },
115 112
116 error => { 113 error => {
@@ -247,10 +244,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
247 return this.video.tags.join(', ') 244 return this.video.tags.join(', ')
248 } 245 }
249 246
250 generateSyndicationList () {
251 this.syndicationItems = this.videoService.getAccountFeedUrls(this.video.account.id)
252 }
253
254 isVideoRemovable () { 247 isVideoRemovable () {
255 return this.video.isRemovableBy(this.authService.getUser()) 248 return this.video.isRemovableBy(this.authService.getUser())
256 } 249 }
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 7e7a38bbd..cbf9b566a 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -314,3 +314,10 @@
314 left: 0.25em; 314 left: 0.25em;
315 transform: rotate(-135deg); 315 transform: rotate(-135deg);
316} 316}
317
318@mixin in-content-small-title {
319 text-transform: uppercase;
320 color: $orange-color;
321 font-weight: $font-bold;
322 font-size: 13px;
323} \ No newline at end of file
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 4dc0cc16d..06ab04033 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,8 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getFormattedObjects } from '../../helpers/utils' 2import { getFormattedObjects } from '../../helpers/utils'
3import { asyncMiddleware, paginationValidator, setDefaultSort, setDefaultPagination } from '../../middlewares' 3import { asyncMiddleware, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort } from '../../middlewares'
4import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators' 4import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators'
5import { AccountModel } from '../../models/account/account' 5import { AccountModel } from '../../models/account/account'
6import { VideoModel } from '../../models/video/video'
7import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
8import { isNSFWHidden } from '../../helpers/express-utils'
6 9
7const accountsRouter = express.Router() 10const accountsRouter = express.Router()
8 11
@@ -19,6 +22,16 @@ accountsRouter.get('/:id',
19 getAccount 22 getAccount
20) 23)
21 24
25accountsRouter.get('/:id/videos',
26 asyncMiddleware(accountsGetValidator),
27 paginationValidator,
28 videosSortValidator,
29 setDefaultSort,
30 setDefaultPagination,
31 optionalAuthenticate,
32 asyncMiddleware(getAccountVideos)
33)
34
22// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
23 36
24export { 37export {
@@ -28,7 +41,9 @@ export {
28// --------------------------------------------------------------------------- 41// ---------------------------------------------------------------------------
29 42
30function getAccount (req: express.Request, res: express.Response, next: express.NextFunction) { 43function getAccount (req: express.Request, res: express.Response, next: express.NextFunction) {
31 return res.json(res.locals.account.toFormattedJSON()) 44 const account: AccountModel = res.locals.account
45
46 return res.json(account.toFormattedJSON())
32} 47}
33 48
34async function listAccounts (req: express.Request, res: express.Response, next: express.NextFunction) { 49async function listAccounts (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -36,3 +51,19 @@ async function listAccounts (req: express.Request, res: express.Response, next:
36 51
37 return res.json(getFormattedObjects(resultList.data, resultList.total)) 52 return res.json(getFormattedObjects(resultList.data, resultList.total))
38} 53}
54
55async function getAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
56 const account: AccountModel = res.locals.account
57
58 const resultList = await VideoModel.listForApi(
59 req.query.start as number,
60 req.query.count as number,
61 req.query.sort as VideoSortField,
62 isNSFWHidden(res),
63 null,
64 false,
65 account.id
66 )
67
68 return res.json(getFormattedObjects(resultList.data, resultList.total))
69}
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index 3b499f3b7..964d5d04c 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -1,5 +1,4 @@
1import * as express from 'express' 1import * as express from 'express'
2import { badRequest } from '../../helpers/utils'
3import { configRouter } from './config' 2import { configRouter } from './config'
4import { jobsRouter } from './jobs' 3import { jobsRouter } from './jobs'
5import { oauthClientsRouter } from './oauth-clients' 4import { oauthClientsRouter } from './oauth-clients'
@@ -7,6 +6,7 @@ import { serverRouter } from './server'
7import { usersRouter } from './users' 6import { usersRouter } from './users'
8import { accountsRouter } from './accounts' 7import { accountsRouter } from './accounts'
9import { videosRouter } from './videos' 8import { videosRouter } from './videos'
9import { badRequest } from '../../helpers/express-utils'
10 10
11const apiRouter = express.Router() 11const apiRouter = express.Router()
12 12
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index 6540adb1c..474329b58 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -7,7 +7,7 @@ import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRat
7import { retryTransactionWrapper } from '../../helpers/database-utils' 7import { retryTransactionWrapper } from '../../helpers/database-utils'
8import { processImage } from '../../helpers/image-utils' 8import { processImage } from '../../helpers/image-utils'
9import { logger } from '../../helpers/logger' 9import { logger } from '../../helpers/logger'
10import { createReqFiles, getFormattedObjects } from '../../helpers/utils' 10import { getFormattedObjects } from '../../helpers/utils'
11import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers' 11import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers'
12import { updateActorAvatarInstance } from '../../lib/activitypub' 12import { updateActorAvatarInstance } from '../../lib/activitypub'
13import { sendUpdateActor } from '../../lib/activitypub/send' 13import { sendUpdateActor } from '../../lib/activitypub/send'
@@ -43,6 +43,7 @@ import { UserModel } from '../../models/account/user'
43import { OAuthTokenModel } from '../../models/oauth/oauth-token' 43import { OAuthTokenModel } from '../../models/oauth/oauth-token'
44import { VideoModel } from '../../models/video/video' 44import { VideoModel } from '../../models/video/video'
45import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type' 45import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
46import { createReqFiles } from '../../helpers/express-utils'
46 47
47const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) 48const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
48const loginRateLimiter = new RateLimit({ 49const loginRateLimiter = new RateLimit({
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 6e8601fa1..61b6c5826 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -6,7 +6,7 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils'
6import { getVideoFileResolution } from '../../../helpers/ffmpeg-utils' 6import { getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
7import { processImage } from '../../../helpers/image-utils' 7import { processImage } from '../../../helpers/image-utils'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
9import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils' 9import { getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
10import { 10import {
11 CONFIG, 11 CONFIG,
12 IMAGE_MIMETYPE_EXT, 12 IMAGE_MIMETYPE_EXT,
@@ -19,11 +19,7 @@ import {
19 VIDEO_MIMETYPE_EXT, 19 VIDEO_MIMETYPE_EXT,
20 VIDEO_PRIVACIES 20 VIDEO_PRIVACIES
21} from '../../../initializers' 21} from '../../../initializers'
22import { 22import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
23 fetchRemoteVideoDescription,
24 getVideoActivityPubUrl,
25 shareVideoByServerAndChannel
26} from '../../../lib/activitypub'
27import { sendCreateVideo, sendCreateView, sendUpdateVideo } from '../../../lib/activitypub/send' 23import { sendCreateVideo, sendCreateView, sendUpdateVideo } from '../../../lib/activitypub/send'
28import { JobQueue } from '../../../lib/job-queue' 24import { JobQueue } from '../../../lib/job-queue'
29import { Redis } from '../../../lib/redis' 25import { Redis } from '../../../lib/redis'
@@ -49,9 +45,9 @@ import { blacklistRouter } from './blacklist'
49import { videoChannelRouter } from './channel' 45import { videoChannelRouter } from './channel'
50import { videoCommentRouter } from './comment' 46import { videoCommentRouter } from './comment'
51import { rateVideoRouter } from './rate' 47import { rateVideoRouter } from './rate'
52import { User } from '../../../../shared/models/users'
53import { VideoFilter } from '../../../../shared/models/videos/video-query.type' 48import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
54import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type' 49import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type'
50import { isNSFWHidden, createReqFiles } from '../../../helpers/express-utils'
55 51
56const videosRouter = express.Router() 52const videosRouter = express.Router()
57 53
@@ -444,12 +440,3 @@ async function searchVideos (req: express.Request, res: express.Response, next:
444 440
445 return res.json(getFormattedObjects(resultList.data, resultList.total)) 441 return res.json(getFormattedObjects(resultList.data, resultList.total))
446} 442}
447
448function isNSFWHidden (res: express.Response) {
449 if (res.locals.oauth) {
450 const user: User = res.locals.oauth.token.User
451 if (user) return user.nsfwPolicy === 'do_not_list'
452 }
453
454 return CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
455}
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 4a4dc3820..6a6af3e09 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -30,29 +30,18 @@ async function generateFeed (req: express.Request, res: express.Response, next:
30 let feed = initFeed() 30 let feed = initFeed()
31 const start = 0 31 const start = 0
32 32
33 let resultList: ResultList<VideoModel>
34 const account: AccountModel = res.locals.account 33 const account: AccountModel = res.locals.account
35 const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list' 34 const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
36 35
37 if (account) { 36 const resultList = await VideoModel.listForApi(
38 resultList = await VideoModel.listAccountVideosForApi( 37 start,
39 account.id, 38 FEEDS.COUNT,
40 start, 39 req.query.sort as VideoSortField,
41 FEEDS.COUNT, 40 hideNSFW,
42 req.query.sort as VideoSortField, 41 req.query.filter,
43 hideNSFW, 42 true,
44 true 43 account ? account.id : null
45 ) 44 )
46 } else {
47 resultList = await VideoModel.listForApi(
48 start,
49 FEEDS.COUNT,
50 req.query.sort as VideoSortField,
51 hideNSFW,
52 req.query.filter,
53 true
54 )
55 }
56 45
57 // Adding video items to the feed, one at a time 46 // Adding video items to the feed, one at a time
58 resultList.data.forEach(video => { 47 resultList.data.forEach(video => {
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts
new file mode 100644
index 000000000..d023117a8
--- /dev/null
+++ b/server/helpers/express-utils.ts
@@ -0,0 +1,77 @@
1import * as express from 'express'
2import * as multer from 'multer'
3import { CONFIG, REMOTE_SCHEME } from '../initializers'
4import { logger } from './logger'
5import { User } from '../../shared/models/users'
6import { generateRandomString } from './utils'
7
8function isNSFWHidden (res: express.Response) {
9 if (res.locals.oauth) {
10 const user: User = res.locals.oauth.token.User
11 if (user) return user.nsfwPolicy === 'do_not_list'
12 }
13
14 return CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
15}
16
17function getHostWithPort (host: string) {
18 const splitted = host.split(':')
19
20 // The port was not specified
21 if (splitted.length === 1) {
22 if (REMOTE_SCHEME.HTTP === 'https') return host + ':443'
23
24 return host + ':80'
25 }
26
27 return host
28}
29
30function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
31 return res.type('json').status(400).end()
32}
33
34function createReqFiles (
35 fieldNames: string[],
36 mimeTypes: { [ id: string ]: string },
37 destinations: { [ fieldName: string ]: string }
38) {
39 const storage = multer.diskStorage({
40 destination: (req, file, cb) => {
41 cb(null, destinations[ file.fieldname ])
42 },
43
44 filename: async (req, file, cb) => {
45 const extension = mimeTypes[ file.mimetype ]
46 let randomString = ''
47
48 try {
49 randomString = await generateRandomString(16)
50 } catch (err) {
51 logger.error('Cannot generate random string for file name.', { err })
52 randomString = 'fake-random-string'
53 }
54
55 cb(null, randomString + extension)
56 }
57 })
58
59 const fields = []
60 for (const fieldName of fieldNames) {
61 fields.push({
62 name: fieldName,
63 maxCount: 1
64 })
65 }
66
67 return multer({ storage }).fields(fields)
68}
69
70// ---------------------------------------------------------------------------
71
72export {
73 isNSFWHidden,
74 getHostWithPort,
75 badRequest,
76 createReqFiles
77}
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts
index c58117219..058c3211e 100644
--- a/server/helpers/utils.ts
+++ b/server/helpers/utils.ts
@@ -1,68 +1,13 @@
1import * as express from 'express'
2import * as multer from 'multer'
3import { Model } from 'sequelize-typescript' 1import { Model } from 'sequelize-typescript'
4import { ResultList } from '../../shared' 2import { ResultList } from '../../shared'
5import { VideoResolution } from '../../shared/models/videos' 3import { VideoResolution } from '../../shared/models/videos'
6import { CONFIG, REMOTE_SCHEME } from '../initializers' 4import { CONFIG } from '../initializers'
7import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
8import { ActorModel } from '../models/activitypub/actor' 6import { ActorModel } from '../models/activitypub/actor'
9import { ApplicationModel } from '../models/application/application' 7import { ApplicationModel } from '../models/application/application'
10import { pseudoRandomBytesPromise } from './core-utils' 8import { pseudoRandomBytesPromise } from './core-utils'
11import { logger } from './logger' 9import { logger } from './logger'
12 10
13function getHostWithPort (host: string) {
14 const splitted = host.split(':')
15
16 // The port was not specified
17 if (splitted.length === 1) {
18 if (REMOTE_SCHEME.HTTP === 'https') return host + ':443'
19
20 return host + ':80'
21 }
22
23 return host
24}
25
26function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
27 return res.type('json').status(400).end()
28}
29
30function createReqFiles (
31 fieldNames: string[],
32 mimeTypes: { [ id: string ]: string },
33 destinations: { [ fieldName: string ]: string }
34) {
35 const storage = multer.diskStorage({
36 destination: (req, file, cb) => {
37 cb(null, destinations[file.fieldname])
38 },
39
40 filename: async (req, file, cb) => {
41 const extension = mimeTypes[file.mimetype]
42 let randomString = ''
43
44 try {
45 randomString = await generateRandomString(16)
46 } catch (err) {
47 logger.error('Cannot generate random string for file name.', { err })
48 randomString = 'fake-random-string'
49 }
50
51 cb(null, randomString + extension)
52 }
53 })
54
55 const fields = []
56 for (const fieldName of fieldNames) {
57 fields.push({
58 name: fieldName,
59 maxCount: 1
60 })
61 }
62
63 return multer({ storage }).fields(fields)
64}
65
66async function generateRandomString (size: number) { 11async function generateRandomString (size: number) {
67 const raw = await pseudoRandomBytesPromise(size) 12 const raw = await pseudoRandomBytesPromise(size)
68 13
@@ -151,14 +96,11 @@ type SortType = { sortModel: any, sortValue: string }
151// --------------------------------------------------------------------------- 96// ---------------------------------------------------------------------------
152 97
153export { 98export {
154 badRequest,
155 generateRandomString, 99 generateRandomString,
156 getFormattedObjects, 100 getFormattedObjects,
157 isSignupAllowed, 101 isSignupAllowed,
158 computeResolutionsToTranscode, 102 computeResolutionsToTranscode,
159 resetSequelizeInstance, 103 resetSequelizeInstance,
160 getServerActor, 104 getServerActor,
161 SortType, 105 SortType
162 getHostWithPort,
163 createReqFiles
164} 106}
diff --git a/server/middlewares/servers.ts b/server/middlewares/servers.ts
index a9dcad2d4..c52f4685b 100644
--- a/server/middlewares/servers.ts
+++ b/server/middlewares/servers.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { getHostWithPort } from '../helpers/utils' 3import { getHostWithPort } from '../helpers/express-utils'
4 4
5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) { 5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) {
6 if (!req.body.hosts) return next() 6 if (!req.body.hosts) return next()
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts
index 3dbec6e44..3b9645048 100644
--- a/server/middlewares/validators/webfinger.ts
+++ b/server/middlewares/validators/webfinger.ts
@@ -2,9 +2,9 @@ import * as express from 'express'
2import { query } from 'express-validator/check' 2import { query } from 'express-validator/check'
3import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger' 3import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { getHostWithPort } from '../../helpers/utils'
6import { ActorModel } from '../../models/activitypub/actor' 5import { ActorModel } from '../../models/activitypub/actor'
7import { areValidationErrors } from './utils' 6import { areValidationErrors } from './utils'
7import { getHostWithPort } from '../../helpers/express-utils'
8 8
9const webfingerValidator = [ 9const webfingerValidator = [
10 query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'), 10 query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'),
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index b0fff6526..2ad9c00dd 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -95,7 +95,33 @@ enum ScopeNames {
95} 95}
96 96
97@Scopes({ 97@Scopes({
98 [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean) => { 98 [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean, accountId?: number) => {
99 const accountInclude = {
100 attributes: [ 'name' ],
101 model: AccountModel.unscoped(),
102 required: true,
103 where: {},
104 include: [
105 {
106 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
107 model: ActorModel.unscoped(),
108 required: true,
109 where: VideoModel.buildActorWhereWithFilter(filter),
110 include: [
111 {
112 attributes: [ 'host' ],
113 model: ServerModel.unscoped(),
114 required: false
115 },
116 {
117 model: AvatarModel.unscoped(),
118 required: false
119 }
120 ]
121 }
122 ]
123 }
124
99 const query: IFindOptions<VideoModel> = { 125 const query: IFindOptions<VideoModel> = {
100 where: { 126 where: {
101 id: { 127 id: {
@@ -125,30 +151,7 @@ enum ScopeNames {
125 model: VideoChannelModel.unscoped(), 151 model: VideoChannelModel.unscoped(),
126 required: true, 152 required: true,
127 include: [ 153 include: [
128 { 154 accountInclude
129 attributes: [ 'name' ],
130 model: AccountModel.unscoped(),
131 required: true,
132 include: [
133 {
134 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
135 model: ActorModel.unscoped(),
136 required: true,
137 where: VideoModel.buildActorWhereWithFilter(filter),
138 include: [
139 {
140 attributes: [ 'host' ],
141 model: ServerModel.unscoped(),
142 required: false
143 },
144 {
145 model: AvatarModel.unscoped(),
146 required: false
147 }
148 ]
149 }
150 ]
151 }
152 ] 155 ]
153 } 156 }
154 ] 157 ]
@@ -166,6 +169,12 @@ enum ScopeNames {
166 query.where['nsfw'] = false 169 query.where['nsfw'] = false
167 } 170 }
168 171
172 if (accountId) {
173 accountInclude.where = {
174 id: accountId
175 }
176 }
177
169 return query 178 return query
170 }, 179 },
171 [ScopeNames.WITH_ACCOUNT_DETAILS]: { 180 [ScopeNames.WITH_ACCOUNT_DETAILS]: {
@@ -688,7 +697,15 @@ export class VideoModel extends Model<VideoModel> {
688 }) 697 })
689 } 698 }
690 699
691 static async listForApi (start: number, count: number, sort: string, hideNSFW: boolean, filter?: VideoFilter, withFiles = false) { 700 static async listForApi (
701 start: number,
702 count: number,
703 sort: string,
704 hideNSFW: boolean,
705 filter?: VideoFilter,
706 withFiles = false,
707 accountId?: number
708 ) {
692 const query = { 709 const query = {
693 offset: start, 710 offset: start,
694 limit: count, 711 limit: count,
@@ -696,7 +713,7 @@ export class VideoModel extends Model<VideoModel> {
696 } 713 }
697 714
698 const serverActor = await getServerActor() 715 const serverActor = await getServerActor()
699 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles ] }) 716 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles, accountId ] })
700 .findAndCountAll(query) 717 .findAndCountAll(query)
701 .then(({ rows, count }) => { 718 .then(({ rows, count }) => {
702 return { 719 return {
@@ -879,8 +896,6 @@ export class VideoModel extends Model<VideoModel> {
879 896
880 private static getLanguageLabel (id: string) { 897 private static getLanguageLabel (id: string) {
881 let languageLabel = VIDEO_LANGUAGES[id] 898 let languageLabel = VIDEO_LANGUAGES[id]
882 console.log(VIDEO_LANGUAGES)
883 console.log(id)
884 if (!languageLabel) languageLabel = 'Unknown' 899 if (!languageLabel) languageLabel = 'Unknown'
885 900
886 return languageLabel 901 return languageLabel