130 lines
3.4 KiB
Lua
130 lines
3.4 KiB
Lua
--- @since 25.12.29
|
|
|
|
local M = {}
|
|
|
|
local audio_ffprobe = function(file)
|
|
-- stylua: ignore
|
|
local cmd = Command('ffprobe'):arg {
|
|
'-v', 'quiet',
|
|
'-show_entries', 'stream_tags:format:stream',
|
|
'-of', 'json=c=1',
|
|
file.name
|
|
}
|
|
|
|
local output, err = cmd:output()
|
|
if not output then
|
|
return {}, Err('Failed to start `ffprobe`, error: %s', err)
|
|
end
|
|
|
|
local json = ya.json_decode(output.stdout)
|
|
if not json then
|
|
return nil, Err('Failed to decode `ffprobe` output: %s', output.stdout)
|
|
elseif type(json) ~= 'table' then
|
|
return nil, Err('Invalid `ffprobe` output: %s', output.stdout)
|
|
end
|
|
ya.dbg(json)
|
|
|
|
local stream = json.streams[1]
|
|
local tags = json.format.tags or stream.tags or stream
|
|
|
|
local data = {}
|
|
local title, album, aar, ar =
|
|
tags.TITLE or tags.title or '',
|
|
tags.ALBUM or tags.album or '',
|
|
tags.ALBUM_ARTIST or tags.album_artist or '',
|
|
tags.ARTIST or tags.artist or ''
|
|
|
|
if title .. album .. ar .. aar ~= '' then
|
|
local cdata = json.streams[2]
|
|
local date = tags.DATE or tags.date or ''
|
|
local c = ''
|
|
local artist = ar
|
|
|
|
if tags.ORIGINALDATE and tags.ORIGINALDATE ~= '' then
|
|
date = date .. ' / ' .. tags.ORIGINALDATE
|
|
end
|
|
if (aar ~= '') and (aar ~= ar) then
|
|
artist = artist .. ' / ' .. aar
|
|
end
|
|
if cdata then
|
|
c = cdata.codec_name .. ' ' .. cdata.width .. 'x' .. cdata.height
|
|
end
|
|
|
|
data = {
|
|
ui.Line(string.format('%s - %s', artist, title)),
|
|
ui.Line(string.format('%s: %s', 'Album', album)),
|
|
ui.Line(string.format('%s: %s', 'Genre', tags.GENRE or tags.genre)),
|
|
ui.Line(string.format('%s: %s', 'Date', date)),
|
|
c ~= '' and ui.Line(string.format('%s: %s', 'Cover art', c)) or nil,
|
|
}
|
|
end
|
|
|
|
local br = tonumber((json.format.bit_rate or 0) // 1000) .. ' kb/s'
|
|
table.insert(data, ui.Line(string.format('%s: %s', 'Format', json.format.format_name)))
|
|
table.insert(data, ui.Line(string.format('%s: %s', 'BitRate', br)))
|
|
table.insert(data, ui.Line(string.format('%s: %s', 'Channels', tostring(stream.channels or '?'))))
|
|
|
|
return data
|
|
end
|
|
|
|
function M:peek(job)
|
|
local start, cache = os.clock(), ya.file_cache(job)
|
|
local ok, err = self:preload(job)
|
|
|
|
local img_area
|
|
if cache and fs.cha(cache) then
|
|
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
|
|
img_area, err = ya.image_show(cache, job.area)
|
|
end
|
|
|
|
ya.preview_widget(job, {
|
|
ui.Text(audio_ffprobe(job.file)):area(ui.Rect {
|
|
x = job.area.x,
|
|
y = job.area.y + (img_area and img_area.h or 0),
|
|
w = job.area.w,
|
|
h = job.area.h,
|
|
}),
|
|
})
|
|
end
|
|
|
|
function M:seek(job) end
|
|
|
|
function M:preload(job)
|
|
local cache = ya.file_cache(job)
|
|
if not cache then
|
|
return true
|
|
end
|
|
|
|
local cha = fs.cha(cache)
|
|
if cha and cha.len > 0 then
|
|
return true
|
|
end
|
|
|
|
-- stylua: ignore
|
|
local output, err = Command('ffmpeg')
|
|
:arg({
|
|
'-hide_banner',
|
|
'-loglevel', 'warning',
|
|
'-i', tostring(job.file.url),
|
|
'-an',
|
|
-- '-vcodec', 'copy',
|
|
string.format('%s.jpg', cache),
|
|
})
|
|
:stderr(Command.PIPED)
|
|
:output()
|
|
|
|
if not output then
|
|
return true, Err('Failed to start `ffmpeg`, error: %s', err)
|
|
elseif not output.status.success then
|
|
return true, Err('Failed to get image, stderr: %s', output.stderr)
|
|
end
|
|
|
|
local ok, err = os.rename(string.format('%s.jpg', cache), tostring(cache))
|
|
if ok then
|
|
return true
|
|
else
|
|
return true, Err('Failed to rename: %s', err)
|
|
end
|
|
end
|
|
|
|
return M
|