summaryrefslogtreecommitdiff
path: root/config/mpv/scripts/subs2srs/subtitles/secondary_sid.lua
blob: 21576e8c64c633e4e630167099f93e5c425154b0 (plain)
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,
}