-- Copyright (c) 2022-2024 dyphire -- License: MIT -- link: https://github.com/dyphire/mpv-scripts --[[ The script calls up a window in mpv to quickly load the folder/files/iso/clipboard (support url)/other subtitles/other audio tracks/other video tracks. Usage, add bindings to input.conf: key script-message-to open_dialog import_folder key script-message-to open_dialog import_files key script-message-to open_dialog import_files # vid, aid, sid (video/audio/subtitle track) key script-message-to open_dialog import_clipboard key script-message-to open_dialog import_clipboard # vid, aid, sid (video/audio/subtitle track) key script-message-to open_dialog set_clipboard # text can be mpv properties as ${path} Also supports open dialog to select folder/files for other scripts. Scripting Example: -- open a folder select dialog mp.commandv('script-message-to', 'open_dialog', 'select_folder', mp.get_script_name()) -- receive the selected folder reply mp.register_script_message('select_folder_reply', function(folder_path) if folder_path and folder_path ~= '' then -- do something with folder_path end end) -- open a xml file select dialog mp.commandv('script-message-to', 'open_dialog', 'select_files', mp.get_script_name(), 'XML File|*.xml') -- receive the selected files reply mp.register_script_message('select_files_reply', function(file_paths) for i, file_path in ipairs(utils.parse_json(file_paths)) do -- do something with file_path end end) ]]-- local msg = require 'mp.msg' local utils = require 'mp.utils' local options = require 'mp.options' o = { video_types = '3g2,3gp,asf,avi,f4v,flv,h264,h265,m2ts,m4v,mkv,mov,mp4,mp4v,mpeg,mpg,ogm,ogv,rm,rmvb,ts,vob,webm,wmv,y4m', audio_types = 'aac,ac3,aiff,ape,au,cue,dsf,dts,flac,m4a,mid,midi,mka,mp3,mp4a,oga,ogg,opus,spx,tak,tta,wav,weba,wma,wv', image_types = 'apng,avif,bmp,gif,j2k,jp2,jfif,jpeg,jpg,jxl,mj2,png,svg,tga,tif,tiff,webp', subtitle_types = 'aqt,ass,gsub,idx,jss,lrc,mks,pgs,pjs,psb,rt,sbv,slt,smi,sub,sup,srt,ssa,ssf,ttxt,txt,usf,vt,vtt', playlist_types = 'm3u,m3u8,pls,cue', iso_types = 'iso', } options.read_options(o) local function split(input) local ret = {} for str in string.gmatch(input, "([^,]+)") do ret[#ret + 1] = string.format("*.%s", str) end return ret end -- pre-defined file types local file_types = { video = table.concat(split(o.video_types), ';'), audio = table.concat(split(o.audio_types), ';'), image = table.concat(split(o.image_types), ';'), iso = table.concat(split(o.iso_types), ';'), subtitle = table.concat(split(o.subtitle_types), ';'), playlist = table.concat(split(o.playlist_types), ';'), } local powershell = nil local function pwsh_check() local arg = {"cmd", "/c", "pwsh", "--version"} local res = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = arg}) if res.status ~= 0 or res.stdout:match("^PowerShell") == nil then powershell = "powershell" else powershell = "pwsh" end end -- escapes a string so that it can be inserted into powershell as a string literal local function escape_powershell(str) if not str then return '""' end str = str:gsub('[$"`]', '`%1'):gsub('“', '`%1'):gsub('”', '`%1') return '"'..str..'"' end local function end_file(event) mp.unregister_event(end_file) if event["reason"] == "eof" or event["reason"] == "stop" or event["reason"] == "error" then local bd_device = mp.get_property_native("bluray-device") local dvd_device = mp.get_property_native("dvd-device") if event["reason"] == "error" and bd_device and bd_device ~= "" then loaded_fail = true else loaded_fail = false end if bd_device then mp.set_property("bluray-device", "") end if dvd_device then mp.set_property("dvd-device", "") end end end -- open bluray iso or dir local function open_bluray(path) mp.set_property('bluray-device', path) mp.commandv('loadfile', 'bd://') end -- open dvd iso or dir local function open_dvd(path) mp.set_property('dvd-device', path) mp.commandv('loadfile', 'dvd://') end -- open folder local function open_folder(path, i) local fpath, dir = utils.split_path(path) if utils.file_info(utils.join_path(path, "BDMV")) then open_bluray(path) elseif utils.file_info(utils.join_path(path, "VIDEO_TS")) then open_dvd(path) elseif dir:upper() == "BDMV" then open_bluray(fpath) elseif dir:upper() == "VIDEO_TS" then open_dvd(fpath) else mp.commandv('loadfile', path, i == 1 and 'replace' or 'append') end end -- open files local function open_files(path, type, i, is_clip) local ext = string.match(path, "%.([^%.]+)$"):lower() if file_types['subtitle']:match(ext) then mp.commandv('sub-add', path, 'cached') elseif type == 'vid' and (not is_clip or (file_types['video']:match(ext) or file_types['image']:match(ext))) then mp.commandv('video-add', path, 'cached') elseif type == 'aid' and (not is_clip or file_types['audio']:match(ext)) then mp.commandv('audio-add', path, 'cached') elseif file_types['iso']:match(ext) then local idle = mp.get_property('idle') if idle ~= 'yes' then mp.set_property('idle', 'yes') end mp.register_event("end-file", end_file) open_bluray(path) mp.add_timeout(1.0, function() if idle ~= 'yes' then mp.set_property('idle', idle) end if loaded_fail then loaded_fail = false open_dvd(path) end end) else mp.commandv('loadfile', path, i == 1 and 'replace' or 'append') end end local function select_folder() if not powershell then pwsh_check() end local powershell_script = string.format([[&{ Add-Type -AssemblyName System.Windows.Forms $fbd = New-Object System.Windows.Forms.FolderBrowserDialog $fbd.RootFolder = "Desktop" $fbd.ShowNewFolderButton = $true $owner = New-Object System.Windows.Forms.NativeWindow $owner.AssignHandle((Get-Process -Id %d).MainWindowHandle) try { if ($fbd.ShowDialog($owner) -eq [System.Windows.Forms.DialogResult]::OK) { $u8 = [System.Text.Encoding]::UTF8 $out = [Console]::OpenStandardOutput() $selectedFolder = $fbd.SelectedPath $u8selectedFolder = $u8.GetBytes("$selectedFolder`n") $out.Write($u8selectedFolder, 0, $u8selectedFolder.Length) } } finally { $owner.ReleaseHandle() $fbd.Dispose() } }]], mp.get_property_number('pid')) local res = mp.command_native({ name = 'subprocess', playback_only = false, capture_stdout = true, args = { powershell, '-NoProfile', '-Command', powershell_script }, }) if res.status ~= 0 then mp.osd_message("Failed to open folder dialog.") return nil end local folder_path = res.stdout:match("(.-)[\r\n]?$") -- Trim any trailing newline return folder_path end local function select_files(filter) if not powershell then pwsh_check() end local powershell_script = string.format([[&{ Add-Type -AssemblyName System.Windows.Forms $ofd = New-Object System.Windows.Forms.OpenFileDialog $ofd.Multiselect = $true $ofd.Filter = %s $owner = New-Object System.Windows.Forms.NativeWindow $owner.AssignHandle((Get-Process -Id %d).MainWindowHandle) try { if ($ofd.ShowDialog($owner) -eq [System.Windows.Forms.DialogResult]::OK) { $u8 = [System.Text.Encoding]::UTF8 $out = [Console]::OpenStandardOutput() ForEach ($filename in $ofd.FileNames) { $u8filename = $u8.GetBytes("$filename`n") $out.Write($u8filename, 0, $u8filename.Length) } } } finally { $owner.ReleaseHandle() $ofd.Dispose() } }]], escape_powershell(filter), mp.get_property_number('pid')) local res = mp.command_native({ name = 'subprocess', playback_only = false, capture_stdout = true, args = { powershell, '-NoProfile', '-Command', powershell_script }, }) local file_paths = {} if res.status ~= 0 then mp.osd_message("Failed to open files dialog.") return file_paths end for file_path in string.gmatch(res.stdout, '[^\r\n]+') do table.insert(file_paths, file_path) end return file_paths end -- import folder local function import_folder() local folder_path = select_folder() if folder_path and folder_path ~= '' then open_folder(folder_path, 1) end end -- import files local function import_files(type) local filter = '' if type == 'vid' then filter = string.format("Video Files|%s|Image Files|%s", file_types['video'], file_types['image']) elseif type == 'aid' then filter = string.format("Audio Files|%s", file_types['audio']) elseif type == 'sid' then filter = string.format("Subtitle Files|%s", file_types['subtitle']) else filter = string.format("All Files (*.*)|*.*|Video Files|%s|Audio Files|%s|Image Files|%s|ISO Files|%s|Subtitle Files|%s|Playlist Files|%s", file_types['video'], file_types['audio'], file_types['image'], file_types['iso'], file_types['subtitle'], file_types['playlist']) end for i, file_path in ipairs(select_files(filter)) do open_files(file_path, type, i, false) end end -- Returns a string of UTF-8 text from the clipboard local function get_clipboard() if mp.get_property('clipboard-backends') ~= nil or mp.get_property_bool('clipboard-enable') then return mp.get_property('clipboard/text', '') end local res = mp.command_native({ name = 'subprocess', playback_only = false, capture_stdout = true, args = { 'powershell', '-NoProfile', '-Command', [[& { Trap { Write-Error -ErrorRecord $_ Exit 1 } $clip = Get-Clipboard -Raw -Format Text -TextFormatType UnicodeText if (-not $clip) { $clip = Get-Clipboard -Raw -Format FileDropList } $u8clip = [System.Text.Encoding]::UTF8.GetBytes($clip) [Console]::OpenStandardOutput().Write($u8clip, 0, $u8clip.Length) }]] } }) if not res.error then return res.stdout end return '' end -- open files from clipboard local function open_clipboard(path, type, i) local path = path:gsub("^[\'\"]", ""):gsub("[\'\"]$", ""):gsub('^%s+', ''):gsub('%s+$', '') if path:find('^%a[%w.+-]-://') then mp.commandv('loadfile', path, i == 1 and 'replace' or 'append') else local meta = utils.file_info(path) if not meta then mp.osd_message('Clipboard path is invalid') msg.warn('Clipboard path is invalid') elseif meta.is_dir then open_folder(path, i) elseif meta.is_file then open_files(path, type, i, true) else mp.osd_message('Clipboard path is invalid') msg.warn('Clipboard path is invalid') end end end -- import clipboard local function import_clipboard(type) local clip = get_clipboard() if clip ~= '' then local i = 0 for path in string.gmatch(clip, '[^\r\n]+') do i = i + 1 open_clipboard(path, type, i) end else mp.osd_message('Clipboard is empty') msg.warn('Clipboard is empty') end end -- sets the contents of the clipboard to the given string local function set_clipboard(text) msg.verbose('setting clipboard text:', text) if mp.get_property('clipboard-backends') ~= nil or mp.get_property_bool('clipboard-enable') then mp.commandv('set', 'clipboard/text', text) else mp.commandv('run', 'powershell', '-NoProfile', '-command', 'set-clipboard', escape_powershell(text)) end end mp.register_script_message('import_folder', import_folder) mp.register_script_message('import_files', import_files) mp.register_script_message('import_clipboard', import_clipboard) mp.register_script_message('set_clipboard', set_clipboard) mp.register_script_message('select_folder', function(script_name) local folder_path = select_folder() mp.commandv('script-message-to', script_name, 'select_folder_reply', folder_path) end) mp.register_script_message('select_files', function(script_name, filter) local file_paths = select_files(filter) mp.commandv('script-message-to', script_name, 'select_files_reply', utils.format_json(file_paths)) end)