diff options
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | github-activity-widget/README.md | 65 | ||||
-rw-r--r-- | github-activity-widget/github-activity-widget.lua | 289 | ||||
-rw-r--r-- | github-activity-widget/icons/comment.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/icons/fork.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/icons/github.png | bin | 0 -> 1571 bytes | |||
-rw-r--r-- | github-activity-widget/icons/issue.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/icons/pr.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/icons/repo.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/icons/star.svg | 1 | ||||
-rw-r--r-- | github-activity-widget/screenshot.png | bin | 0 -> 43288 bytes | |||
-rw-r--r-- | spotify-widget/README.md | 26 | ||||
-rw-r--r-- | spotify-widget/spo-wid-1.png | bin | 1283 -> 1431 bytes | |||
-rw-r--r-- | spotify-widget/spo-wid-custom.png | bin | 4608 -> 0 bytes | |||
-rw-r--r-- | spotify-widget/spo-wid-default.png | bin | 3778 -> 0 bytes | |||
-rw-r--r-- | spotify-widget/spotify-widget-custom-paused.png | bin | 0 -> 2303 bytes | |||
-rw-r--r-- | spotify-widget/spotify-widget-custom-playing.png | bin | 0 -> 2889 bytes | |||
-rw-r--r-- | spotify-widget/spotify.lua | 86 |
18 files changed, 456 insertions, 32 deletions
@@ -51,22 +51,6 @@ From left to right: - [spotify-shell](https://github.com/streetturtle/AwesomeWM/tree/master/spotify-shell) (not on the screenshot) - [run-shell](https://github.com/streetturtle/AwesomeWM/tree/master/run-shell) (not on the screenshot) -Some of these widgets use [Arc icon theme](https://github.com/horst3180/arc-icon-theme) by default but it could be easily -changed to any other icon theme or custom icons. If you want to have separators between widgets like on the screenshot create text widget with ` : ` and place it between widgets: - -```lua -... -sprtr = wibox.widget.textbox() -sprtr:set_text(" : ") -... -sprtr, -volume_icon, -sprtr, -battery_widget, -sprtr, -... -``` - # Installation Clone the repo under **~/.config/awesome/**, then in **rc.lua** add the import of the widget you'd like to use in "require" section on the top of the file: diff --git a/github-activity-widget/README.md b/github-activity-widget/README.md new file mode 100644 index 0000000..4f69eb5 --- /dev/null +++ b/github-activity-widget/README.md @@ -0,0 +1,65 @@ +# GitHub Activity Widget + +Widget shows recent activities on GitHub. It is very similar to the GitHub's "All activity" feed on the main page: + +<p align="center"> + <img src="https://github.com/streetturtle/awesome-wm-widgets/raw/master/github-activity-widget/screenshot.png"> +</p> + +Mouse click on the item opens repo/issue/pr depending on the type of the activity. Mouse click on user's avatar opens user GitHub profile. + +## Customization + +It is possible to customize widget by providing a table with all or some of the following config parameters: + +| Name | Default | Description | +|---|---|---| +| `icon` | github.png from the widget sources | Widget icon displayed on the wibar | +| `username` | your username | Required parameter | +| `number_of_events` | 10 | Number of events to display in the list | + +## How it works + +Everything starts with this timer, which gets recent activities by calling GitHub [Events API](https://developer.github.com/v3/activity/events/) and stores the response under /.cache/awmw/github-activity-widget/activity.json directory: + +```lua +gears.timer { + timeout = 600, -- calls every ten minutes + call_now = true, + autostart = true, + callback = function() + spawn.easy_async(string.format(UPDATE_EVENTS_CMD, username, CACHE_DIR), function(stdout, stderr) + if stderr ~= '' then show_warning(stderr) return end + end) + end +} +``` + +There are several reasons to store output in a file and then use it as a source to build the widget, instead of calling it everytime the widget is opened: + - activity feed does not update that often + - events API doesn't provide filtering of fields, so the output is quite large (300 events) + - it's much faster to read file from filesystem + + Next important part is **rebuild_widget** function, which is called when mouse button clicks on the widget on the wibar. It receives a json string which contains first n events from the cache file. Those events are processed by `jq` (get first n events, remove unused fields, slightly change the json structure to simplify serialization to lua table). And then it builds a widget, row by row in a loop. To display the text part of the row we already have all neccessary information in the json string which was converted to lua table. But to show an avatar we should download it first. This is done in the following snippet. First it creates a template and then checks if file already exists, and sets it in template, otherwise, downloads it asynchronously and only then sets in: + + ```lua +local avatar_img = wibox.widget { + resize = true, + forced_width = 40, + forced_height = 40, + widget = wibox.widget.imagebox +} + +if gfs.file_readable(path_to_avatar) then + avatar_img:set_image(path_to_avatar) +else + -- download it first + spawn.easy_async(string.format( + DOWNLOAD_AVATAR_CMD, + CACHE_DIR, + event.actor.id, + event.actor.avatar_url), + -- and then set + function() avatar_img:set_image(path_to_avatar) end) +end + ``` diff --git a/github-activity-widget/github-activity-widget.lua b/github-activity-widget/github-activity-widget.lua new file mode 100644 index 0000000..b3e29bc --- /dev/null +++ b/github-activity-widget/github-activity-widget.lua @@ -0,0 +1,289 @@ +------------------------------------------------- +-- GitHub Widget for Awesome Window Manager +-- Shows the recent activity from GitHub +-- More details could be found here: +-- https://github.com/streetturtle/awesome-wm-widgets/tree/master/github-activity-widget + +-- @author Pavel Makhov +-- @copyright 2020 Pavel Makhov +------------------------------------------------- + +local awful = require("awful") +local wibox = require("wibox") +local json = require("json") +local spawn = require("awful.spawn") +local naughty = require("naughty") +local gears = require("gears") +local beautiful = require("beautiful") +local gfs = require("gears.filesystem") + +local HOME_DIR = os.getenv("HOME") +local WIDGET_DIR = HOME_DIR .. '/.config/awesome/awesome-wm-widgets/github-activity-widget' +local ICONS_DIR = WIDGET_DIR .. '/icons/' +local CACHE_DIR = HOME_DIR .. '/.cache/awmw/github-activity-widget' + +local GET_EVENTS_CMD = [[bash -c "cat %s/activity.json | jq '.[:%d] | [.[] | {type: .type, actor: .actor, repo: .repo, action: .payload.action, issue_url: .payload.issue.html_url, pr_url: .payload.pull_request.html_url, created_at: .created_at}]'"]] +local DOWNLOAD_AVATAR_CMD = [[bash -c "curl -n --create-dirs -o %s/avatars/%s %s"]] +local UPDATE_EVENTS_CMD = [[bash -c "curl -s --show-error https://api.github.com/users/%s/received_events > %s/activity.json"]] + +--- Utility function to show warning messages +local function show_warning(message) + naughty.notify{ + preset = naughty.config.presets.critical, + title = 'GitHub Activity Widget', + text = message} +end + +--- Converts string representation of date (2020-06-02T11:25:27Z) to date +local function parse_date(date_str) + local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)%Z" + local y, m, d, h, min, sec, mil = date_str:match(pattern) + + return os.time{year = y, month = m, day = d, hour = h, min = min, sec = sec} +end + +--- Converts seconds to "time ago" represenation, like '1 hour ago' +local function to_time_ago(seconds) + local days = seconds / 86400 + if days > 1 then + days = math.floor(days + 0.5) + return days .. (days == 1 and ' day' or ' days') .. ' ago' + end + + local hours = (seconds % 86400) / 3600 + if hours > 1 then + hours = math.floor(hours + 0.5) + return hours .. (hours == 1 and ' hour' or ' hours') .. ' ago' + end + + local minutes = ((seconds % 86400) % 3600) / 60 + if minutes > 1 then + minutes = math.floor(minutes + 0.5) + return minutes .. (minutes == 1 and ' minute' or ' minutes') .. ' ago' + end +end + + +local popup = awful.popup{ + ontop = true, + visible = false, + shape = gears.shape.rounded_rect, + border_width = 1, + border_color = beautiful.bg_focus, + maximum_width = 350, + offset = { y = 5 }, + widget = {} +} + +local function generate_action_string(event) + local action_string = event.type + local icon = 'repo.svg' + local link = 'http://github.com/' .. event.repo.name + + if (event.type == "PullRequestEvent") then + action_string = event.action .. ' a pull request in' + link = event.pr_url + icon = 'pr.svg' + elseif (event.type == "IssuesEvent") then + action_string = event.action .. ' an issue in' + link = event.issue_url + icon = 'issue.svg' + elseif (event.type == "IssueCommentEvent") then + action_string = event.action == 'created' and 'commented in issue' or event.action .. ' a comment in' + link = event.issue_url + icon = 'comment.svg' + elseif (event.type == "WatchEvent") then + action_string = 'starred' + link = 'http://github.com/' .. event.repo.name + icon = 'star.svg' + elseif (event.type == "ForkEvent") then + action_string = 'forked' + link = 'http://github.com/' .. event.repo.name + icon = 'fork.svg' + end + + return { action_string = action_string, link = link, icon = icon } +end + +local github_widget = wibox.widget { + { + { + id = 'icon', + widget = wibox.widget.imagebox + }, + id = "m", + margins = 4, + layout = wibox.container.margin + }, + { + id = "txt", + widget = wibox.widget.textbox + }, + layout = wibox.layout.fixed.horizontal, + set_icon = function(self, new_icon) + self.m.icon.image = new_icon + end, + set_text = function(self, new_value) + self.txt.text = new_value + end +} + + +local function worker(args) + + if not gfs.dir_readable(CACHE_DIR) then + gfs.make_directories(CACHE_DIR) + end + + local args = args or {} + + local icon = args.icon or ICONS_DIR .. 'github.png' + local username = args.username or show_warning('No username provided') + local number_of_events = args.number_of_events or 10 + + github_widget:set_icon(icon) + + local rows = { + { widget = wibox.widget.textbox }, + layout = wibox.layout.fixed.vertical, + } + + local rebuild_widget = function(widget, stdout, stderr, _, _) + if stderr ~= '' then + show_warning(stderr) + return + end + + local current_time = os.time(os.date("!*t")) + + local events = json.decode(stdout) + + for i = 0, #rows do rows[i]=nil end + for _, event in ipairs(events) do + local path_to_avatar = CACHE_DIR .. '/avatars/' .. event.actor.id + + local avatar_img = wibox.widget { + resize = true, + forced_width = 40, + forced_height = 40, + widget = wibox.widget.imagebox + } + + if not gfs.file_readable(path_to_avatar) then + -- download it first + spawn.easy_async(string.format( + DOWNLOAD_AVATAR_CMD, + CACHE_DIR, + event.actor.id, + event.actor.avatar_url), function() avatar_img:set_image(path_to_avatar) end) + else + avatar_img:set_image(path_to_avatar) + end + + local action_and_link = generate_action_string(event) + + local avatar = wibox.widget { + avatar_img, + margins = 8, + layout = wibox.container.margin + } + avatar:buttons( + awful.util.table.join( + awful.button({}, 1, function() + spawn.with_shell('xdg-open http://github.com/' .. event.actor.login) + popup.visible = false + end) + ) + ) + + local repo_info = wibox.widget { + { + markup = '<b> ' .. event.actor.display_login .. '</b> ' .. action_and_link.action_string .. ' <b>' .. event.repo.name .. '</b>', + wrap = 'word', + widget = wibox.widget.textbox + }, + { + { + { + image = ICONS_DIR .. action_and_link.icon, + resize = true, + forced_height = 16, + forced_width = 16, + widget = wibox.widget.imagebox + }, + valign = 'center', + layout = wibox.container.place + }, + { + markup = to_time_ago(os.difftime(current_time, parse_date(event.created_at))), + widget = wibox.widget.textbox + }, + spacing = 4, + layout = wibox.layout.fixed.horizontal, + }, + layout = wibox.layout.align.vertical + } + repo_info:buttons( + awful.util.table.join( + awful.button({}, 1, function() + spawn.with_shell("xdg-open " .. action_and_link.link) + popup.visible = false + end) + ) + ) + + local row = wibox.widget { + { + { + avatar, + repo_info, + spacing = 4, + layout = wibox.layout.fixed.horizontal + }, + margins = 4, + layout = wibox.container.margin + }, + bg = beautiful.bg_normal, + widget = wibox.container.background + } + + row:connect_signal("mouse::enter", function(c) c:set_bg(beautiful.bg_focus) end) + row:connect_signal("mouse::leave", function(c) c:set_bg(beautiful.bg_normal) end) + + table.insert(rows, row) + end + + popup:setup(rows) + end + + github_widget:buttons( + awful.util.table.join( + awful.button({}, 1, function() + if popup.visible then + popup.visible = not popup.visible + else + spawn.easy_async(string.format(GET_EVENTS_CMD, CACHE_DIR, number_of_events), function (stdout, stderr) + rebuild_widget(github_widget, stdout, stderr) + popup:move_next_to(mouse.current_widget_geometry) + end) + end + end) + ) + ) + + -- Calls GitHub event API and stores response in "cache" file + gears.timer { + timeout = 600, + call_now = true, + autostart = true, + callback = function() + spawn.easy_async(string.format(UPDATE_EVENTS_CMD, username, CACHE_DIR), function(stdout, stderr) + if stderr ~= '' then show_warning(stderr) return end + end) + end + } + + return github_widget +end + +return setmetatable(github_widget, { __call = function(_, ...) return worker(...) end }) diff --git a/github-activity-widget/icons/comment.svg b/github-activity-widget/icons/comment.svg new file mode 100644 index 0000000..5cb54bf --- /dev/null +++ b/github-activity-widget/icons/comment.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" fill-rule="evenodd" d="M14 1H2c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1h2v3.5L7.5 11H14c.55 0 1-.45 1-1V2c0-.55-.45-1-1-1zm0 9H7l-2 2v-2H2V2h12v8z"></path></svg>
\ No newline at end of file diff --git a/github-activity-widget/icons/fork.svg b/github-activity-widget/icons/fork.svg new file mode 100644 index 0000000..4c722dc --- /dev/null +++ b/github-activity-widget/icons/fork.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg height="1024" width="640" xmlns="http://www.w3.org/2000/svg"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" d="M512 192c-70.625 0-128 57.344-128 128 0 47.219 25.875 88.062 64 110.281V448c0 0 0 128-128 128-53.062 0-94.656 11.375-128 28.812V302.28099999999995c38.156-22.219 64-63.062 64-110.281 0-70.656-57.344-128-128-128S0 121.34400000000005 0 192c0 47.219 25.844 88.062 64 110.281V721.75C25.844 743.938 0 784.75 0 832c0 70.625 57.344 128 128 128s128-57.375 128-128c0-33.5-13.188-63.75-34.25-86.625C240.375 722.5 270.656 704 320 704c254 0 256-256 256-256v-17.719c38.125-22.219 64-63.062 64-110.281C640 249.34400000000005 582.625 192 512 192zM128 128c35.406 0 64 28.594 64 64s-28.594 64-64 64-64-28.594-64-64S92.594 128 128 128zM128 896c-35.406 0-64-28.625-64-64 0-35.312 28.594-64 64-64s64 28.688 64 64C192 867.375 163.406 896 128 896zM512 384c-35.375 0-64-28.594-64-64s28.625-64 64-64 64 28.594 64 64S547.375 384 512 384z"/></svg>
\ No newline at end of file diff --git a/github-activity-widget/icons/github.png b/github-activity-widget/icons/github.png Binary files differnew file mode 100644 index 0000000..628da97 --- /dev/null +++ b/github-activity-widget/icons/github.png diff --git a/github-activity-widget/icons/issue.svg b/github-activity-widget/icons/issue.svg new file mode 100644 index 0000000..b47543a --- /dev/null +++ b/github-activity-widget/icons/issue.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg height="1024" width="896" xmlns="http://www.w3.org/2000/svg"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" d="M448 64C200.562 64 0 264.562 0 512c0 247.438 200.562 448 448 448 247.438 0 448-200.562 448-448C896 264.562 695.438 64 448 64zM448 832c-176.781 0-320-143.25-320-320 0-176.781 143.219-320 320-320 176.75 0 320 143.219 320 320C768 688.75 624.75 832 448 832zM384 768h128V640H384V768zM384 576h128V256H384V576z"/></svg>
\ No newline at end of file diff --git a/github-activity-widget/icons/pr.svg b/github-activity-widget/icons/pr.svg new file mode 100644 index 0000000..412be1a --- /dev/null +++ b/github-activity-widget/icons/pr.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg height="1024" width="768" xmlns="http://www.w3.org/2000/svg"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" d="M128 64C57.344 64 0 121.34400000000005 0 192c0 47.219 25.906 88.062 64 110.281V721.75C25.906 743.938 0 784.75 0 832c0 70.625 57.344 128 128 128s128-57.375 128-128c0-47.25-25.844-88.062-64-110.25V302.28099999999995c38.156-22.219 64-63.062 64-110.281C256 121.34400000000005 198.656 64 128 64zM128 896c-35.312 0-64-28.625-64-64 0-35.312 28.688-64 64-64 35.406 0 64 28.688 64 64C192 867.375 163.406 896 128 896zM128 256c-35.312 0-64-28.594-64-64s28.688-64 64-64c35.406 0 64 28.594 64 64S163.406 256 128 256zM704 721.75V320c0-192.5-192-192-192-192h-64V0L256 192l192 192V256c0 0 26.688 0 64 0 56.438 0 64 64 64 64v401.75c-38.125 22.188-64 62.938-64 110.25 0 70.625 57.375 128 128 128s128-57.375 128-128C768 784.75 742.125 743.938 704 721.75zM640 896c-35.312 0-64-28.625-64-64 0-35.312 28.688-64 64-64 35.375 0 64 28.688 64 64C704 867.375 675.375 896 640 896z"/></svg>
\ No newline at end of file diff --git a/github-activity-widget/icons/repo.svg b/github-activity-widget/icons/repo.svg new file mode 100644 index 0000000..f74a595 --- /dev/null +++ b/github-activity-widget/icons/repo.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg height="1024" width="768" xmlns="http://www.w3.org/2000/svg"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" d="M320 256h-64v64h64V256zM320 128h-64v64h64V128zM704 0H64C64 0 0 0 0 64v768c0 64 64 64 64 64h128v128l96-96 96 96V896h320c0 0 64-1.125 64-64V64C768 0 704 0 704 0zM704 768c0 61.625-64 64-64 64H384v-64H192v64h-64c-64 0-64-64-64-64v-64h640V768zM704 640H192V64h513L704 640zM320 512h-64v64h64V512zM320 384h-64v64h64V384z"/></svg>
\ No newline at end of file diff --git a/github-activity-widget/icons/star.svg b/github-activity-widget/icons/star.svg new file mode 100644 index 0000000..7ac51ac --- /dev/null +++ b/github-activity-widget/icons/star.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg height="1024" width="896" xmlns="http://www.w3.org/2000/svg"><style type="text/css"><![CDATA[.white{fill: #ffffff;}]]></style><path class="white" d="M896 384l-313.5-40.781L448 64 313.469 343.219 0 384l230.469 208.875L171 895.938l277-148.812 277.062 148.812L665.5 592.875 896 384z"/></svg>
\ No newline at end of file diff --git a/github-activity-widget/screenshot.png b/github-activity-widget/screenshot.png Binary files differnew file mode 100644 index 0000000..175a755 --- /dev/null +++ b/github-activity-widget/screenshot.png diff --git a/spotify-widget/README.md b/spotify-widget/README.md index 083963f..e52e58f 100644 --- a/spotify-widget/README.md +++ b/spotify-widget/README.md @@ -1,9 +1,14 @@ # Spotify widget -This widget displays currently playing song on [Spotify for Linux](https://www.spotify.com/download/linux/) client: ![screenshot](./spo-wid-default.png) and consists of two parts: +This widget displays currently playing song on [Spotify for Linux](https://www.spotify.com/download/linux/) client: ![screenshot](./spo-wid-1.png) + +Some features: - status icon which shows if music is currently playing - artist and name of the current song + - dim widget if spotify is paused + - trim long artist/song names + - tooltip with more info about the song ## Controls @@ -24,6 +29,11 @@ It is possible to customize widget by providing a table with all or some of the | `play_icon` | `/usr/share/icons/Arc/actions/24/player_play.png` | Play icon | | `pause_icon` | `/usr/share/icons/Arc/actions/24/player_pause.png` | Pause icon | | `font` | `Play 9`| Font | +| `dim_when_paused` | `false` | Decrease the widget opacity if spotify is paused | +| `dim_opacity` | `0.2` | Widget's opacity when dimmed, `dim_when_paused` should be set to `true` | +| `max_length` | `15` | Maximum lentgh of artist and title names. Text will be ellipsized if longer. | +| `show_tooltip` | `true`| Show tooltip on hover with information about the playing song | + ### Example: @@ -31,13 +41,21 @@ It is possible to customize widget by providing a table with all or some of the spotify_widget({ font = 'Ubuntu Mono 9', play_icon = '/usr/share/icons/Papirus-Light/24x24/categories/spotify.svg', - pause_icon = '/usr/share/icons/Papirus-Dark/24x24/panel/spotify-indicator.svg' + pause_icon = '/usr/share/icons/Papirus-Dark/24x24/panel/spotify-indicator.svg', + dim_when_paused = true, + dim_opacity = 0.5, + max_length = -1, + show_tooltip = false }) ``` -Gives following widget: +Gives following widget + +Playing: +![screenshot](./spotify-widget-custom-playing.png) -![screenshot](./spo-wid-custom.png) +Paused: +![screenshot](./spotify-widget-custom-paused.png) ## Installation diff --git a/spotify-widget/spo-wid-1.png b/spotify-widget/spo-wid-1.png Binary files differindex 296c2f6..5c7e403 100644 --- a/spotify-widget/spo-wid-1.png +++ b/spotify-widget/spo-wid-1.png diff --git a/spotify-widget/spo-wid-custom.png b/spotify-widget/spo-wid-custom.png Binary files differdeleted file mode 100644 index 979ad31..0000000 --- a/spotify-widget/spo-wid-custom.png +++ /dev/null diff --git a/spotify-widget/spo-wid-default.png b/spotify-widget/spo-wid-default.png Binary files differdeleted file mode 100644 index 67785f2..0000000 --- a/spotify-widget/spo-wid-default.png +++ /dev/null diff --git a/spotify-widget/spotify-widget-custom-paused.png b/spotify-widget/spotify-widget-custom-paused.png Binary files differnew file mode 100644 index 0000000..9ac9c4a --- /dev/null +++ b/spotify-widget/spotify-widget-custom-paused.png diff --git a/spotify-widget/spotify-widget-custom-playing.png b/spotify-widget/spotify-widget-custom-playing.png Binary files differnew file mode 100644 index 0000000..f9628f9 --- /dev/null +++ b/spotify-widget/spotify-widget-custom-playing.png diff --git a/spotify-widget/spotify.lua b/spotify-widget/spotify.lua index f8b2b62..eea9b0b 100644 --- a/spotify-widget/spotify.lua +++ b/spotify-widget/spotify.lua @@ -5,15 +5,22 @@ -- https://github.com/streetturtle/awesome-wm-widgets/tree/master/spotify-widget -- @author Pavel Makhov --- @copyright 2018 Pavel Makhov +-- @copyright 2020 Pavel Makhov ------------------------------------------------- local awful = require("awful") local wibox = require("wibox") local watch = require("awful.widget.watch") +local naughty = require("naughty") local GET_SPOTIFY_STATUS_CMD = 'sp status' -local GET_CURRENT_SONG_CMD = 'sp current-oneline' +local GET_CURRENT_SONG_CMD = 'sp current' + +local function ellipsize(text, length) + return (text:len() > length and length > 0) + and text:sub(0, length - 3) .. '...' + or text +end local spotify_widget = {} @@ -24,24 +31,53 @@ local function worker(args) local play_icon = args.play_icon or '/usr/share/icons/Arc/actions/24/player_play.png' local pause_icon = args.pause_icon or '/usr/share/icons/Arc/actions/24/player_pause.png' local font = args.font or 'Play 9' + local dim_when_paused = args.dim_when_paused == nil and false or args.dim_when_paused + local dim_opacity = args.dim_opacity or 0.2 + local max_length = args.max_length or 15 + local show_tooltip = args.show_tooltip == nil and false or args.show_tooltip + + local cur_artist = '' + local cur_title = '' + local cur_album = '' spotify_widget = wibox.widget { { + id = 'artistw', + font = font, + widget = wibox.widget.textbox, + }, + { id = "icon", widget = wibox.widget.imagebox, }, { - id = 'current_song', - widget = wibox.widget.textbox, - font = font + id = 'titlew', + font = font, + widget = wibox.widget.textbox }, layout = wibox.layout.align.horizontal, set_status = function(self, is_playing) self.icon.image = (is_playing and play_icon or pause_icon) + if dim_when_paused then + self.icon.opacity = (is_playing and 1 or dim_opacity) + + self.titlew:set_opacity(is_playing and 1 or dim_opacity) + self.titlew:emit_signal('widget::redraw_needed') + + self.artistw:set_opacity(is_playing and 1 or dim_opacity) + self.artistw:emit_signal('widget::redraw_needed') + end end, - set_text = function(self, path) - self.current_song.markup = path - end, + set_text = function(self, artist, song) + local artist_to_display = ellipsize(artist, max_length) + if self.artistw.text ~= artist_to_display then + self.artistw.text = artist_to_display + end + local title_to_display = ellipsize(song, max_length) + if self.titlew.text ~= title_to_display then + self.titlew.text = title_to_display + end + end } local update_widget_icon = function(widget, stdout, _, _, _) @@ -50,12 +86,22 @@ local function worker(args) end local update_widget_text = function(widget, stdout, _, _, _) - local escaped = string.gsub(stdout, "&", '&') if string.find(stdout, 'Error: Spotify is not running.') ~= nil then - widget:set_text('') + widget:set_text('','') widget:set_visible(false) - else - widget:set_text(escaped) + return + end + + local escaped = string.gsub(stdout, "&", '&') + local album, album_artist, artist, title = + string.match(escaped, 'Album%s*(.*)\nAlbumArtist%s*(.*)\nArtist%s*(.*)\nTitle%s*(.*)\n') + + if album ~= nil and title ~=nil and artist ~= nil then + cur_artist = artist + cur_title = title + cur_album = album + + widget:set_text(artist, title) widget:set_visible(true) end end @@ -80,6 +126,22 @@ local function worker(args) end) end) + + if show_tooltip then + local spotify_tooltip = awful.tooltip { + mode = 'outside', + preferred_positions = {'bottom'}, + } + + spotify_tooltip:add_to_object(spotify_widget) + + spotify_widget:connect_signal('mouse::enter', function() + spotify_tooltip.markup = '<b>Album</b>: ' .. cur_album + .. '\n<b>Artist</b>: ' .. cur_artist + .. '\n<b>Song</b>: ' .. cur_title + end) + end + return spotify_widget end |