VLC Player插件和自动激活

我习惯用VLC Player播放视频。它有个功能,如果电影看了一半,下次再打开时,会提示是否从上次中断的地方继续播放下去。但这个功能貌似不是很可靠。所以想自己搞个可靠的解决方案。

VLC Player可以用Lua写插件。Lua语言也是第一次用,经过不少时间的查找资料和调试,终于成功运行。

require 'common'

function descriptor()
    return { title = "Monitor Position on Stop", shortcode = "mps", capabilities = { "input-listener", "playing-listener" } }
end

local file = nil

function activate()
    local input = vlc.object.input() 
    if not input then
        vlc.msg.err("[Monitor Position]No input object2 available")
        return
    end
    local filename = "C:\\Temp\\foo.txt"
    file, err = io.open(filename, "a+")
    if not file then
        vlc.msg.err("Failed to open file: " .. (err or "unknown error"))
        return
    end
    local lines = {}
    for line in file:lines() do
        table.insert(lines, line)
    end
    for i = #lines, 1, -1 do
        startIdx, endIdx = string.find(lines[i], "|")
        if startIdx ~= nil and endIdx ~= nil then
            clean_path = getFilePath()
            if clean_path == string.sub(lines[i], 1, startIdx - 1) then
               common.seek(tonumber(string.sub(lines[i], endIdx + 1)))
               break
            end
        end
    end
end

function getFilePath()
    local input = vlc.object.input()
    if not input then
        vlc.msg.err("[Monitor Position] No input available")
        return ""
    end
    local item = vlc.input.item()
    local uri = item:uri() or "unknown"
    local decoded_path = vlc.strings.decode_uri(uri) or "unknown"
    local clean_path = decoded_path
    if decoded_path:match("^file:///") then
        clean_path = decoded_path:sub(9):gsub("/", "\\") 
    end    
    return '"' .. clean_path .. '"'    
end

function playing_changed()
    local input = vlc.object.input()
    if not input then
        vlc.msg.err("[Monitor Position] No input available")
        return
    end

    local state = vlc.var.get(input, "state")  -- 4=stopped, 3=paused
    vlc.msg.err("[Monitor Position] Playing state changed to: " .. state)

    if state == 4 or state == 3 then  -- Stopped (or end-reached)
        local clean_path = getFilePath()        
        local pos_us = vlc.var.get(input, "time") or 0
        local pos_sec = pos_us / 1000000
        local log_line = string.format('%s|%d\n\n', clean_path, pos_sec)
        if file then
            file:write(log_line)
            file:flush()
        end
    end
end

function meta_changed() 
end

function input_changed()
end

激活插件时,首先执行 activate 函数,从文件中读取记录,如果有和当前播放文件名相同的,则从记录的播放时间开始播放。当点击暂停按钮或者停止按钮或者关掉VLC Player时,就把当前的播放时间记录到文件里。
meta_changed() 和 input_changed() 两个函数是空的,作用是避免在日志文件里出现没必要的warning信息。调试语句都用vlc.msg.err而不用vlc.msg.info,目的是为了避免打开日志文件时,出现大量无关信息,影响阅读。当然,在调试完成,运行基本正常后,可以改成info。
但有个问题,这个插件不会自动激活,每次打开VLC Player后,都必须手工到View菜单下,找到插件,再点击激活。感觉很不方便。于是找自动激活的办法。

查找资料后,发现唯一的方法是通过Interface脚本实现。但是Interface脚本里,不能用上面的playing_changed函数,而只能用一个死循环,类似windows的消息循环来实现。感觉占用cpu,不是很满意。

花了不少时间,想了个办法,用autoit脚本去激活:

#include <Constants.au3>
Opt("WinTitleMatchMode", $OPT_MATCHANY)

$hWnd = _WinWaitActivate("- VLC media player","")
$aPos = WinGetPos($hWnd)
MouseClick("right", $aPos[0] + 100, $aPos[1] + $aPos[3] - 20)
For $i = 1 to 10
    Send("{DOWN}")
Next
Send("{RIGHT}")
For $i = 1 to 9
    Send("{DOWN}")
Next
Send("{ENTER}")

Func _WinWaitActivate($title,$text,$timeout=0)
    WinWait($title,$text,$timeout)
    If Not WinActive($title,$text) Then WinActivate($title,$text)
    Return WinWaitActive($title,$text,$timeout)
EndFunc

其中9这个数字是调试出来的,具体要循环几次,要看插件在菜单中的位置。
然后写个Interface脚本,就一行:
os.execute("C:\\autoit\\autoit3.exe C:\\autoit\\activateVlcExtension.au3")

然后在vlcrc文件(一般在C:\Users\<用户名>\AppData\Roaming\vlc目录下)里,找到

# Lua interface (string)
lua-intf=<Interface脚本名>
最后,打开VLC Player,点开Tools - Preference,勾选All,然后找到Main interfaces,点选Lua interpreter,保存。重新启动VLC Player,就可以了。

缺点:一是每次启动VLC时,要等一两秒钟,二是记录时间的文件会逐渐变大,需要手工定期清理。

另外说点对Lua语言的印象。觉得它的函数可以返回多个值,这个不错,string类没有split方法,不太方便。总体感觉语法还算简洁,用起来基本没什么难度。

posted @ 2025-10-02 00:23  平静寄居者  阅读(15)  评论(0)    收藏  举报