diff options
Diffstat (limited to 'client')
7 files changed, 66 insertions, 36 deletions
diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts index 018271efe..c8b7ebc67 100644 --- a/client/src/app/shared/misc/utils.ts +++ b/client/src/app/shared/misc/utils.ts | |||
@@ -51,6 +51,18 @@ function dateToHuman (date: string) { | |||
51 | return datePipe.transform(date, 'medium') | 51 | return datePipe.transform(date, 'medium') |
52 | } | 52 | } |
53 | 53 | ||
54 | function durationToString (duration: number) { | ||
55 | const hours = Math.floor(duration / 3600) | ||
56 | const minutes = Math.floor((duration % 3600) / 60) | ||
57 | const seconds = duration % 60 | ||
58 | |||
59 | const minutesPadding = minutes >= 10 ? '' : '0' | ||
60 | const secondsPadding = seconds >= 10 ? '' : '0' | ||
61 | const displayedHours = hours > 0 ? hours.toString() + ':' : '' | ||
62 | |||
63 | return displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() | ||
64 | } | ||
65 | |||
54 | function immutableAssign <A, B> (target: A, source: B) { | 66 | function immutableAssign <A, B> (target: A, source: B) { |
55 | return Object.assign({}, target, source) | 67 | return Object.assign({}, target, source) |
56 | } | 68 | } |
@@ -114,6 +126,7 @@ function sortBy (obj: any[], key1: string, key2?: string) { | |||
114 | 126 | ||
115 | export { | 127 | export { |
116 | sortBy, | 128 | sortBy, |
129 | durationToString, | ||
117 | objectToUrlEncoded, | 130 | objectToUrlEncoded, |
118 | getParameterByName, | 131 | getParameterByName, |
119 | populateAsyncUserVideoChannels, | 132 | populateAsyncUserVideoChannels, |
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index d80c10459..80794faa6 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts | |||
@@ -2,7 +2,7 @@ import { User } from '../' | |||
2 | import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' | 2 | import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' |
3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' | 4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' |
5 | import { getAbsoluteAPIUrl } from '../misc/utils' | 5 | import { durationToString, getAbsoluteAPIUrl } from '../misc/utils' |
6 | import { peertubeTranslate, ServerConfig } from '../../../../../shared/models' | 6 | import { peertubeTranslate, ServerConfig } from '../../../../../shared/models' |
7 | import { Actor } from '@app/shared/actor/actor.model' | 7 | import { Actor } from '@app/shared/actor/actor.model' |
8 | import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model' | 8 | import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model' |
@@ -70,18 +70,6 @@ export class Video implements VideoServerModel { | |||
70 | return '/videos/watch/' + videoUUID | 70 | return '/videos/watch/' + videoUUID |
71 | } | 71 | } |
72 | 72 | ||
73 | private static createDurationString (duration: number) { | ||
74 | const hours = Math.floor(duration / 3600) | ||
75 | const minutes = Math.floor((duration % 3600) / 60) | ||
76 | const seconds = duration % 60 | ||
77 | |||
78 | const minutesPadding = minutes >= 10 ? '' : '0' | ||
79 | const secondsPadding = seconds >= 10 ? '' : '0' | ||
80 | const displayedHours = hours > 0 ? hours.toString() + ':' : '' | ||
81 | |||
82 | return displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() | ||
83 | } | ||
84 | |||
85 | constructor (hash: VideoServerModel, translations = {}) { | 73 | constructor (hash: VideoServerModel, translations = {}) { |
86 | const absoluteAPIUrl = getAbsoluteAPIUrl() | 74 | const absoluteAPIUrl = getAbsoluteAPIUrl() |
87 | 75 | ||
@@ -95,7 +83,7 @@ export class Video implements VideoServerModel { | |||
95 | this.state = hash.state | 83 | this.state = hash.state |
96 | this.description = hash.description | 84 | this.description = hash.description |
97 | this.duration = hash.duration | 85 | this.duration = hash.duration |
98 | this.durationLabel = Video.createDurationString(hash.duration) | 86 | this.durationLabel = durationToString(hash.duration) |
99 | this.id = hash.id | 87 | this.id = hash.id |
100 | this.uuid = hash.uuid | 88 | this.uuid = hash.uuid |
101 | this.isLocal = hash.isLocal | 89 | this.isLocal = hash.isLocal |
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.html b/client/src/app/videos/+video-watch/modal/video-share.component.html index 02f5f0f44..a20c320a4 100644 --- a/client/src/app/videos/+video-watch/modal/video-share.component.html +++ b/client/src/app/videos/+video-watch/modal/video-share.component.html | |||
@@ -17,6 +17,11 @@ | |||
17 | </div> | 17 | </div> |
18 | </div> | 18 | </div> |
19 | 19 | ||
20 | <div class="form-group qr-code-group"> | ||
21 | <label i18n>QR-Code</label> | ||
22 | <ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-qrcode> | ||
23 | </div> | ||
24 | |||
20 | <div class="form-group"> | 25 | <div class="form-group"> |
21 | <label i18n>Embed</label> | 26 | <label i18n>Embed</label> |
22 | <div class="input-group input-group-sm"> | 27 | <div class="input-group input-group-sm"> |
@@ -32,15 +37,17 @@ | |||
32 | <div i18n *ngIf="notSecure()" class="alert alert-warning"> | 37 | <div i18n *ngIf="notSecure()" class="alert alert-warning"> |
33 | The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). | 38 | The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). |
34 | </div> | 39 | </div> |
40 | </div> | ||
35 | 41 | ||
36 | <div class="form-group qr-code-group"> | 42 | <div *ngIf="currentVideoTimestampString" class="start-at"> |
37 | <label i18n>QR-Code</label> | 43 | <my-peertube-checkbox |
38 | <ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-qrcode> | 44 | inputName="startAt" [(ngModel)]="startAtCheckbox" |
39 | </div> | 45 | i18n-labelText [labelText]="getStartCheckboxLabel()" |
46 | ></my-peertube-checkbox> | ||
40 | </div> | 47 | </div> |
41 | 48 | ||
42 | <div class="modal-footer inputs"> | 49 | <div class="modal-footer inputs"> |
43 | <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span> | 50 | <span i18n class="action-button action-button-cancel" (click)="hide()">Close</span> |
44 | </div> | 51 | </div> |
45 | 52 | ||
46 | </ng-template> | 53 | </ng-template> |
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.scss b/client/src/app/videos/+video-watch/modal/video-share.component.scss index a9e9b8498..4c07bce89 100644 --- a/client/src/app/videos/+video-watch/modal/video-share.component.scss +++ b/client/src/app/videos/+video-watch/modal/video-share.component.scss | |||
@@ -12,3 +12,9 @@ | |||
12 | .qr-code-group { | 12 | .qr-code-group { |
13 | text-align: center; | 13 | text-align: center; |
14 | } | 14 | } |
15 | |||
16 | .start-at { | ||
17 | display: flex; | ||
18 | justify-content: center; | ||
19 | margin-top: 10px; | ||
20 | } | ||
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.ts b/client/src/app/videos/+video-watch/modal/video-share.component.ts index 14f557f9a..71d6f5633 100644 --- a/client/src/app/videos/+video-watch/modal/video-share.component.ts +++ b/client/src/app/videos/+video-watch/modal/video-share.component.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' |
2 | import { NotificationsService } from 'angular2-notifications' | 2 | import { NotificationsService } from 'angular2-notifications' |
3 | import { VideoDetails } from '../../../shared/video/video-details.model' | 3 | import { VideoDetails } from '../../../shared/video/video-details.model' |
4 | import { buildVideoEmbed } from '../../../../assets/player/utils' | 4 | import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils' |
5 | import { I18n } from '@ngx-translate/i18n-polyfill' | 5 | import { I18n } from '@ngx-translate/i18n-polyfill' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | import { durationToString } from '@app/shared/misc/utils' | ||
7 | 8 | ||
8 | @Component({ | 9 | @Component({ |
9 | selector: 'my-video-share', | 10 | selector: 'my-video-share', |
@@ -11,9 +12,14 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | |||
11 | styleUrls: [ './video-share.component.scss' ] | 12 | styleUrls: [ './video-share.component.scss' ] |
12 | }) | 13 | }) |
13 | export class VideoShareComponent { | 14 | export class VideoShareComponent { |
15 | @ViewChild('modal') modal: ElementRef | ||
16 | |||
14 | @Input() video: VideoDetails = null | 17 | @Input() video: VideoDetails = null |
15 | 18 | ||
16 | @ViewChild('modal') modal: ElementRef | 19 | startAtCheckbox = false |
20 | currentVideoTimestampString: string | ||
21 | |||
22 | private currentVideoTimestamp: number | ||
17 | 23 | ||
18 | constructor ( | 24 | constructor ( |
19 | private modalService: NgbModal, | 25 | private modalService: NgbModal, |
@@ -23,16 +29,21 @@ export class VideoShareComponent { | |||
23 | // empty | 29 | // empty |
24 | } | 30 | } |
25 | 31 | ||
26 | show () { | 32 | show (currentVideoTimestamp?: number) { |
33 | this.currentVideoTimestamp = Math.floor(currentVideoTimestamp) | ||
34 | this.currentVideoTimestampString = durationToString(this.currentVideoTimestamp) | ||
35 | |||
27 | this.modalService.open(this.modal) | 36 | this.modalService.open(this.modal) |
28 | } | 37 | } |
29 | 38 | ||
30 | getVideoIframeCode () { | 39 | getVideoIframeCode () { |
31 | return buildVideoEmbed(this.video.embedUrl) | 40 | const embedUrl = buildVideoLink(this.getVideoTimestampIfEnabled(), this.video.embedUrl) |
41 | |||
42 | return buildVideoEmbed(embedUrl) | ||
32 | } | 43 | } |
33 | 44 | ||
34 | getVideoUrl () { | 45 | getVideoUrl () { |
35 | return window.location.href | 46 | return buildVideoLink(this.getVideoTimestampIfEnabled()) |
36 | } | 47 | } |
37 | 48 | ||
38 | notSecure () { | 49 | notSecure () { |
@@ -42,4 +53,14 @@ export class VideoShareComponent { | |||
42 | activateCopiedMessage () { | 53 | activateCopiedMessage () { |
43 | this.notificationsService.success(this.i18n('Success'), this.i18n('Copied')) | 54 | this.notificationsService.success(this.i18n('Success'), this.i18n('Copied')) |
44 | } | 55 | } |
56 | |||
57 | getStartCheckboxLabel () { | ||
58 | return this.i18n('Start at {{timestamp}}', { timestamp: this.currentVideoTimestampString }) | ||
59 | } | ||
60 | |||
61 | private getVideoTimestampIfEnabled () { | ||
62 | if (this.startAtCheckbox === true) return this.currentVideoTimestamp | ||
63 | |||
64 | return undefined | ||
65 | } | ||
45 | } | 66 | } |
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 0909b13f5..d838ebe79 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -204,7 +204,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
204 | } | 204 | } |
205 | 205 | ||
206 | showShareModal () { | 206 | showShareModal () { |
207 | this.videoShareModal.show() | 207 | const currentTime = this.player ? this.player.currentTime() : undefined |
208 | |||
209 | this.videoShareModal.show(currentTime) | ||
208 | } | 210 | } |
209 | 211 | ||
210 | showDownloadModal (event: Event) { | 212 | showDownloadModal (event: Event) { |
@@ -258,12 +260,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
258 | return this.video.isUnblacklistableBy(this.user) | 260 | return this.video.isUnblacklistableBy(this.user) |
259 | } | 261 | } |
260 | 262 | ||
261 | getVideoPoster () { | ||
262 | if (!this.video) return '' | ||
263 | |||
264 | return this.video.previewUrl | ||
265 | } | ||
266 | |||
267 | getVideoTags () { | 263 | getVideoTags () { |
268 | if (!this.video || Array.isArray(this.video.tags) === false) return [] | 264 | if (!this.video || Array.isArray(this.video.tags) === false) return [] |
269 | 265 | ||
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts index c02e19929..cf4f60f55 100644 --- a/client/src/assets/player/utils.ts +++ b/client/src/assets/player/utils.ts | |||
@@ -23,9 +23,8 @@ function isMobile () { | |||
23 | return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) | 23 | return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) |
24 | } | 24 | } |
25 | 25 | ||
26 | function buildVideoLink (time?: number) { | 26 | function buildVideoLink (time?: number, url?: string) { |
27 | const baseEmbedPath = window.location.pathname.replace('/embed/', '/watch/') | 27 | if (!url) url = window.location.origin + window.location.pathname.replace('/embed/', '/watch/') |
28 | const baseEmbedURL = window.location.origin + baseEmbedPath | ||
29 | 28 | ||
30 | if (time) { | 29 | if (time) { |
31 | const timeInt = Math.floor(time) | 30 | const timeInt = Math.floor(time) |
@@ -33,10 +32,10 @@ function buildVideoLink (time?: number) { | |||
33 | const params = new URLSearchParams(window.location.search) | 32 | const params = new URLSearchParams(window.location.search) |
34 | params.set('start', secondsToTime(timeInt)) | 33 | params.set('start', secondsToTime(timeInt)) |
35 | 34 | ||
36 | return baseEmbedURL + '?' + params.toString() | 35 | return url + '?' + params.toString() |
37 | } | 36 | } |
38 | 37 | ||
39 | return baseEmbedURL | 38 | return url |
40 | } | 39 | } |
41 | 40 | ||
42 | function timeToInt (time: number | string) { | 41 | function timeToInt (time: number | string) { |