mpv-autosub/autosub.lua
2024-10-29 18:40:05 +02:00

304 lines
11 KiB
Lua

--=============================================================================
-->> SUBLIMINAL PATH:
--=============================================================================
-- This script uses Subliminal to download subtitles,
-- so make sure to specify your systems's Subliminal locations below:
local subliminal_paths = {
['windows'] = '<path to subliminal.exe>',
['linux'] = '<path to subliminal>',
['darwin'] = '<path to subliminal>',
}
--=============================================================================
-->> SUBTITLE LANGUAGE:
--=============================================================================
-- Specify languages in this order:
-- { 'language name', 'ISO-639-1', 'ISO-639-2' } !
-- (See: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
local languages = {
-- If subtitles are found for the first language,
-- other languages will NOT be downloaded,
-- so put your preferred language first:
{ 'English', 'en', 'eng' },
-- { 'Swedish', 'sv', 'swe' },
-- { 'Spanish', 'es', 'spa' },
-- { 'French', 'fr', 'fre' },
-- { 'German', 'de', 'ger' },
-- { 'Italian', 'it', 'ita' },
-- { 'Portuguese', 'pt', 'por' },
-- { 'Polish', 'pl', 'pol' },
-- { 'Russian', 'ru', 'rus' },
-- { 'Chinese', 'zh', 'chi' },
-- { 'Arabic', 'ar', 'ara' },
}
--=============================================================================
-->> PROVIDER LOGINS:
--=============================================================================
-- These are completely optional and not required
-- for the functioning of the script!
-- If you use any of these services, simply uncomment it
-- and replace 'USERNAME' and 'PASSWORD' with your own:
local logins = {
-- { '--addic7ed', 'USERNAME', 'PASSWORD' },
-- { '--legendastv', 'USERNAME', 'PASSWORD' },
-- { '--opensubtitles', 'USERNAME', 'PASSWORD' },
-- { '--subscenter', 'USERNAME', 'PASSWORD' },
}
--=============================================================================
-->> ADDITIONAL OPTIONS:
--=============================================================================
local bools = {
auto = true, -- Automatically download subtitles, no hotkeys required
debug = false, -- Use `--debug` in subliminal command for debug output
force = true, -- Force download; will overwrite existing subtitle files
utf8 = true, -- Save all subtitle files as UTF-8
}
local excludes = {
-- Movies with a path containing any of these strings/paths
-- will be excluded from auto-downloading subtitles.
-- Full paths are also allowed, e.g.:
-- '/home/david/Videos',
'no-subs-dl',
}
local includes = {
-- If anything is defined here, only the movies with a path
-- containing any of these strings/paths will auto-download subtitles.
-- Full paths are also allowed, e.g.:
-- '/home/david/Videos',
}
--=============================================================================
local utils = require 'mp.utils'
local subliminal = nil
-- Download function: download the best subtitles in most preferred language
function download_subs(language)
language = language or languages[1]
if #language == 0 then
log('No Language found\n')
return false
end
log('Searching ' .. language[1] .. ' subtitles ...', 30)
-- Build the `subliminal` command, starting with the executable:
local table = { args = { subliminal } }
local a = table.args
for _, login in ipairs(logins) do
a[#a + 1] = login[1]
a[#a + 1] = login[2]
a[#a + 1] = login[3]
end
if bools.debug then
-- To see `--debug` output start MPV from the terminal!
a[#a + 1] = '--debug'
end
a[#a + 1] = 'download'
if bools.force then
a[#a + 1] = '-f'
end
if bools.utf8 then
a[#a + 1] = '-e'
a[#a + 1] = 'utf-8'
end
a[#a + 1] = '-l'
a[#a + 1] = language[2]
a[#a + 1] = '-d'
a[#a + 1] = directory
a[#a + 1] = filename --> Subliminal command ends with the movie filename.
local result = utils.subprocess(table)
if string.find(result.stdout, 'Downloaded 1 subtitle') then
-- When multiple external files are present,
-- always activate the most recently downloaded:
mp.set_property('slang', language[2])
-- Subtitles are downloaded successfully, so rescan to activate them:
mp.commandv('rescan_external_files')
log(language[1] .. ' subtitles ready!')
return true
else
log('No ' .. language[1] .. ' subtitles found\n')
return false
end
end
-- Manually download second language subs by pressing 'n':
function download_subs2()
download_subs(languages[2])
end
-- Control function: only download if necessary
function control_downloads()
-- Make MPV accept external subtitle files with language specifier:
mp.set_property('sub-auto', 'fuzzy')
-- Set subtitle language preference:
mp.set_property('slang', languages[1][2])
mp.msg.warn('Reactivate external subtitle files:')
mp.commandv('rescan_external_files')
directory, filename = utils.split_path(mp.get_property('path'))
if not autosub_allowed() then
return
end
sub_tracks = {}
for _, track in ipairs(mp.get_property_native('track-list')) do
if track['type'] == 'sub' then
sub_tracks[#sub_tracks + 1] = track
end
end
if bools.debug then -- Log subtitle properties to terminal:
for _, track in ipairs(sub_tracks) do
mp.msg.warn('Subtitle track', track['id'], ':\n{')
for k, v in pairs(track) do
if type(v) == 'string' then v = '"' .. v .. '"' end
mp.msg.warn(' "' .. k .. '":', v)
end
mp.msg.warn('}\n')
end
end
for _, language in ipairs(languages) do
if should_download_subs_in(language) then
if download_subs(language) then return end -- Download successful!
else
return
end -- No need to download!
end
log('No subtitles were found')
end
-- Check if subtitles should be auto-downloaded:
function autosub_allowed()
local duration = tonumber(mp.get_property('duration'))
local active_format = mp.get_property('file-format')
if not bools.auto then
mp.msg.warn('Automatic downloading disabled!')
return false
elseif duration < 900 then
mp.msg.warn('Video is less than 15 minutes\n' ..
'=> NOT auto-downloading subtitles')
return false
elseif directory:find('^http') then
mp.msg.warn('Automatic subtitle downloading is disabled for web streaming')
return false
elseif active_format:find('^cue') then
mp.msg.warn('Automatic subtitle downloading is disabled for cue files')
return false
else
local not_allowed = { 'aiff', 'ape', 'flac', 'mp3', 'ogg', 'wav', 'wv', 'tta' }
for _, file_format in pairs(not_allowed) do
if file_format == active_format then
mp.msg.warn('Automatic subtitle downloading is disabled for audio files')
return false
end
end
for _, exclude in pairs(excludes) do
local escaped_exclude = exclude:gsub('%W', '%%%0')
local excluded = directory:find(escaped_exclude)
if excluded then
mp.msg.warn('This path is excluded from auto-downloading subs')
return false
end
end
for i, include in ipairs(includes) do
local escaped_include = include:gsub('%W', '%%%0')
local included = directory:find(escaped_include)
if included then
break
elseif i == #includes then
mp.msg.warn('This path is not included for auto-downloading subs')
return false
end
end
end
return true
end
-- Check if subtitles should be downloaded in this language:
function should_download_subs_in(language)
for i, track in ipairs(sub_tracks) do
local subtitles = track['external'] and
'subtitle file' or 'embedded subtitles'
if not track['lang'] and (track['external'] or not track['title'])
and i == #sub_tracks then
local status = track['selected'] and ' active' or ' present'
log('Unknown ' .. subtitles .. status)
mp.msg.warn('=> NOT downloading new subtitles')
return false -- Don't download if 'lang' key is absent
elseif track['lang'] == language[3] or track['lang'] == language[2] or
(track['title'] and track['title']:lower():find(language[3])) then
if not track['selected'] then
mp.set_property('sid', track['id'])
log('Enabled ' .. language[1] .. ' ' .. subtitles .. '!')
else
log(language[1] .. ' ' .. subtitles .. ' active')
end
mp.msg.warn('=> NOT downloading new subtitles')
return false -- The right subtitles are already present
end
end
mp.msg.warn('No ' .. language[1] .. ' subtitles were detected\n' ..
'=> Proceeding to download:')
return true
end
-- Log function: log to both terminal and MPV OSD (On-Screen Display)
function log(string, secs)
secs = secs or 2.5 -- secs defaults to 2.5 when secs parameter is absent
mp.msg.warn(string) -- This logs to the terminal
mp.osd_message(string, secs) -- This logs to MPV screen
end
-- Determine OS to set correct subliminal executable path
-- Source: https://gist.github.com/soulik/82e9d02a818ce12498d1
function determine_os()
local raw_os_name = ''
-- LuaJIT shortcut
if jit and jit.os and jit.arch then
raw_os_name = jit.os
else
local popen_status, popen_result = pcall(io.popen, '')
if popen_status then
popen_result:close()
-- Unix based OS
raw_os_name = io.popen('uname -s', 'r'):read('*l')
else
-- Windows
local env_os = os.getenv('OS')
if env_os then
raw_os_name = env_os
end
end
end
return (raw_os_name):lower()
end
local os_name = determine_os()
if subliminal_paths[os_name] then
subliminal = subliminal_paths[os_name]
else
mp.msg.warn('Subliminal path not found for ' .. os_name)
mp.msg.warn('=> Subliminal path must be set manually in autosub.lua')
end
mp.add_key_binding('b', 'download_subs', download_subs)
mp.add_key_binding('n', 'download_subs2', download_subs2)
mp.register_event('file-loaded', control_downloads)