aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/video
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared/video')
-rw-r--r--client/src/app/shared/video/abstract-video-list.html19
-rw-r--r--client/src/app/shared/video/abstract-video-list.scss25
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts21
-rw-r--r--client/src/app/shared/video/feed.component.html (renamed from client/src/app/shared/video/video-feed.component.html)9
-rw-r--r--client/src/app/shared/video/feed.component.scss18
-rw-r--r--client/src/app/shared/video/feed.component.ts11
-rw-r--r--client/src/app/shared/video/syndication.model.ts7
-rw-r--r--client/src/app/shared/video/video-details.model.ts13
-rw-r--r--client/src/app/shared/video/video-edit.model.ts4
-rw-r--r--client/src/app/shared/video/video-feed.component.scss19
-rw-r--r--client/src/app/shared/video/video-feed.component.ts10
-rw-r--r--client/src/app/shared/video/video-miniature.component.html3
-rw-r--r--client/src/app/shared/video/video-miniature.component.scss4
-rw-r--r--client/src/app/shared/video/video-miniature.component.ts9
-rw-r--r--client/src/app/shared/video/video.model.ts4
-rw-r--r--client/src/app/shared/video/video.service.ts8
16 files changed, 127 insertions, 57 deletions
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index d543ab7c1..1f97bc389 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -1,8 +1,21 @@
1<div [ngClass]="{ 'margin-content': marginContent }"> 1<div [ngClass]="{ 'margin-content': marginContent }">
2 <div *ngIf="titlePage" class="title-page title-page-single"> 2 <div class="videos-header">
3 {{ titlePage }} 3 <div *ngIf="titlePage" class="title-page title-page-single">
4 <div placement="bottom" [ngbTooltip]="titleTooltip" container="body">
5 {{ titlePage }}
6 </div>
7 </div>
8
9 <my-feed [syndicationItems]="syndicationItems"></my-feed>
10
11 <div class="moderation-block" *ngIf="displayModerationBlock">
12 <my-peertube-checkbox
13 (change)="toggleModerationDisplay()"
14 inputName="display-unlisted-private" i18n-labelText labelText="Display unlisted and private videos"
15 >
16 </my-peertube-checkbox>
17 </div>
4 </div> 18 </div>
5 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
6 19
7 <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div> 20 <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
8 <div 21 <div
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss
index 3f9c73a29..292ede698 100644
--- a/client/src/app/shared/video/abstract-video-list.scss
+++ b/client/src/app/shared/video/abstract-video-list.scss
@@ -8,12 +8,27 @@
8 } 8 }
9} 9}
10 10
11.title-page.title-page-single { 11.videos-header {
12 margin-right: 5px; 12 display: flex;
13} 13 height: 80px;
14 align-items: center;
15
16 .title-page.title-page-single {
17 margin: 0 5px 0 0;
18 }
14 19
15my-video-feed { 20 my-feed {
16 display: inline-block; 21 display: inline-block;
22 top: 1px;
23 min-width: 60px;
24 }
25
26 .moderation-block {
27 display: flex;
28 flex-grow: 1;
29 justify-content: flex-end;
30 align-items: center;
31 }
17} 32}
18 33
19@media screen and (max-width: 500px) { 34@media screen and (max-width: 500px) {
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 763791165..b0633be4a 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -3,7 +3,6 @@ import { ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { Location } from '@angular/common' 4import { Location } from '@angular/common'
5import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' 5import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive'
6import { NotificationsService } from 'angular2-notifications'
7import { fromEvent, Observable, Subscription } from 'rxjs' 6import { fromEvent, Observable, Subscription } from 'rxjs'
8import { AuthService } from '../../core/auth' 7import { AuthService } from '../../core/auth'
9import { ComponentPagination } from '../rest/component-pagination.model' 8import { ComponentPagination } from '../rest/component-pagination.model'
@@ -12,6 +11,8 @@ import { Video } from './video.model'
12import { I18n } from '@ngx-translate/i18n-polyfill' 11import { I18n } from '@ngx-translate/i18n-polyfill'
13import { ScreenService } from '@app/shared/misc/screen.service' 12import { ScreenService } from '@app/shared/misc/screen.service'
14import { OwnerDisplayType } from '@app/shared/video/video-miniature.component' 13import { OwnerDisplayType } from '@app/shared/video/video-miniature.component'
14import { Syndication } from '@app/shared/video/syndication.model'
15import { Notifier } from '@app/core'
15 16
16export abstract class AbstractVideoList implements OnInit, OnDestroy { 17export abstract class AbstractVideoList implements OnInit, OnDestroy {
17 private static LINES_PER_PAGE = 4 18 private static LINES_PER_PAGE = 4
@@ -27,7 +28,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
27 sort: VideoSortField = '-publishedAt' 28 sort: VideoSortField = '-publishedAt'
28 categoryOneOf?: number 29 categoryOneOf?: number
29 defaultSort: VideoSortField = '-publishedAt' 30 defaultSort: VideoSortField = '-publishedAt'
30 syndicationItems = [] 31 syndicationItems: Syndication[] = []
31 32
32 loadOnInit = true 33 loadOnInit = true
33 marginContent = true 34 marginContent = true
@@ -37,11 +38,13 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
37 videoPages: Video[][] = [] 38 videoPages: Video[][] = []
38 ownerDisplayType: OwnerDisplayType = 'account' 39 ownerDisplayType: OwnerDisplayType = 'account'
39 firstLoadedPage: number 40 firstLoadedPage: number
41 displayModerationBlock = false
42 titleTooltip: string
40 43
41 protected baseVideoWidth = 215 44 protected baseVideoWidth = 215
42 protected baseVideoHeight = 205 45 protected baseVideoHeight = 205
43 46
44 protected abstract notificationsService: NotificationsService 47 protected abstract notifier: Notifier
45 protected abstract authService: AuthService 48 protected abstract authService: AuthService
46 protected abstract router: Router 49 protected abstract router: Router
47 protected abstract route: ActivatedRoute 50 protected abstract route: ActivatedRoute
@@ -58,7 +61,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
58 private resizeSubscription: Subscription 61 private resizeSubscription: Subscription
59 62
60 abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number}> 63 abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number}>
61 abstract generateSyndicationList () 64 abstract generateSyndicationList (): void
62 65
63 get user () { 66 get user () {
64 return this.authService.getUser() 67 return this.authService.getUser()
@@ -155,11 +158,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
155 }, 158 },
156 error => { 159 error => {
157 this.loadingPage[page] = false 160 this.loadingPage[page] = false
158 this.notificationsService.error(this.i18n('Error'), error.message) 161 this.notifier.error(error.message)
159 } 162 }
160 ) 163 )
161 } 164 }
162 165
166 toggleModerationDisplay () {
167 throw new Error('toggleModerationDisplay is not implemented')
168 }
169
163 protected hasMoreVideos () { 170 protected hasMoreVideos () {
164 // No results 171 // No results
165 if (this.pagination.totalItems === 0) return false 172 if (this.pagination.totalItems === 0) return false
@@ -206,7 +213,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
206 protected setNewRouteParams () { 213 protected setNewRouteParams () {
207 const paramsObject = this.buildRouteParams() 214 const paramsObject = this.buildRouteParams()
208 215
209 const queryParams = Object.keys(paramsObject).map(p => p + '=' + paramsObject[p]).join('&') 216 const queryParams = Object.keys(paramsObject)
217 .map(p => p + '=' + paramsObject[p])
218 .join('&')
210 this.location.replaceState(this.currentRoute, queryParams) 219 this.location.replaceState(this.currentRoute, queryParams)
211 } 220 }
212 221
diff --git a/client/src/app/shared/video/video-feed.component.html b/client/src/app/shared/video/feed.component.html
index 16116ba88..f7624ec01 100644
--- a/client/src/app/shared/video/video-feed.component.html
+++ b/client/src/app/shared/video/feed.component.html
@@ -1,10 +1,11 @@
1<div class="video-feed"> 1<div class="video-feed">
2 <span 2 <my-global-icon
3 *ngIf="syndicationItems.length !== 0" [ngbPopover]="feedsList" [autoClose]="true" placement="bottom" 3 *ngIf="syndicationItems.length !== 0" [ngbPopover]="feedsList" [autoClose]="true" placement="bottom"
4 class="icon icon-syndication" role="button" 4 class="icon-syndication" role="button" iconName="syndication"
5 ></span> 5 >
6 </my-global-icon>
6 7
7 <ng-template #feedsList> 8 <ng-template #feedsList>
8 <a *ngFor="let item of syndicationItems" [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a> 9 <a *ngFor="let item of syndicationItems" [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a>
9 </ng-template> 10 </ng-template>
10</div> \ No newline at end of file 11</div>
diff --git a/client/src/app/shared/video/feed.component.scss b/client/src/app/shared/video/feed.component.scss
new file mode 100644
index 000000000..ed1dc17d3
--- /dev/null
+++ b/client/src/app/shared/video/feed.component.scss
@@ -0,0 +1,18 @@
1@import '_variables';
2@import '_mixins';
3
4.video-feed {
5 a {
6 color: black;
7 display: block;
8 }
9
10 my-global-icon {
11 cursor: pointer;
12 width: 12px;
13 position: relative;
14 top: -2px;
15
16 @include apply-svg-color(var(--mainForegroundColor))
17 }
18}
diff --git a/client/src/app/shared/video/feed.component.ts b/client/src/app/shared/video/feed.component.ts
new file mode 100644
index 000000000..12507458f
--- /dev/null
+++ b/client/src/app/shared/video/feed.component.ts
@@ -0,0 +1,11 @@
1import { Component, Input } from '@angular/core'
2import { Syndication } from '@app/shared/video/syndication.model'
3
4@Component({
5 selector: 'my-feed',
6 styleUrls: [ './feed.component.scss' ],
7 templateUrl: './feed.component.html'
8})
9export class FeedComponent {
10 @Input() syndicationItems: Syndication[]
11}
diff --git a/client/src/app/shared/video/syndication.model.ts b/client/src/app/shared/video/syndication.model.ts
new file mode 100644
index 000000000..c59ab01e8
--- /dev/null
+++ b/client/src/app/shared/video/syndication.model.ts
@@ -0,0 +1,7 @@
1import { FeedFormat } from '../../../../../shared/models/feeds/feed-format.enum'
2
3export interface Syndication {
4 format: FeedFormat,
5 label: string,
6 url: string
7}
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index 5ff3926c4..388357343 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -3,6 +3,8 @@ import { AuthUser } from '../../core'
3import { Video } from '../../shared/video/video.model' 3import { Video } from '../../shared/video/video.model'
4import { Account } from '@app/shared/account/account.model' 4import { Account } from '@app/shared/account/account.model'
5import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 5import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6import { VideoStreamingPlaylist } from '../../../../../shared/models/videos/video-streaming-playlist.model'
7import { VideoStreamingPlaylistType } from '../../../../../shared/models/videos/video-streaming-playlist.type'
6 8
7export class VideoDetails extends Video implements VideoDetailsServerModel { 9export class VideoDetails extends Video implements VideoDetailsServerModel {
8 descriptionPath: string 10 descriptionPath: string
@@ -20,6 +22,10 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
20 likesPercent: number 22 likesPercent: number
21 dislikesPercent: number 23 dislikesPercent: number
22 24
25 trackerUrls: string[]
26
27 streamingPlaylists: VideoStreamingPlaylist[]
28
23 constructor (hash: VideoDetailsServerModel, translations = {}) { 29 constructor (hash: VideoDetailsServerModel, translations = {}) {
24 super(hash, translations) 30 super(hash, translations)
25 31
@@ -32,6 +38,9 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
32 this.commentsEnabled = hash.commentsEnabled 38 this.commentsEnabled = hash.commentsEnabled
33 this.downloadEnabled = hash.downloadEnabled 39 this.downloadEnabled = hash.downloadEnabled
34 40
41 this.trackerUrls = hash.trackerUrls
42 this.streamingPlaylists = hash.streamingPlaylists
43
35 this.buildLikeAndDislikePercents() 44 this.buildLikeAndDislikePercents()
36 } 45 }
37 46
@@ -55,4 +64,8 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
55 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 64 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
56 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100 65 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100
57 } 66 }
67
68 getHlsPlaylist () {
69 return this.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
70 }
58} 71}
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts
index 5bb0510f9..18c62a1f9 100644
--- a/client/src/app/shared/video/video-edit.model.ts
+++ b/client/src/app/shared/video/video-edit.model.ts
@@ -59,14 +59,14 @@ export class VideoEdit implements VideoUpdate {
59 } 59 }
60 } 60 }
61 61
62 patch (values: Object) { 62 patch (values: { [ id: string ]: string }) {
63 Object.keys(values).forEach((key) => { 63 Object.keys(values).forEach((key) => {
64 this[ key ] = values[ key ] 64 this[ key ] = values[ key ]
65 }) 65 })
66 66
67 // If schedule publication, the video is private and will be changed to public privacy 67 // If schedule publication, the video is private and will be changed to public privacy
68 if (parseInt(values['privacy'], 10) === VideoEdit.SPECIAL_SCHEDULED_PRIVACY) { 68 if (parseInt(values['privacy'], 10) === VideoEdit.SPECIAL_SCHEDULED_PRIVACY) {
69 const updateAt = (values['schedulePublicationAt'] as Date) 69 const updateAt = new Date(values['schedulePublicationAt'])
70 updateAt.setSeconds(0) 70 updateAt.setSeconds(0)
71 71
72 this.privacy = VideoPrivacy.PRIVATE 72 this.privacy = VideoPrivacy.PRIVATE
diff --git a/client/src/app/shared/video/video-feed.component.scss b/client/src/app/shared/video/video-feed.component.scss
deleted file mode 100644
index 385764be0..000000000
--- a/client/src/app/shared/video/video-feed.component.scss
+++ /dev/null
@@ -1,19 +0,0 @@
1@import '_mixins';
2
3.video-feed {
4 a {
5 color: black;
6 display: block;
7 }
8
9 .icon {
10 @include icon(12px);
11
12 &.icon-syndication {
13 position: relative;
14 top: -2px;
15 background-color: var(--mainForegroundColor);
16 mask-image: url('../../../assets/images/global/syndication.svg');
17 }
18 }
19} \ No newline at end of file
diff --git a/client/src/app/shared/video/video-feed.component.ts b/client/src/app/shared/video/video-feed.component.ts
deleted file mode 100644
index 6922153c0..000000000
--- a/client/src/app/shared/video/video-feed.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
1import { Component, Input } from '@angular/core'
2
3@Component({
4 selector: 'my-video-feed',
5 styleUrls: [ './video-feed.component.scss' ],
6 templateUrl: './video-feed.component.html'
7})
8export class VideoFeedComponent {
9 @Input() syndicationItems
10}
diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html
index cfc483018..2c635fa2f 100644
--- a/client/src/app/shared/video/video-miniature.component.html
+++ b/client/src/app/shared/video/video-miniature.component.html
@@ -7,6 +7,9 @@
7 class="video-miniature-name" 7 class="video-miniature-name"
8 [routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur }" 8 [routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur }"
9 > 9 >
10 <span *ngIf="isUnlistedVideo()" class="badge badge-warning" i18n>Unlisted</span>
11 <span *ngIf="isPrivateVideo()" class="badge badge-danger" i18n>Private</span>
12
10 {{ video.name }} 13 {{ video.name }}
11 </a> 14 </a>
12 15
diff --git a/client/src/app/shared/video/video-miniature.component.scss b/client/src/app/shared/video/video-miniature.component.scss
index 895879adc..f44bdf9a9 100644
--- a/client/src/app/shared/video/video-miniature.component.scss
+++ b/client/src/app/shared/video/video-miniature.component.scss
@@ -50,10 +50,10 @@
50 text-overflow: ellipsis; 50 text-overflow: ellipsis;
51 white-space: nowrap; 51 white-space: nowrap;
52 font-size: 13px; 52 font-size: 13px;
53 color: #585858; 53 color: $grey-foreground-color;
54 54
55 &:hover { 55 &:hover {
56 color: #303030; 56 color: $grey-foreground-hover-color;
57 } 57 }
58 } 58 }
59 } 59 }
diff --git a/client/src/app/shared/video/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts
index 7e8692b0b..2f951a1f1 100644
--- a/client/src/app/shared/video/video-miniature.component.ts
+++ b/client/src/app/shared/video/video-miniature.component.ts
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core
2import { User } from '../users' 2import { User } from '../users'
3import { Video } from './video.model' 3import { Video } from './video.model'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { VideoPrivacy } from '../../../../../shared'
5 6
6export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto' 7export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto'
7 8
@@ -49,4 +50,12 @@ export class VideoMiniatureComponent implements OnInit {
49 displayOwnerVideoChannel () { 50 displayOwnerVideoChannel () {
50 return this.ownerDisplayTypeChosen === 'videoChannel' 51 return this.ownerDisplayTypeChosen === 'videoChannel'
51 } 52 }
53
54 isUnlistedVideo () {
55 return this.video.privacy.id === VideoPrivacy.UNLISTED
56 }
57
58 isPrivateVideo () {
59 return this.video.privacy.id === VideoPrivacy.PRIVATE
60 }
52} 61}
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index b92c96450..6ea83d13b 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -53,7 +53,7 @@ export class Video implements VideoServerModel {
53 displayName: string 53 displayName: string
54 url: string 54 url: string
55 host: string 55 host: string
56 avatar: Avatar 56 avatar?: Avatar
57 } 57 }
58 58
59 channel: { 59 channel: {
@@ -63,7 +63,7 @@ export class Video implements VideoServerModel {
63 displayName: string 63 displayName: string
64 url: string 64 url: string
65 host: string 65 host: string
66 avatar: Avatar 66 avatar?: Avatar
67 } 67 }
68 68
69 userHistory?: { 69 userHistory?: {
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index c9d6da7a4..565aad93b 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -6,11 +6,11 @@ import { Video as VideoServerModel, VideoDetails as VideoDetailsServerModel } fr
6import { ResultList } from '../../../../../shared/models/result-list.model' 6import { ResultList } from '../../../../../shared/models/result-list.model'
7import { 7import {
8 UserVideoRate, 8 UserVideoRate,
9 UserVideoRateType,
9 UserVideoRateUpdate, 10 UserVideoRateUpdate,
10 VideoConstant, 11 VideoConstant,
11 VideoFilter, 12 VideoFilter,
12 VideoPrivacy, 13 VideoPrivacy,
13 VideoRateType,
14 VideoUpdate 14 VideoUpdate
15} from '../../../../../shared/models/videos' 15} from '../../../../../shared/models/videos'
16import { FeedFormat } from '../../../../../shared/models/feeds/feed-format.enum' 16import { FeedFormat } from '../../../../../shared/models/feeds/feed-format.enum'
@@ -275,9 +275,9 @@ export class VideoService implements VideosProvider {
275 275
276 loadCompleteDescription (descriptionPath: string) { 276 loadCompleteDescription (descriptionPath: string) {
277 return this.authHttp 277 return this.authHttp
278 .get(environment.apiUrl + descriptionPath) 278 .get<{ description: string }>(environment.apiUrl + descriptionPath)
279 .pipe( 279 .pipe(
280 map(res => res[ 'description' ]), 280 map(res => res.description),
281 catchError(err => this.restExtractor.handleError(err)) 281 catchError(err => this.restExtractor.handleError(err))
282 ) 282 )
283 } 283 }
@@ -333,7 +333,7 @@ export class VideoService implements VideosProvider {
333 return privacies 333 return privacies
334 } 334 }
335 335
336 private setVideoRate (id: number, rateType: VideoRateType) { 336 private setVideoRate (id: number, rateType: UserVideoRateType) {
337 const url = VideoService.BASE_VIDEO_URL + id + '/rate' 337 const url = VideoService.BASE_VIDEO_URL + id + '/rate'
338 const body: UserVideoRateUpdate = { 338 const body: UserVideoRateUpdate = {
339 rating: rateType 339 rating: rateType