diff options
Diffstat (limited to 'config/mpv/scripts/subs2srsa/helpers.lua')
| -rw-r--r-- | config/mpv/scripts/subs2srsa/helpers.lua | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/config/mpv/scripts/subs2srsa/helpers.lua b/config/mpv/scripts/subs2srsa/helpers.lua new file mode 100644 index 0000000..e8f911e --- /dev/null +++ b/config/mpv/scripts/subs2srsa/helpers.lua @@ -0,0 +1,280 @@ +--[[ +Copyright: Ren Tatsumoto and contributors +License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html + +Various helper functions. +]] + +local mp = require('mp') +local msg = require('mp.msg') +local utils = require('mp.utils') +local this = {} + +this.unpack = unpack and unpack or table.unpack + +this.remove_all_spaces = function(str) + return str:gsub('%s*', '') +end + +this.table_get = function(table, key, default) + if table[key] == nil then + return default or 'nil' + else + return table[key] + end +end + +this.max_num = function(table) + local max = table[1] + for _, value in ipairs(table) do + if value > max then + max = value + end + end + return max +end + +this.get_last_n_added_notes = function(note_ids, n) + table.sort(note_ids) + return { this.unpack(note_ids, math.max(#note_ids - n + 1, 1), #note_ids) } +end + +this.contains = function(table, element) + for _, value in pairs(table) do + if value == element then + return true + end + end + return false +end + +this.minutes_ago = function(m) + return (os.time() - 60 * m) * 1000 +end + +this.is_wayland = function() + return os.getenv('WAYLAND_DISPLAY') ~= nil +end + +this.is_win = function() + return mp.get_property('options/vo-mmcss-profile') ~= nil +end + +this.is_mac = function() + return mp.get_property('options/macos-force-dedicated-gpu') ~= nil +end + +local function map(tab, func) + local t = {} + for k, v in pairs(tab) do + t[k] = func(v) + end + return t +end + +local function args_as_str(args) + return table.concat(map(args, function(str) return string.format("'%s'", str) end), " ") +end + +this.subprocess = function(args, completion_fn, override_settings) + -- if `completion_fn` is passed, the command is ran asynchronously, + -- and upon completion, `completion_fn` is called to process the results. + msg.info("Executing: " .. args_as_str(args)) + local command_native = type(completion_fn) == 'function' and mp.command_native_async or mp.command_native + local command_table = { + name = "subprocess", + playback_only = false, + capture_stdout = true, + capture_stderr = true, + args = args + } + if not this.is_empty(override_settings) then + for k,v in pairs(override_settings) do + command_table[k] = v + end + end + return command_native(command_table, completion_fn) +end + +this.subprocess_detached = function(args, completion_fn) + local overwrite_settings = { + detach=true, + capture_stdout = false, + capture_stderr = false, + } + return this.subprocess(args, completion_fn, overwrite_settings) +end + +this.is_empty = function(var) + return var == nil or var == '' or (type(var) == 'table' and next(var) == nil) +end + +this.contains_non_latin_letters = function(str) + return str:match("[^%c%p%s%w—]") +end + +this.capitalize_first_letter = function(string) + return string:gsub("^%l", string.upper) +end + +this.remove_leading_trailing_spaces = function(str) + return str:gsub('^%s*(.-)%s*$', '%1') +end + +this.remove_leading_trailing_dashes = function(str) + return str:gsub('^[%-_]*(.-)[%-_]*$', '%1') +end + +this.remove_text_in_parentheses = function(str) + -- Remove text like (泣き声) or (ドアの開く音) + -- No deletion is performed if there's no text after the parentheses. + -- Note: the modifier `-´ matches zero or more occurrences. + -- However, instead of matching the longest sequence, it matches the shortest one. + return str:gsub('(%b())(.)', '%2'):gsub('((.-))(.)', '%2') +end + +this.remove_newlines = function(str) + return str:gsub('[\n\r]+', ' ') +end + +this.trim = function(str) + str = this.remove_leading_trailing_spaces(str) + str = this.remove_text_in_parentheses(str) + str = this.remove_newlines(str) + return str +end + +this.escape_special_characters = (function() + local entities = { + ['&'] = '&', + ['"'] = '"', + ["'"] = ''', + ['<'] = '<', + ['>'] = '>', + } + return function(s) + return s:gsub('[&"\'<>]', entities) + end +end)() + +this.remove_extension = function(filename) + return filename:gsub('%.%w+$', '') +end + +this.remove_special_characters = function(str) + return str:gsub('[%c%p%s]', ''):gsub(' ', '') +end + +this.remove_text_in_brackets = function(str) + return str:gsub('%b[]', ''):gsub('【.-】', '') +end + +this.remove_filename_text_in_parentheses = function(str) + return str:gsub('%b()', ''):gsub('(.-)', '') +end + +this.remove_common_resolutions = function(str) + -- Also removes empty leftover parentheses and brackets. + return str:gsub("2160p", ""):gsub("1080p", ""):gsub("720p", ""):gsub("576p", ""):gsub("480p", ""):gsub("%(%)", ""):gsub("%[%]", "") +end + +this.human_readable_time = function(seconds) + if type(seconds) ~= 'number' or seconds < 0 then + return 'empty' + end + + local parts = { + h = math.floor(seconds / 3600), + m = math.floor(seconds / 60) % 60, + s = math.floor(seconds % 60), + ms = math.floor((seconds * 1000) % 1000), + } + + local ret = string.format("%02dm%02ds%03dms", parts.m, parts.s, parts.ms) + + if parts.h > 0 then + ret = string.format('%dh%s', parts.h, ret) + end + + return ret +end + +this.get_episode_number = function(filename) + -- Reverses the filename to start the search from the end as the media title might contain similar numbers. + local filename_reversed = filename:reverse() + + local ep_num_patterns = { + "[%s_](%d?%d?%d)[pP]?[eE]", -- Starting with E or EP (case-insensitive). "Example Series S01E01 [94Z295D1]" + "^(%d?%d?%d)[pP]?[eE]", -- Starting with E or EP (case-insensitive) at the end of filename. "Example Series S01E01" + "%)(%d?%d?%d)%(", -- Surrounded by parentheses. "Example Series (12)" + "%](%d?%d?%d)%[", -- Surrounded by brackets. "Example Series [01]" + "%s(%d?%d?%d)%s", -- Surrounded by whitespace. "Example Series 124 [1080p 10-bit]" + "_(%d?%d?%d)_", -- Surrounded by underscores. "Example_Series_04_1080p" + "^(%d?%d?%d)[%s_]", -- Ending to the episode number. "Example Series 124" + "(%d?%d?%d)%-edosipE", -- Prepended by "Episode-". "Example Episode-165" + } + + local s, e, episode_num + for _, pattern in pairs(ep_num_patterns) do + s, e, episode_num = string.find(filename_reversed, pattern) + if not this.is_empty(episode_num) then + return #filename - e, #filename - s, episode_num:reverse() + end + end +end + +this.notify = function(message, level, duration) + level = level or 'info' + duration = duration or 1 + msg[level](message) + mp.osd_message(message, duration) +end + +this.get_active_track = function(track_type) + -- track_type == audio|sub + for _, track in pairs(mp.get_property_native('track-list')) do + if track.type == track_type and track.selected == true then + return track + end + end + return nil +end + +this.has_video_track = function() + return mp.get_property_native('vid') ~= false +end + +this.has_audio_track = function() + return mp.get_property_native('aid') ~= false +end + +this.str_contains = function(s, pattern) + return not this.is_empty(s) and string.find(string.lower(s), string.lower(pattern)) ~= nil +end + +this.filter = function(arr, func) + local filtered = {} + for _, elem in ipairs(arr) do + if func(elem) == true then + table.insert(filtered, elem) + end + end + return filtered +end + +this.file_exists = function(filepath) + if not this.is_empty(filepath) then + local info = utils.file_info(filepath) + if info and info.is_file and info.size > 0 then + return true + end + end + return false +end + +this.get_loaded_tracks = function(track_type) + --- Return all sub tracks, audio tracks, etc. + return this.filter(mp.get_property_native('track-list'), function(track) return track.type == track_type end) +end + +return this |
