Add additional video player keyboard shortcuts (#275)

*  Skip forward 10 seconds ((CMD OR CTRL) ALT right)
 Skip back 10 seconds ((CMD OR CTRL) ALT left)
 Increase video speed ((CMD OR CTRL) +)
 Decrease video speed ((CMD OR CTRL) -)

* Codestyle fix

* The 'steps' should be implemented in base2, standard players use 1x, 2x, 4x, 8x, 16x

fixed bug with shift + "=" which is "+"

* resolve conflicts

* remove ide specific data
make playback rate more granular
add to menu skip and speed entries

* intendation fix

* conflict resolve

* rename setPlaybackRate to changePlaybackRate
setRate return boolean depending on whether this cast target supports setting the playback rate.
if setRate returns false - don`t change state
redundant else if statement in changePlaybackRate function
This commit is contained in:
Sergey Bargamon
2016-05-23 10:15:57 +03:00
committed by DC
parent ffce76a9b1
commit 8af4f42c42
8 changed files with 126 additions and 4 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
node_modules node_modules
dist dist

View File

@@ -92,6 +92,30 @@ function openSubtitles () {
} }
} }
function skipForward () {
if (windows.main) {
windows.main.send('dispatch', 'skip', 1)
}
}
function skipBack () {
if (windows.main) {
windows.main.send('dispatch', 'skip', -1)
}
}
function increasePlaybackRate () {
if (windows.main) {
windows.main.send('dispatch', 'changePlaybackRate', 1)
}
}
function decreasePlaybackRate () {
if (windows.main) {
windows.main.send('dispatch', 'changePlaybackRate', -1)
}
}
function onWindowShow () { function onWindowShow () {
log('onWindowShow') log('onWindowShow')
getMenuItem('Full Screen').enabled = true getMenuItem('Full Screen').enabled = true
@@ -110,6 +134,10 @@ function onPlayerOpen () {
getMenuItem('Increase Volume').enabled = true getMenuItem('Increase Volume').enabled = true
getMenuItem('Decrease Volume').enabled = true getMenuItem('Decrease Volume').enabled = true
getMenuItem('Add Subtitles File...').enabled = true getMenuItem('Add Subtitles File...').enabled = true
getMenuItem('Skip forward 10 seconds').enabled = true
getMenuItem('Skip back 10 seconds').enabled = true
getMenuItem('Increase video speed').enabled = true
getMenuItem('Decrease video speed').enabled = true
} }
function onPlayerClose () { function onPlayerClose () {
@@ -118,6 +146,10 @@ function onPlayerClose () {
getMenuItem('Increase Volume').enabled = false getMenuItem('Increase Volume').enabled = false
getMenuItem('Decrease Volume').enabled = false getMenuItem('Decrease Volume').enabled = false
getMenuItem('Add Subtitles File...').enabled = false getMenuItem('Add Subtitles File...').enabled = false
getMenuItem('Skip forward 10 seconds').enabled = false
getMenuItem('Skip back 10 seconds').enabled = false
getMenuItem('Increase video speed').enabled = false
getMenuItem('Decrease video speed').enabled = false
} }
function onToggleFullScreen (isFullScreen) { function onToggleFullScreen (isFullScreen) {
@@ -311,6 +343,36 @@ function getAppMenuTemplate () {
label: 'Add Subtitles File...', label: 'Add Subtitles File...',
click: openSubtitles, click: openSubtitles,
enabled: false enabled: false
},
{
type: 'separator'
},
{
label: 'Skip forward 10 seconds',
accelerator: 'CmdOrCtrl+Alt+Right',
click: skipForward,
enabled: false
},
{
label: 'Skip back 10 seconds',
accelerator: 'CmdOrCtrl+Alt+Left',
click: skipBack,
enabled: false
},
{
type: 'separator'
},
{
label: 'Increase video speed',
accelerator: 'CmdOrCtrl+plus',
click: increasePlaybackRate,
enabled: false
},
{
label: 'Decrease video speed',
accelerator: 'CmdOrCtrl+-',
click: decreasePlaybackRate,
enabled: false
} }
] ]
}, },

View File

@@ -748,6 +748,13 @@ body.drag .app::after {
padding: 5px; padding: 5px;
margin: 0 auto; margin: 0 auto;
} }
.player-controls .rate {
display: inline;
height: 30px;
padding: 5px;
margin-left: 20px;
float: left;
}
.player-controls .device, .player-controls .device,
.player-controls .fullscreen, .player-controls .fullscreen,

View File

@@ -285,6 +285,12 @@ function dispatch (action, ...args) {
if (action === 'playbackJump') { if (action === 'playbackJump') {
jumpToTime(args[0] /* seconds */) jumpToTime(args[0] /* seconds */)
} }
if (action === 'skip') {
jumpToTime(state.playing.currentTime + (args[0] /* direction */ * 10))
}
if (action === 'changePlaybackRate') {
changePlaybackRate(args[0] /* direction */)
}
if (action === 'changeVolume') { if (action === 'changeVolume') {
changeVolume(args[0] /* increase */) changeVolume(args[0] /* increase */)
} }
@@ -399,7 +405,26 @@ function jumpToTime (time) {
state.playing.jumpToTime = time state.playing.jumpToTime = time
} }
} }
function changePlaybackRate (direction) {
var rate = state.playing.playbackRate
if (direction > 0 && rate >= 0.25 && rate < 2) {
rate += 0.25
} else if (direction < 0 && rate > 0.25 && rate <= 2) {
rate -= 0.25
} else if (direction < 0 && rate === 0.25) { /* when we set playback rate at 0 in html 5, playback hangs ;( */
rate = -1
} else if (direction > 0 && rate === -1) {
rate = 0.25
} else if ((direction > 0 && rate >= 1 && rate < 16) || (direction < 0 && rate > -16 && rate <= -1)) {
rate *= 2
} else if ((direction < 0 && rate > 1 && rate <= 16) || (direction > 0 && rate >= -16 && rate < -1)) {
rate /= 2
}
state.playing.playbackRate = rate
if (lazyLoadCast().isCasting() && !Cast.setRate(rate)) {
state.playing.playbackRate = 1
}
}
function changeVolume (delta) { function changeVolume (delta) {
// change volume with delta value // change volume with delta value
setVolume(state.playing.volume + delta) setVolume(state.playing.volume + delta)

View File

@@ -8,7 +8,8 @@ module.exports = {
play, play,
pause, pause,
seek, seek,
setVolume setVolume,
setRate
} }
var airplay = require('airplay-js') var airplay = require('airplay-js')
@@ -344,6 +345,22 @@ function pause () {
} }
} }
function setRate (rate) {
var device
var result = true
if (state.playing.location === 'chromecast') {
// TODO find how to control playback rate on chromecast
castCallback()
result = false
} else if (state.playing.location === 'airplay') {
device = state.devices.airplay
device.rate(rate, castCallback)
} else {
result = false
}
return result
}
function seek (time) { function seek (time) {
var device = getDevice() var device = getDevice()
if (device) { if (device) {

View File

@@ -80,6 +80,7 @@ function getDefaultPlayState () {
isStalled: false, isStalled: false,
lastTimeUpdate: 0, /* Unix time in ms */ lastTimeUpdate: 0, /* Unix time in ms */
mouseStationarySince: 0, /* Unix time in ms */ mouseStationarySince: 0, /* Unix time in ms */
playbackRate: 1,
subtitles: { subtitles: {
tracks: [], /* subtitle tracks, each {label, language, ...} */ tracks: [], /* subtitle tracks, each {label, language, ...} */
selectedIndex: -1, /* current subtitle track */ selectedIndex: -1, /* current subtitle track */

View File

@@ -26,7 +26,8 @@ function App (state) {
state.playing.mouseStationarySince !== 0 && state.playing.mouseStationarySince !== 0 &&
new Date().getTime() - state.playing.mouseStationarySince > 2000 && new Date().getTime() - state.playing.mouseStationarySince > 2000 &&
!state.playing.isPaused && !state.playing.isPaused &&
state.playing.location === 'local' state.playing.location === 'local' &&
state.playing.playbackRate === 1
// Hide the header on Windows/Linux when in the player // Hide the header on Windows/Linux when in the player
// On OSX, the header appears as part of the title bar // On OSX, the header appears as part of the title bar

View File

@@ -48,6 +48,9 @@ function renderMedia (state) {
mediaElement.currentTime = state.playing.jumpToTime mediaElement.currentTime = state.playing.jumpToTime
state.playing.jumpToTime = null state.playing.jumpToTime = null
} }
if (state.playing.playbackRate !== mediaElement.playbackRate) {
mediaElement.playbackRate = state.playing.playbackRate
}
// Set volume // Set volume
if (state.playing.setVolume !== null && isFinite(state.playing.setVolume)) { if (state.playing.setVolume !== null && isFinite(state.playing.setVolume)) {
mediaElement.volume = state.playing.setVolume mediaElement.volume = state.playing.setVolume
@@ -440,6 +443,12 @@ function renderPlayerControls (state) {
</span> </span>
`) `)
// render playback rate
if (state.playing.playbackRate !== 1) {
elements.push(hx`
<span class="rate">speed: ${state.playing.playbackRate}X</span>
`)
}
// Finally, the big button in the center plays or pauses the video // Finally, the big button in the center plays or pauses the video
elements.push(hx` elements.push(hx`
<i class='icon play-pause' onclick=${dispatcher('playPause')}> <i class='icon play-pause' onclick=${dispatcher('playPause')}>