update
This commit is contained in:
+176
-74
@@ -1,10 +1,13 @@
|
||||
-- trackselect.lua
|
||||
-- https://github.com/po5/trackselect
|
||||
-- Because --slang isn't smart enough.
|
||||
--
|
||||
-- This script tries to select non-dub
|
||||
-- audio and subtitle tracks.
|
||||
-- Idea from https://github.com/siikamiika/scripts/blob/master/mpv%20scripts/dualaudiofix.lua
|
||||
--[[
|
||||
SOURCE_: https://github.com/po5/trackselect
|
||||
Modify_: https://github.com/dyphire/mpv-scripts
|
||||
|
||||
-- Because --slang isn't smart enough.
|
||||
--
|
||||
-- This script tries to select non-dub
|
||||
-- audio and subtitle tracks.
|
||||
-- Idea from https://github.com/siikamiika/scripts/blob/master/mpv%20scripts/dualaudiofix.lua
|
||||
]]
|
||||
|
||||
local opt = require 'mp.options'
|
||||
local utils = require 'mp.utils'
|
||||
@@ -14,9 +17,10 @@ local defaults = {
|
||||
selected = nil,
|
||||
best = {},
|
||||
lang_score = nil,
|
||||
expected_score = nil,
|
||||
channels_score = -math.huge,
|
||||
preferred = "jpn/japanese",
|
||||
excluded = "",
|
||||
excluded = "commentary/cast/staff/dub/guide",
|
||||
expected = "",
|
||||
id = ""
|
||||
},
|
||||
@@ -24,6 +28,7 @@ local defaults = {
|
||||
selected = nil,
|
||||
best = {},
|
||||
lang_score = nil,
|
||||
expected_score = nil,
|
||||
preferred = "",
|
||||
excluded = "",
|
||||
expected = "",
|
||||
@@ -33,8 +38,9 @@ local defaults = {
|
||||
selected = nil,
|
||||
best = {},
|
||||
lang_score = nil,
|
||||
expected_score = nil,
|
||||
preferred = "eng",
|
||||
excluded = "sign",
|
||||
excluded = "sign/song",
|
||||
expected = "",
|
||||
id = ""
|
||||
}
|
||||
@@ -54,11 +60,6 @@ local options = {
|
||||
|
||||
-- Try to re-select the last track if mpv cannot do it e.g. when fingerprint changes
|
||||
smart_keep = false,
|
||||
|
||||
--add above (after a comma) any protocol to disable
|
||||
special_protocols = [[
|
||||
["://", "^magnet:"]
|
||||
]],
|
||||
}
|
||||
|
||||
for _type, track in pairs(defaults) do
|
||||
@@ -72,41 +73,82 @@ options["preferred_audio_channels"] = ""
|
||||
local tracks = {}
|
||||
local last = {}
|
||||
local fingerprint = ""
|
||||
local trackselect_ran = false
|
||||
|
||||
opt.read_options(options, _, function() end)
|
||||
|
||||
options.special_protocols = utils.parse_json(options.special_protocols)
|
||||
local function is_protocol(path)
|
||||
return type(path) == 'string' and (path:find('^%a[%w.+-]-://') ~= nil or path:find('^%a[%w.+-]-:%?') ~= nil)
|
||||
end
|
||||
|
||||
local function need_ignore(tab, val)
|
||||
for index, element in ipairs(tab) do
|
||||
if string.find(val, element) then
|
||||
return true
|
||||
local function hex_to_char(x)
|
||||
return string.char(tonumber(x, 16))
|
||||
end
|
||||
|
||||
local function url_decode(str)
|
||||
if str ~= nil then
|
||||
str = str:gsub('^%a[%a%d-_]+://', '')
|
||||
:gsub('^%a[%a%d-_]+:\\?', '')
|
||||
:gsub('%%(%x%x)', hex_to_char)
|
||||
if str:find('://localhost:?') then
|
||||
str = str:gsub('^.*/', '')
|
||||
end
|
||||
str = str:gsub("%?.+", ""):gsub("%+", " ")
|
||||
local last_pos = str:match('.*[\\/:%?]()')
|
||||
if last_pos then
|
||||
str = str:sub(last_pos)
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local function is_empty(input)
|
||||
if input == nil or input == "" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function replace(str, what, with)
|
||||
if is_empty(str) then return "" end
|
||||
if is_empty(what) then return str end
|
||||
if with == nil then with = "" end
|
||||
what = string.gsub(what, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1")
|
||||
with = string.gsub(with, "[%%]", "%%%%")
|
||||
return string.gsub(str, what, with)
|
||||
end
|
||||
|
||||
local function comparison(str1, str2)
|
||||
if not str1 and not str2 then
|
||||
return true
|
||||
end
|
||||
if str1 and str2 then
|
||||
return str1:lower() == str2:lower()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function contains(track, words, attr)
|
||||
local function contains(track, words, attr)
|
||||
if not track[attr] then return false end
|
||||
local i = 0
|
||||
if track.external then
|
||||
i = 1
|
||||
end
|
||||
for word in string.gmatch(words:lower(), "([^/]+)") do
|
||||
local w = word:match("^%s*(.-)%s*$")
|
||||
i = i - 1
|
||||
if string.find(tostring(track[attr] or ""):lower(), word) then
|
||||
if w ~= "" and string.find(tostring(track[attr] or ""):lower(), w) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function preferred(track, words, attr, title)
|
||||
local function preferred(track, words, attr, title)
|
||||
local score = contains(track, words, attr)
|
||||
if not score then
|
||||
if tracks[track.type][title] == nil then
|
||||
tracks[track.type][title] = -math.huge
|
||||
return true
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -117,10 +159,11 @@ function preferred(track, words, attr, title)
|
||||
return false
|
||||
end
|
||||
|
||||
function preferred_or_equals(track, words, attr, title)
|
||||
local function preferred_or_equals(track, words, attr, title)
|
||||
local score = contains(track, words, attr)
|
||||
if not score then
|
||||
if tracks[track.type][title] == nil or tracks[track.type][title] == -math.huge then
|
||||
if (tracks[track.type][title] == nil or
|
||||
tracks[track.type][title] == -math.huge) and track.default then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -131,14 +174,14 @@ function preferred_or_equals(track, words, attr, title)
|
||||
return false
|
||||
end
|
||||
|
||||
function copy(obj)
|
||||
local function copy(obj)
|
||||
if type(obj) ~= "table" then return obj end
|
||||
local res = {}
|
||||
for k, v in pairs(obj) do res[k] = copy(v) end
|
||||
return res
|
||||
end
|
||||
|
||||
function track_layout_hash(tracklist)
|
||||
local function track_layout_hash(tracklist)
|
||||
local t = {}
|
||||
for _, track in ipairs(tracklist) do
|
||||
t[#t + 1] = string.format("%s-%d-%s-%s-%s-%s", track.type, track.id, tostring(track.default),
|
||||
@@ -147,12 +190,51 @@ function track_layout_hash(tracklist)
|
||||
return table.concat(t, "\n")
|
||||
end
|
||||
|
||||
function trackselect()
|
||||
local fpath = mp.get_property('path')
|
||||
if not options.enabled then return end
|
||||
if need_ignore(options.special_protocols, fpath) then return end
|
||||
tracks = copy(defaults)
|
||||
local function sanitize_title(track, path, filename, media_title)
|
||||
if not track or not track.title then return end
|
||||
local title = filename
|
||||
if is_protocol(path) then
|
||||
title = url_decode(media_title):gsub("%.?([^%.]+)$", "")
|
||||
track.title = url_decode(track.title)
|
||||
end
|
||||
track.title = replace(track.title, title, "")
|
||||
end
|
||||
|
||||
local function selected_tracks()
|
||||
if not trackselect_ran then
|
||||
return
|
||||
end
|
||||
last = {}
|
||||
local tracklist = mp.get_property_native("track-list")
|
||||
local path = mp.get_property("path")
|
||||
local filename = mp.get_property("filename/no-ext")
|
||||
local media_title = mp.get_property("media-title", "")
|
||||
for _, track in ipairs(tracklist) do
|
||||
if track.selected then
|
||||
sanitize_title(track, path, filename, media_title)
|
||||
last[track.type] = track
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function trackselect()
|
||||
if options.smart_keep then
|
||||
-- observe specific current-tracks sub-properties
|
||||
local props = { 'current-tracks/video', 'current-tracks/audio', 'current-tracks/sub' }
|
||||
for _, p in ipairs(props) do
|
||||
mp.observe_property(p, 'native', selected_tracks)
|
||||
end
|
||||
end
|
||||
|
||||
trackselect_ran = true
|
||||
if not options.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
tracks = copy(defaults)
|
||||
local path = mp.get_property("path")
|
||||
local filename = mp.get_property("filename/no-ext")
|
||||
local media_title = mp.get_property("media-title", "")
|
||||
local tracklist = mp.get_property_native("track-list")
|
||||
local tracklist_changed = false
|
||||
local found_last = {}
|
||||
@@ -164,10 +246,14 @@ function trackselect()
|
||||
fingerprint = new_fingerprint
|
||||
tracklist_changed = true
|
||||
end
|
||||
|
||||
for _, track in ipairs(tracklist) do
|
||||
if options.smart_keep and last[track.type] ~= nil and last[track.type].lang == track.lang and
|
||||
last[track.type].codec == track.codec and last[track.type].external == track.external and
|
||||
last[track.type].title == track.title then
|
||||
sanitize_title(track, path, filename, media_title)
|
||||
if options.smart_keep and last[track.type] ~= nil and
|
||||
comparison(last[track.type].lang, track.lang) and
|
||||
track.codec ~= "null" and last[track.type].codec == track.codec and
|
||||
last[track.type].external == track.external and
|
||||
comparison(last[track.type].title, track.title) then
|
||||
tracks[track.type].best = track
|
||||
options["preferred_" .. track.type .. "_lang"] = ""
|
||||
options["excluded_" .. track.type .. "_words"] = ""
|
||||
@@ -185,40 +271,64 @@ function trackselect()
|
||||
options["preferred_" .. track.type .. "_channels"] = ""
|
||||
end
|
||||
end
|
||||
if options["preferred_" .. track.type .. "_lang"] ~= "" or options["excluded_" .. track.type .. "_words"] ~= ""
|
||||
or options["expected_" .. track.type .. "_words"] ~= "" or
|
||||
if track.codec ~= "null" and options["preferred_" .. track.type .. "_lang"] ~= "" or
|
||||
options["excluded_" .. track.type .. "_words"] ~= "" or
|
||||
options["expected_" .. track.type .. "_words"] ~= "" or
|
||||
(options["preferred_" .. track.type .. "_channels"] or "") ~= "" then
|
||||
local preferred_lang = options["preferred_" .. track.type .. "_lang"]
|
||||
local excluded_words = options["excluded_" .. track.type .. "_words"]
|
||||
local expected_words = options["expected_" .. track.type .. "_words"]
|
||||
local preferred_channels = options["preferred_" .. track.type .. "_channels"]
|
||||
|
||||
if preferred_lang == "" then
|
||||
if track.type == "video" then
|
||||
preferred_lang = mp.get_property("vlang", ""):gsub(",", "/")
|
||||
elseif track.type == "audio" then
|
||||
preferred_lang = mp.get_property("alang", ""):gsub(",", "/")
|
||||
elseif track.type == "sub" then
|
||||
preferred_lang = mp.get_property("slang", ""):gsub(",", "/")
|
||||
end
|
||||
end
|
||||
if track.selected then
|
||||
tracks[track.type].selected = track.id
|
||||
if options.smart_keep then
|
||||
last[track.type] = track
|
||||
end
|
||||
end
|
||||
if track.title then
|
||||
track.title = string.gsub(string.gsub(track.title, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1"), filename, "")
|
||||
end
|
||||
if next(tracks[track.type].best) == nil or not (tracks[track.type].best.external
|
||||
and tracks[track.type].best.lang ~= nil and not track.external) then
|
||||
if options["excluded_" .. track.type .. "_words"] == "" or
|
||||
not contains(track, options["excluded_" .. track.type .. "_words"], "title") then
|
||||
if options["expected_" .. track.type .. "_words"] == "" or
|
||||
contains(track, options["expected_" .. track.type .. "_words"], "title") then
|
||||
local pass = true
|
||||
local channels = false
|
||||
local lang = false
|
||||
if (options["preferred_" .. track.type .. "_channels"] or "") ~= "" and
|
||||
preferred_or_equals(track, options["preferred_" .. track.type .. "_lang"], "lang",
|
||||
"lang_score") then
|
||||
channels = preferred(track, options["preferred_" .. track.type .. "_channels"],
|
||||
"demux-channel-count", "channels_score")
|
||||
pass = channels
|
||||
|
||||
if (next(tracks[track.type].best) == nil or not (tracks[track.type].best.external
|
||||
and tracks[track.type].best.lang ~= nil and not track.external)) then
|
||||
if excluded_words == "" or not contains(track, excluded_words, "title") then
|
||||
local channels = false
|
||||
local lang = false
|
||||
if (preferred_channels or "") ~= "" and
|
||||
preferred_or_equals(track, preferred_lang, "lang", "lang_score") then
|
||||
channels = preferred(track, preferred_channels, "demux-channel-count", "channels_score")
|
||||
end
|
||||
if preferred_lang ~= "" then
|
||||
lang = preferred(track, preferred_lang, "lang", "lang_score")
|
||||
end
|
||||
|
||||
local exp_score = nil
|
||||
if expected_words then
|
||||
exp_score = contains(track, expected_words, "title")
|
||||
end
|
||||
if exp_score then
|
||||
local lang_score = nil
|
||||
local existing_lang = nil
|
||||
local existing_exp = tracks[track.type].expected_score
|
||||
if next(tracks[track.type].best) ~= nil and preferred_lang ~= "" then
|
||||
existing_lang = contains(tracks[track.type].best, preferred_lang, "lang")
|
||||
lang_score = contains(track, preferred_lang, "lang")
|
||||
end
|
||||
if options["preferred_" .. track.type .. "_lang"] ~= "" then
|
||||
lang = preferred(track, options["preferred_" .. track.type .. "_lang"], "lang", "lang_score")
|
||||
if existing_exp == nil or exp_score > existing_exp or (exp_score == existing_exp and
|
||||
((lang_score or -math.huge) > (existing_lang or -math.huge) or channels)) then
|
||||
tracks[track.type].expected_score = exp_score
|
||||
tracks[track.type].best = track
|
||||
end
|
||||
if (options["preferred_" .. track.type .. "_lang"] == "" and pass) or channels or lang or
|
||||
(track.external and track.lang == nil and
|
||||
(not tracks[track.type].best.external or tracks[track.type].best.lang == nil)) then
|
||||
elseif tracks[track.type].expected_score == nil then
|
||||
if (preferred_lang == "" and track.default) or channels or lang or
|
||||
(track.external and track.lang == nil and next(tracks[track.type].best) == nil) then
|
||||
tracks[track.type].best = track
|
||||
end
|
||||
end
|
||||
@@ -226,9 +336,10 @@ function trackselect()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _type, track in pairs(tracks) do
|
||||
if next(track.best) ~= nil and track.best.id ~= track.selected then
|
||||
mp.set_property(_type:sub(1, 1) .. "id", track.best.id)
|
||||
mp.set_property('file-local-options/'.. _type:sub(1, 1) .. "id", track.best.id)
|
||||
if options.smart_keep and found_last[track.best.type] then
|
||||
last[track.best.type] = track.best
|
||||
end
|
||||
@@ -236,22 +347,13 @@ function trackselect()
|
||||
end
|
||||
end
|
||||
|
||||
function selected_tracks()
|
||||
local tracklist = mp.get_property_native("track-list")
|
||||
last = {}
|
||||
for _, track in ipairs(tracklist) do
|
||||
if track.selected then
|
||||
last[track.type] = track
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if options.hook then
|
||||
mp.add_hook("on_preloaded", 50, trackselect)
|
||||
else
|
||||
mp.register_event("file-loaded", trackselect)
|
||||
end
|
||||
|
||||
if options.smart_keep then
|
||||
mp.register_event("track-switched", selected_tracks)
|
||||
end
|
||||
mp.add_hook("on_unload", 50, function()
|
||||
trackselect_ran = false
|
||||
mp.unobserve_property(selected_tracks)
|
||||
end)
|
||||
Reference in New Issue
Block a user