]>
Commit | Line | Data |
---|---|---|
2dd0a8a8 | 1 | import debug from 'debug' |
42b40636 C |
2 | import videojs from 'video.js' |
3 | import { logger } from '@root-helpers/logger' | |
4 | import { PeerTubeMobileButtons } from './peertube-mobile-buttons' | |
2dd0a8a8 | 5 | |
42b40636 | 6 | const debugLogger = debug('peertube:player:mobile') |
f1a0555a C |
7 | |
8 | const Plugin = videojs.getPlugin('plugin') | |
9 | ||
10 | class PeerTubeMobilePlugin extends Plugin { | |
2dd0a8a8 C |
11 | private static readonly DOUBLE_TAP_DELAY_MS = 250 |
12 | private static readonly SET_CURRENT_TIME_DELAY = 1000 | |
13 | ||
14 | private peerTubeMobileButtons: PeerTubeMobileButtons | |
15 | ||
16 | private seekAmount = 0 | |
17 | ||
18 | private lastTapEvent: TouchEvent | |
f85b5afb | 19 | private tapTimeout: ReturnType<typeof setTimeout> |
2dd0a8a8 C |
20 | private newActiveState: boolean |
21 | ||
f85b5afb | 22 | private setCurrentTimeTimeout: ReturnType<typeof setTimeout> |
f1a0555a C |
23 | |
24 | constructor (player: videojs.Player, options: videojs.PlayerOptions) { | |
25 | super(player, options) | |
26 | ||
15a0e5f3 | 27 | this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons |
e98ef69d C |
28 | |
29 | if (videojs.browser.IS_ANDROID && screen.orientation) { | |
30 | this.handleFullscreenRotation() | |
31 | } | |
2dd0a8a8 C |
32 | |
33 | if (!this.player.options_.userActions) this.player.options_.userActions = {}; | |
34 | ||
35 | // FIXME: typings | |
36 | (this.player.options_.userActions as any).click = false | |
37 | this.player.options_.userActions.doubleClick = false | |
38 | ||
39 | this.player.one('play', () => { | |
40 | this.initTouchStartEvents() | |
41 | }) | |
e98ef69d C |
42 | } |
43 | ||
44 | private handleFullscreenRotation () { | |
45 | this.player.on('fullscreenchange', () => { | |
46 | if (!this.player.isFullscreen() || this.isPortraitVideo()) return | |
47 | ||
48 | screen.orientation.lock('landscape') | |
42b40636 | 49 | .catch(err => logger.error('Cannot lock screen to landscape.', err)) |
e98ef69d C |
50 | }) |
51 | } | |
52 | ||
53 | private isPortraitVideo () { | |
54 | return this.player.videoWidth() < this.player.videoHeight() | |
f1a0555a | 55 | } |
2dd0a8a8 C |
56 | |
57 | private initTouchStartEvents () { | |
457c8348 | 58 | const handleTouchStart = (event: TouchEvent) => { |
2dd0a8a8 C |
59 | if (this.tapTimeout) { |
60 | clearTimeout(this.tapTimeout) | |
61 | this.tapTimeout = undefined | |
62 | } | |
63 | ||
64 | if (this.lastTapEvent && event.timeStamp - this.lastTapEvent.timeStamp < PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) { | |
42b40636 | 65 | debugLogger('Detected double tap') |
2dd0a8a8 C |
66 | |
67 | this.lastTapEvent = undefined | |
68 | this.onDoubleTap(event) | |
69 | return | |
70 | } | |
71 | ||
72 | this.newActiveState = !this.player.userActive() | |
73 | ||
74 | this.tapTimeout = setTimeout(() => { | |
42b40636 | 75 | debugLogger('No double tap detected, set user active state to %s.', this.newActiveState) |
2dd0a8a8 C |
76 | |
77 | this.player.userActive(this.newActiveState) | |
78 | }, PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) | |
79 | ||
80 | this.lastTapEvent = event | |
457c8348 C |
81 | } |
82 | ||
83 | this.player.on('touchstart', (event: TouchEvent) => { | |
84 | // Only enable user active on player touch, we listen event on peertube mobile buttons to disable it | |
85 | if (this.player.userActive()) return | |
86 | ||
87 | handleTouchStart(event) | |
2dd0a8a8 | 88 | }) |
457c8348 | 89 | |
15a0e5f3 C |
90 | this.peerTubeMobileButtons.el().addEventListener('touchstart', (event: TouchEvent) => { |
91 | // Prevent mousemove/click events firing on the player, that conflict with our user active logic | |
92 | event.preventDefault() | |
93 | ||
94 | handleTouchStart(event) | |
95 | }, { passive: false }) | |
2dd0a8a8 C |
96 | } |
97 | ||
98 | private onDoubleTap (event: TouchEvent) { | |
99 | const playerWidth = this.player.currentWidth() | |
100 | ||
101 | const rect = this.findPlayerTarget((event.target as HTMLElement)).getBoundingClientRect() | |
102 | const offsetX = event.targetTouches[0].pageX - rect.left | |
103 | ||
42b40636 | 104 | debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX) |
2dd0a8a8 C |
105 | |
106 | if (offsetX > 0.66 * playerWidth) { | |
107 | if (this.seekAmount < 0) this.seekAmount = 0 | |
108 | ||
109 | this.seekAmount += 10 | |
110 | ||
42b40636 | 111 | debugLogger('Will forward %d seconds', this.seekAmount) |
2dd0a8a8 C |
112 | } else if (offsetX < 0.33 * playerWidth) { |
113 | if (this.seekAmount > 0) this.seekAmount = 0 | |
114 | ||
115 | this.seekAmount -= 10 | |
42b40636 | 116 | debugLogger('Will rewind %d seconds', this.seekAmount) |
2dd0a8a8 C |
117 | } |
118 | ||
119 | this.peerTubeMobileButtons.displayFastSeek(this.seekAmount) | |
120 | ||
121 | this.scheduleSetCurrentTime() | |
2dd0a8a8 C |
122 | } |
123 | ||
124 | private findPlayerTarget (target: HTMLElement): HTMLElement { | |
125 | if (target.classList.contains('video-js')) return target | |
126 | ||
127 | return this.findPlayerTarget(target.parentElement) | |
128 | } | |
129 | ||
130 | private scheduleSetCurrentTime () { | |
131 | this.player.pause() | |
132 | this.player.addClass('vjs-fast-seeking') | |
133 | ||
134 | if (this.setCurrentTimeTimeout) clearTimeout(this.setCurrentTimeTimeout) | |
135 | ||
136 | this.setCurrentTimeTimeout = setTimeout(() => { | |
137 | let newTime = this.player.currentTime() + this.seekAmount | |
138 | this.seekAmount = 0 | |
139 | ||
140 | newTime = Math.max(0, newTime) | |
141 | newTime = Math.min(this.player.duration(), newTime) | |
142 | ||
143 | this.player.currentTime(newTime) | |
144 | this.seekAmount = 0 | |
145 | this.peerTubeMobileButtons.displayFastSeek(0) | |
146 | ||
147 | this.player.removeClass('vjs-fast-seeking') | |
148 | this.player.userActive(false) | |
149 | ||
150 | this.player.play() | |
151 | }, PeerTubeMobilePlugin.SET_CURRENT_TIME_DELAY) | |
152 | } | |
f1a0555a C |
153 | } |
154 | ||
155 | videojs.registerPlugin('peertubeMobile', PeerTubeMobilePlugin) | |
156 | export { PeerTubeMobilePlugin } |