You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Conky中两个Lua函数无法独立更新的问题

问题描述

我编写了两个Lua函数getSongLyrics和getSongTitle,用于在Conky窗口展示内容:

  • getSongLyrics通过Python脚本执行sptlrx pipe,将结果输出到FIFO文件后读取,再通过conky_displaySongLyrics展示
  • getSongTitle借助playerctl获取当前播放歌曲的歌手与标题,通过conky_displaySongTitle直接展示

当前存在的问题:

  • 切换歌曲时,getSongTitle的输出仅在getSongLyrics更新(出现新歌词行)时才会刷新
  • 若播放无歌词的歌曲,conky_displaySongLyrics始终不更新,conky_displaySongTitle也会停留在上一首歌曲的信息,直到播放有歌词的歌曲才会更新

推测是FIFO文件等待sptlrx pipe新内容导致的阻塞问题,希望实现两个函数独立运行、互不影响。


相关代码

scripts.lua

-- read file and print contents of file
function readPrintFile(file)
    local f = io.open(file, "rb")
    
    -- if file doesn't exist, return nil
    if not f then return nil end

    local content = f:read("*a")
    f:close()

    return content
end

-- get song title from a music source
function getSongTitle()
    local command = io.popen("playerctl metadata --player spotify --format '{{ artist }} - {{ title }}'", "r")
    local title = command:read("*l")
    command:close()
    return format:gsub("\n", "")
end

-- get song lyrics from a music source
function getSongLyrics()
    local lyrics = readPrintFile("/tmp/sptlrx_lyrics") or ""
    return lyrics:gsub("\n", "")
end

function conky_displaySongTitle()
    return getSongTitle()
end

function conky_displaySongLyrics()
    return getSongLyrics()
end

sptlrx_lyrics.py

import os
import subprocess

pipe_path_lyrics = "/tmp/sptlrx_lyrics"
try:
    os.mkfifo(pipe_path_lyrics)
except FileExistsError:
    pass

process_lyrics = subprocess.Popen(["sptlrx", "pipe"], stdout=subprocess.PIPE)

for line_lyrics in iter(process_lyrics.stdout.readline, ""):
    with open(pipe_path_lyrics, "wb") as lyrics:
        lyrics.write(line_lyrics)

conky.conf

conky.config = {
    lua_load = "$HOME/.config/conky/scripts.lua",
    ...
}

conky.text = [[
${if_running spotify}\${alignc} ${lua displaySongTitle}
${alignc} ${lua displaySongLyrics}\...
${endif}
]]

解决方案

问题根源

Conky默认同步执行所有Lua函数,而FIFO文件是阻塞式读取——如果sptlrx pipe没有新内容写入,io.open或read操作会卡住整个Conky的更新循环,导致getSongTitle无法执行。

具体修改步骤

  1. 修复Lua函数的bug并实现非阻塞读取FIFO
    首先修正getSongTitle里的变量错误,再修改readPrintFile为非阻塞读取:

    -- 非阻塞读取文件内容
    function readPrintFile(file)
        local f = io.open(file, "rb")
        if not f then return "" end
        -- 设置非阻塞模式
        f:setvbuf("no")
        -- 用pcall捕获读取阻塞的异常
        local ok, content = pcall(function() return f:read("*a") end)
        f:close()
        -- 读取失败则返回空字符串
        return ok and content or ""
    end
    
    -- 获取歌曲标题(修复变量错误)
    function getSongTitle()
        local command = io.popen("playerctl metadata --player spotify --format '{{ artist }} - {{ title }}'", "r")
        local title = command:read("*l") or ""
        command:close()
        return title:gsub("\n", "")
    end
    
  2. 优化Python脚本,监听歌曲切换事件
    原脚本仅在有歌词时写入FIFO,切换无歌词歌曲时不会更新。修改后添加歌曲切换监听,主动清空FIFO:

    import os
    import subprocess
    import select
    
    pipe_path_lyrics = "/tmp/sptlrx_lyrics"
    try:
        os.mkfifo(pipe_path_lyrics)
    except FileExistsError:
        pass
    
    # 启动sptlrx获取歌词,同时用playerctl监听歌曲切换
    sptlrx_proc = subprocess.Popen(["sptlrx", "pipe"], stdout=subprocess.PIPE, text=True)
    playerctl_proc = subprocess.Popen(
        ["playerctl", "metadata", "--player", "spotify", "--follow", "--format", "changed"],
        stdout=subprocess.PIPE, text=True
    )
    
    def write_fifo(content):
        try:
            with open(pipe_path_lyrics, "w") as f:
                f.write(content.strip() + "\n")
        except Exception:
            pass
    
    # 初始写入空内容
    write_fifo("")
    
    while True:
        # 非阻塞监听两个进程的输出
        read_streams, _, _ = select.select([sptlrx_proc.stdout, playerctl_proc.stdout], [], [], 1)
        
        for stream in read_streams:
            line = stream.readline().strip()
            if stream == sptlrx_proc.stdout and line:
                write_fifo(line)
            elif stream == playerctl_proc.stdout:
                # 歌曲切换时清空歌词
                write_fifo("")
        
        # 进程退出则终止循环
        if sptlrx_proc.poll() is not None or playerctl_proc.poll() is not None:
            break
    
  3. 调整Conky更新配置
    在conky.config中确保足够的更新频率:

    conky.config = {
        lua_load = "$HOME/.config/conky/scripts.lua",
        update_interval = 1, -- 每秒更新一次
        lua_draw_hook_pre = "", -- 确保Lua函数每次更新都执行
        ...
    }
    

内容的提问来源于stack exchange,提问作者Anapal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 22:45:56