Add multiple subtitles support (#406)
* add multiple subtitles support * cleanup and remove log
This commit is contained in:
committed by
Feross Aboukhadijeh
parent
5e6e5fce1e
commit
04318d7580
@@ -27,6 +27,7 @@
|
|||||||
"electron-localshortcut": "^0.6.0",
|
"electron-localshortcut": "^0.6.0",
|
||||||
"electron-prebuilt": "0.37.5",
|
"electron-prebuilt": "0.37.5",
|
||||||
"hyperx": "^2.0.2",
|
"hyperx": "^2.0.2",
|
||||||
|
"languagedetect": "^1.1.1",
|
||||||
"main-loop": "^3.2.0",
|
"main-loop": "^3.2.0",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"musicmetadata": "^2.0.2",
|
"musicmetadata": "^2.0.2",
|
||||||
|
|||||||
@@ -707,6 +707,7 @@ body.drag .app::after {
|
|||||||
margin-top: 8px !important;
|
margin-top: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.player-controls .closed-captions.active,
|
||||||
.player-controls .device.active {
|
.player-controls .device.active {
|
||||||
color: #9af;
|
color: #9af;
|
||||||
}
|
}
|
||||||
@@ -789,6 +790,29 @@ body.drag .app::after {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subtitles list
|
||||||
|
*/
|
||||||
|
|
||||||
|
.subtitles-list {
|
||||||
|
position: fixed;
|
||||||
|
background: rgba(40, 40, 40, 0.8);
|
||||||
|
min-width: 100px;
|
||||||
|
bottom: 45px;
|
||||||
|
right: 3px;
|
||||||
|
transition: opacity 0.15s ease-out;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitles-list i {
|
||||||
|
font-size: 11px; /* make the cast icons less huge */
|
||||||
|
margin-right: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MEDIA OVERLAY / AUDIO DETAILS
|
* MEDIA OVERLAY / AUDIO DETAILS
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var fs = require('fs')
|
|||||||
var mainLoop = require('main-loop')
|
var mainLoop = require('main-loop')
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var srtToVtt = require('srt-to-vtt')
|
var srtToVtt = require('srt-to-vtt')
|
||||||
|
var LanguageDetect = require('languagedetect')
|
||||||
|
|
||||||
var createElement = require('virtual-dom/create-element')
|
var createElement = require('virtual-dom/create-element')
|
||||||
var diff = require('virtual-dom/diff')
|
var diff = require('virtual-dom/diff')
|
||||||
@@ -267,6 +268,12 @@ function dispatch (action, ...args) {
|
|||||||
if (action === 'openSubtitles') {
|
if (action === 'openSubtitles') {
|
||||||
openSubtitles()
|
openSubtitles()
|
||||||
}
|
}
|
||||||
|
if (action === 'selectSubtitle') {
|
||||||
|
selectSubtitle(args[0] /* label */)
|
||||||
|
}
|
||||||
|
if (action === 'showSubtitles') {
|
||||||
|
showSubtitles()
|
||||||
|
}
|
||||||
if (action === 'mediaStalled') {
|
if (action === 'mediaStalled') {
|
||||||
state.playing.isStalled = true
|
state.playing.isStalled = true
|
||||||
}
|
}
|
||||||
@@ -553,17 +560,42 @@ function addSubtitle (file) {
|
|||||||
// Set the cue text position so it appears above the player controls.
|
// Set the cue text position so it appears above the player controls.
|
||||||
// The only way to change cue text position is by modifying the VTT. It is not
|
// The only way to change cue text position is by modifying the VTT. It is not
|
||||||
// possible via CSS.
|
// possible via CSS.
|
||||||
|
var langDetected = (new LanguageDetect()).detect(buf.toString().replace(/(.*-->.*)/g, ''), 2)
|
||||||
|
langDetected = langDetected.length ? langDetected[0][0] : 'subtitle'
|
||||||
|
langDetected = langDetected.slice(0, 1).toUpperCase() + langDetected.slice(1)
|
||||||
var subtitles = Buffer(buf.toString().replace(/(-->.*)/g, '$1 line:88%'))
|
var subtitles = Buffer(buf.toString().replace(/(-->.*)/g, '$1 line:88%'))
|
||||||
var track = {
|
var track = {
|
||||||
buffer: 'data:text/vtt;base64,' + subtitles.toString('base64'),
|
buffer: 'data:text/vtt;base64,' + subtitles.toString('base64'),
|
||||||
language: 'Language ' + state.playing.subtitles.tracks.length,
|
label: langDetected,
|
||||||
selected: true
|
selected: true
|
||||||
}
|
}
|
||||||
|
state.playing.subtitles.tracks.forEach(function (trackItem) {
|
||||||
|
trackItem.selected = false
|
||||||
|
if (trackItem.label === track.label) {
|
||||||
|
track.label = Number.isNaN(track.label.slice(-1))
|
||||||
|
? track.label + ' 2'
|
||||||
|
: track.label.slice(0, -1) + (parseInt(track.label.slice(-1)) + 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
state.playing.subtitles.change = track.label
|
||||||
state.playing.subtitles.tracks.push(track)
|
state.playing.subtitles.tracks.push(track)
|
||||||
state.playing.subtitles.enabled = true
|
state.playing.subtitles.enabled = true
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectSubtitle (label) {
|
||||||
|
state.playing.subtitles.tracks.forEach(function (track) {
|
||||||
|
track.selected = (track.label === label)
|
||||||
|
})
|
||||||
|
state.playing.subtitles.enabled = !!label
|
||||||
|
state.playing.subtitles.change = label
|
||||||
|
state.playing.subtitles.show = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSubtitles () {
|
||||||
|
state.playing.subtitles.show = !state.playing.subtitles.show
|
||||||
|
}
|
||||||
|
|
||||||
// Starts downloading and/or seeding a given torrentSummary. Returns WebTorrent object
|
// Starts downloading and/or seeding a given torrentSummary. Returns WebTorrent object
|
||||||
function startTorrentingSummary (torrentSummary) {
|
function startTorrentingSummary (torrentSummary) {
|
||||||
var s = torrentSummary
|
var s = torrentSummary
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ function renderMedia (state) {
|
|||||||
state.playing.setVolume = null
|
state.playing.setVolume = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fix textTrack cues not been removed <track> rerender
|
||||||
|
if (state.playing.subtitles.change) {
|
||||||
|
var tracks = mediaElement.textTracks
|
||||||
|
for (var j = 0; j < tracks.length; j++) {
|
||||||
|
// mode is not an <track> attribute, only available on DOM
|
||||||
|
tracks[j].mode = (tracks[j].label === state.playing.subtitles.change) ? 'showing' : 'hidden'
|
||||||
|
}
|
||||||
|
state.playing.subtitles.change = null
|
||||||
|
}
|
||||||
|
|
||||||
state.playing.currentTime = mediaElement.currentTime
|
state.playing.currentTime = mediaElement.currentTime
|
||||||
state.playing.duration = mediaElement.duration
|
state.playing.duration = mediaElement.duration
|
||||||
state.playing.volume = mediaElement.volume
|
state.playing.volume = mediaElement.volume
|
||||||
@@ -59,13 +69,12 @@ function renderMedia (state) {
|
|||||||
if (state.playing.subtitles.enabled && state.playing.subtitles.tracks.length > 0) {
|
if (state.playing.subtitles.enabled && state.playing.subtitles.tracks.length > 0) {
|
||||||
for (var i = 0; i < state.playing.subtitles.tracks.length; i++) {
|
for (var i = 0; i < state.playing.subtitles.tracks.length; i++) {
|
||||||
var track = state.playing.subtitles.tracks[i]
|
var track = state.playing.subtitles.tracks[i]
|
||||||
|
|
||||||
trackTags.push(hx`
|
trackTags.push(hx`
|
||||||
<track
|
<track
|
||||||
default=${track.selected ? 'default' : ''}
|
${track.selected ? 'default' : ''}
|
||||||
label=${track.language}
|
label=${track.label}
|
||||||
type='subtitles'
|
type='subtitles'
|
||||||
src=${track.buffer}>
|
src=${track.buffer}>
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,9 +238,27 @@ function renderCastScreen (state) {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSubtitlesOptions (state) {
|
||||||
|
var subtitles = state.playing.subtitles
|
||||||
|
if (subtitles.tracks.length && subtitles.show) {
|
||||||
|
return hx`<ul.subtitles-list>
|
||||||
|
${subtitles.tracks.map(function (w, i) {
|
||||||
|
return hx`<li onclick=${dispatcher('selectSubtitle', w.label)}><i.icon>${w.selected ? 'radio_button_checked' : 'radio_button_unchecked'}</i>${w.label}</li>`
|
||||||
|
})}
|
||||||
|
<li onclick=${dispatcher('selectSubtitle', '')}><i.icon>${!subtitles.enabled ? 'radio_button_checked' : 'radio_button_unchecked'}</i>None</li>
|
||||||
|
</ul>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderPlayerControls (state) {
|
function renderPlayerControls (state) {
|
||||||
var positionPercent = 100 * state.playing.currentTime / state.playing.duration
|
var positionPercent = 100 * state.playing.currentTime / state.playing.duration
|
||||||
var playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 8px)' }
|
var playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 8px)' }
|
||||||
|
var captionsClass = state.playing.subtitles.tracks.length === 0
|
||||||
|
? 'disabled'
|
||||||
|
: state.playing.subtitles.enabled
|
||||||
|
? 'active'
|
||||||
|
: ''
|
||||||
|
|
||||||
var elements = [
|
var elements = [
|
||||||
hx`
|
hx`
|
||||||
@@ -256,7 +283,7 @@ function renderPlayerControls (state) {
|
|||||||
// show closed captions icon
|
// show closed captions icon
|
||||||
elements.push(hx`
|
elements.push(hx`
|
||||||
<i.icon.closed-captions
|
<i.icon.closed-captions
|
||||||
class=${state.playing.subtitles.enabled ? 'active' : 'disabled'}
|
class=${captionsClass}
|
||||||
onclick=${handleSubtitles}>
|
onclick=${handleSubtitles}>
|
||||||
closed_captions
|
closed_captions
|
||||||
</i>
|
</i>
|
||||||
@@ -369,7 +396,12 @@ function renderPlayerControls (state) {
|
|||||||
</i>
|
</i>
|
||||||
`)
|
`)
|
||||||
|
|
||||||
return hx`<div class='player-controls'>${elements}</div>`
|
return hx`
|
||||||
|
<div class='player-controls'>
|
||||||
|
${elements}
|
||||||
|
${renderSubtitlesOptions(state)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
// Handles a click or drag to scrub (jump to another position in the video)
|
// Handles a click or drag to scrub (jump to another position in the video)
|
||||||
function handleScrub (e) {
|
function handleScrub (e) {
|
||||||
@@ -414,14 +446,11 @@ function renderPlayerControls (state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleSubtitles (e) {
|
function handleSubtitles (e) {
|
||||||
if (!state.playing.subtitles.tracks.length) {
|
if (!state.playing.subtitles.tracks.length || e.ctrlKey || e.metaKey) {
|
||||||
// if no subtitles available select it
|
// if no subtitles available select it
|
||||||
dispatch('openSubtitles')
|
dispatch('openSubtitles')
|
||||||
} else {
|
} else {
|
||||||
// TODO: Show subtitles selector / disable
|
dispatch('showSubtitles')
|
||||||
// dispatch('showSubtitlesMenu')
|
|
||||||
// meanwhile, just enable/disable
|
|
||||||
state.playing.subtitles.enabled = !state.playing.subtitles.enabled
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user