aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2016-05-21 18:03:34 +0200
committerChocobozzz <florian.bigard@gmail.com>2016-05-21 18:08:23 +0200
commit501bc6c2b186f6a724a5b619d15aa44791f13995 (patch)
tree4a6e1d244c5f94305a25b6ec6f50f1a71ce9295d
parent295ba044afc394ef51dac22263063670362787ec (diff)
downloadPeerTube-501bc6c2b186f6a724a5b619d15aa44791f13995.tar.gz
PeerTube-501bc6c2b186f6a724a5b619d15aa44791f13995.tar.zst
PeerTube-501bc6c2b186f6a724a5b619d15aa44791f13995.zip
Thumbnail, author and duration support in client
-rw-r--r--client/angular/app/app.component.ts2
-rw-r--r--client/angular/videos/components/list/video-miniature.component.html22
-rw-r--r--client/angular/videos/components/list/video-miniature.component.scss57
-rw-r--r--client/angular/videos/components/list/video-miniature.component.ts47
-rw-r--r--client/angular/videos/components/list/videos-list.component.html13
-rw-r--r--client/angular/videos/components/list/videos-list.component.scss34
-rw-r--r--client/angular/videos/components/list/videos-list.component.ts14
-rw-r--r--client/angular/videos/components/watch/videos-watch.component.ts4
-rw-r--r--client/angular/videos/models/video.ts8
-rw-r--r--client/angular/videos/video.ts60
-rw-r--r--client/angular/videos/videos.service.ts (renamed from client/angular/videos/services/videos.service.ts)29
-rw-r--r--server/middlewares/reqValidators/videos.js2
12 files changed, 221 insertions, 71 deletions
diff --git a/client/angular/app/app.component.ts b/client/angular/app/app.component.ts
index 359d7128e..a105ed26a 100644
--- a/client/angular/app/app.component.ts
+++ b/client/angular/app/app.component.ts
@@ -5,7 +5,7 @@ import { HTTP_PROVIDERS } from '@angular/http';
5import { VideosAddComponent } from '../videos/components/add/videos-add.component'; 5import { VideosAddComponent } from '../videos/components/add/videos-add.component';
6import { VideosListComponent } from '../videos/components/list/videos-list.component'; 6import { VideosListComponent } from '../videos/components/list/videos-list.component';
7import { VideosWatchComponent } from '../videos/components/watch/videos-watch.component'; 7import { VideosWatchComponent } from '../videos/components/watch/videos-watch.component';
8import { VideosService } from '../videos/services/videos.service'; 8import { VideosService } from '../videos/videos.service';
9import { FriendsService } from '../friends/services/friends.service'; 9import { FriendsService } from '../friends/services/friends.service';
10import { UserLoginComponent } from '../users/components/login/login.component'; 10import { UserLoginComponent } from '../users/components/login/login.component';
11import { AuthService } from '../users/services/auth.service'; 11import { AuthService } from '../users/services/auth.service';
diff --git a/client/angular/videos/components/list/video-miniature.component.html b/client/angular/videos/components/list/video-miniature.component.html
new file mode 100644
index 000000000..b88a19d5e
--- /dev/null
+++ b/client/angular/videos/components/list/video-miniature.component.html
@@ -0,0 +1,22 @@
1<div class="video-miniature" (mouseenter)="onHover()" (mouseleave)="onBlur()">
2 <a
3 [routerLink]="['VideosWatch', { id: video.id }]" [attr.title]="video.description"
4 class="video-miniature-thumbnail"
5 >
6 <img [attr.src]="video.thumbnailPath" alt="video thumbnail" />
7 <span class="video-miniature-duration">{{ video.duration }}</span>
8 </a>
9 <span
10 *ngIf="displayRemoveIcon()" (click)="removeVideo(video.id)"
11 class="video-miniature-remove glyphicon glyphicon-remove"
12 ></span>
13
14 <div class="video-miniature-informations">
15 <a [routerLink]="['VideosWatch', { id: video.id }]" class="video-miniature-name">
16 <span>{{ video.name }}</span>
17 </a>
18
19 <span class="video-miniature-author">by {{ video.by }}</span>
20 <span class="video-miniature-created-date">on {{ video.createdDate | date:'short' }}</span>
21 </div>
22</div>
diff --git a/client/angular/videos/components/list/video-miniature.component.scss b/client/angular/videos/components/list/video-miniature.component.scss
new file mode 100644
index 000000000..dbcd65820
--- /dev/null
+++ b/client/angular/videos/components/list/video-miniature.component.scss
@@ -0,0 +1,57 @@
1.video-miniature {
2 width: 200px;
3 height: 200px;
4 display: inline-block;
5 margin-right: 40px;
6 position: relative;
7
8 .video-miniature-thumbnail {
9 display: block;
10 position: relative;
11
12 .video-miniature-duration {
13 position: absolute;
14 right: 2px;
15 bottom: 2px;
16 display: inline-block;
17 background-color: rgba(0, 0, 0, 0.8);
18 color: rgba(255, 255, 255, 0.8);
19 padding: 2px;
20 font-size: 11px;
21 }
22 }
23
24 .video-miniature-remove {
25 display: inline-block;
26 position: absolute;
27 left: 2px;
28 background-color: rgba(0, 0, 0, 0.8);
29 color: rgba(255, 255, 255, 0.8);
30 padding: 2px;
31 cursor: pointer;
32
33 &:hover {
34 color: rgba(255, 255, 255, 0.9);
35 }
36 }
37
38 .video-miniature-informations {
39 margin-left: 3px;
40
41 .video-miniature-name {
42 display: block;
43 font-weight: bold;
44
45 &:hover {
46 text-decoration: none;
47 }
48 }
49
50 .video-miniature-author, .video-miniature-created-date {
51 display: block;
52 margin-left: 1px;
53 font-size: 11px;
54 color: rgba(0, 0, 0, 0.5);
55 }
56 }
57}
diff --git a/client/angular/videos/components/list/video-miniature.component.ts b/client/angular/videos/components/list/video-miniature.component.ts
new file mode 100644
index 000000000..383c2c609
--- /dev/null
+++ b/client/angular/videos/components/list/video-miniature.component.ts
@@ -0,0 +1,47 @@
1import { Component, Input, Output, EventEmitter } from '@angular/core';
2import { DatePipe } from '@angular/common';
3import { ROUTER_DIRECTIVES } from '@angular/router-deprecated';
4
5import { Video } from '../../video';
6import { VideosService } from '../../videos.service';
7import { User } from '../../../users/models/user';
8
9@Component({
10 selector: 'my-video-miniature',
11 styleUrls: [ 'app/angular/videos/components/list/video-miniature.component.css' ],
12 templateUrl: 'app/angular/videos/components/list/video-miniature.component.html',
13 directives: [ ROUTER_DIRECTIVES ],
14 pipes: [ DatePipe ]
15})
16
17export class VideoMiniatureComponent {
18 @Output() removed = new EventEmitter<any>();
19
20 @Input() video: Video;
21 @Input() user: User;
22
23 hovering: boolean = false;
24
25 constructor(private _videosService: VideosService) {}
26
27 onHover() {
28 this.hovering = true;
29 }
30
31 onBlur() {
32 this.hovering = false;
33 }
34
35 displayRemoveIcon(): boolean {
36 return this.hovering && this.video.isRemovableBy(this.user);
37 }
38
39 removeVideo(id: string) {
40 if (confirm('Do you really want to remove this video?')) {
41 this._videosService.removeVideo(id).subscribe(
42 status => this.removed.emit(true),
43 error => alert(error)
44 );
45 }
46 }
47}
diff --git a/client/angular/videos/components/list/videos-list.component.html b/client/angular/videos/components/list/videos-list.component.html
index 4eeacbc77..776339d10 100644
--- a/client/angular/videos/components/list/videos-list.component.html
+++ b/client/angular/videos/components/list/videos-list.component.html
@@ -1,12 +1,3 @@
1<div *ngIf="videos.length === 0">There is no video.</div> 1<div *ngIf="videos.length === 0">There is no video.</div>
2<div *ngFor="let video of videos" class="video"> 2<my-video-miniature *ngFor="let video of videos" [video]="video" [user]="user" (removed)="onRemoved(video)">
3 <div> 3</my-video-miniature>
4 <a [routerLink]="['VideosWatch', { id: video.id }]" class="video_name">{{ video.name }}</a>
5 <span class="video_pod_url">{{ video.podUrl }}</span>
6 <span *ngIf="video.isLocal === true && user && video.author === user.username" (click)="removeVideo(video.id)" class="video_remove glyphicon glyphicon-remove"></span>
7 </div>
8
9 <div class="video_description">
10 {{ video.description }}
11 </div>
12</div>
diff --git a/client/angular/videos/components/list/videos-list.component.scss b/client/angular/videos/components/list/videos-list.component.scss
index 82ddd80e5..ac930978c 100644
--- a/client/angular/videos/components/list/videos-list.component.scss
+++ b/client/angular/videos/components/list/videos-list.component.scss
@@ -1,34 +1,8 @@
1.video {
2 margin-bottom: 10px;
3 transition: margin 0.5s ease;
4
5 &:hover {
6 margin-left: 5px;
7 }
8
9 a.video_name {
10 color: #333333;
11 margin-right: 5px;
12 }
13
14 .video_pod_url {
15 font-size: small;
16 color: rgba(0, 0, 0, 0.5);
17 }
18
19 .video_description {
20 font-size: small;
21 font-style: italic;
22 margin-left: 7px;
23 }
24
25 .video_remove {
26 margin: 5px;
27 cursor: pointer;
28 }
29}
30
31.loading { 1.loading {
32 display: inline-block; 2 display: inline-block;
33 margin-top: 100px; 3 margin-top: 100px;
34} 4}
5
6my-videos-miniature {
7 display: inline-block;
8}
diff --git a/client/angular/videos/components/list/videos-list.component.ts b/client/angular/videos/components/list/videos-list.component.ts
index 6ff0b2afb..6fc0c1f04 100644
--- a/client/angular/videos/components/list/videos-list.component.ts
+++ b/client/angular/videos/components/list/videos-list.component.ts
@@ -3,14 +3,15 @@ import { ROUTER_DIRECTIVES, RouteParams } from '@angular/router-deprecated';
3 3
4import { AuthService } from '../../../users/services/auth.service'; 4import { AuthService } from '../../../users/services/auth.service';
5import { User } from '../../../users/models/user'; 5import { User } from '../../../users/models/user';
6import { VideosService } from '../../services/videos.service'; 6import { VideosService } from '../../videos.service';
7import { Video } from '../../models/video'; 7import { Video } from '../../video';
8import { VideoMiniatureComponent } from './video-miniature.component';
8 9
9@Component({ 10@Component({
10 selector: 'my-videos-list', 11 selector: 'my-videos-list',
11 styleUrls: [ 'app/angular/videos/components/list/videos-list.component.css' ], 12 styleUrls: [ 'app/angular/videos/components/list/videos-list.component.css' ],
12 templateUrl: 'app/angular/videos/components/list/videos-list.component.html', 13 templateUrl: 'app/angular/videos/components/list/videos-list.component.html',
13 directives: [ ROUTER_DIRECTIVES ] 14 directives: [ ROUTER_DIRECTIVES, VideoMiniatureComponent ]
14}) 15})
15 16
16export class VideosListComponent implements OnInit { 17export class VideosListComponent implements OnInit {
@@ -50,11 +51,8 @@ export class VideosListComponent implements OnInit {
50 ); 51 );
51 } 52 }
52 53
53 removeVideo(id: string) { 54 onRemoved(video: Video): void {
54 this._videosService.removeVideo(id).subscribe( 55 this.videos.splice(this.videos.indexOf(video), 1);
55 status => this.getVideos(),
56 error => alert(error)
57 );
58 } 56 }
59 57
60} 58}
diff --git a/client/angular/videos/components/watch/videos-watch.component.ts b/client/angular/videos/components/watch/videos-watch.component.ts
index 3d1829b99..3eb005d07 100644
--- a/client/angular/videos/components/watch/videos-watch.component.ts
+++ b/client/angular/videos/components/watch/videos-watch.component.ts
@@ -5,8 +5,8 @@ import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe';
5// TODO import it with systemjs 5// TODO import it with systemjs
6declare var WebTorrent: any; 6declare var WebTorrent: any;
7 7
8import { Video } from '../../models/video'; 8import { Video } from '../../video';
9import { VideosService } from '../../services/videos.service'; 9import { VideosService } from '../../videos.service';
10 10
11@Component({ 11@Component({
12 selector: 'my-video-watch', 12 selector: 'my-video-watch',
diff --git a/client/angular/videos/models/video.ts b/client/angular/videos/models/video.ts
deleted file mode 100644
index e52c6d886..000000000
--- a/client/angular/videos/models/video.ts
+++ /dev/null
@@ -1,8 +0,0 @@
1export interface Video {
2 id: string;
3 name: string;
4 description: string;
5 magnetUri: string;
6 podUrl: string;
7 isLocal: boolean;
8}
diff --git a/client/angular/videos/video.ts b/client/angular/videos/video.ts
new file mode 100644
index 000000000..32ff64eb3
--- /dev/null
+++ b/client/angular/videos/video.ts
@@ -0,0 +1,60 @@
1export class Video {
2 id: string;
3 name: string;
4 description: string;
5 magnetUri: string;
6 podUrl: string;
7 isLocal: boolean;
8 thumbnailPath: string;
9 author: string;
10 createdDate: Date;
11 by: string;
12 duration: string;
13
14 constructor(hash: {
15 id: string,
16 name: string,
17 description: string,
18 magnetUri: string,
19 podUrl: string,
20 isLocal: boolean,
21 thumbnailPath: string,
22 author: string,
23 createdDate: string,
24 duration: number;
25 }) {
26 this.id = hash.id;
27 this.name = hash.name;
28 this.description = hash.description;
29 this.magnetUri = hash.magnetUri;
30 this.podUrl = hash.podUrl;
31 this.isLocal = hash.isLocal;
32 this.thumbnailPath = hash.thumbnailPath;
33 this.author = hash.author;
34 this.createdDate = new Date(hash.createdDate);
35 this.duration = Video.createDurationString(hash.duration);
36 this.by = Video.createByString(hash.author, hash.podUrl);
37 }
38
39 isRemovableBy(user): boolean {
40 return this.isLocal === true && user && this.author === user.username;
41 }
42
43 private static createDurationString(duration: number): string {
44 const minutes = Math.floor(duration / 60);
45 const seconds = duration % 60;
46 const minutes_padding = minutes >= 10 ? '' : '0';
47 const seconds_padding = seconds >= 10 ? '' : '0'
48
49 return minutes_padding + minutes.toString() + ':' + seconds_padding + seconds.toString();
50 }
51
52 private static createByString(author: string, podUrl: string): string {
53 let [ host, port ] = podUrl.replace(/^https?:\/\//, '').split(':');
54
55 if (port === '80' || port === '443') port = '';
56 else port = ':' + port;
57
58 return author + '@' + host + port;
59 }
60}
diff --git a/client/angular/videos/services/videos.service.ts b/client/angular/videos/videos.service.ts
index d08548339..f4790b511 100644
--- a/client/angular/videos/services/videos.service.ts
+++ b/client/angular/videos/videos.service.ts
@@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
2import { Http, Response } from '@angular/http'; 2import { Http, Response } from '@angular/http';
3import { Observable } from 'rxjs/Rx'; 3import { Observable } from 'rxjs/Rx';
4 4
5import { Video } from '../models/video'; 5import { Video } from './video';
6import { AuthService } from '../../users/services/auth.service'; 6import { AuthService } from '../users/services/auth.service';
7 7
8@Injectable() 8@Injectable()
9export class VideosService { 9export class VideosService {
@@ -13,7 +13,8 @@ export class VideosService {
13 13
14 getVideos() { 14 getVideos() {
15 return this.http.get(this._baseVideoUrl) 15 return this.http.get(this._baseVideoUrl)
16 .map(res => <Video[]> res.json()) 16 .map(res => res.json())
17 .map(this.extractVideos)
17 .catch(this.handleError); 18 .catch(this.handleError);
18 } 19 }
19 20
@@ -24,20 +25,28 @@ export class VideosService {
24 } 25 }
25 26
26 removeVideo(id: string) { 27 removeVideo(id: string) {
27 if (confirm('Are you sure?')) { 28 const options = this._authService.getAuthRequestOptions();
28 const options = this._authService.getAuthRequestOptions(); 29 return this.http.delete(this._baseVideoUrl + id, options)
29 return this.http.delete(this._baseVideoUrl + id, options) 30 .map(res => <number> res.status)
30 .map(res => <number> res.status) 31 .catch(this.handleError);
31 .catch(this.handleError);
32 }
33 } 32 }
34 33
35 searchVideos(search: string) { 34 searchVideos(search: string) {
36 return this.http.get(this._baseVideoUrl + 'search/' + search) 35 return this.http.get(this._baseVideoUrl + 'search/' + search)
37 .map(res => <Video> res.json()) 36 .map(res => res.json())
37 .map(this.extractVideos)
38 .catch(this.handleError); 38 .catch(this.handleError);
39 } 39 }
40 40
41 private extractVideos (body: any[]) {
42 const videos = [];
43 for (const video_json of body) {
44 videos.push(new Video(video_json));
45 }
46
47 return videos;
48 }
49
41 private handleError (error: Response) { 50 private handleError (error: Response) {
42 console.error(error); 51 console.error(error);
43 return Observable.throw(error.json().error || 'Server error'); 52 return Observable.throw(error.json().error || 'Server error');
diff --git a/server/middlewares/reqValidators/videos.js b/server/middlewares/reqValidators/videos.js
index 6e6e75fb3..d4dec1a59 100644
--- a/server/middlewares/reqValidators/videos.js
+++ b/server/middlewares/reqValidators/videos.js
@@ -30,7 +30,7 @@ function videosAdd (req, res, next) {
30 } 30 }
31 31
32 if (duration > constants.MAXIMUM_VIDEO_DURATION) { 32 if (duration > constants.MAXIMUM_VIDEO_DURATION) {
33 return res.status(400).send('Duration of the video file is too big.') 33 return res.status(400).send('Duration of the video file is too big (' + constants.MAXIMUM_VIDEO_DURATION + ').')
34 } 34 }
35 35
36 videoFile.duration = duration 36 videoFile.duration = duration