1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
--[[
Copyright: Ren Tatsumoto and contributors
License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html
This module automatically finds and sets secondary sid if it's not already set.
Secondary sid will be shown when mouse is moved to the top part of the mpv window.
]]
local mp = require('mp')
local h = require('helpers')
local self = {
visibility = 'auto',
visibility_states = { auto = true, never = true, always = true, },
}
local function is_accepted_language(sub_lang)
-- for missing keys compares nil to true
return self.accepted_languages[sub_lang] == true
end
local function is_selected_language(track, active_track)
return track.id == mp.get_property_native('sid') or (active_track and active_track.lang == track.lang)
end
local function is_full(track)
return h.str_contains(track.title, 'full')
end
local function is_garbage(track)
for _, keyword in pairs({ 'song', 'sign', 'caption', 'commentary' }) do
if h.str_contains(track.title, keyword) then
return true
end
end
return false
end
local function prioritize_full_subs(tracks_list)
return table.sort(tracks_list, function(first, second)
return (is_full(first) and not is_full(second)) or (is_garbage(second) and not is_garbage(first))
end)
end
local function find_best_secondary_sid()
local active_track = h.get_active_track('sub')
local sub_tracks = h.get_loaded_tracks('sub')
prioritize_full_subs(sub_tracks)
for _, track in ipairs(sub_tracks) do
if is_accepted_language(track.lang) and not is_selected_language(track, active_track) then
return track.id
end
end
return nil
end
local function window_height()
return mp.get_property_native('osd-dimensions/h')
end
local function get_accepted_sub_langs()
local languages = {}
for lang in self.config.secondary_sub_lang:gmatch('[a-zA-Z-]+') do
languages[lang] = true
end
return languages
end
local function on_mouse_move(_, state)
-- state = {x=int,y=int, hover=true|false, }
if mp.get_property_native('secondary-sid') and self.visibility == 'auto' and state ~= nil then
mp.set_property_bool(
'secondary-sub-visibility',
state.hover and (state.y / window_height()) < self.config.secondary_sub_area
)
end
end
local function on_file_loaded()
-- If secondary sid is not already set, try to find and set it.
local secondary_sid = mp.get_property_native('secondary-sid')
if secondary_sid == false and self.config.secondary_sub_auto_load == true then
secondary_sid = find_best_secondary_sid()
if secondary_sid ~= nil then
mp.set_property_native('secondary-sid', secondary_sid)
end
end
end
local function update_visibility()
mp.set_property_bool('secondary-sub-visibility', self.visibility == 'always')
end
local function init(config)
self.config = config
self.visibility = config.secondary_sub_visibility
self.accepted_languages = get_accepted_sub_langs()
mp.register_event('file-loaded', on_file_loaded)
if config.secondary_sub_area > 0 then
mp.observe_property('mouse-pos', 'native', on_mouse_move)
end
update_visibility()
end
local function change_visibility()
while true do
self.visibility = next(self.visibility_states, self.visibility)
if self.visibility ~= nil then
break
end
end
update_visibility()
h.notify("Secondary sid visibility: " .. self.visibility)
end
local function compare_by_preference_then_id(track1, track2)
if is_accepted_language(track1.lang) and not is_accepted_language(track2.lang) then
return true
elseif not is_accepted_language(track1.lang) and is_accepted_language(track2.lang) then
return false
else
return (track1.id < track2.id)
end
end
local function split_before_after(previous_tracks, next_tracks, all_tracks, current_track_id)
-- works like take_while() and drop_while() combined
local prev = true
for _, track in ipairs(all_tracks) do
if prev == true and track.id == current_track_id then
prev = false
end
if track.id ~= current_track_id then
if prev then
table.insert(previous_tracks, track)
else
table.insert(next_tracks, track)
end
end
end
end
local function not_primary_sid(track)
return mp.get_property_native('sid') ~= track.id
end
local function find_new_secondary_sub(direction)
local subtitle_tracks = h.filter(h.get_loaded_tracks('sub'), not_primary_sid)
table.sort(subtitle_tracks, compare_by_preference_then_id)
local secondary_sid = mp.get_property_native('secondary-sid')
local new_secondary_sub = { id = false, title = "removed" }
if #subtitle_tracks > 0 then
if not secondary_sid then
new_secondary_sub = (direction == 'prev') and subtitle_tracks[#subtitle_tracks] or subtitle_tracks[1]
else
local previous_tracks = {}
local next_tracks = {}
split_before_after(previous_tracks, next_tracks, subtitle_tracks, secondary_sid)
if direction == 'prev' and #previous_tracks > 0 then
new_secondary_sub = previous_tracks[#previous_tracks]
elseif direction == 'next' and #next_tracks > 0 then
new_secondary_sub = next_tracks[1]
end
end
end
return new_secondary_sub
end
local function switch_secondary_sid(direction)
local new_secondary_sub = find_new_secondary_sub(direction)
mp.set_property_native('secondary-sid', new_secondary_sub.id)
if new_secondary_sub.id == false then
h.notify("Removed secondary sid.")
else
h.notify(string.format(
"Secondary #%d: %s (%s)",
new_secondary_sub.id,
new_secondary_sub.title or "No title",
new_secondary_sub.lang or "Unknown"
))
end
end
return {
init = init,
change_visibility = change_visibility,
select_previous = function()
switch_secondary_sid('prev')
end,
select_next = function()
switch_secondary_sid('next')
end,
}
|