aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CREDITS.md9
-rw-r--r--client/src/app/shared/account/account.model.ts20
-rw-r--r--client/src/app/shared/shared.module.ts3
-rw-r--r--client/src/app/shared/users/user.model.ts6
-rw-r--r--client/src/app/shared/video/abstract-video-list.html2
-rw-r--r--client/src/app/shared/video/video-details.model.ts9
-rw-r--r--client/src/app/shared/video/video-miniature.component.html (renamed from client/src/app/videos/video-list/shared/video-miniature.component.html)0
-rw-r--r--client/src/app/shared/video/video-miniature.component.scss (renamed from client/src/app/videos/video-list/shared/video-miniature.component.scss)0
-rw-r--r--client/src/app/shared/video/video-miniature.component.ts (renamed from client/src/app/videos/video-list/shared/video-miniature.component.ts)6
-rw-r--r--client/src/app/shared/video/video-pagination.model.ts2
-rw-r--r--client/src/app/shared/video/video.model.ts8
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html233
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.scss308
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts43
-rw-r--r--client/src/app/videos/video-list/index.ts1
-rw-r--r--client/src/app/videos/video-list/shared/index.ts1
-rw-r--r--client/src/app/videos/videos.module.ts3
-rw-r--r--client/src/assets/images/video/dislike.svg14
-rw-r--r--client/src/assets/images/video/like.svg15
-rw-r--r--client/src/assets/images/video/more.svg11
-rw-r--r--client/src/assets/images/video/share.svg16
-rw-r--r--server/models/video/video.ts3
-rw-r--r--server/tests/api/follows.ts2
-rw-r--r--server/tests/api/multiple-servers.ts11
-rw-r--r--server/tests/api/services.ts4
-rw-r--r--server/tests/api/single-server.ts15
-rw-r--r--server/tests/api/users.ts4
-rw-r--r--server/tests/utils/servers.ts2
-rw-r--r--shared/models/videos/video.model.ts4
29 files changed, 327 insertions, 428 deletions
diff --git a/CREDITS.md b/CREDITS.md
index a7b2b5568..65017bbc1 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -8,14 +8,9 @@
8 8
9# Design 9# Design
10 10
11Inspirations from: 11By [Olivier Massain](https://twitter.com/omassain)
12 12
13 * [Aurélien Salomon](https://dribbble.com/shots/1338727-Youtube-Redesign) 13Icons from [Robbie Pearce](https://robbiepearce.com/softies/)
14 * [Wojciech Zieliński](https://dribbble.com/shots/3000315-youtube-concept)
15
16Video.js theme:
17
18 * [zanechua](https://github.com/zanechua/videojs-sublime-inspired-skin)
19 14
20# Fonts 15# Fonts
21 16
diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts
new file mode 100644
index 000000000..0b008188a
--- /dev/null
+++ b/client/src/app/shared/account/account.model.ts
@@ -0,0 +1,20 @@
1import { Account as ServerAccount } from '../../../../../shared/models/accounts/account.model'
2import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
3
4export class Account implements ServerAccount {
5 id: number
6 uuid: string
7 name: string
8 host: string
9 followingCount: number
10 followersCount: number
11 createdAt: Date
12 updatedAt: Date
13 avatar: Avatar
14
15 static GET_ACCOUNT_AVATAR_PATH (account: Account) {
16 if (account && account.avatar) return account.avatar.path
17
18 return API_URL + '/client/assets/images/default-avatar.png'
19 }
20}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 86e1a380e..bd9aee345 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -20,6 +20,7 @@ import { RestExtractor, RestService } from './rest'
20import { UserService } from './users' 20import { UserService } from './users'
21import { VideoAbuseService } from './video-abuse' 21import { VideoAbuseService } from './video-abuse'
22import { VideoBlacklistService } from './video-blacklist' 22import { VideoBlacklistService } from './video-blacklist'
23import { VideoMiniatureComponent } from './video/video-miniature.component'
23import { VideoThumbnailComponent } from './video/video-thumbnail.component' 24import { VideoThumbnailComponent } from './video/video-thumbnail.component'
24import { VideoService } from './video/video.service' 25import { VideoService } from './video/video.service'
25 26
@@ -44,6 +45,7 @@ import { VideoService } from './video/video.service'
44 declarations: [ 45 declarations: [
45 LoaderComponent, 46 LoaderComponent,
46 VideoThumbnailComponent, 47 VideoThumbnailComponent,
48 VideoMiniatureComponent,
47 NumberFormatterPipe, 49 NumberFormatterPipe,
48 FromNowPipe 50 FromNowPipe
49 ], 51 ],
@@ -66,6 +68,7 @@ import { VideoService } from './video/video.service'
66 68
67 LoaderComponent, 69 LoaderComponent,
68 VideoThumbnailComponent, 70 VideoThumbnailComponent,
71 VideoMiniatureComponent,
69 72
70 NumberFormatterPipe, 73 NumberFormatterPipe,
71 FromNowPipe 74 FromNowPipe
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts
index b1c323114..b4d13f37c 100644
--- a/client/src/app/shared/users/user.model.ts
+++ b/client/src/app/shared/users/user.model.ts
@@ -1,5 +1,5 @@
1import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared' 1import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
2import { Account } from '../../../../../shared/models/accounts' 2import { Account } from '../account/account.model'
3 3
4export type UserConstructorHash = { 4export type UserConstructorHash = {
5 id: number, 5 id: number,
@@ -52,8 +52,6 @@ export class User implements UserServerModel {
52 } 52 }
53 53
54 getAvatarPath () { 54 getAvatarPath () {
55 if (this.account && this.account.avatar) return this.account.avatar.path 55 return Account.GET_ACCOUNT_AVATAR_PATH(this.account)
56
57 return API_URL + '/client/assets/images/default-avatar.png'
58 } 56 }
59} 57}
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index bd4f6b1f8..5d07a276b 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -12,7 +12,7 @@
12 > 12 >
13 <my-video-miniature 13 <my-video-miniature
14 class="ng-animate" 14 class="ng-animate"
15 *ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort" 15 *ngFor="let video of videos" [video]="video" [user]="user"
16 > 16 >
17 </my-video-miniature> 17 </my-video-miniature>
18 </div> 18 </div>
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index 93c380b73..1a956da7c 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -1,3 +1,4 @@
1import { Account } from '../../../../../shared/models/accounts'
1import { Video } from '../../shared/video/video.model' 2import { Video } from '../../shared/video/video.model'
2import { AuthUser } from '../../core' 3import { AuthUser } from '../../core'
3import { 4import {
@@ -10,7 +11,7 @@ import {
10} from '../../../../../shared' 11} from '../../../../../shared'
11 12
12export class VideoDetails extends Video implements VideoDetailsServerModel { 13export class VideoDetails extends Video implements VideoDetailsServerModel {
13 account: string 14 accountName: string
14 by: string 15 by: string
15 createdAt: Date 16 createdAt: Date
16 updatedAt: Date 17 updatedAt: Date
@@ -44,6 +45,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
44 channel: VideoChannel 45 channel: VideoChannel
45 privacy: VideoPrivacy 46 privacy: VideoPrivacy
46 privacyLabel: string 47 privacyLabel: string
48 account: Account
47 49
48 constructor (hash: VideoDetailsServerModel) { 50 constructor (hash: VideoDetailsServerModel) {
49 super(hash) 51 super(hash)
@@ -53,6 +55,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
53 this.descriptionPath = hash.descriptionPath 55 this.descriptionPath = hash.descriptionPath
54 this.files = hash.files 56 this.files = hash.files
55 this.channel = hash.channel 57 this.channel = hash.channel
58 this.account = hash.account
56 } 59 }
57 60
58 getAppropriateMagnetUri (actualDownloadSpeed = 0) { 61 getAppropriateMagnetUri (actualDownloadSpeed = 0) {
@@ -71,7 +74,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
71 } 74 }
72 75
73 isRemovableBy (user: AuthUser) { 76 isRemovableBy (user: AuthUser) {
74 return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) 77 return user && this.isLocal === true && (this.accountName === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO))
75 } 78 }
76 79
77 isBlackistableBy (user: AuthUser) { 80 isBlackistableBy (user: AuthUser) {
@@ -79,6 +82,6 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
79 } 82 }
80 83
81 isUpdatableBy (user: AuthUser) { 84 isUpdatableBy (user: AuthUser) {
82 return user && this.isLocal === true && user.username === this.account 85 return user && this.isLocal === true && user.username === this.accountName
83 } 86 }
84} 87}
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html
index 7ac017235..7ac017235 100644
--- a/client/src/app/videos/video-list/shared/video-miniature.component.html
+++ b/client/src/app/shared/video/video-miniature.component.html
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/shared/video/video-miniature.component.scss
index 37e84897b..37e84897b 100644
--- a/client/src/app/videos/video-list/shared/video-miniature.component.scss
+++ b/client/src/app/shared/video/video-miniature.component.scss
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts
index e8fc8e911..4d79a74bb 100644
--- a/client/src/app/videos/video-list/shared/video-miniature.component.ts
+++ b/client/src/app/shared/video/video-miniature.component.ts
@@ -1,7 +1,6 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { User } from '../../../shared' 2import { User } from '../users'
3import { SortField } from '../../../shared/video/sort-field.type' 3import { Video } from './video.model'
4import { Video } from '../../../shared/video/video.model'
5 4
6@Component({ 5@Component({
7 selector: 'my-video-miniature', 6 selector: 'my-video-miniature',
@@ -9,7 +8,6 @@ import { Video } from '../../../shared/video/video.model'
9 templateUrl: './video-miniature.component.html' 8 templateUrl: './video-miniature.component.html'
10}) 9})
11export class VideoMiniatureComponent { 10export class VideoMiniatureComponent {
12 @Input() currentSort: SortField
13 @Input() user: User 11 @Input() user: User
14 @Input() video: Video 12 @Input() video: Video
15 13
diff --git a/client/src/app/shared/video/video-pagination.model.ts b/client/src/app/shared/video/video-pagination.model.ts
index 9e71769cb..e9db61596 100644
--- a/client/src/app/shared/video/video-pagination.model.ts
+++ b/client/src/app/shared/video/video-pagination.model.ts
@@ -1,5 +1,5 @@
1export interface VideoPagination { 1export interface VideoPagination {
2 currentPage: number 2 currentPage: number
3 itemsPerPage: number 3 itemsPerPage: number
4 totalItems: number 4 totalItems?: number
5} 5}
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index 6929c8755..d86ef8f92 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -1,8 +1,9 @@
1import { Video as VideoServerModel } from '../../../../../shared' 1import { Video as VideoServerModel } from '../../../../../shared'
2import { User } from '../' 2import { User } from '../'
3import { Account } from '../../../../../shared/models/accounts'
3 4
4export class Video implements VideoServerModel { 5export class Video implements VideoServerModel {
5 account: string 6 accountName: string
6 by: string 7 by: string
7 createdAt: Date 8 createdAt: Date
8 updatedAt: Date 9 updatedAt: Date
@@ -31,6 +32,7 @@ export class Video implements VideoServerModel {
31 likes: number 32 likes: number
32 dislikes: number 33 dislikes: number
33 nsfw: boolean 34 nsfw: boolean
35 account: Account
34 36
35 private static createByString (account: string, serverHost: string) { 37 private static createByString (account: string, serverHost: string) {
36 return account + '@' + serverHost 38 return account + '@' + serverHost
@@ -52,7 +54,7 @@ export class Video implements VideoServerModel {
52 absoluteAPIUrl = window.location.origin 54 absoluteAPIUrl = window.location.origin
53 } 55 }
54 56
55 this.account = hash.account 57 this.accountName = hash.accountName
56 this.createdAt = new Date(hash.createdAt.toString()) 58 this.createdAt = new Date(hash.createdAt.toString())
57 this.categoryLabel = hash.categoryLabel 59 this.categoryLabel = hash.categoryLabel
58 this.category = hash.category 60 this.category = hash.category
@@ -80,7 +82,7 @@ export class Video implements VideoServerModel {
80 this.dislikes = hash.dislikes 82 this.dislikes = hash.dislikes
81 this.nsfw = hash.nsfw 83 this.nsfw = hash.nsfw
82 84
83 this.by = Video.createByString(hash.account, hash.serverHost) 85 this.by = Video.createByString(hash.accountName, hash.serverHost)
84 } 86 }
85 87
86 isVideoNSFWForUser (user: User) { 88 isVideoNSFWForUser (user: User) {
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 aa1f2f77e..f31e82bff 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -1,18 +1,3 @@
1<div *ngIf="error" class="row">
2 <div class="alert alert-danger">
3 The video load seems to be abnormally long.
4 <ul>
5 <li>Maybe the server {{ video.serverHost }} is down :(</li>
6 <li>
7 If not, you can report an issue on
8 <a href="https://github.com/Chocobozzz/PeerTube/issues" title="Report an issue">
9 https://github.com/Chocobozzz/PeerTube/issues
10 </a>
11 </li>
12 </ul>
13 </div>
14</div>
15
16<div class="row"> 1<div class="row">
17 <!-- We need the video container for videojs so we just hide it --> 2 <!-- We need the video container for videojs so we just hide it -->
18 <div [hidden]="videoNotFound" id="video-container"> 3 <div [hidden]="videoNotFound" id="video-container">
@@ -23,167 +8,153 @@
23</div> 8</div>
24 9
25<!-- Video information --> 10<!-- Video information -->
26<div *ngIf="video !== null" id="video-info"> 11<div *ngIf="video" class="margin-content video-bottom">
27 <div class="row video-name-views"> 12 <div class="video-info">
28 <div class="col-xs-8 col-md-8 video-name"> 13 <div class="video-info-name-actions">
29 {{ video.name }} 14 <div class="video-info-name">{{ video.name }}</div>
30 </div> 15
31 16 <div class="video-info-actions">
32 <div class="col-xs-4 col-md-4 pull-right video-views"> 17 <div class="action-button">
33 {{ video.views}} views 18 <span
34 </div> 19 class="icon icon-like" title="Like this video"
35 </div> 20 [ngClass]="{ 'interactive': isUserLoggedIn(), 'activated': userRating === 'like' }" (click)="setLike()"
21 ></span>
22 </div>
36 23
37 <div class="row video-small-blocks"> 24 <div class="action-button">
38 <div class="col-xs-5 col-xs-3 col-md-3 video-small-block video-small-block-account"> 25 <span
39 <a class="option" title="Access to all videos of this user" [routerLink]="['/videos/list', { field: 'account', search: video.account }]"> 26 class="icon icon-dislike" title="Dislike this video"
40 <span class="glyphicon glyphicon-user"></span> 27 [ngClass]="{ 'interactive': isUserLoggedIn(), 'activated': userRating === 'dislike' }" (click)="setDislike()"
41 <span class="video-small-block-text">{{ video.by }}</span> 28 ></span>
42 </a> 29 </div>
43 </div>
44 30
45 <div class="col-xs-2 col-md-3 video-small-block video-small-block-share"> 31 <div (click)="showShareModal()" class="action-button">
46 <a class="option" (click)="showShareModal()" title="Share the video"> 32 <span class="icon icon-share"></span>
47 <span class="glyphicon glyphicon-share"></span> 33 Share
48 <span class="hidden-xs video-small-block-text">Share</span> 34 </div>
49 </a>
50 </div>
51 35
52 <div class="col-xs-2 col-md-3 video-small-block video-small-block-more"> 36 <div class="action-more" dropdown dropup="true" placement="right">
53 <div class="video-small-block-dropdown" dropdown dropup="true" placement="right"> 37 <div class="action-button" dropdownToggle>
54 <a class="option" title="Access to more options" dropdownToggle> 38 <span class="icon icon-more"></span>
55 <span class="glyphicon glyphicon-option-horizontal"></span> 39 </div>
56 <span class="hidden-xs video-small-block-text">More</span> 40
57 </a> 41 <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button">
58 42 <li *ngIf="canUserUpdateVideo()" role="menuitem">
59 <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button"> 43 <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.uuid ]">
60 <li *ngIf="canUserUpdateVideo()" role="menuitem"> 44 <span class="glyphicon glyphicon-pencil"></span> Update
61 <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.uuid ]"> 45 </a>
62 <span class="glyphicon glyphicon-pencil"></span> Update 46 </li>
63 </a> 47
64 </li> 48 <li role="menuitem">
65 49 <a class="dropdown-item" title="Download the video" href="#" (click)="showDownloadModal($event)">
66 <li role="menuitem"> 50 <span class="glyphicon glyphicon-download-alt"></span> Download
67 <a class="dropdown-item" title="Download the video" href="#" (click)="showDownloadModal($event)"> 51 </a>
68 <span class="glyphicon glyphicon-download-alt"></span> Download 52 </li>
69 </a> 53
70 </li> 54 <li *ngIf="isUserLoggedIn()" role="menuitem">
71 55 <a class="dropdown-item" title="Report this video" href="#" (click)="showReportModal($event)">
72 <li *ngIf="isUserLoggedIn()" role="menuitem"> 56 <span class="glyphicon glyphicon-alert"></span> Report
73 <a class="dropdown-item" title="Report this video" href="#" (click)="showReportModal($event)"> 57 </a>
74 <span class="glyphicon glyphicon-alert"></span> Report 58 </li>
75 </a> 59
76 </li> 60 <li *ngIf="isVideoRemovable()" role="menuitem">
77 61 <a class="dropdown-item" title="Delete this video" href="#" (click)="removeVideo($event)">
78 <li *ngIf="isVideoRemovable()" role="menuitem"> 62 <span class="glyphicon glyphicon-remove"></span> Delete
79 <a class="dropdown-item" title="Delete this video" href="#" (click)="removeVideo($event)"> 63 </a>
80 <span class="glyphicon glyphicon-remove"></span> Delete 64 </li>
81 </a> 65
82 </li> 66 <li *ngIf="isVideoBlacklistable()" role="menuitem">
83 67 <a class="dropdown-item" title="Blacklist this video" href="#" (click)="blacklistVideo($event)">
84 <li *ngIf="isVideoBlacklistable()" role="menuitem"> 68 <span class="glyphicon glyphicon-eye-close"></span> Blacklist
85 <a class="dropdown-item" title="Blacklist this video" href="#" (click)="blacklistVideo($event)"> 69 </a>
86 <span class="glyphicon glyphicon-eye-close"></span> Blacklist 70 </li>
87 </a> 71 </ul>
88 </li> 72 </div>
89 </ul>
90 </div> 73 </div>
91 </div> 74 </div>
92 75
93 <div class="col-xs-3 col-md-3 video-small-block video-small-block-rating"> 76 <div class="video-info-date-views">
94 <div class="video-small-block-like"> 77 {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views
95 <span 78 </div>
96 class="glyphicon glyphicon-thumbs-up" title="Like this video"
97 [ngClass]="{ 'interactive': isUserLoggedIn(), 'activated': userRating === 'like' }" (click)="setLike()"
98 ></span>
99
100 <span class="video-small-block-text">
101 {{ video.likes }}
102 </span>
103 </div>
104
105 <div class="video-small-block-dislike">
106 <span
107 class="glyphicon glyphicon-thumbs-down" title="Dislike this video"
108 [ngClass]="{ 'interactive': isUserLoggedIn(), 'activated': userRating === 'dislike' }" (click)="setDislike()"
109 ></span>
110 79
111 <span class="video-small-block-text"> 80 <div class="video-info-channel">
112 {{ video.dislikes }} 81 {{ video.channel.name }}
113 </span> 82 <!-- Here will be the subscribe button -->
114 </div>
115 </div> 83 </div>
116 </div>
117 84
118 <div class="row video-details"> 85 <div class="video-info-by">
119 <div class="video-details-date-description col-xs-8 col-md-9"> 86 By {{ video.by }}
120 <div class="video-details-date"> 87 <img [src]="getAvatarPath()" alt="Account avatar" />
121 Published on {{ video.createdAt | date:'short' }} 88 </div>
122 </div>
123 89
124 <div class="video-details-description" [innerHTML]="videoHTMLDescription"></div> 90 <div class="video-info-description">
91 <div class="video-info-description-html" [innerHTML]="videoHTMLDescription"></div>
125 92
126 <div class="video-details-description-more" *ngIf="completeDescriptionShown === false && video.description.length === 250" (click)="showMoreDescription()"> 93 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description.length === 250" (click)="showMoreDescription()">
127 Show more 94 Show more
128 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span> 95 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span>
129 <my-loader class="description-loading" [loading]="descriptionLoading"></my-loader> 96 <my-loader class="description-loading" [loading]="descriptionLoading"></my-loader>
130 </div> 97 </div>
131 98
132 <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-details-description-more"> 99 <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more">
133 Show less 100 Show less
134 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span> 101 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span>
135 </div> 102 </div>
136 </div> 103 </div>
137 104
138 <div class="video-details-attributes col-xs-4 col-md-3"> 105 <div class="video-attributes">
139 <div class="video-details-attribute"> 106 <div class="video-attribute">
140 <span class="video-details-attribute-label"> 107 <span class="video-attribute-label">
141 Privacy: 108 Privacy
142 </span> 109 </span>
143 <span class="video-details-attribute-value"> 110 <span class="video-attribute-value">
144 {{ video.privacyLabel }} 111 {{ video.privacyLabel }}
145 </span> 112 </span>
146 </div> 113 </div>
147 114
148 <div class="video-details-attribute"> 115 <div class="video-attribute">
149 <span class="video-details-attribute-label"> 116 <span class="video-attribute-label">
150 Category: 117 Category
151 </span> 118 </span>
152 <span class="video-details-attribute-value"> 119 <span class="video-attribute-value">
153 {{ video.categoryLabel }} 120 {{ video.categoryLabel }}
154 </span> 121 </span>
155 </div> 122 </div>
156 123
157 <div class="video-details-attribute"> 124 <div class="video-attribute">
158 <span class="video-details-attribute-label"> 125 <span class="video-attribute-label">
159 Licence: 126 Licence
160 </span> 127 </span>
161 <span class="video-details-attribute-value"> 128 <span class="video-attribute-value">
162 {{ video.licenceLabel }} 129 {{ video.licenceLabel }}
163 </span> 130 </span>
164 </div> 131 </div>
165 132
166 <div class="video-details-attribute"> 133 <div class="video-attribute">
167 <span class="video-details-attribute-label"> 134 <span class="video-attribute-label">
168 Language: 135 Language
169 </span> 136 </span>
170 <span class="video-details-attribute-value"> 137 <span class="video-attribute-value">
171 {{ video.languageLabel }} 138 {{ video.languageLabel }}
172 </span> 139 </span>
173 </div> 140 </div>
174 141
175 <div class="video-details-attribute"> 142 <div class="video-attribute">
176 <span class="video-details-attribute-label"> 143 <span class="video-attribute-label">
177 Tags: 144 Tags
178 </span> 145 </span>
179 146
180 <div class="video-details-tags"> 147 <span class="video-attribute-value">
181 <a *ngFor="let tag of video.tags" [routerLink]="['/videos/list', { field: 'tags', search: tag }]" class="label label-primary"> 148 {{ getVideoTags() }}
182 {{ tag }} 149 </span>
183 </a>
184 </div>
185 </div> 150 </div>
151 </div>
152
153 </div>
186 154
155 <div class="other-videos">
156 <div *ngFor="let video of otherVideos">
157 <my-video-miniature [video]="video" [user]="user"></my-video-miniature>
187 </div> 158 </div>
188 </div> 159 </div>
189</div> 160</div>
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss
index 06c2de7c6..7bcfeb7c3 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/videos/+video-watch/video-watch.component.scss
@@ -17,167 +17,108 @@
17 font-weight: bold; 17 font-weight: bold;
18} 18}
19 19
20#torrent-info { 20.video-bottom {
21 font-size: 10px; 21 margin-top: 40px;
22 margin-top: 10px; 22 display: flex;
23 text-align: center;
24
25 div {
26 min-width: 60px;
27 }
28}
29
30#video-info {
31 .video-name-views {
32 font-weight: bold;
33 font-size: 18px;
34 min-height: $video-watch-title-height;
35 display: flex;
36 align-items: center;
37
38 .video-name {
39 padding-left: $video-watch-info-padding-left;
40 }
41 23
42 .video-views { 24 .video-info {
43 text-align: right; 25 flex-grow: 1;
44 // Keep a symmetry with the video name 26 margin-right: 28px;
45 padding-right: $video-watch-info-padding-left
46 }
47 27
48 } 28 .video-info-name-actions {
29 display: flex;
30 align-items: center;
49 31
50 .video-small-blocks { 32 .video-info-name {
51 height: $video-watch-info-height; 33 font-size: 27px;
52 color: $video-watch-info-color; 34 font-weight: $font-semibold;
53 border-color: $video-watch-border-color; 35 flex-grow: 1;
54 border-width: 1px 0px; 36 }
55 border-style: solid;
56 37
57 .video-small-block { 38 .video-info-actions {
58 height: $video-watch-info-height; 39 .action-button {
59 display: flex; 40 @include peertube-button;
60 flex-direction: column;
61 justify-content: center;
62 text-align: center;
63 41
64 a { 42 font-size: 15px;
65 cursor: pointer; 43 font-weight: $font-semibold;
66 transition: color 0.3s; 44 color: #585858;
67 white-space: nowrap; 45 background-color: #E5E5E5;
68 overflow: hidden; 46 display: inline-block;
69 text-overflow: ellipsis; 47 padding: 0 10px 0 10px;
70 48
71 &, &:hover { 49 &:hover {
72 color: inherit; 50 background-color: #EFEFEF;
73 text-decoration:none; 51 }
74 } 52 }
75 53
76 &:hover { 54 .action-more {
77 color: #000 !important; 55 display: inline-block;
78 } 56 }
79 57
80 &:hover > .glyphicon { 58 .icon {
81 opacity: 1 !important; 59 display: inline-block;
82 } 60 background-repeat: no-repeat;
83 } 61 background-size: contain;
62 width: 21px;
63 height: 21px;
64 vertical-align: middle;
65 position: relative;
66 top: -2px;
84 67
85 .option .glyphicon { 68 &.icon-like {
86 font-size: 22px; 69 background-image: url('../../../assets/images/video/like.svg');
87 color: inherit; 70 }
88 opacity: 0.15;
89 margin-bottom: 10px;
90 transition: opacity 0.3s;
91 }
92 71
93 .video-small-block-text { 72 &.icon-dislike {
94 font-size: 15px; 73 background-image: url('../../../assets/images/video/dislike.svg');
95 font-weight: bold; 74 }
96 }
97 }
98 75
99 .video-small-block:not(:last-child) { 76 &.icon-share {
100 border-width: 0 1px 0 0; 77 background-image: url('../../../assets/images/video/share.svg');
101 border-color: $video-watch-border-color; 78 }
102 border-style: solid;
103 }
104 79
105 .video-small-block-account, .video-small-block-more { 80 &.icon-more {
106 a.option { 81 background-image: url('../../../assets/images/video/more.svg');
107 display: block; 82 }
108
109 .glyphicon {
110 display: block;
111 } 83 }
112 } 84 }
113 } 85 }
114 86
115 .video-small-block-share, .video-small-block-more { 87 .video-info-date-views {
116 a.option { 88 font-size: 16px;
117 display: block; 89 margin-bottom: 10px;
118
119 .glyphicon {
120 display: block;
121 }
122 }
123 } 90 }
124 91
125 .video-small-block-more .video-small-block-dropdown { 92 .video-info-channel {
126 position: relative; 93 font-weight: $font-semibold;
127 94 font-size: 15px;
128 .dropdown-item .glyphicon {
129 margin-right: 5px;
130 }
131 } 95 }
132 96
133 .video-small-block-rating { 97 .video-info-by {
134 98 display: flex;
135 .video-small-block-like { 99 align-items: center;
136 margin-bottom: 10px; 100 font-size: 13px;
137 }
138
139 .video-small-block-text {
140 vertical-align: top;
141 }
142
143 .glyphicon {
144 font-size: 18px;
145 margin: 0 10px 0 0;
146 opacity: 0.3;
147 }
148
149 .interactive {
150 cursor: pointer;
151 transition: opacity, color 0.3s;
152 101
153 &.activated, &:hover { 102 img {
154 opacity: 1; 103 width: 16px;
155 color: #000; 104 height: 16px;
156 } 105 margin-left: 3px;
157 } 106 }
158 } 107 }
159 }
160
161 .video-details {
162 margin-top: 30px;
163 108
164 .video-details-date-description { 109 .video-info-description {
165 padding-left: $video-watch-info-padding-left; 110 margin: 20px 0;
111 font-size: 15px;
166 112
167 .description-loading { 113 .description-loading {
168 display: inline-block; 114 display: inline-block;
169 } 115 }
170 116
171 .video-details-date { 117 .video-info-description-more {
172 font-weight: bold;
173 margin-bottom: 30px;
174 }
175
176 .video-details-description-more {
177 cursor: pointer; 118 cursor: pointer;
178 margin-top: 15px; 119 font-weight: $font-semibold;
179 font-weight: bold; 120 color: #585858;
180 color: #acaeb7; 121 font-size: 14px;
181 122
182 .glyphicon { 123 .glyphicon {
183 position: relative; 124 position: relative;
@@ -186,109 +127,20 @@
186 } 127 }
187 } 128 }
188 129
189 .video-details-attributes { 130 .video-attributes {
190 font-weight: bold; 131 .video-attribute {
191 font-size: 12px; 132 font-size: 13px;
192 133 display: block;
193 .video-details-attribute { 134 margin-bottom: 12px;
194 display: flex;
195
196 .video-details-attribute-label {
197 color: $video-watch-info-color;
198 flex-basis: 60px;
199 flex-grow: 0;
200 flex-shrink: 0;
201 margin-right: 5px;
202 }
203 }
204 }
205
206 .video-details-tags {
207 display: flex;
208 flex-wrap: wrap;
209
210 a {
211 margin: 0 3px 3px 0;
212 font-size: 11px;
213 }
214 }
215 }
216
217 @media screen and (max-width: 800px) {
218 .video-name-views {
219 .video-name {
220 padding-left: 5px;
221 padding-right: 0px;
222 }
223
224 .video-views {
225 padding-left: 0px;
226 padding-right: 5px;
227 }
228 }
229
230 .video-small-blocks {
231 a, .video-small-block-text {
232 font-size: 13px !important;
233 }
234
235 .glyphicon {
236 font-size: 18px !important;
237 }
238
239 .video-small-block-account {
240 padding-left: 10px;
241 padding-right: 10px;
242 }
243 }
244
245 .video-details {
246 .video-details-date-description {
247 padding-left: 10px;
248 font-size: 13px !important;
249 }
250
251 .video-details-attributes {
252 font-size: 11px !important;
253 135
254 .video-details-attribute-label { 136 .video-attribute-label {
255 width: 50px; 137 width: 86px;
138 display: inline-block;
139 color: #585858;
140 font-weight: $font-bold;
256 } 141 }
257 } 142 }
258 } 143 }
259 }
260 144
261 @media screen and (max-width: 500px) {
262 .video-name-views {
263 font-size: 16px !important;
264 }
265
266 // Keep the same hierarchy than max-width: 800px
267 .video-small-blocks {
268 a, .video-small-block-text {
269 font-size: 10px !important;
270 }
271
272 .video-small-block-account {
273 padding-left: 5px;
274 padding-right: 5px;
275 }
276 }
277
278 .video-details {
279 .video-details-date-description {
280 margin-bottom: 30px;
281 width: 100%;
282
283 .video-details-date {
284 margin-bottom: 15px;
285 }
286 }
287
288 .video-details-attributes {
289 padding-left: 10px;
290 padding-right: 10px;
291 }
292 }
293 } 145 }
294} 146}
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 48842602e..3c6951403 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -10,6 +10,8 @@ import { UserVideoRateType, VideoRateType } from '../../../../../shared'
10import '../../../assets/player/peertube-videojs-plugin' 10import '../../../assets/player/peertube-videojs-plugin'
11import { AuthService, ConfirmService } from '../../core' 11import { AuthService, ConfirmService } from '../../core'
12import { VideoBlacklistService } from '../../shared' 12import { VideoBlacklistService } from '../../shared'
13import { Account } from '../../shared/account/account.model'
14import { Video } from '../../shared/video/video.model'
13import { MarkdownService } from '../shared' 15import { MarkdownService } from '../shared'
14import { VideoDownloadComponent } from './video-download.component' 16import { VideoDownloadComponent } from './video-download.component'
15import { VideoReportComponent } from './video-report.component' 17import { VideoReportComponent } from './video-report.component'
@@ -26,6 +28,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
26 @ViewChild('videoShareModal') videoShareModal: VideoShareComponent 28 @ViewChild('videoShareModal') videoShareModal: VideoShareComponent
27 @ViewChild('videoReportModal') videoReportModal: VideoReportComponent 29 @ViewChild('videoReportModal') videoReportModal: VideoReportComponent
28 30
31 otherVideos: Video[] = []
32
29 error = false 33 error = false
30 loading = false 34 loading = false
31 player: videojs.Player 35 player: videojs.Player
@@ -57,6 +61,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
57 ) {} 61 ) {}
58 62
59 ngOnInit () { 63 ngOnInit () {
64 this.videoService.getVideos({ currentPage: 1, itemsPerPage: 5 }, '-createdAt')
65 .subscribe(
66 data => this.otherVideos = data.videos,
67
68 err => console.error(err)
69 )
70
60 this.paramsSub = this.route.params.subscribe(routeParams => { 71 this.paramsSub = this.route.params.subscribe(routeParams => {
61 let uuid = routeParams['uuid'] 72 let uuid = routeParams['uuid']
62 this.videoService.getVideo(uuid).subscribe( 73 this.videoService.getVideo(uuid).subscribe(
@@ -114,27 +125,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
114 ) 125 )
115 } 126 }
116 127
117 removeVideo (event: Event) {
118 event.preventDefault()
119
120 this.confirmService.confirm('Do you really want to delete this video?', 'Delete').subscribe(
121 res => {
122 if (res === false) return
123
124 this.videoService.removeVideo(this.video.id)
125 .subscribe(
126 status => {
127 this.notificationsService.success('Success', `Video ${this.video.name} deleted.`)
128 // Go back to the video-list.
129 this.router.navigate(['/videos/list'])
130 },
131
132 error => this.notificationsService.error('Error', error.text)
133 )
134 }
135 )
136 }
137
138 blacklistVideo (event: Event) { 128 blacklistVideo (event: Event) {
139 event.preventDefault() 129 event.preventDefault()
140 130
@@ -165,7 +155,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
165 } 155 }
166 156
167 showLessDescription () { 157 showLessDescription () {
168
169 this.updateVideoDescription(this.shortVideoDescription) 158 this.updateVideoDescription(this.shortVideoDescription)
170 this.completeDescriptionShown = false 159 this.completeDescriptionShown = false
171 } 160 }
@@ -222,6 +211,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
222 return this.video.isBlackistableBy(this.authService.getUser()) 211 return this.video.isBlackistableBy(this.authService.getUser())
223 } 212 }
224 213
214 getAvatarPath () {
215 return Account.GET_ACCOUNT_AVATAR_PATH(this.video.account)
216 }
217
218 getVideoTags () {
219 if (!this.video || Array.isArray(this.video.tags) === false) return []
220
221 return this.video.tags.join(', ')
222 }
223
225 private updateVideoDescription (description: string) { 224 private updateVideoDescription (description: string) {
226 this.video.description = description 225 this.video.description = description
227 this.setVideoDescriptionHTML() 226 this.setVideoDescriptionHTML()
diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts
index 13024294e..5e7c7886c 100644
--- a/client/src/app/videos/video-list/index.ts
+++ b/client/src/app/videos/video-list/index.ts
@@ -1,4 +1,3 @@
1export * from './video-recently-added.component' 1export * from './video-recently-added.component'
2export * from './video-trending.component' 2export * from './video-trending.component'
3export * from './video-search.component' 3export * from './video-search.component'
4export * from './shared'
diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts
deleted file mode 100644
index 2778f2d9e..000000000
--- a/client/src/app/videos/video-list/shared/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * from './video-miniature.component'
diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts
index 8c8d52ad9..4b14d1da8 100644
--- a/client/src/app/videos/videos.module.ts
+++ b/client/src/app/videos/videos.module.ts
@@ -1,6 +1,6 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedModule } from '../shared' 2import { SharedModule } from '../shared'
3import { VideoMiniatureComponent, VideoSearchComponent } from './video-list' 3import { VideoSearchComponent } from './video-list'
4import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' 4import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component'
5import { VideoTrendingComponent } from './video-list/video-trending.component' 5import { VideoTrendingComponent } from './video-list/video-trending.component'
6import { VideosRoutingModule } from './videos-routing.module' 6import { VideosRoutingModule } from './videos-routing.module'
@@ -17,7 +17,6 @@ import { VideosComponent } from './videos.component'
17 17
18 VideoTrendingComponent, 18 VideoTrendingComponent,
19 VideoRecentlyAddedComponent, 19 VideoRecentlyAddedComponent,
20 VideoMiniatureComponent,
21 VideoSearchComponent 20 VideoSearchComponent
22 ], 21 ],
23 22
diff --git a/client/src/assets/images/video/dislike.svg b/client/src/assets/images/video/dislike.svg
new file mode 100644
index 000000000..56a7908fb
--- /dev/null
+++ b/client/src/assets/images/video/dislike.svg
@@ -0,0 +1,14 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
5 <g id="Artboard-4" transform="translate(-752.000000, -1090.000000)" stroke="#585858" stroke-width="2">
6 <g id="Extras" transform="translate(48.000000, 1046.000000)">
7 <g id="thumbs-down" transform="translate(704.000000, 44.000000)">
8 <path d="M6,16 C6,18.5 6.5,21 8,21 L16.9938335,21 C17.5495239,21 18.1819788,20.5956028 18.4072817,20.0949295 L20.8562951,14.6526776 C21.7640882,12.6353595 20.7154925,11 18.5092545,11 L15.5,11 C15.5,11 18.5,5 15,5 C12.5,5 11.5,11 8,11 C6.5,11 6,13.5 6,16 Z" id="Path-188" stroke-linejoin="round" transform="translate(13.591488, 13.000000) scale(1, -1) translate(-13.591488, -13.000000) "></path>
9 <path d="M4,4.5 C4,4.5 3,7 3,10 C3,13 4,15.5 4,15.5" id="Path-189" transform="translate(3.500000, 10.000000) scale(1, -1) translate(-3.500000, -10.000000) "></path>
10 </g>
11 </g>
12 </g>
13 </g>
14</svg>
diff --git a/client/src/assets/images/video/like.svg b/client/src/assets/images/video/like.svg
new file mode 100644
index 000000000..5ef6c7b31
--- /dev/null
+++ b/client/src/assets/images/video/like.svg
@@ -0,0 +1,15 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>thumbs-up</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-708.000000, -643.000000)" stroke="#585858" stroke-width="2">
9 <g id="256" transform="translate(708.000000, 643.000000)">
10 <path d="M6,14 C6,16.5 6.5,19 8,19 L16.9938335,19 C17.5495239,19 18.1819788,18.5956028 18.4072817,18.0949295 L20.8562951,12.6526776 C21.7640882,10.6353595 20.7154925,9 18.5092545,9 L15.5,9 C15.5,9 18.5,3 15,3 C12.5,3 11.5,9 8,9 C6.5,9 6,11.5 6,14 Z" id="Path-188" stroke-linejoin="round"></path>
11 <path d="M4,8.5 C4,8.5 3,11 3,14 C3,17 4,19.5 4,19.5" id="Path-189"></path>
12 </g>
13 </g>
14 </g>
15</svg>
diff --git a/client/src/assets/images/video/more.svg b/client/src/assets/images/video/more.svg
new file mode 100644
index 000000000..dea392136
--- /dev/null
+++ b/client/src/assets/images/video/more.svg
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <g id="Artboard-4" transform="translate(-444.000000, -115.000000)" fill="#585858">
6 <g id="10" transform="translate(444.000000, 115.000000)">
7 <path d="M10,12 C10,10.8954305 10.8877296,10 12,10 C13.1045695,10 14,10.8877296 14,12 C14,13.1045695 13.1122704,14 12,14 C10.8954305,14 10,13.1122704 10,12 Z M17,12 C17,10.8954305 17.8877296,10 19,10 C20.1045695,10 21,10.8877296 21,12 C21,13.1045695 20.1122704,14 19,14 C17.8954305,14 17,13.1122704 17,12 Z M3,12 C3,10.8954305 3.88772964,10 5,10 C6.1045695,10 7,10.8877296 7,12 C7,13.1045695 6.11227036,14 5,14 C3.8954305,14 3,13.1122704 3,12 Z" id="Combined-Shape"></path>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/video/share.svg b/client/src/assets/images/video/share.svg
new file mode 100644
index 000000000..da0f43e81
--- /dev/null
+++ b/client/src/assets/images/video/share.svg
@@ -0,0 +1,16 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>share</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-312.000000, -203.000000)" stroke="#585858" stroke-width="2">
9 <g id="47" transform="translate(312.000000, 203.000000)">
10 <path d="M20,15 L20,18.0026083 C20,19.1057373 19.1073772,20 18.0049107,20 L5.99508929,20 C4.8932319,20 4,19.1073772 4,18.0049107 L4,5.99508929 C4,4.8932319 4.89585781,4 5.9973917,4 L9,4" id="Rectangle-460"></path>
11 <polyline id="Path-93" stroke-linejoin="round" points="13 4 20.0207973 4 20.0207973 11.0191059"></polyline>
12 <path d="M19,5 L12,12" id="Path-94" stroke-linejoin="round"></path>
13 </g>
14 </g>
15 </g>
16</svg>
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 4dce8e2fc..60023bc8c 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -486,7 +486,7 @@ toFormattedJSON = function (this: VideoInstance) {
486 description: this.getTruncatedDescription(), 486 description: this.getTruncatedDescription(),
487 serverHost, 487 serverHost,
488 isLocal: this.isOwned(), 488 isLocal: this.isOwned(),
489 account: this.VideoChannel.Account.name, 489 accountName: this.VideoChannel.Account.name,
490 duration: this.duration, 490 duration: this.duration,
491 views: this.views, 491 views: this.views,
492 likes: this.likes, 492 likes: this.likes,
@@ -514,6 +514,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) {
514 privacy: this.privacy, 514 privacy: this.privacy,
515 descriptionPath: this.getDescriptionPath(), 515 descriptionPath: this.getDescriptionPath(),
516 channel: this.VideoChannel.toFormattedJSON(), 516 channel: this.VideoChannel.toFormattedJSON(),
517 account: this.VideoChannel.Account.toFormattedJSON(),
517 files: [] 518 files: []
518 } 519 }
519 520
diff --git a/server/tests/api/follows.ts b/server/tests/api/follows.ts
index aadae3cce..dcb4c8bd9 100644
--- a/server/tests/api/follows.ts
+++ b/server/tests/api/follows.ts
@@ -227,7 +227,7 @@ describe('Test follows', function () {
227 expect(videoDetails.nsfw).to.be.ok 227 expect(videoDetails.nsfw).to.be.ok
228 expect(videoDetails.description).to.equal('my super description') 228 expect(videoDetails.description).to.equal('my super description')
229 expect(videoDetails.serverHost).to.equal('localhost:9003') 229 expect(videoDetails.serverHost).to.equal('localhost:9003')
230 expect(videoDetails.account).to.equal('root') 230 expect(videoDetails.accountName).to.equal('root')
231 expect(videoDetails.likes).to.equal(1) 231 expect(videoDetails.likes).to.equal(1)
232 expect(videoDetails.dislikes).to.equal(1) 232 expect(videoDetails.dislikes).to.equal(1)
233 expect(videoDetails.isLocal).to.be.false 233 expect(videoDetails.isLocal).to.be.false
diff --git a/server/tests/api/multiple-servers.ts b/server/tests/api/multiple-servers.ts
index c80ded862..c7f19f261 100644
--- a/server/tests/api/multiple-servers.ts
+++ b/server/tests/api/multiple-servers.ts
@@ -111,13 +111,14 @@ describe('Test multiple servers', function () {
111 expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) 111 expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
112 expect(dateIsValid(video.createdAt)).to.be.true 112 expect(dateIsValid(video.createdAt)).to.be.true
113 expect(dateIsValid(video.updatedAt)).to.be.true 113 expect(dateIsValid(video.updatedAt)).to.be.true
114 expect(video.account).to.equal('root') 114 expect(video.accountName).to.equal('root')
115 115
116 const res2 = await getVideo(server.url, video.uuid) 116 const res2 = await getVideo(server.url, video.uuid)
117 const videoDetails = res2.body 117 const videoDetails = res2.body
118 118
119 expect(videoDetails.channel.name).to.equal('my channel') 119 expect(videoDetails.channel.name).to.equal('my channel')
120 expect(videoDetails.channel.description).to.equal('super channel') 120 expect(videoDetails.channel.description).to.equal('super channel')
121 expect(videoDetails.account.name).to.equal('root')
121 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true 122 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
122 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true 123 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
123 expect(videoDetails.files).to.have.lengthOf(1) 124 expect(videoDetails.files).to.have.lengthOf(1)
@@ -201,7 +202,7 @@ describe('Test multiple servers', function () {
201 expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) 202 expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
202 expect(dateIsValid(video.createdAt)).to.be.true 203 expect(dateIsValid(video.createdAt)).to.be.true
203 expect(dateIsValid(video.updatedAt)).to.be.true 204 expect(dateIsValid(video.updatedAt)).to.be.true
204 expect(video.account).to.equal('user1') 205 expect(video.accountName).to.equal('user1')
205 206
206 if (server.url !== 'http://localhost:9002') { 207 if (server.url !== 'http://localhost:9002') {
207 expect(video.isLocal).to.be.false 208 expect(video.isLocal).to.be.false
@@ -316,7 +317,7 @@ describe('Test multiple servers', function () {
316 expect(video1.serverHost).to.equal('localhost:9003') 317 expect(video1.serverHost).to.equal('localhost:9003')
317 expect(video1.duration).to.equal(5) 318 expect(video1.duration).to.equal(5)
318 expect(video1.tags).to.deep.equal([ 'tag1p3' ]) 319 expect(video1.tags).to.deep.equal([ 'tag1p3' ])
319 expect(video1.account).to.equal('root') 320 expect(video1.accountName).to.equal('root')
320 expect(dateIsValid(video1.createdAt)).to.be.true 321 expect(dateIsValid(video1.createdAt)).to.be.true
321 expect(dateIsValid(video1.updatedAt)).to.be.true 322 expect(dateIsValid(video1.updatedAt)).to.be.true
322 323
@@ -342,7 +343,7 @@ describe('Test multiple servers', function () {
342 expect(video2.serverHost).to.equal('localhost:9003') 343 expect(video2.serverHost).to.equal('localhost:9003')
343 expect(video2.duration).to.equal(5) 344 expect(video2.duration).to.equal(5)
344 expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) 345 expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
345 expect(video2.account).to.equal('root') 346 expect(video2.accountName).to.equal('root')
346 expect(dateIsValid(video2.createdAt)).to.be.true 347 expect(dateIsValid(video2.createdAt)).to.be.true
347 expect(dateIsValid(video2.updatedAt)).to.be.true 348 expect(dateIsValid(video2.updatedAt)).to.be.true
348 349
@@ -690,7 +691,7 @@ describe('Test multiple servers', function () {
690 expect(baseVideo.licence).to.equal(video.licence) 691 expect(baseVideo.licence).to.equal(video.licence)
691 expect(baseVideo.category).to.equal(video.category) 692 expect(baseVideo.category).to.equal(video.category)
692 expect(baseVideo.nsfw).to.equal(video.nsfw) 693 expect(baseVideo.nsfw).to.equal(video.nsfw)
693 expect(baseVideo.account).to.equal(video.account) 694 expect(baseVideo.accountName).to.equal(video.accountName)
694 expect(baseVideo.tags).to.deep.equal(video.tags) 695 expect(baseVideo.tags).to.deep.equal(video.tags)
695 } 696 }
696 }) 697 })
diff --git a/server/tests/api/services.ts b/server/tests/api/services.ts
index 8d96ccc5e..4d480c305 100644
--- a/server/tests/api/services.ts
+++ b/server/tests/api/services.ts
@@ -46,7 +46,7 @@ describe('Test services', function () {
46 46
47 expect(res.body.html).to.equal(expectedHtml) 47 expect(res.body.html).to.equal(expectedHtml)
48 expect(res.body.title).to.equal(server.video.name) 48 expect(res.body.title).to.equal(server.video.name)
49 expect(res.body.author_name).to.equal(server.video.account) 49 expect(res.body.author_name).to.equal(server.video.accountName)
50 expect(res.body.width).to.equal(560) 50 expect(res.body.width).to.equal(560)
51 expect(res.body.height).to.equal(315) 51 expect(res.body.height).to.equal(315)
52 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) 52 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
@@ -66,7 +66,7 @@ describe('Test services', function () {
66 66
67 expect(res.body.html).to.equal(expectedHtml) 67 expect(res.body.html).to.equal(expectedHtml)
68 expect(res.body.title).to.equal(server.video.name) 68 expect(res.body.title).to.equal(server.video.name)
69 expect(res.body.author_name).to.equal(server.video.account) 69 expect(res.body.author_name).to.equal(server.video.accountName)
70 expect(res.body.height).to.equal(50) 70 expect(res.body.height).to.equal(50)
71 expect(res.body.width).to.equal(50) 71 expect(res.body.width).to.equal(50)
72 expect(res.body).to.not.have.property('thumbnail_url') 72 expect(res.body).to.not.have.property('thumbnail_url')
diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts
index fe192d391..d7e9ad41f 100644
--- a/server/tests/api/single-server.ts
+++ b/server/tests/api/single-server.ts
@@ -127,7 +127,7 @@ describe('Test a single server', function () {
127 expect(video.nsfw).to.be.ok 127 expect(video.nsfw).to.be.ok
128 expect(video.description).to.equal('my super description') 128 expect(video.description).to.equal('my super description')
129 expect(video.serverHost).to.equal('localhost:9001') 129 expect(video.serverHost).to.equal('localhost:9001')
130 expect(video.account).to.equal('root') 130 expect(video.accountName).to.equal('root')
131 expect(video.isLocal).to.be.true 131 expect(video.isLocal).to.be.true
132 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) 132 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
133 expect(dateIsValid(video.createdAt)).to.be.true 133 expect(dateIsValid(video.createdAt)).to.be.true
@@ -176,7 +176,7 @@ describe('Test a single server', function () {
176 expect(video.nsfw).to.be.ok 176 expect(video.nsfw).to.be.ok
177 expect(video.description).to.equal('my super description') 177 expect(video.description).to.equal('my super description')
178 expect(video.serverHost).to.equal('localhost:9001') 178 expect(video.serverHost).to.equal('localhost:9001')
179 expect(video.account).to.equal('root') 179 expect(video.accountName).to.equal('root')
180 expect(video.isLocal).to.be.true 180 expect(video.isLocal).to.be.true
181 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) 181 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
182 expect(dateIsValid(video.createdAt)).to.be.true 182 expect(dateIsValid(video.createdAt)).to.be.true
@@ -243,7 +243,7 @@ describe('Test a single server', function () {
243 expect(video.nsfw).to.be.ok 243 expect(video.nsfw).to.be.ok
244 expect(video.description).to.equal('my super description') 244 expect(video.description).to.equal('my super description')
245 expect(video.serverHost).to.equal('localhost:9001') 245 expect(video.serverHost).to.equal('localhost:9001')
246 expect(video.account).to.equal('root') 246 expect(video.accountName).to.equal('root')
247 expect(video.isLocal).to.be.true 247 expect(video.isLocal).to.be.true
248 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) 248 expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
249 expect(dateIsValid(video.createdAt)).to.be.true 249 expect(dateIsValid(video.createdAt)).to.be.true
@@ -298,7 +298,7 @@ describe('Test a single server', function () {
298 // expect(video.nsfw).to.be.ok 298 // expect(video.nsfw).to.be.ok
299 // expect(video.description).to.equal('my super description') 299 // expect(video.description).to.equal('my super description')
300 // expect(video.serverHost).to.equal('localhost:9001') 300 // expect(video.serverHost).to.equal('localhost:9001')
301 // expect(video.account).to.equal('root') 301 // expect(video.accountName).to.equal('root')
302 // expect(video.isLocal).to.be.true 302 // expect(video.isLocal).to.be.true
303 // expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) 303 // expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
304 // expect(dateIsValid(video.createdAt)).to.be.true 304 // expect(dateIsValid(video.createdAt)).to.be.true
@@ -563,7 +563,8 @@ describe('Test a single server', function () {
563 expect(video.nsfw).to.be.ok 563 expect(video.nsfw).to.be.ok
564 expect(video.description).to.equal('my super description updated') 564 expect(video.description).to.equal('my super description updated')
565 expect(video.serverHost).to.equal('localhost:9001') 565 expect(video.serverHost).to.equal('localhost:9001')
566 expect(video.account).to.equal('root') 566 expect(video.accountName).to.equal('root')
567 expect(video.account.name).to.equal('root')
567 expect(video.isLocal).to.be.true 568 expect(video.isLocal).to.be.true
568 expect(video.tags).to.deep.equal([ 'tagup1', 'tagup2' ]) 569 expect(video.tags).to.deep.equal([ 'tagup1', 'tagup2' ])
569 expect(dateIsValid(video.createdAt)).to.be.true 570 expect(dateIsValid(video.createdAt)).to.be.true
@@ -612,7 +613,7 @@ describe('Test a single server', function () {
612 expect(video.nsfw).to.be.ok 613 expect(video.nsfw).to.be.ok
613 expect(video.description).to.equal('my super description updated') 614 expect(video.description).to.equal('my super description updated')
614 expect(video.serverHost).to.equal('localhost:9001') 615 expect(video.serverHost).to.equal('localhost:9001')
615 expect(video.account).to.equal('root') 616 expect(video.accountName).to.equal('root')
616 expect(video.isLocal).to.be.true 617 expect(video.isLocal).to.be.true
617 expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ]) 618 expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
618 expect(dateIsValid(video.createdAt)).to.be.true 619 expect(dateIsValid(video.createdAt)).to.be.true
@@ -652,7 +653,7 @@ describe('Test a single server', function () {
652 expect(video.nsfw).to.be.ok 653 expect(video.nsfw).to.be.ok
653 expect(video.description).to.equal('hello everybody') 654 expect(video.description).to.equal('hello everybody')
654 expect(video.serverHost).to.equal('localhost:9001') 655 expect(video.serverHost).to.equal('localhost:9001')
655 expect(video.account).to.equal('root') 656 expect(video.accountName).to.equal('root')
656 expect(video.isLocal).to.be.true 657 expect(video.isLocal).to.be.true
657 expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ]) 658 expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
658 expect(dateIsValid(video.createdAt)).to.be.true 659 expect(dateIsValid(video.createdAt)).to.be.true
diff --git a/server/tests/api/users.ts b/server/tests/api/users.ts
index 33646e84f..5066e73fc 100644
--- a/server/tests/api/users.ts
+++ b/server/tests/api/users.ts
@@ -117,7 +117,7 @@ describe('Test users', function () {
117 const res = await getVideosList(server.url) 117 const res = await getVideosList(server.url)
118 const video = res.body.data[ 0 ] 118 const video = res.body.data[ 0 ]
119 119
120 expect(video.account) 120 expect(video.accountName)
121 .to 121 .to
122 .equal('root') 122 .equal('root')
123 videoId = video.id 123 videoId = video.id
@@ -487,7 +487,7 @@ describe('Test users', function () {
487 .equal(1) 487 .equal(1)
488 488
489 const video = res.body.data[ 0 ] 489 const video = res.body.data[ 0 ]
490 expect(video.account) 490 expect(video.accountName)
491 .to 491 .to
492 .equal('root') 492 .equal('root')
493 }) 493 })
diff --git a/server/tests/utils/servers.ts b/server/tests/utils/servers.ts
index faa2f19ff..8340fbc18 100644
--- a/server/tests/utils/servers.ts
+++ b/server/tests/utils/servers.ts
@@ -24,7 +24,7 @@ interface ServerInfo {
24 id: number 24 id: number
25 uuid: string 25 uuid: string
26 name: string 26 name: string
27 account: string 27 accountName: string
28 } 28 }
29 29
30 remoteVideo?: { 30 remoteVideo?: {
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index 08b29425c..dc12a05d9 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -1,3 +1,4 @@
1import { Account } from '../accounts'
1import { VideoChannel } from './video-channel.model' 2import { VideoChannel } from './video-channel.model'
2import { VideoPrivacy } from './video-privacy.enum' 3import { VideoPrivacy } from './video-privacy.enum'
3 4
@@ -13,7 +14,7 @@ export interface VideoFile {
13export interface Video { 14export interface Video {
14 id: number 15 id: number
15 uuid: string 16 uuid: string
16 account: string 17 accountName: string
17 createdAt: Date | string 18 createdAt: Date | string
18 updatedAt: Date | string 19 updatedAt: Date | string
19 categoryLabel: string 20 categoryLabel: string
@@ -43,4 +44,5 @@ export interface VideoDetails extends Video {
43 descriptionPath: string 44 descriptionPath: string
44 channel: VideoChannel 45 channel: VideoChannel
45 files: VideoFile[] 46 files: VideoFile[]
47 account: Account
46} 48}