140 lines
4.5 KiB
JavaScript
140 lines
4.5 KiB
JavaScript
const electron = require('electron')
|
|
const fs = require('fs-extra')
|
|
const path = require('path')
|
|
const parallel = require('run-parallel')
|
|
|
|
const remote = electron.remote
|
|
|
|
const {dispatch} = require('../lib/dispatcher')
|
|
|
|
module.exports = class SubtitlesController {
|
|
constructor (state) {
|
|
this.state = state
|
|
}
|
|
|
|
openSubtitles () {
|
|
remote.dialog.showOpenDialog({
|
|
title: 'Select a subtitles file.',
|
|
filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ],
|
|
properties: [ 'openFile' ]
|
|
}, (filenames) => {
|
|
if (!Array.isArray(filenames)) return
|
|
this.addSubtitles(filenames, true)
|
|
})
|
|
}
|
|
|
|
selectSubtitle (ix) {
|
|
this.state.playing.subtitles.selectedIndex = ix
|
|
}
|
|
|
|
toggleSubtitlesMenu () {
|
|
const subtitles = this.state.playing.subtitles
|
|
subtitles.showMenu = !subtitles.showMenu
|
|
}
|
|
|
|
addSubtitles (files, autoSelect) {
|
|
const state = this.state
|
|
// Subtitles are only supported when playing video files
|
|
if (state.playing.type !== 'video') return
|
|
if (files.length === 0) return
|
|
const subtitles = state.playing.subtitles
|
|
|
|
// Read the files concurrently, then add all resulting subtitle tracks
|
|
const tasks = files.map((file) => (cb) => loadSubtitle(file, cb))
|
|
parallel(tasks, function (err, tracks) {
|
|
if (err) return dispatch('error', err)
|
|
|
|
for (let i = 0; i < tracks.length; i++) {
|
|
// No dupes allowed
|
|
const track = tracks[i]
|
|
let trackIndex = state.playing.subtitles.tracks
|
|
.findIndex((t) => track.filePath === t.filePath)
|
|
|
|
// Add the track
|
|
if (trackIndex === -1) {
|
|
trackIndex = state.playing.subtitles.tracks.push(track) - 1
|
|
}
|
|
|
|
// If we're auto-selecting a track, try to find one in the user's language
|
|
if (autoSelect && (i === 0 || isSystemLanguage(track.language))) {
|
|
state.playing.subtitles.selectedIndex = trackIndex
|
|
}
|
|
}
|
|
|
|
// Finally, make sure no two tracks have the same label
|
|
relabelSubtitles(subtitles)
|
|
})
|
|
}
|
|
|
|
checkForSubtitles () {
|
|
if (this.state.playing.type !== 'video') return
|
|
const torrentSummary = this.state.getPlayingTorrentSummary()
|
|
if (!torrentSummary || !torrentSummary.progress) return
|
|
|
|
torrentSummary.progress.files.forEach((fp, ix) => {
|
|
if (fp.numPieces !== fp.numPiecesPresent) return // ignore incomplete files
|
|
const file = torrentSummary.files[ix]
|
|
if (!this.isSubtitle(file.name)) return
|
|
const filePath = path.join(torrentSummary.path, file.path)
|
|
this.addSubtitles([filePath], false)
|
|
})
|
|
}
|
|
|
|
isSubtitle (file) {
|
|
const name = typeof file === 'string' ? file : file.name
|
|
const ext = path.extname(name).toLowerCase()
|
|
return ext === '.srt' || ext === '.vtt'
|
|
}
|
|
}
|
|
|
|
function loadSubtitle (file, cb) {
|
|
// Lazy load to keep startup fast
|
|
const concat = require('simple-concat')
|
|
const LanguageDetect = require('languagedetect')
|
|
const srtToVtt = require('srt-to-vtt')
|
|
|
|
// Read the .SRT or .VTT file, parse it, add subtitle track
|
|
const filePath = file.path || file
|
|
|
|
const vttStream = fs.createReadStream(filePath).pipe(srtToVtt())
|
|
|
|
concat(vttStream, function (err, buf) {
|
|
if (err) return dispatch('error', 'Can\'t parse subtitles file.')
|
|
|
|
// Detect what language the subtitles are in
|
|
const vttContents = buf.toString().replace(/(.*-->.*)/g, '')
|
|
let langDetected = (new LanguageDetect()).detect(vttContents, 2)
|
|
langDetected = langDetected.length ? langDetected[0][0] : 'subtitle'
|
|
langDetected = langDetected.slice(0, 1).toUpperCase() + langDetected.slice(1)
|
|
|
|
const track = {
|
|
buffer: 'data:text/vtt;base64,' + buf.toString('base64'),
|
|
language: langDetected,
|
|
label: langDetected,
|
|
filePath: filePath
|
|
}
|
|
|
|
cb(null, track)
|
|
})
|
|
}
|
|
|
|
// Checks whether a language name like 'English' or 'German' matches the system
|
|
// language, aka the current locale
|
|
function isSystemLanguage (language) {
|
|
const iso639 = require('iso-639-1')
|
|
const osLangISO = window.navigator.language.split('-')[0] // eg 'en'
|
|
const langIso = iso639.getCode(language) // eg 'de' if language is 'German'
|
|
return langIso === osLangISO
|
|
}
|
|
|
|
// Make sure we don't have two subtitle tracks with the same label
|
|
// Labels each track by language, eg 'German', 'English', 'English 2', ...
|
|
function relabelSubtitles (subtitles) {
|
|
const counts = {}
|
|
subtitles.tracks.forEach(function (track) {
|
|
const lang = track.language
|
|
counts[lang] = (counts[lang] || 0) + 1
|
|
track.label = counts[lang] > 1 ? (lang + ' ' + counts[lang]) : lang
|
|
})
|
|
}
|