aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/package.json4
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.ts6
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts1
-rw-r--r--client/src/assets/player/peertube-link-button.ts20
-rw-r--r--client/src/assets/player/peertube-player.ts27
-rw-r--r--client/src/assets/player/peertube-videojs-plugin.ts4
-rw-r--r--client/src/assets/player/utils.ts34
-rw-r--r--client/src/assets/player/video-renderer.ts6
-rw-r--r--client/src/sass/video-js-custom.scss30
-rw-r--r--client/src/standalone/videos/embed.ts1
-rw-r--r--client/yarn.lock15
11 files changed, 127 insertions, 21 deletions
diff --git a/client/package.json b/client/package.json
index 0d5acf997..61f94758a 100644
--- a/client/package.json
+++ b/client/package.json
@@ -23,7 +23,7 @@
23 }, 23 },
24 "license": "GPLv3", 24 "license": "GPLv3",
25 "resolutions": { 25 "resolutions": {
26 "videojs-dock/video.js": "^7", 26 "video.js": "^7",
27 "webtorrent/create-torrent/junk": "^1", 27 "webtorrent/create-torrent/junk": "^1",
28 "simple-get": "^2.8.1" 28 "simple-get": "^2.8.1"
29 }, 29 },
@@ -99,6 +99,8 @@
99 "typescript": "2.7", 99 "typescript": "2.7",
100 "uglifyjs-webpack-plugin": "^1.1.2", 100 "uglifyjs-webpack-plugin": "^1.1.2",
101 "video.js": "^7.0.3", 101 "video.js": "^7.0.3",
102 "videojs-contextmenu": "^2.0.0",
103 "videojs-contextmenu-ui": "^4.0.0",
102 "videojs-dock": "^2.0.2", 104 "videojs-dock": "^2.0.2",
103 "videojs-hotkeys": "^0.2.21", 105 "videojs-hotkeys": "^0.2.21",
104 "webpack": "^4.5.0", 106 "webpack": "^4.5.0",
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 678cccfb5..33998c5d8 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
@@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
4 4
5import { ModalDirective } from 'ngx-bootstrap/modal' 5import { ModalDirective } from 'ngx-bootstrap/modal'
6import { VideoDetails } from '../../../shared/video/video-details.model' 6import { VideoDetails } from '../../../shared/video/video-details.model'
7import { buildVideoEmbed } from '../../../../assets/player/utils'
7 8
8@Component({ 9@Component({
9 selector: 'my-video-share', 10 selector: 'my-video-share',
@@ -28,10 +29,7 @@ export class VideoShareComponent {
28 } 29 }
29 30
30 getVideoIframeCode () { 31 getVideoIframeCode () {
31 return '<iframe width="560" height="315" ' + 32 return buildVideoEmbed(this.video.embedUrl)
32 'src="' + this.video.embedUrl + '" ' +
33 'frameborder="0" allowfullscreen>' +
34 '</iframe>'
35 } 33 }
36 34
37 getVideoUrl () { 35 getVideoUrl () {
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 35a7c04cc..c71051649 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -348,6 +348,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
348 inactivityTimeout: 2500, 348 inactivityTimeout: 2500,
349 videoFiles: this.video.files, 349 videoFiles: this.video.files,
350 playerElement: this.playerElement, 350 playerElement: this.playerElement,
351 videoEmbedUrl: this.video.embedUrl,
351 videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid), 352 videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid),
352 videoDuration: this.video.duration, 353 videoDuration: this.video.duration,
353 enableHotkeys: true, 354 enableHotkeys: true,
diff --git a/client/src/assets/player/peertube-link-button.ts b/client/src/assets/player/peertube-link-button.ts
index 54d802732..a13815d61 100644
--- a/client/src/assets/player/peertube-link-button.ts
+++ b/client/src/assets/player/peertube-link-button.ts
@@ -1,15 +1,19 @@
1import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 1import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
2import { buildVideoLink } from './utils'
2 3
3const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') 4const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
4class PeerTubeLinkButton extends Button { 5class PeerTubeLinkButton extends Button {
5 6
7 constructor (player: videojs.Player, options) {
8 super(player, options)
9 }
10
6 createEl () { 11 createEl () {
7 return this.buildElement() 12 return this.buildElement()
8 } 13 }
9 14
10 updateHref () { 15 updateHref () {
11 const currentTime = Math.floor(this.player().currentTime()) 16 this.el().setAttribute('href', buildVideoLink(this.player().currentTime()))
12 this.el().setAttribute('href', this.buildHref(currentTime))
13 } 17 }
14 18
15 handleClick () { 19 handleClick () {
@@ -18,7 +22,7 @@ class PeerTubeLinkButton extends Button {
18 22
19 private buildElement () { 23 private buildElement () {
20 const el = videojsUntyped.dom.createEl('a', { 24 const el = videojsUntyped.dom.createEl('a', {
21 href: this.buildHref(), 25 href: buildVideoLink(),
22 innerHTML: 'PeerTube', 26 innerHTML: 'PeerTube',
23 title: 'Go to the video page', 27 title: 'Go to the video page',
24 className: 'vjs-peertube-link', 28 className: 'vjs-peertube-link',
@@ -29,15 +33,5 @@ class PeerTubeLinkButton extends Button {
29 33
30 return el 34 return el
31 } 35 }
32
33 private buildHref (time?: number) {
34 let href = window.location.href.replace('embed', 'watch')
35 if (time) {
36 if (window.location.search) href += '&start=' + time
37 else href += '?start=' + time
38 }
39
40 return href
41 }
42} 36}
43Button.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton) 37Button.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton)
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index f419d58fc..9fe5af569 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -2,12 +2,15 @@ import { VideoFile } from '../../../../shared/models/videos'
2 2
3import 'videojs-hotkeys' 3import 'videojs-hotkeys'
4import 'videojs-dock' 4import 'videojs-dock'
5import 'videojs-contextmenu'
6import 'videojs-contextmenu-ui'
5import './peertube-link-button' 7import './peertube-link-button'
6import './resolution-menu-button' 8import './resolution-menu-button'
7import './settings-menu-button' 9import './settings-menu-button'
8import './webtorrent-info-button' 10import './webtorrent-info-button'
9import './peertube-videojs-plugin' 11import './peertube-videojs-plugin'
10import { videojsUntyped } from './peertube-videojs-typings' 12import { videojsUntyped } from './peertube-videojs-typings'
13import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
11 14
12// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 15// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
13videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' 16videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -16,6 +19,7 @@ function getVideojsOptions (options: {
16 autoplay: boolean, 19 autoplay: boolean,
17 playerElement: HTMLVideoElement, 20 playerElement: HTMLVideoElement,
18 videoViewUrl: string, 21 videoViewUrl: string,
22 videoEmbedUrl: string,
19 videoDuration: number, 23 videoDuration: number,
20 videoFiles: VideoFile[], 24 videoFiles: VideoFile[],
21 enableHotkeys: boolean, 25 enableHotkeys: boolean,
@@ -38,6 +42,29 @@ function getVideojsOptions (options: {
38 videoViewUrl: options.videoViewUrl, 42 videoViewUrl: options.videoViewUrl,
39 videoDuration: options.videoDuration, 43 videoDuration: options.videoDuration,
40 startTime: options.startTime 44 startTime: options.startTime
45 },
46 contextmenuUI: {
47 content: [
48 {
49 label: 'Copy the video URL',
50 listener: function () {
51 copyToClipboard(buildVideoLink())
52 }
53 },
54 {
55 label: 'Copy the video URL at the current time',
56 listener: function () {
57 const player = this
58 copyToClipboard(buildVideoLink(player.currentTime()))
59 }
60 },
61 {
62 label: 'Copy embed code',
63 listener: () => {
64 copyToClipboard(buildVideoEmbed(options.videoEmbedUrl))
65 }
66 }
67 ]
41 } 68 }
42 }, 69 },
43 controlBar: { 70 controlBar: {
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts
index d9ded7a7e..65103f3ab 100644
--- a/client/src/assets/player/peertube-videojs-plugin.ts
+++ b/client/src/assets/player/peertube-videojs-plugin.ts
@@ -268,6 +268,10 @@ class PeerTubePlugin extends Plugin {
268 this.trigger('autoResolutionUpdate') 268 this.trigger('autoResolutionUpdate')
269 } 269 }
270 270
271 getCurrentVideoFile () {
272 return this.currentVideoFile
273 }
274
271 private tryToPlay (done?: Function) { 275 private tryToPlay (done?: Function) {
272 if (!done) done = function () { /* empty */ } 276 if (!done) done = function () { /* empty */ }
273 277
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 1df39d4e4..487b3a1be 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -64,14 +64,48 @@ function isMobile () {
64 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) 64 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
65} 65}
66 66
67function buildVideoLink (time?: number) {
68 let href = window.location.href.replace('/embed/', '/watch/')
69 if (time) {
70 const timeInt = Math.floor(time)
71
72 if (window.location.search) href += '&start=' + timeInt
73 else href += '?start=' + timeInt
74 }
75
76 return href
77}
78
79function buildVideoEmbed (embedUrl: string) {
80 return '<iframe width="560" height="315" ' +
81 'src="' + embedUrl + '" ' +
82 'frameborder="0" allowfullscreen>' +
83 '</iframe>'
84}
85
86function copyToClipboard (text: string) {
87 const el = document.createElement('textarea')
88 el.value = text
89 el.setAttribute('readonly', '')
90 el.style.position = 'absolute'
91 el.style.left = '-9999px'
92 document.body.appendChild(el)
93 el.select()
94 document.execCommand('copy')
95 document.body.removeChild(el)
96}
97
67export { 98export {
68 toTitleCase, 99 toTitleCase,
100 buildVideoLink,
69 getStoredVolume, 101 getStoredVolume,
70 saveVolumeInStore, 102 saveVolumeInStore,
71 saveAverageBandwidth, 103 saveAverageBandwidth,
72 getAverageBandwidth, 104 getAverageBandwidth,
73 saveMuteInStore, 105 saveMuteInStore,
106 buildVideoEmbed,
74 getStoredMute, 107 getStoredMute,
108 copyToClipboard,
75 isMobile, 109 isMobile,
76 bytes 110 bytes
77} 111}
diff --git a/client/src/assets/player/video-renderer.ts b/client/src/assets/player/video-renderer.ts
index 4b54b661a..4affb43cf 100644
--- a/client/src/assets/player/video-renderer.ts
+++ b/client/src/assets/player/video-renderer.ts
@@ -50,7 +50,7 @@ function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, ca
50 50
51 return fallbackToMediaSource() 51 return fallbackToMediaSource()
52 }) 52 })
53 preparedElem.addEventListener('canplay', onLoadStart) 53 preparedElem.addEventListener('loadstart', onLoadStart)
54 return videostream(file, preparedElem) 54 return videostream(file, preparedElem)
55 } 55 }
56 56
@@ -66,7 +66,7 @@ function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, ca
66 66
67 return callback(err) 67 return callback(err)
68 }) 68 })
69 preparedElem.addEventListener('canplay', onLoadStart) 69 preparedElem.addEventListener('loadstart', onLoadStart)
70 70
71 const wrapper = new MediaElementWrapper(preparedElem) 71 const wrapper = new MediaElementWrapper(preparedElem)
72 const writable = wrapper.createWriteStream(codecs) 72 const writable = wrapper.createWriteStream(codecs)
@@ -95,7 +95,7 @@ function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, ca
95 } 95 }
96 96
97 function onLoadStart () { 97 function onLoadStart () {
98 preparedElem.removeEventListener('canplay', onLoadStart) 98 preparedElem.removeEventListener('loadstart', onLoadStart)
99 if (opts.autoplay) preparedElem.play() 99 if (opts.autoplay) preparedElem.play()
100 100
101 callback(null, renderer) 101 callback(null, renderer)
diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss
index 680958a9f..350e7cdd5 100644
--- a/client/src/sass/video-js-custom.scss
+++ b/client/src/sass/video-js-custom.scss
@@ -21,6 +21,8 @@ $slider-bg-color: lighten($primary-background-color, 33%);
21$setting-transition-duration: 0.15s; 21$setting-transition-duration: 0.15s;
22$setting-transition-easing: ease-out; 22$setting-transition-easing: ease-out;
23 23
24$context-menu-width: 350px;
25
24.video-js.vjs-peertube-skin { 26.video-js.vjs-peertube-skin {
25 font-size: $font-size; 27 font-size: $font-size;
26 color: $primary-foreground-color; 28 color: $primary-foreground-color;
@@ -787,4 +789,32 @@ $setting-transition-easing: ease-out;
787 } 789 }
788 } 790 }
789 } 791 }
792}
793
794/* Sass for videojs-contextmenu-ui */
795
796.video-js .vjs-contextmenu-ui-menu {
797 position: absolute;
798 background-color: rgba(0, 0, 0, 0.5);
799 padding: 5px 0;
800 width: $context-menu-width;
801
802 .vjs-menu-content {
803 opacity: $primary-foreground-opacity;
804 color: $primary-foreground-color;
805 font-size: $font-size !important;
806 font-weight: $font-semibold;
807 }
808
809 .vjs-menu-item {
810 cursor: pointer;
811 font-size: 1em;
812 padding: 8px 16px;
813 text-align: left;
814 text-transform: none;
815
816 &:hover {
817 background-color: rgba(255, 255, 255, 0.2);
818 }
819 }
790} \ No newline at end of file 820} \ No newline at end of file
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index ba906cc32..d603690ca 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -91,6 +91,7 @@ loadVideoInfo(videoId)
91 const videojsOptions = getVideojsOptions({ 91 const videojsOptions = getVideojsOptions({
92 autoplay, 92 autoplay,
93 inactivityTimeout: 1500, 93 inactivityTimeout: 1500,
94 videoEmbedUrl: window.location.origin + videoInfo.embedPath,
94 videoViewUrl: getVideoUrl(videoId) + '/views', 95 videoViewUrl: getVideoUrl(videoId) + '/views',
95 playerElement: videoElement, 96 playerElement: videoElement,
96 videoFiles: videoInfo.files, 97 videoFiles: videoInfo.files,
diff --git a/client/yarn.lock b/client/yarn.lock
index 7806611f2..fe2e040d8 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -10006,6 +10006,21 @@ video.js@^5.19.2, "video.js@^6.8.0 || ^7.0.0", video.js@^7, video.js@^7.0.3:
10006 videojs-vtt.js "0.14.1" 10006 videojs-vtt.js "0.14.1"
10007 xhr "2.4.0" 10007 xhr "2.4.0"
10008 10008
10009videojs-contextmenu-ui@^4.0.0:
10010 version "4.0.0"
10011 resolved "https://registry.yarnpkg.com/videojs-contextmenu-ui/-/videojs-contextmenu-ui-4.0.0.tgz#e7ffceacac95c5d2bc7f80db6f75675404de542a"
10012 dependencies:
10013 global "^4.3.2"
10014 video.js "^5.19.2"
10015 videojs-contextmenu "^2.0.0"
10016
10017videojs-contextmenu@^2.0.0:
10018 version "2.0.0"
10019 resolved "https://registry.yarnpkg.com/videojs-contextmenu/-/videojs-contextmenu-2.0.0.tgz#7213c8c420ecd2904d26f19c21085f7ebf496e9e"
10020 dependencies:
10021 global "^4.3.2"
10022 video.js "^5.19.2"
10023
10009videojs-dock@^2.0.2: 10024videojs-dock@^2.0.2:
10010 version "2.1.2" 10025 version "2.1.2"
10011 resolved "https://registry.yarnpkg.com/videojs-dock/-/videojs-dock-2.1.2.tgz#621c27c6f7dd131c541535300ac545377e515a0e" 10026 resolved "https://registry.yarnpkg.com/videojs-dock/-/videojs-dock-2.1.2.tgz#621c27c6f7dd131c541535300ac545377e515a0e"