aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/shared/misc/utils.ts13
-rw-r--r--client/src/app/shared/video/video.model.ts16
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.html17
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.scss6
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.ts31
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts10
-rw-r--r--client/src/assets/player/utils.ts9
-rw-r--r--support/doc/tools.md1
8 files changed, 67 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
54function 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
54function immutableAssign <A, B> (target: A, source: B) { 66function 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
115export { 127export {
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 '../'
2import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' 2import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared'
3import { Avatar } from '../../../../../shared/models/avatars/avatar.model' 3import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
4import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' 4import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model'
5import { getAbsoluteAPIUrl } from '../misc/utils' 5import { durationToString, getAbsoluteAPIUrl } from '../misc/utils'
6import { peertubeTranslate, ServerConfig } from '../../../../../shared/models' 6import { peertubeTranslate, ServerConfig } from '../../../../../shared/models'
7import { Actor } from '@app/shared/actor/actor.model' 7import { Actor } from '@app/shared/actor/actor.model'
8import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model' 8import { 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 @@
1import { Component, ElementRef, Input, ViewChild } from '@angular/core' 1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
3import { VideoDetails } from '../../../shared/video/video-details.model' 3import { VideoDetails } from '../../../shared/video/video-details.model'
4import { buildVideoEmbed } from '../../../../assets/player/utils' 4import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils'
5import { I18n } from '@ngx-translate/i18n-polyfill' 5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { 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})
13export class VideoShareComponent { 14export 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
26function buildVideoLink (time?: number) { 26function 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
42function timeToInt (time: number | string) { 41function timeToInt (time: number | string) {
diff --git a/support/doc/tools.md b/support/doc/tools.md
index 2f36d07fd..8d1af2d1f 100644
--- a/support/doc/tools.md
+++ b/support/doc/tools.md
@@ -4,6 +4,7 @@
4 - [import-videos.js](#import-videosjs) 4 - [import-videos.js](#import-videosjs)
5 - [upload.js](#uploadjs) 5 - [upload.js](#uploadjs)
6 - [Server tools](#server-tools) 6 - [Server tools](#server-tools)
7 - [parse-log](#parse-log)
7 - [create-transcoding-job.js](#create-transcoding-jobjs) 8 - [create-transcoding-job.js](#create-transcoding-jobjs)
8 - [create-import-video-file-job.js](#create-import-video-file-jobjs) 9 - [create-import-video-file-job.js](#create-import-video-file-jobjs)
9 - [prune-storage.js](#prune-storagejs) 10 - [prune-storage.js](#prune-storagejs)