diff options
Diffstat (limited to 'experiments/spotify-player')
4 files changed, 682 insertions, 0 deletions
diff --git a/experiments/spotify-player/media-playback-start-symbolic.svg b/experiments/spotify-player/media-playback-start-symbolic.svg new file mode 100644 index 0000000..b94454d --- /dev/null +++ b/experiments/spotify-player/media-playback-start-symbolic.svg @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.48.1 r9760" + version="1.0" + sodipodi:docname="media-playback-start.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective18" /> + <linearGradient + id="StandardGradient" + gradientTransform="scale(0.9306049,1.0745699)" + x1="9.2393684" + y1="7.4479208" + x2="9.2393684" + y2="14.326297" + gradientUnits="userSpaceOnUse"> + <stop + style="stop-color:#000000;stop-opacity:0.86274511;" + offset="0" + id="stop3389" /> + <stop + style="stop-color:#000000;stop-opacity:0.47058824;" + offset="1" + id="stop3391" /> + </linearGradient> + <inkscape:perspective + id="perspective3648" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient3627" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(0.9306049,1.0745699)" + x1="9.2393684" + y1="7.4479208" + x2="9.2393684" + y2="14.326297" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3641"> + <path + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" + d="m 3.3493431,6.6636072 0,10.7176378 11.3443579,0 0,-10.7176378 -11.3443579,0 z m 2.8360895,1.3397048 6.3812014,3.684188 -6.3812014,3.684188 0,-7.368376 z" + id="path3643" /> + </clipPath> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient4019" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(0.9306049,1.0745699)" + x1="9.2393684" + y1="7.4479208" + x2="9.2393684" + y2="14.326297" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient4023" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3125184,0,0,1.6041891,-4.7238894,-9.9478739)" + x1="9.2393684" + y1="7.4479208" + x2="9.2393684" + y2="14.326297" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#e7e7e7" + borderopacity="1" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="24.711919" + inkscape:cx="-5.3028199" + inkscape:cy="6.3085737" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:showpageshadow="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1028" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:window-maximized="1"> + <sodipodi:guide + orientation="1,0" + position="0,112" + id="guide2383" /> + <sodipodi:guide + orientation="0,1" + position="78.156291,0" + id="guide2389" /> + <inkscape:grid + type="xygrid" + id="grid3672" + visible="true" + enabled="true" /> + <sodipodi:guide + orientation="1,0" + position="22.008699,4.1542523" + id="guide2950" /> + <sodipodi:guide + orientation="0,1" + position="11.22532,22.008699" + id="guide2952" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + sodipodi:type="star" + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="Symbol" + sodipodi:sides="3" + sodipodi:cx="8.3125" + sodipodi:cy="11.6875" + sodipodi:r1="4.2541342" + sodipodi:r2="2.1270671" + sodipodi:arg1="0" + sodipodi:arg2="1.0471976" + inkscape:flatsided="true" + inkscape:rounded="0" + inkscape:randomized="0" + d="M 12.566634,11.6875 L 6.1854329,15.371688 L 6.1854329,8.0033117 L 12.566634,11.6875 z" + transform="matrix(1.4103927,0,0,1.4928662,-4.7238894,-9.9478739)" + inkscape:label="#path3175" /> + </g> +</svg> diff --git a/experiments/spotify-player/media-skip-backward-symbolic.svg b/experiments/spotify-player/media-skip-backward-symbolic.svg new file mode 100644 index 0000000..e25937f --- /dev/null +++ b/experiments/spotify-player/media-skip-backward-symbolic.svg @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.48.1 r9760" + version="1.0" + sodipodi:docname="media-skip-backward.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective18" /> + <linearGradient + id="StandardGradient" + gradientTransform="scale(1.1952286,0.83666)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" + gradientUnits="userSpaceOnUse"> + <stop + style="stop-color:#000000;stop-opacity:0.86274511;" + offset="0" + id="stop3389" /> + <stop + style="stop-color:#000000;stop-opacity:0.47058824;" + offset="1" + id="stop3391" /> + </linearGradient> + <inkscape:perspective + id="perspective3648" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient3627" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1952286,0,0,0.83666,1,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3634"> + <path + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" + d="m -20,0 0,16 16,0 0,-16 -16,0 z m 2,4 2,0 0,3.34375 L -11,4 -11,7.34375 -6,4 -6,12 -11,8.65625 -11,12 -16,8.65625 -16,12 l -2,0 0,-8 z" + id="path3636" /> + </clipPath> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient5544" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1952286,0,0,0.83666,21,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient5548" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1952286,0,0,0.83666,21,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#e7e7e7" + borderopacity="1" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="17.578558" + inkscape:cx="-22.871208" + inkscape:cy="3.801442" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:showpageshadow="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1028" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:window-maximized="1"> + <sodipodi:guide + orientation="1,0" + position="0,112" + id="guide2383" /> + <sodipodi:guide + orientation="0,1" + position="78.156291,0" + id="guide2389" /> + <inkscape:grid + type="xygrid" + id="grid3672" + visible="true" + enabled="true" /> + <sodipodi:guide + orientation="1,0" + position="22.008699,4.1542523" + id="guide2950" /> + <sodipodi:guide + orientation="0,1" + position="11.22532,22.008699" + id="guide2952" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="M 2,4 L 2,12 L 4,12 L 4,8.65625 L 9,12 L 9,8.65625 L 14,12 L 14,4 L 9,7.34375 L 9,4 L 4,7.34375 L 4,4 L 2,4 z" + id="Symbol" + sodipodi:nodetypes="ccccccccccccc" /> + </g> +</svg> diff --git a/experiments/spotify-player/media-skip-forward-symbolic.svg b/experiments/spotify-player/media-skip-forward-symbolic.svg new file mode 100644 index 0000000..8dc5cc9 --- /dev/null +++ b/experiments/spotify-player/media-skip-forward-symbolic.svg @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.48.1 r9760" + version="1.0" + sodipodi:docname="media-skip-forward.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective18" /> + <linearGradient + id="StandardGradient" + gradientTransform="scale(1.1952286,0.83666)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" + gradientUnits="userSpaceOnUse"> + <stop + style="stop-color:#000000;stop-opacity:0.86274511;" + offset="0" + id="stop3389" /> + <stop + style="stop-color:#000000;stop-opacity:0.47058824;" + offset="1" + id="stop3391" /> + </linearGradient> + <inkscape:perspective + id="perspective3648" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_z="1 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient3627" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1952286,0,0,0.83666,-25,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3634"> + <path + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" + d="m -20,0 0,16 16,0 0,-16 -16,0 z m 2,4 5,3.34375 L -13,4 -8,7.34375 -8,4 -6,4 -6,12 -8,12 -8,8.65625 -13,12 -13,8.65625 -18,12 -18,4 z" + id="path3636" /> + </clipPath> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient5025" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1952286,0,0,0.83666,-5,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + <linearGradient + inkscape:collect="always" + xlink:href="#StandardGradient" + id="linearGradient5029" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1952286,0,0,0.83666,-5,0)" + x1="5.8043289" + y1="4.7809148" + x2="5.8043289" + y2="13.168831" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#e7e7e7" + borderopacity="1" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="17.578558" + inkscape:cx="-24.464058" + inkscape:cy="1.7006395" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:showpageshadow="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1028" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:window-maximized="1"> + <sodipodi:guide + orientation="1,0" + position="0,112" + id="guide2383" /> + <sodipodi:guide + orientation="0,1" + position="78.156291,0" + id="guide2389" /> + <inkscape:grid + type="xygrid" + id="grid3672" + visible="true" + enabled="true" /> + <sodipodi:guide + orientation="1,0" + position="22.008699,4.1542523" + id="guide2950" /> + <sodipodi:guide + orientation="0,1" + position="11.22532,22.008699" + id="guide2952" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="M 14,4 L 14,12 L 12,12 L 12,8.65625 L 7,12 L 7,8.65625 L 2,12 L 2,4 L 7,7.34375 L 7,4 L 12,7.34375 L 12,4 L 14,4 z" + id="Symbol" + sodipodi:nodetypes="ccccccccccccc" /> + </g> +</svg> diff --git a/experiments/spotify-player/spotify-player.lua b/experiments/spotify-player/spotify-player.lua new file mode 100644 index 0000000..1873a83 --- /dev/null +++ b/experiments/spotify-player/spotify-player.lua @@ -0,0 +1,199 @@ +------------------------------------------------- +-- Spotify Player Widget for Awesome Window Manager +-- More details could be found here: +-- https://github.com/streetturtle/awesome-wm-widgets/tree/master/spotify-player + +-- @author Pavel Makhov +-- @copyright 2020 Pavel Makhov +------------------------------------------------- + +local awful = require("awful") +local wibox = require("wibox") +local watch = require("awful.widget.watch") +local spawn = require("awful.spawn") +local naughty = require("naughty") +local gears = require("gears") +local beautiful = require("beautiful") +local gfs = require("gears.filesystem") +local gs = require("gears.string") + +local HOME_DIR = os.getenv("HOME") + +local spotify_player = {} + +local BLUR_CMD = 'convert %s ( -clone 0 -fill white -colorize 100 -fill black -draw "polygon 0,200 300,200 300,300 0,300" -alpha off -write mpr:mask +delete ) -mask mpr:mask -blur 0x3 +mask %s' + +local function show_warning(message) + naughty.notify{ + preset = naughty.config.presets.critical, + title = 'Bitbucket Widget', + text = message} +end + +local function worker(args) + + local args = args or {} + + local function get_artwork(track_id, url) + if ((url ~= nil or url ~='') and not gfs.file_readable('/tmp/' .. track_id)) then + spawn.easy_async('touch /tmp/' .. track_id, function() + spawn.easy_async('curl -L -s --show-error --create-dirs -o /tmp/' .. track_id .. ' '.. url, function(stdout, stderr) + if stderr ~= '' then + show_warning(stderr) + return + end + -- spawn.easy_async(string.format(BLUR_CMD, '/tmp/' .. track_id, '/tmp/' .. track_id .. 'test')) + end) + end) + end + end + + local create_button = function (path) + return wibox.widget { + { { + forced_width=32, + forced_height=32, + resize = true, + image = path, + widget = wibox.widget.imagebox + }, + margins = 13, + widget = wibox.container.margin, + }, + forced_height = 50, + forced_width = 50, + shape = function(cr, width, height) + gears.shape.circle(cr, width, height, 20) + end, + shape_border_color = '#88888888', + shape_border_width = 2, + widget = wibox.container.background + } + end + + local popup = awful.popup{ + ontop = true, + bg = beautiful.bg_normal .. '88', + visible = false, + shape = gears.shape.rounded_rect, + border_width = 1, + border_color = beautiful.bg_focus, + maximum_width = 400, + offset = { y = 5 }, + widget = {} + } + + local rows = { + expand = 'none', + layout = wibox.layout.align.vertical, + } + + spotify_player = wibox.widget { + text = 'icon', + widget = wibox.widget.textbox + } + + local update_widget = function(widget, stdout, stderr, _, _) + for i = 0, #rows do rows[i]=nil end + + local track_id, length, art_url, album, album_artist, artist, auto_rating, disc_number, title, track_number, url = + string.match(stdout, 'trackid|(.*)\nlength|(.*)\nartUrl|(.*)\nalbum|(.*)\nalbumArtist|(.*)\nartist|(.*)\nautoRating|(.*)\ndiscNumber|(.*)\ntitle|(.*)\ntrackNumber|(.*)\nurl|(.*)') + + title = string.gsub(title, "&", '&') + get_artwork(track_id, art_url) + + local artwork_widget = wibox.widget { + -- image = '/tmp/' .. track_id .. 'test', + image = '/tmp/' .. track_id, + widget = wibox.widget.imagebox + } + + local artist_w = wibox.widget { + markup = '<span size="large" color="#ffffff">' .. artist .. '</span>', + align = 'center', + widget = wibox.widget.textbox + } + + local title_w = wibox.widget { + markup = '<span size="x-large" font_weight="bold" color="#ffffff">' .. title .. '</span>', + align = 'center', + forced_height = 30, + widget = wibox.widget.textbox + } + + local prev_button = create_button(HOME_DIR .. '/.config/awesome/awesome-wm-widgets/spotify-player/media-skip-backward-symbolic.svg') + local play_button = create_button(HOME_DIR .. '/.config/awesome/awesome-wm-widgets/spotify-player/media-playback-start-symbolic.svg') + local next_button = create_button(HOME_DIR .. '/.config/awesome/awesome-wm-widgets/spotify-player/media-skip-forward-symbolic.svg') + + -- prev_button:buttons(awful.util.table.join(awful.button({}, 1, function() prev_button:set_bg(beautiful.bg_focus);spawn.with_shell('sp prev') end))) + play_button:buttons(awful.util.table.join(awful.button({}, 1, function() spawn.with_shell('sp play') end))) + next_button:buttons(awful.util.table.join(awful.button({}, 1, function() spawn.with_shell('sp next') end))) + + prev_button:connect_signal("button::press", function(c) c:set_bg(beautiful.bg_focus) end) + prev_button:connect_signal("button::release", function(c) c:set_bg(beautiful.bg_normal) spawn.with_shell('sp prev') end) + + local buttons_w = wibox.widget { + { + prev_button, + play_button, + next_button, + spacing = 10, + layout = wibox.layout.fixed.horizontal + }, + halign = 'center', + layout = wibox.container.place, + } + + local some_w = wibox.widget { + artwork_widget, + { + { + -- { + { + title_w, + artist_w, + buttons_w, + layout = wibox.layout.fixed.vertical + }, + top = 10, + bottom = 10, + widget = wibox.container.margin + -- }, + -- bg = '#33333388', + -- widget = wibox.container.background + }, + valign = 'bottom', + content_fill_horizontal = true, + layout = wibox.container.place, + }, + layout = wibox.layout.stack + } + + popup:setup({ + -- artwork_widget, + -- artist_w, + -- title_w, + some_w, + -- buttons_w, + layout = wibox.layout.fixed.vertical, + }) + end + + spotify_player:buttons( + awful.util.table.join( + awful.button({}, 1, function() + if popup.visible then + popup.visible = not popup.visible + else + popup:move_next_to(mouse.current_widget_geometry) + end + end) + ) + ) + + watch('sp metadata', 1, update_widget, spotify_player) + + return spotify_player +end + +return setmetatable(spotify_player, { __call = function(_, ...) return worker(...) end }) |