diff options
Diffstat (limited to 'client/src/app/videos')
8 files changed, 92 insertions, 16 deletions
diff --git a/client/src/app/videos/shared/video.model.ts b/client/src/app/videos/shared/video.model.ts index 5ed622dce..3c588c446 100644 --- a/client/src/app/videos/shared/video.model.ts +++ b/client/src/app/videos/shared/video.model.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | import { User } from '../../shared'; | ||
2 | |||
1 | export class Video { | 3 | export class Video { |
2 | author: string; | 4 | author: string; |
3 | by: string; | 5 | by: string; |
@@ -16,6 +18,7 @@ export class Video { | |||
16 | views: number; | 18 | views: number; |
17 | likes: number; | 19 | likes: number; |
18 | dislikes: number; | 20 | dislikes: number; |
21 | nsfw: boolean; | ||
19 | 22 | ||
20 | private static createByString(author: string, podHost: string) { | 23 | private static createByString(author: string, podHost: string) { |
21 | return author + '@' + podHost; | 24 | return author + '@' + podHost; |
@@ -47,6 +50,7 @@ export class Video { | |||
47 | views: number, | 50 | views: number, |
48 | likes: number, | 51 | likes: number, |
49 | dislikes: number, | 52 | dislikes: number, |
53 | nsfw: boolean | ||
50 | }) { | 54 | }) { |
51 | this.author = hash.author; | 55 | this.author = hash.author; |
52 | this.createdAt = new Date(hash.createdAt); | 56 | this.createdAt = new Date(hash.createdAt); |
@@ -64,11 +68,17 @@ export class Video { | |||
64 | this.views = hash.views; | 68 | this.views = hash.views; |
65 | this.likes = hash.likes; | 69 | this.likes = hash.likes; |
66 | this.dislikes = hash.dislikes; | 70 | this.dislikes = hash.dislikes; |
71 | this.nsfw = hash.nsfw; | ||
67 | 72 | ||
68 | this.by = Video.createByString(hash.author, hash.podHost); | 73 | this.by = Video.createByString(hash.author, hash.podHost); |
69 | } | 74 | } |
70 | 75 | ||
71 | isRemovableBy(user) { | 76 | isRemovableBy(user: User) { |
72 | return this.isLocal === true && user && this.author === user.username; | 77 | return this.isLocal === true && user && this.author === user.username; |
73 | } | 78 | } |
79 | |||
80 | isVideoNSFWForUser(user: User) { | ||
81 | // If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos... | ||
82 | return (this.nsfw && (!user || user.displayNSFW === false)); | ||
83 | } | ||
74 | } | 84 | } |
diff --git a/client/src/app/videos/video-add/video-add.component.html b/client/src/app/videos/video-add/video-add.component.html index 97a3c846a..a3c25c14b 100644 --- a/client/src/app/videos/video-add/video-add.component.html +++ b/client/src/app/videos/video-add/video-add.component.html | |||
@@ -15,6 +15,14 @@ | |||
15 | </div> | 15 | </div> |
16 | 16 | ||
17 | <div class="form-group"> | 17 | <div class="form-group"> |
18 | <label for="nsfw">NSFW</label> | ||
19 | <input | ||
20 | type="checkbox" id="nsfw" | ||
21 | formControlName="nsfw" | ||
22 | > | ||
23 | </div> | ||
24 | |||
25 | <div class="form-group"> | ||
18 | <label for="category">Category</label> | 26 | <label for="category">Category</label> |
19 | <select class="form-control" id="category" formControlName="category"> | 27 | <select class="form-control" id="category" formControlName="category"> |
20 | <option></option> | 28 | <option></option> |
diff --git a/client/src/app/videos/video-add/video-add.component.ts b/client/src/app/videos/video-add/video-add.component.ts index 8fae233d3..ea7ad2e5c 100644 --- a/client/src/app/videos/video-add/video-add.component.ts +++ b/client/src/app/videos/video-add/video-add.component.ts | |||
@@ -71,6 +71,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
71 | buildForm() { | 71 | buildForm() { |
72 | this.form = this.formBuilder.group({ | 72 | this.form = this.formBuilder.group({ |
73 | name: [ '', VIDEO_NAME.VALIDATORS ], | 73 | name: [ '', VIDEO_NAME.VALIDATORS ], |
74 | nsfw: [ false ], | ||
74 | category: [ '', VIDEO_CATEGORY.VALIDATORS ], | 75 | category: [ '', VIDEO_CATEGORY.VALIDATORS ], |
75 | licence: [ '', VIDEO_LICENCE.VALIDATORS ], | 76 | licence: [ '', VIDEO_LICENCE.VALIDATORS ], |
76 | description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], | 77 | description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], |
@@ -93,12 +94,14 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
93 | 94 | ||
94 | this.uploader.onBuildItemForm = (item, form) => { | 95 | this.uploader.onBuildItemForm = (item, form) => { |
95 | const name = this.form.value['name']; | 96 | const name = this.form.value['name']; |
97 | const nsfw = this.form.value['nsfw']; | ||
96 | const category = this.form.value['category']; | 98 | const category = this.form.value['category']; |
97 | const licence = this.form.value['licence']; | 99 | const licence = this.form.value['licence']; |
98 | const description = this.form.value['description']; | 100 | const description = this.form.value['description']; |
99 | 101 | ||
100 | form.append('name', name); | 102 | form.append('name', name); |
101 | form.append('category', category); | 103 | form.append('category', category); |
104 | form.append('nsfw', nsfw); | ||
102 | form.append('licence', licence); | 105 | form.append('licence', licence); |
103 | form.append('description', description); | 106 | form.append('description', description); |
104 | 107 | ||
diff --git a/client/src/app/videos/video-list/video-miniature.component.html b/client/src/app/videos/video-list/video-miniature.component.html index b2bf35435..94b892698 100644 --- a/client/src/app/videos/video-list/video-miniature.component.html +++ b/client/src/app/videos/video-list/video-miniature.component.html | |||
@@ -3,7 +3,11 @@ | |||
3 | [routerLink]="['/videos/watch', video.id]" [attr.title]="video.description" | 3 | [routerLink]="['/videos/watch', video.id]" [attr.title]="video.description" |
4 | class="video-miniature-thumbnail" | 4 | class="video-miniature-thumbnail" |
5 | > | 5 | > |
6 | <img [attr.src]="video.thumbnailPath" alt="video thumbnail" /> | 6 | <img *ngIf="isVideoNSFWForThisUser() === false" [attr.src]="video.thumbnailPath" alt="video thumbnail" /> |
7 | <div *ngIf="isVideoNSFWForThisUser()" class="thumbnail-nsfw"> | ||
8 | NSFW | ||
9 | </div> | ||
10 | |||
7 | <span class="video-miniature-duration">{{ video.duration }}</span> | 11 | <span class="video-miniature-duration">{{ video.duration }}</span> |
8 | </a> | 12 | </a> |
9 | <span | 13 | <span |
@@ -13,7 +17,7 @@ | |||
13 | 17 | ||
14 | <div class="video-miniature-informations"> | 18 | <div class="video-miniature-informations"> |
15 | <span class="video-miniature-name-tags"> | 19 | <span class="video-miniature-name-tags"> |
16 | <a [routerLink]="['/videos/watch', video.id]" [attr.title]="video.name" class="video-miniature-name">{{ video.name }}</a> | 20 | <a [routerLink]="['/videos/watch', video.id]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a> |
17 | 21 | ||
18 | <div class="video-miniature-tags"> | 22 | <div class="video-miniature-tags"> |
19 | <span *ngFor="let tag of video.tags" class="video-miniature-tag"> | 23 | <span *ngFor="let tag of video.tags" class="video-miniature-tag"> |
diff --git a/client/src/app/videos/video-list/video-miniature.component.scss b/client/src/app/videos/video-list/video-miniature.component.scss index b5d24271a..b8e90e8c5 100644 --- a/client/src/app/videos/video-list/video-miniature.component.scss +++ b/client/src/app/videos/video-list/video-miniature.component.scss | |||
@@ -15,6 +15,21 @@ | |||
15 | display: inline-block; | 15 | display: inline-block; |
16 | position: relative; | 16 | position: relative; |
17 | 17 | ||
18 | &:hover { | ||
19 | text-decoration: none !important; | ||
20 | } | ||
21 | |||
22 | .thumbnail-nsfw { | ||
23 | background-color: #000; | ||
24 | color: #fff; | ||
25 | text-align: center; | ||
26 | font-size: 30px; | ||
27 | line-height: 110px; | ||
28 | |||
29 | width: 200px; | ||
30 | height: 110px; | ||
31 | } | ||
32 | |||
18 | .video-miniature-duration { | 33 | .video-miniature-duration { |
19 | position: absolute; | 34 | position: absolute; |
20 | right: 5px; | 35 | right: 5px; |
diff --git a/client/src/app/videos/video-list/video-miniature.component.ts b/client/src/app/videos/video-list/video-miniature.component.ts index ba4715597..888026dde 100644 --- a/client/src/app/videos/video-list/video-miniature.component.ts +++ b/client/src/app/videos/video-list/video-miniature.component.ts | |||
@@ -2,7 +2,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; | |||
2 | 2 | ||
3 | import { NotificationsService } from 'angular2-notifications'; | 3 | import { NotificationsService } from 'angular2-notifications'; |
4 | 4 | ||
5 | import { ConfirmService } from '../../core'; | 5 | import { ConfirmService, ConfigService } from '../../core'; |
6 | import { SortField, Video, VideoService } from '../shared'; | 6 | import { SortField, Video, VideoService } from '../shared'; |
7 | import { User } from '../../shared'; | 7 | import { User } from '../../shared'; |
8 | 8 | ||
@@ -24,6 +24,7 @@ export class VideoMiniatureComponent { | |||
24 | constructor( | 24 | constructor( |
25 | private notificationsService: NotificationsService, | 25 | private notificationsService: NotificationsService, |
26 | private confirmService: ConfirmService, | 26 | private confirmService: ConfirmService, |
27 | private configService: ConfigService, | ||
27 | private videoService: VideoService | 28 | private videoService: VideoService |
28 | ) {} | 29 | ) {} |
29 | 30 | ||
@@ -31,6 +32,13 @@ export class VideoMiniatureComponent { | |||
31 | return this.hovering && this.video.isRemovableBy(this.user); | 32 | return this.hovering && this.video.isRemovableBy(this.user); |
32 | } | 33 | } |
33 | 34 | ||
35 | getVideoName() { | ||
36 | if (this.isVideoNSFWForThisUser()) | ||
37 | return 'NSFW'; | ||
38 | |||
39 | return this.video.name; | ||
40 | } | ||
41 | |||
34 | onBlur() { | 42 | onBlur() { |
35 | this.hovering = false; | 43 | this.hovering = false; |
36 | } | 44 | } |
@@ -52,4 +60,8 @@ export class VideoMiniatureComponent { | |||
52 | } | 60 | } |
53 | ); | 61 | ); |
54 | } | 62 | } |
63 | |||
64 | isVideoNSFWForThisUser() { | ||
65 | return this.video.isVideoNSFWForUser(this.user); | ||
66 | } | ||
55 | } | 67 | } |
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 5678f6df8..37ed70a99 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -1,12 +1,13 @@ | |||
1 | import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; | 1 | import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; |
2 | import { ActivatedRoute } from '@angular/router'; | 2 | import { ActivatedRoute, Router } from '@angular/router'; |
3 | import { Observable } from 'rxjs/Observable'; | ||
3 | import { Subscription } from 'rxjs/Subscription'; | 4 | import { Subscription } from 'rxjs/Subscription'; |
4 | 5 | ||
5 | import * as videojs from 'video.js'; | 6 | import * as videojs from 'video.js'; |
6 | import { MetaService } from '@nglibs/meta'; | 7 | import { MetaService } from '@nglibs/meta'; |
7 | import { NotificationsService } from 'angular2-notifications'; | 8 | import { NotificationsService } from 'angular2-notifications'; |
8 | 9 | ||
9 | import { AuthService } from '../../core'; | 10 | import { AuthService, ConfirmService } from '../../core'; |
10 | import { VideoMagnetComponent } from './video-magnet.component'; | 11 | import { VideoMagnetComponent } from './video-magnet.component'; |
11 | import { VideoShareComponent } from './video-share.component'; | 12 | import { VideoShareComponent } from './video-share.component'; |
12 | import { VideoReportComponent } from './video-report.component'; | 13 | import { VideoReportComponent } from './video-report.component'; |
@@ -47,7 +48,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
47 | private elementRef: ElementRef, | 48 | private elementRef: ElementRef, |
48 | private ngZone: NgZone, | 49 | private ngZone: NgZone, |
49 | private route: ActivatedRoute, | 50 | private route: ActivatedRoute, |
51 | private router: Router, | ||
50 | private videoService: VideoService, | 52 | private videoService: VideoService, |
53 | private confirmService: ConfirmService, | ||
51 | private metaService: MetaService, | 54 | private metaService: MetaService, |
52 | private webTorrentService: WebTorrentService, | 55 | private webTorrentService: WebTorrentService, |
53 | private authService: AuthService, | 56 | private authService: AuthService, |
@@ -58,15 +61,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
58 | this.paramsSub = this.route.params.subscribe(routeParams => { | 61 | this.paramsSub = this.route.params.subscribe(routeParams => { |
59 | let id = routeParams['id']; | 62 | let id = routeParams['id']; |
60 | this.videoService.getVideo(id).subscribe( | 63 | this.videoService.getVideo(id).subscribe( |
61 | video => { | 64 | video => this.onVideoFetched(video), |
62 | this.video = video; | 65 | |
63 | this.setOpenGraphTags(); | 66 | error => this.videoNotFound = true |
64 | this.loadVideo(); | ||
65 | this.checkUserRating(); | ||
66 | }, | ||
67 | error => { | ||
68 | this.videoNotFound = true; | ||
69 | } | ||
70 | ); | 67 | ); |
71 | }); | 68 | }); |
72 | 69 | ||
@@ -92,7 +89,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
92 | window.clearInterval(this.torrentInfosInterval); | 89 | window.clearInterval(this.torrentInfosInterval); |
93 | window.clearTimeout(this.errorTimer); | 90 | window.clearTimeout(this.errorTimer); |
94 | 91 | ||
95 | if (this.video !== null) { | 92 | if (this.video !== null && this.webTorrentService.has(this.video.magnetUri)) { |
96 | this.webTorrentService.remove(this.video.magnetUri); | 93 | this.webTorrentService.remove(this.video.magnetUri); |
97 | } | 94 | } |
98 | 95 | ||
@@ -206,6 +203,29 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
206 | ); | 203 | ); |
207 | } | 204 | } |
208 | 205 | ||
206 | private onVideoFetched(video: Video) { | ||
207 | this.video = video; | ||
208 | |||
209 | let observable; | ||
210 | if (this.video.isVideoNSFWForUser(this.authService.getUser())) { | ||
211 | observable = this.confirmService.confirm('This video is not safe for work. Are you sure you want to watch it?', 'NSFW'); | ||
212 | } else { | ||
213 | observable = Observable.of(true); | ||
214 | } | ||
215 | |||
216 | observable.subscribe( | ||
217 | res => { | ||
218 | if (res === false) { | ||
219 | return this.router.navigate([ '/videos/list' ]); | ||
220 | } | ||
221 | |||
222 | this.setOpenGraphTags(); | ||
223 | this.loadVideo(); | ||
224 | this.checkUserRating(); | ||
225 | } | ||
226 | ); | ||
227 | } | ||
228 | |||
209 | private updateVideoRating(oldRating: RateType, newRating: RateType) { | 229 | private updateVideoRating(oldRating: RateType, newRating: RateType) { |
210 | let likesToIncrement = 0; | 230 | let likesToIncrement = 0; |
211 | let dislikesToIncrement = 0; | 231 | let dislikesToIncrement = 0; |
diff --git a/client/src/app/videos/video-watch/webtorrent.service.ts b/client/src/app/videos/video-watch/webtorrent.service.ts index 0192167ee..630a5c469 100644 --- a/client/src/app/videos/video-watch/webtorrent.service.ts +++ b/client/src/app/videos/video-watch/webtorrent.service.ts | |||
@@ -26,4 +26,8 @@ export class WebTorrentService { | |||
26 | remove(magnetUri: string) { | 26 | remove(magnetUri: string) { |
27 | return this.client.remove(magnetUri); | 27 | return this.client.remove(magnetUri); |
28 | } | 28 | } |
29 | |||
30 | has(magnetUri: string) { | ||
31 | return this.client.get(magnetUri) !== null; | ||
32 | } | ||
29 | } | 33 | } |