From 92e53c91b639fea79d427799b2dd40a7af5d1be5 Mon Sep 17 00:00:00 2001 From: eylles Date: Wed, 21 Apr 2021 22:47:11 -0600 Subject: [PATCH 1/5] video thumbnailing and always right image path --- notify.lua | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/notify.lua b/notify.lua index 98cbcf1..5d63b17 100644 --- a/notify.lua +++ b/notify.lua @@ -82,11 +82,26 @@ end -- extract image from audio file function extracted_image_from_audiofile (audiofile, imagedst) - local ffmpeg_cmd = ("ffmpeg -loglevel -8 -vsync 2 -i %s %s > /dev/null"):format( + local ffmpeg_cmd_a = ("ffmpeg -loglevel -8 -vsync 2 -y -i %s %s > /dev/null"):format( string.shellescape(audiofile), string.shellescape(imagedst) ) - -- print_debug("executing " .. ffmpeg_cmd) - if os.execute(ffmpeg_cmd) then + + -- print_debug("executing " .. ffmpeg_cmd_a) + if os.execute(ffmpeg_cmd_a) then + return true + end + + return false +end + +-- extract image from video file +function extracted_image_from_videofile (audiofile, imagedst) + local ffmpeg_cmd_v = ("ffmpegthumbnailer -i %s -o %s 2>&1 > /dev/null"):format( + string.shellescape(audiofile), string.shellescape(imagedst) + ) + + -- print_debug("executing " .. ffmpeg_cmd_v) + if os.execute(ffmpeg_cmd_v) then return true end @@ -106,11 +121,11 @@ COVER_ART_PATH = "/tmp/covert_art.jpg" ICON_PATH = "/tmp/icon.jpg" function notify_current_track() - os.remove(COVER_ART_PATH) + -- os.remove(COVER_ART_PATH) os.remove(ICON_PATH) local metadata = mp.get_property_native("metadata") - + -- track doesn't contain metadata if not metadata then return @@ -123,18 +138,24 @@ function notify_current_track() -- print_debug("notify_current_track(): -> extracted metadata:") -- print_debug("artist: " .. artist) - -- print_debug("album: " .. album) + -- print_debug("album: " .. album) -- print_debug("title: " .. title) -- absolute filename of currently playing audio file local abs_filename = os.getenv("PWD") .. "/" .. mp.get_property_native("path") + local bad_string = os.getenv("PWD") .. "//" + local abs_filename = string.gsub(abs_filename,bad_string,"/") -- print_debug(abs_filename) params = "" -- extract cover art: set it as icon in notification params - if extracted_image_from_audiofile(abs_filename, COVER_ART_PATH) then - if scaled_image(COVER_ART_PATH, ICON_PATH) then - params = "-i " .. ICON_PATH + if extracted_image_from_videofile(abs_filename, COVER_ART_PATH) then + params = "-i "..COVER_ART_PATH + else + if extracted_image_from_audiofile(abs_filename, COVER_ART_PATH) then + if scaled_image(COVER_ART_PATH, ICON_PATH) then + params = "-i " .. ICON_PATH + end end end @@ -161,7 +182,7 @@ function notify_current_track() -- print_debug("command: " .. command) os.execute(command) - + end function notify_metadata_updated(name, data) From 2e4e54616adaff0bd738f3fa9275d4a970f9ce20 Mon Sep 17 00:00:00 2001 From: eylles Date: Wed, 21 Apr 2021 22:53:29 -0600 Subject: [PATCH 2/5] optional ffmpegthumbnailer dependency --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a8bd46a..5d90c84 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Features -------- * shows artist, title and album name (as far as detected by mpv) -* extracts cover art using ffmpeg +* extracts cover art using ffmpeg Requirements ------------ @@ -16,10 +16,11 @@ Requirements * [mpv](http://mpv.io) (>= 0.3.6) * [Lua](http://lua.org) (>= 5.2) * `ffmpeg` from [https://www.ffmpeg.org/](https://www.ffmpeg.org/) +* `ffmpegthumbnailer` from [ffmpegthumbnailer](https://github.com/dirkvdb/ffmpegthumbnailer) `(optional only for video thumbnails)` * `notify-send` from [libnotify](https://github.com/GNOME/libnotify) * `convert` from [ImageMagick](http://www.imagemagick.org) -Install mpv, lua, ffmpeg, libnotify and ImageMagick packages +Install mpv, lua, ffmpeg, ffmpegthumbnailer(optionl), libnotify and ImageMagick packages Installation ------------ @@ -32,6 +33,6 @@ and mpv will find it. Optionally, you can add it to mpv's command line: License ------- -mpv-notify was originally written by Roland Hieber . I have simply -refactored it according to my needs. You may use it under the terms of the +mpv-notify was originally written by Roland Hieber . I have simply +refactored it according to my needs. You may use it under the terms of the [MIT license](http://choosealicense.com/licenses/mit/). From a46fd66d7bc726aa472b540b8b89e30201a6b485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz?= Date: Wed, 9 Jun 2021 19:50:01 +0200 Subject: [PATCH 3/5] autoformat --- notify.lua | 100 ++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/notify.lua b/notify.lua index 5d63b17..1bab70f 100644 --- a/notify.lua +++ b/notify.lua @@ -27,41 +27,41 @@ ------------------------------------------------------------------------------- function print_debug(s) - print("DEBUG: " .. s) -- comment out for no debug info - return true + print("DEBUG: " .. s) -- comment out for no debug info + return true end -- url-escape a string, per RFC 2396, Section 2 function string.urlescape(str) - local s, c = string.gsub(str, "([^A-Za-z0-9_.!~*'()/-])", - function(c) - return ("%%%02x"):format(c:byte()) - end) - return s; + local s, c = string.gsub(str, "([^A-Za-z0-9_.!~*'()/-])", + function(c) + return ("%%%02x"):format(c:byte()) + end) + return s; end -- escape string for html function string.htmlescape(str) - local str = string.gsub(str, "<", "<") - str = string.gsub(str, ">", ">") - str = string.gsub(str, "&", "&") - str = string.gsub(str, "\"", """) - str = string.gsub(str, "'", "'") - return str + local str = string.gsub(str, "<", "<") + str = string.gsub(str, ">", ">") + str = string.gsub(str, "&", "&") + str = string.gsub(str, "\"", """) + str = string.gsub(str, "'", "'") + return str end -- escape string for shell inclusion function string.shellescape(str) - return "'"..string.gsub(str, "'", "'\"'\"'").."'" + return "'"..string.gsub(str, "'", "'\"'\"'").."'" end -- converts string to a valid filename on most (modern) filesystems function string.safe_filename(str) - local s, _ = string.gsub(str, "([^A-Za-z0-9_.-])", - function(c) - return ("%02x"):format(c:byte()) - end) - return s; + local s, _ = string.gsub(str, "([^A-Za-z0-9_.-])", + function(c) + return ("%02x"):format(c:byte()) + end) + return s; end ------------------------------------------------------------------------------- @@ -71,19 +71,19 @@ end -- scale an image file -- @return boolean of success function scaled_image(src, dst) - local convert_cmd = ("convert -scale x64 -- %s %s"):format( - string.shellescape(src), string.shellescape(dst)) - -- print_debug("executing " .. convert_cmd) - if os.execute(convert_cmd) then - return true - end - return false + local convert_cmd = ("convert -scale x64 -- %s %s"):format( + string.shellescape(src), string.shellescape(dst)) + -- print_debug("executing " .. convert_cmd) + if os.execute(convert_cmd) then + return true + end + return false end -- extract image from audio file function extracted_image_from_audiofile (audiofile, imagedst) local ffmpeg_cmd_a = ("ffmpeg -loglevel -8 -vsync 2 -y -i %s %s > /dev/null"):format( - string.shellescape(audiofile), string.shellescape(imagedst) + string.shellescape(audiofile), string.shellescape(imagedst) ) -- print_debug("executing " .. ffmpeg_cmd_a) @@ -97,7 +97,7 @@ end -- extract image from video file function extracted_image_from_videofile (audiofile, imagedst) local ffmpeg_cmd_v = ("ffmpegthumbnailer -i %s -o %s 2>&1 > /dev/null"):format( - string.shellescape(audiofile), string.shellescape(imagedst) + string.shellescape(audiofile), string.shellescape(imagedst) ) -- print_debug("executing " .. ffmpeg_cmd_v) @@ -109,12 +109,12 @@ function extracted_image_from_videofile (audiofile, imagedst) end function get_value(data, keys) - for _,v in pairs(keys) do - if data[v] and string.len(data[v]) > 0 then - return data[v] - end - end - return "" + for _,v in pairs(keys) do + if data[v] and string.len(data[v]) > 0 then + return data[v] + end + end + return "" end COVER_ART_PATH = "/tmp/covert_art.jpg" @@ -128,21 +128,21 @@ function notify_current_track() -- track doesn't contain metadata if not metadata then - return - end + return + end -- we try to fetch metadata values using all possible keys - local artist = get_value(metadata, {"artist", "ARTIST"}) + local artist = get_value(metadata, {"artist", "ARTIST"}) local album = get_value(metadata, {"album", "ALBUM"}) - local title = get_value(metadata, {"title", "TITLE", "icy-title"}) + local title = get_value(metadata, {"title", "TITLE", "icy-title"}) - -- print_debug("notify_current_track(): -> extracted metadata:") - -- print_debug("artist: " .. artist) - -- print_debug("album: " .. album) + -- print_debug("notify_current_track(): -> extracted metadata:") + -- print_debug("artist: " .. artist) + -- print_debug("album: " .. album) -- print_debug("title: " .. title) - -- absolute filename of currently playing audio file - local abs_filename = os.getenv("PWD") .. "/" .. mp.get_property_native("path") + -- absolute filename of currently playing audio file + local abs_filename = os.getenv("PWD") .. "/" .. mp.get_property_native("path") local bad_string = os.getenv("PWD") .. "//" local abs_filename = string.gsub(abs_filename,bad_string,"/") -- print_debug(abs_filename) @@ -150,11 +150,11 @@ function notify_current_track() params = "" -- extract cover art: set it as icon in notification params if extracted_image_from_videofile(abs_filename, COVER_ART_PATH) then - params = "-i "..COVER_ART_PATH + params = "-i "..COVER_ART_PATH else if extracted_image_from_audiofile(abs_filename, COVER_ART_PATH) then if scaled_image(COVER_ART_PATH, ICON_PATH) then - params = "-i " .. ICON_PATH + params = "-i " .. ICON_PATH end end end @@ -170,7 +170,7 @@ function notify_current_track() if (string.len(title) > 0) then if (string.len(album) > 0) then body_str = ("%s
%s"):format( - string.htmlescape(title), string.htmlescape(album)) + string.htmlescape(title), string.htmlescape(album)) else body_str = string.htmlescape(title) end @@ -178,15 +178,15 @@ function notify_current_track() body = string.shellescape(body_str) - local command = ("notify-send -a mpv %s -- %s %s"):format(params, summary, body) - -- print_debug("command: " .. command) - os.execute(command) + local command = ("notify-send -a mpv %s -- %s %s"):format(params, summary, body) + -- print_debug("command: " .. command) + os.execute(command) end function notify_metadata_updated(name, data) - notify_current_track() + notify_current_track() end From 339d23c3cb7925cafd4703dd6b5026d21f55709e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz?= Date: Wed, 9 Jun 2021 20:54:38 +0200 Subject: [PATCH 4/5] added writing metadata info to tmp files --- README.md | 1 + notify.lua | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5d90c84..88ea212 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Features * shows artist, title and album name (as far as detected by mpv) * extracts cover art using ffmpeg +* writes icon, artist, album, title to temporary files (useful for OBS) Requirements ------------ diff --git a/notify.lua b/notify.lua index 1bab70f..bfe46c8 100644 --- a/notify.lua +++ b/notify.lua @@ -64,6 +64,13 @@ function string.safe_filename(str) return s; end +-- write the string to a file +function string.dumpf(str, path) + local file = io.open(path, "w") + file:write(str) + file:close() +end + ------------------------------------------------------------------------------- -- here we go. ------------------------------------------------------------------------------- @@ -117,8 +124,11 @@ function get_value(data, keys) return "" end -COVER_ART_PATH = "/tmp/covert_art.jpg" -ICON_PATH = "/tmp/icon.jpg" +COVER_ART_PATH = "/tmp/mpv.covert_art.jpg" +ICON_PATH = "/tmp/mpv.icon.jpg" +ARTIST_PATH = "/tmp/mpv.artist.txt" +ALBUM_PATH = "/tmp/mpv.album.txt" +TITLE_PATH = "/tmp/mpv.title.txt" function notify_current_track() -- os.remove(COVER_ART_PATH) @@ -135,6 +145,9 @@ function notify_current_track() local artist = get_value(metadata, {"artist", "ARTIST"}) local album = get_value(metadata, {"album", "ALBUM"}) local title = get_value(metadata, {"title", "TITLE", "icy-title"}) + artist:dumpf(ARTIST_PATH) + album:dumpf(ALBUM_PATH) + title:dumpf(TITLE_PATH) -- print_debug("notify_current_track(): -> extracted metadata:") -- print_debug("artist: " .. artist) @@ -182,15 +195,29 @@ function notify_current_track() -- print_debug("command: " .. command) os.execute(command) - end +-- notify hook function notify_metadata_updated(name, data) notify_current_track() end +-- empty or remove files so the clients know nothing is playing +function cleanup(event) + local text_files = {ARTIST_PATH, ALBUM_PATH, TITLE_PATH} + local emptystr = "" + for _, path in ipairs(created_files) do + emptystr:dumpf(path) + end + local binary_files = {ICON_PATH, COVER_ART_PATH} + for _, path in ipairs(created_files) do + os.remove(path) + end +end + -- insert main() here mp.register_event("file-loaded", notify_current_track) +mp.register_event("shutdown", cleanup) -- mp.observe_property("metadata", nil, notify_metadata_updated) From 6f5d0f002aa8f895a942c8495cb559506482fa5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz?= Date: Wed, 9 Jun 2021 22:40:05 +0200 Subject: [PATCH 5/5] added local file art support, fixed error --- notify.lua | 75 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/notify.lua b/notify.lua index bfe46c8..f38cd7b 100644 --- a/notify.lua +++ b/notify.lua @@ -27,7 +27,7 @@ ------------------------------------------------------------------------------- function print_debug(s) - print("DEBUG: " .. s) -- comment out for no debug info + print("INFO: " .. s) -- comment out for no debug info return true end @@ -37,7 +37,7 @@ function string.urlescape(str) function(c) return ("%%%02x"):format(c:byte()) end) - return s; + return s end -- escape string for html @@ -61,7 +61,7 @@ function string.safe_filename(str) function(c) return ("%02x"):format(c:byte()) end) - return s; + return s end -- write the string to a file @@ -71,6 +71,16 @@ function string.dumpf(str, path) file:close() end +-- check if a file exists ignoring case +function find_lowercase_file(path, filename) + local command = ("find %s -iname %s -type f"):format(string.shellescape(path), string.shellescape(filename), "r") + local pfile = io.popen(command) + local output = pfile:read() + pfile:close() + return output +end + + ------------------------------------------------------------------------------- -- here we go. ------------------------------------------------------------------------------- @@ -103,7 +113,7 @@ end -- extract image from video file function extracted_image_from_videofile (audiofile, imagedst) - local ffmpeg_cmd_v = ("ffmpegthumbnailer -i %s -o %s 2>&1 > /dev/null"):format( + local ffmpeg_cmd_v = ("ffmpegthumbnailer -i %s -o %s 2>&1 >/dev/null"):format( string.shellescape(audiofile), string.shellescape(imagedst) ) @@ -124,6 +134,43 @@ function get_value(data, keys) return "" end +-- return path without .. or . +function os.realpath(path) + local pfile = io.popen(("realpath %s"):format(string.shellescape(path)), "r") + local output = pfile:read() + pfile:close() + return output +end + +-- copy file +function os.copy(source, dest) + local command = ("cp %s %s"):format(string.shellescape(source), string.shellescape(dest)) + os.execute(command) +end + +-- look for a list of possible cover art images in the same folder as the file +-- @param path absolute file path of currently played file, or nil if no match +function coppied_directory_cover(path, imagedst) + -- print_debug("get_folder_cover_art: file path is " .. path) + local cover_extensions = { "png", "jpg", "jpeg", "gif" } + local cover_names = { "cover", "folder", "front", "back", "insert" } + + local dir = os.realpath(string.match(path, "^(.*)/[^/]+$")) + + for _, name in pairs(cover_names) do + for _, ext in pairs(cover_extensions) do + -- print_debug("get_folder_cover_art: trying " .. cover_path) + local cover_name = name .. "." .. ext + local actual_name = find_lowercase_file(dir, cover_name) + if actual_name ~= nil then + os.copy(actual_name, imagedst) + return true + end + end + end + return false +end + COVER_ART_PATH = "/tmp/mpv.covert_art.jpg" ICON_PATH = "/tmp/mpv.icon.jpg" ARTIST_PATH = "/tmp/mpv.artist.txt" @@ -162,14 +209,16 @@ function notify_current_track() params = "" -- extract cover art: set it as icon in notification params - if extracted_image_from_videofile(abs_filename, COVER_ART_PATH) then - params = "-i "..COVER_ART_PATH - else - if extracted_image_from_audiofile(abs_filename, COVER_ART_PATH) then - if scaled_image(COVER_ART_PATH, ICON_PATH) then - params = "-i " .. ICON_PATH - end + if coppied_directory_cover(abs_filename, COVER_ART_PATH) then + if scaled_image(COVER_ART_PATH, ICON_PATH) then + params = "-i " .. ICON_PATH end + elseif extracted_image_from_audiofile(abs_filename, COVER_ART_PATH) then + if scaled_image(COVER_ART_PATH, ICON_PATH) then + params = "-i " .. ICON_PATH + end + elseif extracted_image_from_videofile(abs_filename, COVER_ART_PATH) then + params = "-i "..COVER_ART_PATH end -- form notification summary @@ -206,11 +255,11 @@ end function cleanup(event) local text_files = {ARTIST_PATH, ALBUM_PATH, TITLE_PATH} local emptystr = "" - for _, path in ipairs(created_files) do + for _, path in ipairs(text_files) do emptystr:dumpf(path) end local binary_files = {ICON_PATH, COVER_ART_PATH} - for _, path in ipairs(created_files) do + for _, path in ipairs(binary_files) do os.remove(path) end end