5 Commits

Author SHA1 Message Date
757804b192 disable debug
Some checks failed
Luacheck / luacheck (push) Failing after 2m10s
2025-02-15 01:36:30 -08:00
33f10c84b9 clean up code 2025-02-15 01:15:55 -08:00
412634c943 update config file and readme 2025-02-14 20:21:19 -08:00
3f7a5fb183 add additional fields for history database
Some checks failed
Luacheck / luacheck (push) Failing after 2m27s
2025-02-13 18:53:00 -08:00
68875120c5 add documentation and update get_video_info
- change to json dump and use mpv native json reader
2025-02-13 01:50:48 -08:00

View File

@@ -15,9 +15,8 @@
-- along with this program. If not, see <https://www.gnu.org/licenses/>. -- along with this program. If not, see <https://www.gnu.org/licenses/>.
local mp = require("mp") local mp = require("mp")
mp.options = require("mp.options") mp.options = require("mp.options")
local assdraw = require("mp.assdraw")
local msg = require("mp.msg")
local utils = require("mp.utils") local utils = require("mp.utils")
local assdraw = require("mp.assdraw")
local styleOn = mp.get_property("osd-ass-cc/0") local styleOn = mp.get_property("osd-ass-cc/0")
local styleOff = mp.get_property("osd-ass-cc/1") local styleOff = mp.get_property("osd-ass-cc/1")
local YouTubeQueue = {} local YouTubeQueue = {}
@@ -77,8 +76,7 @@ local function destroy()
destroyer = nil destroyer = nil
end end
timeout = mp.add_timeout(options.menu_timeout, destroy) timeout = mp.add_periodic_timer(options.menu_timeout, destroy)
timeout:kill()
-- STYLE {{{ -- STYLE {{{
local colors = { local colors = {
@@ -123,11 +121,8 @@ local style = {
--- @param s string - the string to surround with quotes --- @param s string - the string to surround with quotes
--- @return string | nil - the string surrounded with quotes --- @return string | nil - the string surrounded with quotes
local function surround_with_quotes(s) local function surround_with_quotes(s)
if type(s) ~= "string" then if string.sub(s, 0, 1) == '"' and string.sub(s, -1) == '"' then
return s return nil
end
if string.sub(s, 1, 1) == '"' and string.sub(s, -1) == '"' then
return s
else else
return '"' .. s .. '"' return '"' .. s .. '"'
end end
@@ -179,11 +174,13 @@ end
---@return boolean - true if the path is a file, false otherwise ---@return boolean - true if the path is a file, false otherwise
local function is_file(filepath) local function is_file(filepath)
local result = utils.file_info(filepath) local result = utils.file_info(filepath)
msg.debug("IS_FILE() check: " .. tostring(result.is_file)) if debug and type(result) == "table" then
print("IS_FILE() check: " .. tostring(result.is_file))
end
if result == nil or type(result) ~= "table" then if result == nil or type(result) ~= "table" then
return false return false
end end
return result.is_file == true return true
end end
---returns the filename given a path (eg. /home/user/file.txt -> file.txt) ---returns the filename given a path (eg. /home/user/file.txt -> file.txt)
@@ -218,14 +215,8 @@ end
---Open a URL in the browser ---Open a URL in the browser
---@param url string ---@param url string
local function open_url_in_browser(url) local function open_url_in_browser(url)
if isnull(url) then local command = options.browser .. " " .. surround_with_quotes(url)
return os.execute(command)
end
mp.command_native({
name = "subprocess",
detach = true,
args = { options.browser, url },
})
end end
--- Opens the current video in the browser --- Opens the current video in the browser
@@ -245,10 +236,10 @@ end
-- Internal function to print the contents of the internal playlist to the console -- Internal function to print the contents of the internal playlist to the console
local function print_internal_playlist() local function print_internal_playlist()
local count = mp.get_property_number("playlist-count") local count = mp.get_property_number("playlist-count")
msg.info("Playlist contents:") print("Playlist contents:")
for i = 0, count - 1 do for i = 0, count - 1 do
local uri = mp.get_property(string.format("playlist/%d/filename", i)) local uri = mp.get_property(string.format("playlist/%d/filename", i))
msg.info(string.format("%d: %s", i, uri)) print(string.format("%d: %s", i, uri))
end end
end end
@@ -332,11 +323,26 @@ end
--- @return string | nil - The resulting JSON string, or nil if the input is invalid. --- @return string | nil - The resulting JSON string, or nil if the input is invalid.
local function convert_to_json(key, val) local function convert_to_json(key, val)
if type(key) == "table" then if type(key) == "table" then
return utils.format_json(key) -- Handle the case where key is a table of key-value pairs
local json = "{"
local first = true
for k, v in pairs(key) do
if not first then
json = json .. ", "
end
first = false
local quoted_val = string.format('"%s"', v)
json = json .. string.format('"%s": %s', k, quoted_val)
end
json = json .. "}"
return json
else else
local t = {} if type(val) == "string" then
t[key] = val return string.format('{"%s": "%s"}', key, val)
return utils.format_json(t) else
return string.format('{"%s": %s}', key, tostring(val))
end
end end
end end
@@ -405,7 +411,7 @@ function YouTubeQueue.get_video_info(url)
if res.status ~= 0 or isnull(res.stdout) then if res.status ~= 0 or isnull(res.stdout) then
print_osd_message("Failed to get video info (yt-dlp error)", MSG_DURATION, style.error) print_osd_message("Failed to get video info (yt-dlp error)", MSG_DURATION, style.error)
msg.warn("yt-dlp status: " .. res.status) print("yt-dlp status: " .. res.status)
return nil return nil
end end
@@ -415,21 +421,15 @@ function YouTubeQueue.get_video_info(url)
return nil return nil
end end
local category = nil
if data.categories then
category = data.categories[1]
else
category = "Unknown"
end
local info = { local info = {
channel_url = data.channel_url or "", channel_url = data.channel_url or "",
channel_name = data.uploader or "", channel_name = data.uploader or "",
video_name = data.title or "", video_name = data.title or "",
view_count = data.view_count or "", view_count = data.view_count or "",
upload_date = data.upload_date or "", upload_date = data.upload_date or "",
category = category or "", category = data.categories[1] or "",
thumbnail_url = data.thumbnail or "", thumbnail_url = data.thumbnail or "",
subscribers = data.channel_follower_count or 0, subscribers = data.channel_follower_count or "",
} }
if isnull(info.channel_url) or isnull(info.channel_name) or isnull(info.video_name) then if isnull(info.channel_url) or isnull(info.channel_name) or isnull(info.video_name) then
@@ -444,7 +444,7 @@ end
function YouTubeQueue.print_current_video() function YouTubeQueue.print_current_video()
destroy() destroy()
local current = current_video local current = current_video
if current and current.video_url ~= "" and is_file(current.video_url) then if current and current.vidro_url ~= "" and is_file(current.video_url) then
print_osd_message("Playing: " .. current.video_url, 3) print_osd_message("Playing: " .. current.video_url, 3)
else else
if current and current.video_url then if current and current.video_url then
@@ -463,7 +463,7 @@ end
--- @return table | nil - the video at the new index --- @return table | nil - the video at the new index
function YouTubeQueue.set_video(direction) function YouTubeQueue.set_video(direction)
local amt local amt
direction = (direction and string.upper(direction)) or "NEXT" direction = string.upper(direction)
if direction == "NEXT" or direction == nil then if direction == "NEXT" or direction == nil then
amt = 1 amt = 1
elseif direction == "PREV" or direction == "PREVIOUS" then elseif direction == "PREV" or direction == "PREVIOUS" then
@@ -497,7 +497,9 @@ end
--- @param update_history boolean - whether to update the history database --- @param update_history boolean - whether to update the history database
--- @return number | nil - the index of the currently playing video --- @return number | nil - the index of the currently playing video
function YouTubeQueue.update_current_index(update_history) function YouTubeQueue.update_current_index(update_history)
msg.debug("Updating current index") if debug then
print("Updating current index")
end
if #video_queue == 0 then if #video_queue == 0 then
return return
end end
@@ -580,6 +582,7 @@ function YouTubeQueue.print_queue(duration)
-- Reset and prepare OSD -- Reset and prepare OSD
timeout:kill() timeout:kill()
mp.set_osd_ass(0, 0, "") mp.set_osd_ass(0, 0, "")
timeout:resume()
if #video_queue == 0 then if #video_queue == 0 then
print_osd_message("No videos in the queue or history.", duration, style.error) print_osd_message("No videos in the queue or history.", duration, style.error)
@@ -607,13 +610,9 @@ function YouTubeQueue.print_queue(duration)
ass:append(style.font .. message .. "\n") ass:append(style.font .. message .. "\n")
end end
mp.set_osd_ass(0, 0, ass.text) mp.set_osd_ass(0, 0, ass.text)
-- restart one-shot timer for menu hide if duration then
if duration and type(duration) == "number" then mp.add_timeout(duration, destroy)
timeout.timeout = duration
else
timeout.timeout = options.menu_timeout
end end
timeout:resume()
destroyer = destroy destroyer = destroy
end end
@@ -623,7 +622,6 @@ end
--- @param amt number - the number of steps to move the cursor. Positive values move up, negative values move down. --- @param amt number - the number of steps to move the cursor. Positive values move up, negative values move down.
function YouTubeQueue.move_cursor(amt) function YouTubeQueue.move_cursor(amt)
timeout:kill() timeout:kill()
timeout.timeout = options.menu_timeout
timeout:resume() timeout:resume()
selected_index = selected_index - amt selected_index = selected_index - amt
if selected_index < 1 then if selected_index < 1 then
@@ -842,9 +840,10 @@ function YouTubeQueue.add_to_history_db(v)
local url = options.backend_host .. ":" .. options.backend_port .. "/add_video" local url = options.backend_host .. ":" .. options.backend_port .. "/add_video"
local json = convert_to_json(v) local json = convert_to_json(v)
local command = { "curl", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", json } local command = { "curl", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", json }
msg.debug("Adding video to history") if debug then
msg.debug("Command: " .. table.concat(command, " ")) print("Adding video to history")
print_osd_message("Adding video to history...", MSG_DURATION) print("Command: " .. table.concat(command, " "))
end
mp.command_native_async({ mp.command_native_async({
name = "subprocess", name = "subprocess",
playback_only = false, playback_only = false,
@@ -856,7 +855,6 @@ function YouTubeQueue.add_to_history_db(v)
return false return false
end end
end) end)
print_osd_message("Video added to history db", MSG_DURATION)
return true return true
end end
@@ -872,16 +870,19 @@ function YouTubeQueue.save_queue(idx)
idx = index idx = index
end end
local url = options.backend_host .. ":" .. options.backend_port .. "/save_queue" local url = options.backend_host .. ":" .. options.backend_port .. "/save_queue"
local urls_list = YouTubeQueue.get_urls(idx + 1) local data = convert_to_json("urls", YouTubeQueue.get_urls(idx + 1))
if urls_list == nil or #urls_list == 0 then if data == nil or data == '{"urls": []}' then
print_osd_message("Failed to save queue: No videos remaining in queue", MSG_DURATION, style.error) print_osd_message("Failed to save queue: No videos remaining in queue", MSG_DURATION, style.error)
return false return false
end end
local data = convert_to_json({ urls = urls_list }) if debug then
msg.debug("Data: " .. data) print("Data: " .. data)
end
local command = { "curl", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", data } local command = { "curl", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", data }
msg.debug("Saving queue to history") if debug then
msg.debug("Command: " .. table.concat(command, " ")) print("Saving queue to history")
print("Command: " .. table.concat(command, " "))
end
mp.command_native_async({ mp.command_native_async({
name = "subprocess", name = "subprocess",
playback_only = false, playback_only = false,
@@ -892,7 +893,9 @@ function YouTubeQueue.save_queue(idx)
print_osd_message("Failed to save queue: " .. err, MSG_DURATION, style.error) print_osd_message("Failed to save queue: " .. err, MSG_DURATION, style.error)
return false return false
end end
msg.debug("Status: " .. result.status) if debug then
print("Status: " .. result.status)
end
if result.status == 0 then if result.status == 0 then
if idx > 1 then if idx > 1 then
print_osd_message("Queue saved to history from index: " .. idx, MSG_DURATION) print_osd_message("Queue saved to history from index: " .. idx, MSG_DURATION)
@@ -923,15 +926,17 @@ function YouTubeQueue.load_queue()
return false return false
else else
if result.status == 0 then if result.status == 0 then
local ok, parsed = pcall(utils.parse_json, result.stdout) -- split urls based on commas
if not ok or type(parsed) ~= "table" then local urls = {}
print_osd_message("Invalid JSON from history backend", MSG_DURATION, style.error) -- Remove the brackets from json list
return false local l = result.stdout:sub(2, -3)
local item
for turl in l:gmatch("[^,]+") do
item = turl:match("^%s*(.-)%s*$"):gsub('"', "'")
table.insert(urls, item)
end end
for _, turl in ipairs(parsed) do for _, turl in ipairs(urls) do
if type(turl) == "string" then YouTubeQueue.add_to_queue(turl, 0)
YouTubeQueue.add_to_queue(turl, 0)
end
end end
print_osd_message("Loaded queue from history.", MSG_DURATION) print_osd_message("Loaded queue from history.", MSG_DURATION)
end end
@@ -946,7 +951,9 @@ end
-- This function is called when the current file ends or when moving to the -- This function is called when the current file ends or when moving to the
-- next or previous item in the internal playlist -- next or previous item in the internal playlist
local function on_end_file(event) local function on_end_file(event)
msg.debug("End file event triggered: " .. event.reason) if debug then
print("End file event triggered: " .. event.reason)
end
if event.reason == "eof" then -- The file ended normally if event.reason == "eof" then -- The file ended normally
YouTubeQueue.update_current_index(true) YouTubeQueue.update_current_index(true)
end end
@@ -954,18 +961,24 @@ end
-- Function to be called when the track-changed event is triggered -- Function to be called when the track-changed event is triggered
local function on_track_changed() local function on_track_changed()
msg.debug("Track changed event triggered.") if debug then
print("Track changed event triggered.")
end
YouTubeQueue.update_current_index() YouTubeQueue.update_current_index()
end end
local function on_file_loaded() local function on_file_loaded()
msg.debug("Load file event triggered.") if debug then
print("Load file event triggered.")
end
YouTubeQueue.update_current_index(true) YouTubeQueue.update_current_index(true)
end end
-- Function to be called when the playback-restart event is triggered -- Function to be called when the playback-restart event is triggered
local function on_playback_restart() local function on_playback_restart()
msg.debug("Playback restart event triggered.") if debug then
print("Playback restart event triggered.")
end
if current_video == nil then if current_video == nil then
local url = mp.get_property("path") local url = mp.get_property("path")
YouTubeQueue.add_to_queue(url) YouTubeQueue.add_to_queue(url)