Add subtitle support (via drag-n-drop) (#361)

* issue template

* cleanup closePlayer() and stopServer()

* Add subtitle support (via drag-n-drop)

Drag and drop a subtitles file (.srt or .vtt) onto the player (or the
app icon on OS X) to add subtitles to the currently playing video.

For #281

* add multiple subtitles structure

* add open subtitle dialog from cc player controls
This commit is contained in:
Feross Aboukhadijeh
2016-04-10 16:42:18 -07:00
parent f9141dd39c
commit 1a0a2b3658
6 changed files with 124 additions and 30 deletions

View File

@@ -1,6 +1,7 @@
console.time('init')
var cfg = require('application-config')('WebTorrent')
var concat = require('concat-stream')
var dragDrop = require('drag-drop')
var electron = require('electron')
var EventEmitter = require('events')
@@ -8,6 +9,7 @@ var fs = require('fs')
var mainLoop = require('main-loop')
var path = require('path')
var remote = require('remote')
var srtToVtt = require('srt-to-vtt')
var createElement = require('virtual-dom/create-element')
var diff = require('virtual-dom/diff')
@@ -255,6 +257,9 @@ function dispatch (action, ...args) {
if (action === 'setVolume') {
setVolume(args[0] /* increase */)
}
if (action === 'openSubtitles') {
openSubtitles()
}
if (action === 'mediaPlaying') {
state.playing.isPaused = false
ipcRenderer.send('blockPowerSave')
@@ -331,7 +336,6 @@ function changeVolume (delta) {
setVolume(state.playing.volume + delta)
}
// TODO: never called. Either remove or make a volume control that calls it
function setVolume (volume) {
// check if its in [0.0 - 1.0] range
volume = Math.max(0, Math.min(1, volume))
@@ -342,6 +346,17 @@ function setVolume (volume) {
}
}
function openSubtitles () {
dialog.showOpenDialog({
title: 'Select a subtitles file.',
filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ],
properties: [ 'openFile' ]
}, function (filenames) {
if (!Array.isArray(filenames)) return
addSubtitle({path: filenames[0]})
})
}
// Checks whether we are connected and already casting
// Returns false if we not casting (state.playing.location === 'local')
// or if we're trying to connect but haven't yet ('chromecast-pending', etc)
@@ -459,12 +474,35 @@ function onOpen (files) {
if (!Array.isArray(files)) files = [ files ]
// .torrent file = start downloading the torrent
files.filter(isTorrent).forEach(function (torrentFile) {
addTorrent(torrentFile)
})
files.filter(isTorrent).forEach(addTorrent)
// subtitle file
files.filter(isSubtitle).forEach(addSubtitle)
// everything else = seed these files
createTorrentFromFileObjects(files.filter(isNotTorrent))
var rest = files.filter(not(isTorrent)).filter(not(isSubtitle))
if (rest.length > 0) {
createTorrentFromFileObjects(rest)
}
}
function isTorrent (file) {
var name = typeof file === 'string' ? file : file.name
var isTorrentFile = path.extname(name).toLowerCase() === '.torrent'
var isMagnet = typeof file === 'string' && /^magnet:/.test(file)
return isTorrentFile || isMagnet
}
function isSubtitle (file) {
var name = typeof file === 'string' ? file : file.name
var ext = path.extname(name).toLowerCase()
return ext === '.srt' || ext === '.vtt'
}
function not (test) {
return function (...args) {
return !test(...args)
}
}
function onPaste (e) {
@@ -478,17 +516,6 @@ function onPaste (e) {
})
}
function isTorrent (file) {
var name = typeof file === 'string' ? file : file.name
var isTorrentFile = path.extname(name).toLowerCase() === '.torrent'
var isMagnet = typeof file === 'string' && /^magnet:/.test(file)
return isTorrentFile || isMagnet
}
function isNotTorrent (file) {
return !isTorrent(file)
}
// Gets a torrent summary {name, infoHash, status} from state.saved.torrents
// Returns undefined if we don't know that infoHash
function getTorrentSummary (torrentKey) {
@@ -509,6 +536,23 @@ function addTorrent (torrentId) {
ipcRenderer.send('wt-start-torrenting', torrentKey, torrentId, path)
}
function addSubtitle (file) {
if (state.playing.type !== 'video') return
fs.createReadStream(file.path || file).pipe(srtToVtt()).pipe(concat(function (buf) {
// 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
// possible via CSS.
var subtitles = Buffer(buf.toString().replace(/(-->.*)/g, '$1 line:88%'))
var track = {
buffer: 'data:text/vtt;base64,' + subtitles.toString('base64'),
language: 'Language ' + state.playing.subtitles.tracks.length,
selected: true
}
state.playing.subtitles.tracks.push(track)
state.playing.subtitles.enabled = true
}))
}
// Starts downloading and/or seeding a given torrentSummary. Returns WebTorrent object
function startTorrentingSummary (torrentSummary) {
var s = torrentSummary
@@ -744,12 +788,6 @@ function pickFileToPlay (files) {
return undefined
}
function stopServer () {
ipcRenderer.send('wt-stop-server')
state.playing = State.getDefaultPlayState()
state.server = null
}
// Opens the video player
function openPlayer (infoHash, index, cb) {
var torrentSummary = getTorrentSummary(infoHash)
@@ -817,23 +855,23 @@ function openPlayerFromActiveTorrent (torrentSummary, index, timeout, cb) {
}
function closePlayer (cb) {
state.window.title = config.APP_WINDOW_TITLE
update() /* needed for OSX: toggleFullScreen animation w/ correct title */
if (isCasting()) {
Cast.close()
}
state.window.title = config.APP_WINDOW_TITLE
state.playing = State.getDefaultPlayState()
state.server = null
if (state.window.isFullScreen) {
dispatch('toggleFullScreen', false)
}
restoreBounds()
stopServer()
update()
ipcRenderer.send('wt-stop-server')
ipcRenderer.send('unblockPowerSave')
ipcRenderer.send('onPlayerClose')
update()
cb()
}