autosub.lua (9820B)
1 --============================================================================= 2 -->> SUBLIMINAL PATH: 3 --============================================================================= 4 -- This script uses Subliminal to download subtitles, 5 -- so make sure to specify your system's Subliminal location below: 6 local subliminal = '/Users/alex/.local/bin/subliminal' 7 --============================================================================= 8 -->> SUBTITLE LANGUAGE: 9 --============================================================================= 10 -- Specify languages in this order: 11 -- { 'language name', 'ISO-639-1', 'ISO-639-2' } ! 12 -- (See: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 13 local languages = { 14 -- If subtitles are found for the first language, 15 -- other languages will NOT be downloaded, 16 -- so put your preferred language first: 17 { 'English', 'en', 'eng' }, 18 -- { 'Dutch', 'nl', 'dut' }, 19 -- { 'Spanish', 'es', 'spa' }, 20 -- { 'French', 'fr', 'fre' }, 21 -- { 'German', 'de', 'ger' }, 22 -- { 'Italian', 'it', 'ita' }, 23 -- { 'Portuguese', 'pt', 'por' }, 24 -- { 'Polish', 'pl', 'pol' }, 25 -- { 'Russian', 'ru', 'rus' }, 26 -- { 'Chinese', 'zh', 'chi' }, 27 -- { 'Arabic', 'ar', 'ara' }, 28 } 29 --============================================================================= 30 -->> PROVIDER LOGINS: 31 --============================================================================= 32 -- These are completely optional and not required 33 -- for the functioning of the script! 34 -- If you use any of these services, simply uncomment it 35 -- and replace 'USERNAME' and 'PASSWORD' with your own: 36 local logins = { 37 -- { '--addic7ed', 'USERNAME', 'PASSWORD' }, 38 -- { '--legendastv', 'USERNAME', 'PASSWORD' }, 39 -- { '--opensubtitles', 'USERNAME', 'PASSWORD' }, 40 -- { '--subscenter', 'USERNAME', 'PASSWORD' }, 41 } 42 --============================================================================= 43 -->> ADDITIONAL OPTIONS: 44 --============================================================================= 45 local bools = { 46 auto = false, -- Automatically download subtitles, no hotkeys required 47 debug = true, -- Use `--debug` in subliminal command for debug output 48 force = true, -- Force download; will overwrite existing subtitle files 49 utf8 = true, -- Save all subtitle files as UTF-8 50 } 51 local excludes = { 52 -- Movies with a path containing any of these strings/paths 53 -- will be excluded from auto-downloading subtitles. 54 -- Full paths are also allowed, e.g.: 55 -- '/home/david/Videos', 56 'no-subs-dl', 57 } 58 local includes = { 59 -- If anything is defined here, only the movies with a path 60 -- containing any of these strings/paths will auto-download subtitles. 61 -- Full paths are also allowed, e.g.: 62 -- '/home/david/Videos', 63 } 64 --============================================================================= 65 local utils = require 'mp.utils' 66 67 68 -- Download function: download the best subtitles in most preferred language 69 function download_subs(language) 70 language = language or languages[1] 71 if #language == 0 then 72 log('No Language found\n') 73 return false 74 end 75 76 log('Searching ' .. language[1] .. ' subtitles ...', 30) 77 78 -- Build the `subliminal` command, starting with the executable: 79 local table = { args = { subliminal } } 80 local a = table.args 81 82 for _, login in ipairs(logins) do 83 a[#a + 1] = login[1] 84 a[#a + 1] = login[2] 85 a[#a + 1] = login[3] 86 end 87 if bools.debug then 88 -- To see `--debug` output start MPV from the terminal! 89 a[#a + 1] = '--debug' 90 end 91 92 a[#a + 1] = 'download' 93 if bools.force then 94 a[#a + 1] = '-f' 95 end 96 if bools.utf8 then 97 a[#a + 1] = '-e' 98 a[#a + 1] = 'utf-8' 99 end 100 101 a[#a + 1] = '-l' 102 a[#a + 1] = language[2] 103 a[#a + 1] = '-d' 104 a[#a + 1] = directory 105 a[#a + 1] = filename --> Subliminal command ends with the movie filename. 106 107 local result = utils.subprocess(table) 108 109 if string.find(result.stdout, 'Downloaded 1 subtitle') then 110 -- When multiple external files are present, 111 -- always activate the most recently downloaded: 112 mp.set_property('slang', language[2]) 113 -- Subtitles are downloaded successfully, so rescan to activate them: 114 mp.commandv('rescan_external_files') 115 log(language[1] .. ' subtitles ready!') 116 return true 117 else 118 log('No ' .. language[1] .. ' subtitles found\n') 119 return false 120 end 121 end 122 123 -- Manually download second language subs by pressing 'n': 124 function download_subs2() 125 download_subs(languages[2]) 126 end 127 128 -- Control function: only download if necessary 129 function control_downloads() 130 -- Make MPV accept external subtitle files with language specifier: 131 mp.set_property('sub-auto', 'fuzzy') 132 -- Set subtitle language preference: 133 mp.set_property('slang', languages[1][2]) 134 mp.msg.warn('Reactivate external subtitle files:') 135 mp.commandv('rescan_external_files') 136 directory, filename = utils.split_path(mp.get_property('path')) 137 138 if not autosub_allowed() then 139 return 140 end 141 142 sub_tracks = {} 143 for _, track in ipairs(mp.get_property_native('track-list')) do 144 if track['type'] == 'sub' then 145 sub_tracks[#sub_tracks + 1] = track 146 end 147 end 148 if bools.debug then -- Log subtitle properties to terminal: 149 for _, track in ipairs(sub_tracks) do 150 mp.msg.warn('Subtitle track', track['id'], ':\n{') 151 for k, v in pairs(track) do 152 if type(v) == 'string' then v = '"' .. v .. '"' end 153 mp.msg.warn(' "' .. k .. '":', v) 154 end 155 mp.msg.warn('}\n') 156 end 157 end 158 159 for _, language in ipairs(languages) do 160 if should_download_subs_in(language) then 161 if download_subs(language) then return end -- Download successful! 162 else return end -- No need to download! 163 end 164 log('No subtitles were found') 165 end 166 167 -- Check if subtitles should be auto-downloaded: 168 function autosub_allowed() 169 local duration = tonumber(mp.get_property('duration')) 170 local active_format = mp.get_property('file-format') 171 172 if not bools.auto then 173 mp.msg.warn('Automatic downloading disabled!') 174 return false 175 elseif duration < 900 then 176 mp.msg.warn('Video is less than 15 minutes\n' .. 177 '=> NOT auto-downloading subtitles') 178 return false 179 elseif directory:find('^http') then 180 mp.msg.warn('Automatic subtitle downloading is disabled for web streaming') 181 return false 182 elseif active_format:find('^cue') then 183 mp.msg.warn('Automatic subtitle downloading is disabled for cue files') 184 return false 185 else 186 local not_allowed = {'aiff', 'ape', 'flac', 'mp3', 'ogg', 'wav', 'wv', 'tta'} 187 188 for _, file_format in pairs(not_allowed) do 189 if file_format == active_format then 190 mp.msg.warn('Automatic subtitle downloading is disabled for audio files') 191 return false 192 end 193 end 194 195 for _, exclude in pairs(excludes) do 196 local escaped_exclude = exclude:gsub('%W','%%%0') 197 local excluded = directory:find(escaped_exclude) 198 199 if excluded then 200 mp.msg.warn('This path is excluded from auto-downloading subs') 201 return false 202 end 203 end 204 205 for i, include in ipairs(includes) do 206 local escaped_include = include:gsub('%W','%%%0') 207 local included = directory:find(escaped_include) 208 209 if included then break 210 elseif i == #includes then 211 mp.msg.warn('This path is not included for auto-downloading subs') 212 return false 213 end 214 end 215 end 216 217 return true 218 end 219 220 -- Check if subtitles should be downloaded in this language: 221 function should_download_subs_in(language) 222 for i, track in ipairs(sub_tracks) do 223 local subtitles = track['external'] and 224 'subtitle file' or 'embedded subtitles' 225 226 if not track['lang'] and (track['external'] or not track['title']) 227 and i == #sub_tracks then 228 local status = track['selected'] and ' active' or ' present' 229 log('Unknown ' .. subtitles .. status) 230 mp.msg.warn('=> NOT downloading new subtitles') 231 return false -- Don't download if 'lang' key is absent 232 elseif track['lang'] == language[3] or track['lang'] == language[2] or 233 (track['title'] and track['title']:lower():find(language[3])) then 234 if not track['selected'] then 235 mp.set_property('sid', track['id']) 236 log('Enabled ' .. language[1] .. ' ' .. subtitles .. '!') 237 else 238 log(language[1] .. ' ' .. subtitles .. ' active') 239 end 240 mp.msg.warn('=> NOT downloading new subtitles') 241 return false -- The right subtitles are already present 242 end 243 end 244 mp.msg.warn('No ' .. language[1] .. ' subtitles were detected\n' .. 245 '=> Proceeding to download:') 246 return true 247 end 248 249 -- Log function: log to both terminal and MPV OSD (On-Screen Display) 250 function log(string, secs) 251 secs = secs or 2.5 -- secs defaults to 2.5 when secs parameter is absent 252 mp.msg.warn(string) -- This logs to the terminal 253 mp.osd_message(string, secs) -- This logs to MPV screen 254 end 255 256 257 mp.add_key_binding('b', 'download_subs', download_subs) 258 mp.add_key_binding('n', 'download_subs2', download_subs2) 259 mp.register_event('file-loaded', control_downloads)