--[[ Configuration options for anilistUpdater (set in anilistUpdater.conf): DIRECTORIES: Table or comma/semicolon-separated string. The directories the script will work on. Leaving it empty will make it work on every video you watch with mpv. Example: DIRECTORIES = {"D:/Torrents", "D:/Anime"} UPDATE_PERCENTAGE: Number (0-100). The percentage of the video you need to watch before it updates AniList automatically. Default is 85 (usually before the ED of a usual episode duration). SET_COMPLETED_TO_REWATCHING_ON_FIRST_EPISODE: Boolean. If true, when watching episode 1 of a completed anime, set it to rewatching and update progress. UPDATE_PROGRESS_WHEN_REWATCHING: Boolean. If true, allow updating progress for anime set to rewatching. This is for if you want to set anime to rewatching manually, but still update progress automatically. SET_TO_COMPLETED_AFTER_LAST_EPISODE_CURRENT: Boolean. If true, set to COMPLETED after last episode if status was CURRENT. SET_TO_COMPLETED_AFTER_LAST_EPISODE_REWATCHING: Boolean. If true, set to COMPLETED after last episode if status was REPEATING (rewatching). ]] local utils = require 'mp.utils' local mpoptions = require("mp.options") local conf_name = "anilistUpdater.conf" local script_dir = (debug.getinfo(1).source:match("@?(.*/)") or "./") -- Try script-opts directory (sibling to scripts) local script_opts_dir = script_dir:match("^(.-)[/\\]scripts[/\\]") if script_opts_dir then script_opts_dir = utils.join_path(script_opts_dir, "script-opts") else -- Fallback: try to find mpv config dir script_opts_dir = os.getenv("APPDATA") and utils.join_path(utils.join_path(os.getenv("APPDATA"), "mpv"), "script-opts") or os.getenv("HOME") and utils.join_path(utils.join_path(utils.join_path(os.getenv("HOME"), ".config"), "mpv"), "script-opts") or nil end local script_opts_path = script_opts_dir and utils.join_path(script_opts_dir, conf_name) or nil -- Try script directory local script_path = utils.join_path(script_dir, conf_name) -- Try mpv config directory local mpv_conf_dir = os.getenv("APPDATA") and utils.join_path(os.getenv("APPDATA"), "mpv") or os.getenv("HOME") and utils.join_path(utils.join_path(os.getenv("HOME"), ".config"), "mpv") or nil local mpv_conf_path = mpv_conf_dir and utils.join_path(mpv_conf_dir, conf_name) or nil local conf_paths = {script_opts_path, script_path, mpv_conf_path} local default_conf = [[ # Use 'yes' or 'no' for boolean options below # Example for multiple directories (comma or semicolon separated): # DIRECTORIES=D:/Torrents,D:/Anime # or # DIRECTORIES=D:/Torrents;D:/Anime DIRECTORIES= UPDATE_PERCENTAGE=85 SET_COMPLETED_TO_REWATCHING_ON_FIRST_EPISODE=no UPDATE_PROGRESS_WHEN_REWATCHING=yes SET_TO_COMPLETED_AFTER_LAST_EPISODE_CURRENT=yes SET_TO_COMPLETED_AFTER_LAST_EPISODE_REWATCHING=yes ]] -- Try to find config file local conf_path = nil for _, path in ipairs(conf_paths) do if path then local f = io.open(path, "r") if f then f:close() conf_path = path print("Found config at: " .. path) break end end end -- If not found, try to create in order if not conf_path then for _, path in ipairs(conf_paths) do if path then local f = io.open(path, "w") if f then f:write(default_conf) f:close() conf_path = path print("Created config at: " .. path) break end end end end -- If still not found or created, warn and use defaults if not conf_path then mp.msg.warn("Could not find or create anilistUpdater.conf in any known location! Using default options.") end -- Now load options as usual local options = { DIRECTORIES = "", UPDATE_PERCENTAGE = 85, SET_COMPLETED_TO_REWATCHING_ON_FIRST_EPISODE = false, UPDATE_PROGRESS_WHEN_REWATCHING = true, SET_TO_COMPLETED_AFTER_LAST_EPISODE_CURRENT = true, SET_TO_COMPLETED_AFTER_LAST_EPISODE_REWATCHING = true } if conf_path then mpoptions.read_options(options, "anilistUpdater") end -- Parse DIRECTORIES if it's a string (comma or semicolon separated) if type(options.DIRECTORIES) == "string" and options.DIRECTORIES ~= "" then local dirs = {} for dir in string.gmatch(options.DIRECTORIES, "([^,;]+)") do table.insert(dirs, (dir:gsub("^%s*(.-)%s*$", "%1"))) -- trim end options.DIRECTORIES = dirs elseif type(options.DIRECTORIES) == "string" then options.DIRECTORIES = {} end -- When calling Python, pass only the options relevant to it local python_options = { SET_COMPLETED_TO_REWATCHING_ON_FIRST_EPISODE = options.SET_COMPLETED_TO_REWATCHING_ON_FIRST_EPISODE, UPDATE_PROGRESS_WHEN_REWATCHING = options.UPDATE_PROGRESS_WHEN_REWATCHING, SET_TO_COMPLETED_AFTER_LAST_EPISODE_CURRENT = options.SET_TO_COMPLETED_AFTER_LAST_EPISODE_CURRENT, SET_TO_COMPLETED_AFTER_LAST_EPISODE_REWATCHING = options.SET_TO_COMPLETED_AFTER_LAST_EPISODE_REWATCHING } local python_options_json = utils.format_json(python_options) DIRECTORIES = options.DIRECTORIES UPDATE_PERCENTAGE = tonumber(options.UPDATE_PERCENTAGE) or 85 local function path_starts_with_any(path, directories) for _, dir in ipairs(directories) do if path:sub(1, #dir) == dir then return true end end return false end function callback(success, result, error) if result.status == 0 then mp.osd_message("Updated anime correctly.", 2) end end local function get_python_command() local os_name = package.config:sub(1, 1) if os_name == '\\' then -- Windows return "python" else -- Linux return "python3" end end local python_command = get_python_command() -- Make sure it doesnt trigger twice in 1 video local triggered = false -- Function to check if we've reached the user-defined percentage of the video function check_progress() if triggered then return end local percent_pos = mp.get_property_number("percent-pos") if percent_pos then if percent_pos >= UPDATE_PERCENTAGE then update_anilist("update") triggered = true end end end -- Function to launch the .py script function update_anilist(action) if action == "launch" then mp.osd_message("Launching AniList", 2) end local script_dir = debug.getinfo(1).source:match("@?(.*/)") local directory = mp.get_property("working-directory") -- It seems like in Linux working-directory sometimes returns it without a "/" at the end directory = (directory:sub(-1) == '/' or directory:sub(-1) == '\\') and directory or directory .. '/' -- For some reason, "path" sometimes returns the absolute path, sometimes it doesn't. local file_path = mp.get_property("path") local path = utils.join_path(directory, file_path) local table = {} table.name = "subprocess" table.args = {python_command, script_dir .. "anilistUpdater.py", path, action, python_options_json} local cmd = mp.command_native_async(table, callback) end mp.observe_property("percent-pos", "number", check_progress) -- Reset triggered mp.register_event("file-loaded", function() triggered = false if #DIRECTORIES > 0 then local directory = mp.get_property("working-directory") directory = (directory:sub(-1) == '/' or directory:sub(-1) == '\\') and directory or directory .. '/' local file_path = mp.get_property("path") local path = utils.join_path(directory, file_path) path = path:gsub("\\", "/") if not path_starts_with_any(path, DIRECTORIES) then mp.unobserve_property(check_progress) end end end) -- Keybinds, modify as you please mp.add_key_binding('ctrl+a', 'update_anilist', function() update_anilist("update") end) mp.add_key_binding('ctrl+b', 'launch_anilist', function() update_anilist("launch") end) -- Open the folder that the video is function open_folder() local path = mp.get_property("path") local directory if not path then mp.msg.warn("No file is currently playing.") return end if path:find('\\') then directory = path:match("(.*)\\") elseif path:find('\\\\') then directory = path:match("(.*)\\\\") else directory = mp.get_property("working-directory") end -- Use the system command to open the folder in File Explorer local args if package.config:sub(1, 1) == '\\' then -- Windows args = {'explorer', directory} elseif os.getenv("XDG_CURRENT_DESKTOP") or os.getenv("WAYLAND_DISPLAY") or os.getenv("DISPLAY") then -- Linux (assume a desktop environment like GNOME, KDE, etc.) args = {'xdg-open', directory} elseif package.config:sub(1, 1) == '/' then -- macOS args = {'open', directory} end mp.command_native({ name = "subprocess", args = args, detach = true }) end mp.add_key_binding('ctrl+d', 'open_folder', open_folder)