From 3b1ebf63c180289ad4f6077c1322fe54cb24f63f Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Fri, 15 Aug 2014 22:15:51 +0400 Subject: [PATCH] Clean the config code. --- awesompd/README.md | 19 - awesompd/asyncshell.lua | 67 -- awesompd/awesompd.lua | 1140 ------------------------ awesompd/icons/check_icon.png | Bin 241 -> 0 bytes awesompd/icons/default_album_cover.png | Bin 18940 -> 0 bytes awesompd/icons/next_icon.png | Bin 242 -> 0 bytes awesompd/icons/pause_icon.png | Bin 210 -> 0 bytes awesompd/icons/play_icon.png | Bin 234 -> 0 bytes awesompd/icons/play_pause_icon.png | Bin 248 -> 0 bytes awesompd/icons/prev_icon.png | Bin 240 -> 0 bytes awesompd/icons/radio_icon.png | Bin 342 -> 0 bytes awesompd/icons/stop_icon.png | Bin 210 -> 0 bytes awesompd/jamendo.lua | 524 ----------- awesompd/rcsample.lua | 407 --------- awesompd/utf8.lua | 158 ---- 15 files changed, 2315 deletions(-) delete mode 100644 awesompd/README.md delete mode 100644 awesompd/asyncshell.lua delete mode 100644 awesompd/awesompd.lua delete mode 100644 awesompd/icons/check_icon.png delete mode 100644 awesompd/icons/default_album_cover.png delete mode 100644 awesompd/icons/next_icon.png delete mode 100644 awesompd/icons/pause_icon.png delete mode 100644 awesompd/icons/play_icon.png delete mode 100644 awesompd/icons/play_pause_icon.png delete mode 100644 awesompd/icons/prev_icon.png delete mode 100644 awesompd/icons/radio_icon.png delete mode 100644 awesompd/icons/stop_icon.png delete mode 100644 awesompd/jamendo.lua delete mode 100644 awesompd/rcsample.lua delete mode 100644 awesompd/utf8.lua diff --git a/awesompd/README.md b/awesompd/README.md deleted file mode 100644 index 739800b..0000000 --- a/awesompd/README.md +++ /dev/null @@ -1,19 +0,0 @@ -## Description ## - -This is an advanced MPD widget\client for AwesomeWM. - -For the detailed installation guide please see http://awesome.naquadah.org/wiki/Awesompd_widget . - -Also you can find an example of the widget configuration in the file rcsample.lua. - -## Version explanation ## - -Use this version with Awesome v3.4.x. If you are using the git pre-4.0 version of Awesome, please consider using [this](https://github.com/alexander-yakushev/awesompd/tree/for-awesome-git) version instead. - -### Changes in 1.1.0 ### - -* Album covers are now also shown for the local tracks (images are taken from the current track's folder) -* When the Jamendo track is playing you can visit artist's or album's page from the Jamendo menu -* Notification now shows the album name for the current track (for both local and Jamendo tracks) -* A few minor modifications and bugfixes - diff --git a/awesompd/asyncshell.lua b/awesompd/asyncshell.lua deleted file mode 100644 index 7510065..0000000 --- a/awesompd/asyncshell.lua +++ /dev/null @@ -1,67 +0,0 @@ --- Asynchronous io.popen for Awesome WM. --- How to use... --- ...asynchronously: --- asyncshell.request('wscript -Kiev', function(f) wwidget.text = f:read("*l") end) --- ...synchronously --- wwidget.text = asyncshell.demand('wscript -Kiev', 5):read("*l") or "Error" - -asyncshell = {} -asyncshell.request_table = {} -asyncshell.id_counter = 0 -asyncshell.folder = "/tmp/asyncshell" -asyncshell.file_template = asyncshell.folder .. '/req' - --- Create a directory for asynchell response files -os.execute("mkdir -p " .. asyncshell.folder) - --- Returns next tag - unique identifier of the request -local function next_id() - asyncshell.id_counter = (asyncshell.id_counter + 1) % 100000 - return asyncshell.id_counter -end - --- Sends an asynchronous request for an output of the shell command. --- @param command Command to be executed and taken output from --- @param callback Function to be called when the command finishes --- @return Request ID -function asyncshell.request(command, callback) - local id = next_id() - local tmpfname = asyncshell.file_template .. id - asyncshell.request_table[id] = {callback = callback} - local req = - string.format("bash -c '%s > %s; " .. - 'echo "asyncshell.deliver(%s)" | ' .. - "awesome-client' 2> /dev/null", - string.gsub(command, "'", "'\\''"), tmpfname, id, tmpfname) - awful.util.spawn(req) - return id -end - --- Calls the remembered callback function on the output of the shell --- command. --- @param id Request ID --- @param output The output file of the shell command to be delievered -function asyncshell.deliver(id) - if asyncshell.request_table[id] and - asyncshell.request_table[id].callback then - local output = io.open(asyncshell.file_template .. id, 'r') - asyncshell.request_table[id].callback(output) - end -end - --- Sends a synchronous request for an output of the command. Waits for --- the output, but if the given timeout expires returns nil. --- @param command Command to be executed and taken output from --- @param timeout Maximum amount of time to wait for the result --- @return File handler on success, nil otherwise -function asyncshell.demand(command, timeout) - local id = next_id() - local tmpfname = asyncshell.file_template .. id - local f = io.popen(string.format("(%s > %s; echo asyncshell_done) & " .. - "(sleep %s; echo asynchell_timeout)", - command, tmpfname, timeout)) - local result = f:read("*line") - if result == "asyncshell_done" then - return io.open(tmpfname) - end -end \ No newline at end of file diff --git a/awesompd/awesompd.lua b/awesompd/awesompd.lua deleted file mode 100644 index faf1253..0000000 --- a/awesompd/awesompd.lua +++ /dev/null @@ -1,1140 +0,0 @@ ---------------------------------------------------------------------------- --- @author Alexander Yakushev --- @copyright 2010-2011 Alexander Yakushev --- @release v1.1.5 ---------------------------------------------------------------------------- - -awesompd = {} - --- Function for checking icons and modules. Checks if a file exists, --- and if it does, returns the path to file, nil otherwise. -function awesompd.try_load(file) - if awful.util.file_readable(file) then - return file - end -end - --- Function for loading modules. -function awesompd.try_require(module) - if awesompd.try_load(awful.util.getdir("config") .. - "/awesompd/" .. module .. ".lua") then - return require('awesompd/' .. module) - else - return require(module) - end -end - -awesompd.try_require("utf8") -awesompd.try_require("asyncshell") -awesompd.try_require("jamendo") -local beautiful = require('beautiful') -local naughty = naughty -local awful = awful -local format = string.format -local keygrabber = keygrabber - --- Debug stuff - -local enable_dbg = true -local function dbg (...) - if enable_dbg then - print(...) - end -end - -local function tbl_pr(tbl,shift) - if enable_dbg then - local shift = shift or "" - for k, v in pairs(tbl) do - print(shift .. k .. ": " .. tostring(v)) - if type(v) == "table" then - tbl_pr(v, shift .. " ") - end - end - end -end - --- Constants -awesompd.PLAYING = "Playing" -awesompd.PAUSED = "Paused" -awesompd.STOPPED = "MPD stopped" -awesompd.DISCONNECTED = "Disconnected" - -awesompd.MOUSE_LEFT = 1 -awesompd.MOUSE_MIDDLE = 2 -awesompd.MOUSE_RIGHT = 3 -awesompd.MOUSE_SCROLL_UP = 4 -awesompd.MOUSE_SCROLL_DOWN = 5 - -awesompd.NOTIFY_VOLUME = 1 -awesompd.NOTIFY_REPEAT = 2 -awesompd.NOTIFY_RANDOM = 3 -awesompd.NOTIFY_SINGLE = 4 -awesompd.NOTIFY_CONSUME = 5 -awesompd.FORMAT_MP3 = jamendo.FORMAT_MP3 -awesompd.FORMAT_OGG = jamendo.FORMAT_OGG -awesompd.ESCAPE_SYMBOL_MAPPING = {} -awesompd.ESCAPE_SYMBOL_MAPPING["&"] = "&" --- Menus do not handle symbol escaping correctly, so they need their --- own mapping. -awesompd.ESCAPE_MENU_SYMBOL_MAPPING = {} -awesompd.ESCAPE_MENU_SYMBOL_MAPPING["&"] = "'n'" - --- /// Current track variables and functions /// - --- Returns a string for the given track to be displayed in the widget --- and notification. -function awesompd.get_display_name(track) - if track.display_name then - return track.display_name - elseif track.artist_name and track.track_name then - return track.artist_name .. " - " .. track.name - end -end - --- Returns a track display name, album name (if exists) and album --- release year (if exists). -function awesompd.get_extended_info(track) - local result = awesompd.get_display_name(track) - if track.album_name then - result = result .. "\n" .. track.album_name - end - if track.year then - result = result .. "\n" .. track.year - end - return result -end - --- Returns true if the current status is either PLAYING or PAUSED -function awesompd:playing_or_paused() - return self.status == awesompd.PLAYING - or self.status == awesompd.PAUSED -end - --- /// Helper functions /// - --- Just like awful.util.pread, but takes an argument how to read like --- "*line" or "*all". -function awesompd.pread(com, mode) - local f = io.popen(com, 'r') - local result = nil - if f then - result = f:read(mode) - f:close() - end - return result -end - --- Slightly modified function awful.util.table.join. -function awesompd.ajoin(buttons) - local result = {} - for i = 1, table.getn(buttons) do - if buttons[i] then - for k, v in pairs(buttons[i]) do - if type(k) == "number" then - table.insert(result, v) - else - result[k] = v - end - end - end - end - return result - end - --- Splits a given string with linebreaks into an array. -function awesompd.split(s) - local l = { n = 0 } - if s == "" then - return l - end - s = s .. "\n" - local f = function (s) - l.n = l.n + 1 - l[l.n] = s - end - local p = "%s*(.-)%s*\n%s*" - s = string.gsub(s,p,f) - return l -end - --- Icons - -function awesompd.load_icons(path) - awesompd.ICONS = {} - awesompd.ICONS.PLAY = awesompd.try_load(path .. "/play_icon.png") - awesompd.ICONS.PAUSE = awesompd.try_load(path .. "/pause_icon.png") - awesompd.ICONS.PLAY_PAUSE = awesompd.try_load(path .. "/play_pause_icon.png") - awesompd.ICONS.STOP = awesompd.try_load(path .. "/stop_icon.png") - awesompd.ICONS.NEXT = awesompd.try_load(path .. "/next_icon.png") - awesompd.ICONS.PREV = awesompd.try_load(path .. "/prev_icon.png") - awesompd.ICONS.CHECK = awesompd.try_load(path .. "/check_icon.png") - awesompd.ICONS.RADIO = awesompd.try_load(path .. "/radio_icon.png") - awesompd.ICONS.DEFAULT_ALBUM_COVER = - awesompd.try_load(path .. "/default_album_cover.png") -end - --- Function that returns a new awesompd object. -function awesompd:create() --- Initialization - local instance = {} - setmetatable(instance,self) - self.__index = self - instance.current_server = 1 - instance.widget = widget({ type = "textbox" }) - instance.notification = nil - instance.scroll_pos = 1 - instance.text = "" - instance.to_notify = false - instance.album_cover = nil - instance.current_track = { } - instance.recreate_menu = true - instance.recreate_playback = true - instance.recreate_list = true - instance.recreate_servers = true - instance.recreate_options = true - instance.recreate_jamendo_formats = true - instance.recreate_jamendo_order = true - instance.recreate_jamendo_browse = true - instance.current_number = 0 - instance.menu_shown = false - --- Default user options - instance.servers = { { server = "localhost", port = 6600 } } - instance.font = "Monospace" - instance.scrolling = true - instance.output_size = 30 - instance.update_interval = 10 - instance.path_to_icons = "" - instance.ldecorator = " " - instance.rdecorator = " " - instance.jamendo_format = awesompd.FORMAT_MP3 - instance.show_album_cover = true - instance.album_cover_size = 50 - instance.browser = "firefox" - --- Widget configuration - instance.widget:add_signal("mouse::enter", function(c) - instance:notify_track() - end) - instance.widget:add_signal("mouse::leave", function(c) - instance:remove_hint() - end) - return instance -end - --- Registers timers for the widget -function awesompd:run() - enable_dbg = self.debug_mode - self.load_icons(self.path_to_icons) - jamendo.set_current_format(self.jamendo_format) - if self.album_cover_size > 100 then - self.album_cover_size = 100 - end - - self:update_track() - self:check_playlists() - self.update_widget_timer = timer({ timeout = 1 }) - self.update_widget_timer:add_signal("timeout", function() - self:update_widget() - end) - self.update_widget_timer:start() - self.update_track_timer = timer({ timeout = self.update_interval }) - self.update_track_timer:add_signal("timeout", function() - self:update_track() - end) - self.update_track_timer:start() -end - --- Function that registers buttons on the widget. -function awesompd:register_buttons(buttons) - widget_buttons = {} - self.global_bindings = {} - for b=1,table.getn(buttons) do - if type(buttons[b][1]) == "string" then - mods = { buttons[b][1] } - else - mods = buttons[b][1] - end - if type(buttons[b][2]) == "number" then - -- This is a mousebinding, bind it to the widget - table.insert(widget_buttons, - awful.button(mods, buttons[b][2], buttons[b][3])) - else - -- This is a global keybinding, remember it for later usage in append_global_keys - table.insert(self.global_bindings, awful.key(mods, buttons[b][2], buttons[b][3])) - end - end - self.widget:buttons(self.ajoin(widget_buttons)) -end - --- Takes the current table with keybindings and adds widget's own --- global keybindings that were specified in register_buttons. --- If keytable is not specified, then adds bindings to default --- globalkeys table. If specified, then adds bindings to keytable and --- returns it. -function awesompd:append_global_keys(keytable) - if keytable then - for i = 1, table.getn(self.global_bindings) do - keytable = awful.util.table.join(keytable, self.global_bindings[i]) - end - return keytable - else - for i = 1, table.getn(self.global_bindings) do - globalkeys = awful.util.table.join(globalkeys, self.global_bindings[i]) - end - end -end - --- /// Group of mpc command functions /// - --- Takes a command to mpc and a hook that is provided with awesompd --- instance and the result of command execution. -function awesompd:command(com,hook) - local file = io.popen(self:mpcquery() .. com) - if hook then - hook(self,file) - end - file:close() -end - --- Takes a command to mpc and read mode and returns the result. -function awesompd:command_read(com, mode) - mode = mode or "*line" - self:command(com, function(_, f) - result = f:read(mode) - end) - return result -end - -function awesompd:command_playpause() - return function() - self:command("toggle",self.update_track) - end -end - -function awesompd:command_next_track() - return function() - self:command("next",self.update_track) - end -end - -function awesompd:command_prev_track() - return function() - self:command("seek 0") - self:command("prev",self.update_track) - end -end - -function awesompd:command_stop() - return function() - self:command("stop",self.update_track) - end -end - -function awesompd:command_play_specific(n) - return function() - self:command("play " .. n,self.update_track) - end -end - -function awesompd:command_volume_up() - return function() - self:command("volume +5") - self:update_track() -- Nasty! I should replace it with proper callback later. - self:notify_state(self.NOTIFY_VOLUME) - end -end - -function awesompd:command_volume_down() - return function() - self:command("volume -5") - self:update_track() - self:notify_state(self.NOTIFY_VOLUME) - end -end - -function awesompd:command_load_playlist(name) - return function() - self:command("load " .. name, function() - self.recreate_menu = true - end) - end -end - -function awesompd:command_replace_playlist(name) - return function() - self:command("clear") - self:command("load " .. name) - self:command("play 1", self.update_track) - end -end - -function awesompd:command_clear_playlist() - return function() - self:command("clear", self.update_track) - self.recreate_list = true - self.recreate_menu = true - end -end - -function awesompd:command_open_in_browser(link) - return function() - if self.browser then - awful.util.spawn(self.browser .. " '" .. link .. "'") - end - end -end - --- /// End of mpc command functions /// - --- /// Menu generation functions /// - -function awesompd:command_show_menu() - return - function() - self:remove_hint() - if self.recreate_menu then - local new_menu = {} - if self.main_menu ~= nil then - self.main_menu:hide() - end - if self.status ~= awesompd.DISCONNECTED - then - self:check_list() - self:check_playlists() - local jamendo_menu = { { "Search by", - { { "Nothing (Top 100)", self:menu_jamendo_top() }, - { "Artist", self:menu_jamendo_search_by(jamendo.SEARCH_ARTIST) }, - { "Album", self:menu_jamendo_search_by(jamendo.SEARCH_ALBUM) }, - { "Tag", self:menu_jamendo_search_by(jamendo.SEARCH_TAG) }}} } - local browse_menu = self:menu_jamendo_browse() - if browse_menu then - table.insert(jamendo_menu, browse_menu) - end - table.insert(jamendo_menu, self:menu_jamendo_format()) - table.insert(jamendo_menu, self:menu_jamendo_order()) - - new_menu = { { "Playback", self:menu_playback() }, - { "Options", self:menu_options() }, - { "List", self:menu_list() }, - { "Playlists", self:menu_playlists() }, - { "Jamendo", jamendo_menu } } - end - table.insert(new_menu, { "Servers", self:menu_servers() }) - self.main_menu = awful.menu({ items = new_menu, width = 300 }) - self.recreate_menu = false - end - self.main_menu:toggle() - end -end - --- Returns an icon for a checkbox menu item if it is checked, nil --- otherwise. -function awesompd:menu_item_toggle(checked) - return checked and self.ICONS.CHECK or nil -end - --- Returns an icon for a radiobox menu item if it is selected, nil --- otherwise. -function awesompd:menu_item_radio(selected) - return selected and self.ICONS.RADIO or nil -end - --- Returns the playback menu. Menu contains of: --- Play\Pause - always --- Previous - if the current track is not the first --- in the list and playback is not stopped --- Next - if the current track is not the last --- in the list and playback is not stopped --- Stop - if the playback is not stopped --- Clear playlist - always -function awesompd:menu_playback() - if self.recreate_playback then - local new_menu = {} - table.insert(new_menu, { "Play\\Pause", - self:command_toggle(), - self.ICONS.PLAY_PAUSE }) - if self:playing_or_paused() then - if self.list_array and self.list_array[self.current_number-1] then - table.insert(new_menu, - { "Prev: " .. - awesompd.protect_string(jamendo.replace_link( - self.list_array[self.current_number - 1]), - true), - self:command_prev_track(), self.ICONS.PREV }) - end - if self.list_array and self.current_number ~= table.getn(self.list_array) then - table.insert(new_menu, - { "Next: " .. - awesompd.protect_string(jamendo.replace_link( - self.list_array[self.current_number + 1]), - true), - self:command_next_track(), self.ICONS.NEXT }) - end - table.insert(new_menu, { "Stop", self:command_stop(), self.ICONS.STOP }) - table.insert(new_menu, { "", nil }) - end - table.insert(new_menu, { "Clear playlist", self:command_clear_playlist() }) - self.recreate_playback = false - playback_menu = new_menu - end - return playback_menu -end - --- Returns the current playlist menu. Menu consists of all elements in the playlist. -function awesompd:menu_list() - if self.recreate_list then - local new_menu = {} - if self.list_array then - local total_count = table.getn(self.list_array) - local start_num = (self.current_number - 15 > 0) and self.current_number - 15 or 1 - local end_num = (self.current_number + 15 < total_count ) and self.current_number + 15 or total_count - for i = start_num, end_num do - table.insert(new_menu, { jamendo.replace_link(self.list_array[i]), - self:command_play_specific(i), - self.current_number == i and - (self.status == self.PLAYING and self.ICONS.PLAY or self.ICONS.PAUSE) - or nil} ) - end - end - self.recreate_list = false - self.list_menu = new_menu - end - return self.list_menu -end - --- Returns the playlists menu. Menu consists of all files in the playlist folder. -function awesompd:menu_playlists() - if self.recreate_playlists then - local new_menu = {} - if table.getn(self.playlists_array) > 0 then - for i = 1, table.getn(self.playlists_array) do - local submenu = {} - submenu[1] = { "Add to current", self:command_load_playlist(self.playlists_array[i]) } - submenu[2] = { "Replace current", self:command_replace_playlist(self.playlists_array[i]) } - new_menu[i] = { self.playlists_array[i], submenu } - end - table.insert(new_menu, {"", ""}) -- This is a separator - end - table.insert(new_menu, { "Refresh", function() self:check_playlists() end }) - self.recreate_playlists = false - self.playlists_menu = new_menu - end - return self.playlists_menu -end - --- Returns the server menu. Menu consists of all servers specified by user during initialization. -function awesompd:menu_servers() - if self.recreate_servers then - local new_menu = {} - for i = 1, table.getn(self.servers) do - table.insert(new_menu, {"Server: " .. self.servers[i].server .. - ", port: " .. self.servers[i].port, - function() self:change_server(i) end, - self:menu_item_radio(i == self.current_server)}) - end - self.servers_menu = new_menu - end - return self.servers_menu -end - --- Returns the options menu. Menu works like checkboxes for it's elements. -function awesompd:menu_options() - if self.recreate_options then - local new_menu = { { "Repeat", self:menu_toggle_repeat(), - self:menu_item_toggle(self.state_repeat == "on")}, - { "Random", self:menu_toggle_random(), - self:menu_item_toggle(self.state_random == "on")}, - { "Single", self:menu_toggle_single(), - self:menu_item_toggle(self.state_single == "on")}, - { "Consume", self:menu_toggle_consume(), - self:menu_item_toggle(self.state_consume == "on")} } - self.options_menu = new_menu - self.recreate_options = false - end - return self.options_menu -end - -function awesompd:menu_toggle_random() - return function() - self:command("random",self.update_track) - self:notify_state(self.NOTIFY_RANDOM) - end -end - -function awesompd:menu_toggle_repeat() - return function() - self:command("repeat",self.update_track) - self:notify_state(self.NOTIFY_REPEAT) - end -end - -function awesompd:menu_toggle_single() - return function() - self:command("single",self.update_track) - self:notify_state(self.NOTIFY_SINGLE) - end -end - -function awesompd:menu_toggle_consume() - return function() - self:command("consume",self.update_track) - self:notify_state(self.NOTIFY_CONSUME) - end -end - -function awesompd:menu_jamendo_top() - return - function () - local track_table = jamendo.return_track_table() - if not track_table then - self:add_hint("Can't connect to Jamendo server", "Please check your network connection") - else - self:add_jamendo_tracks(track_table) - self:add_hint("Jamendo Top 100 by " .. - jamendo.current_request_table.params.order.short_display, - format("Added %s tracks to the playlist", - table.getn(track_table))) - end - end -end - -function awesompd:menu_jamendo_format() - if self.recreate_jamendo_formats then - local setformat = - function(format) - return function() - jamendo.set_current_format(format) - self.recreate_menu = true - self.recreate_jamendo_formats = true - end - end - - local iscurr = - function(f) - return jamendo.current_request_table.params.streamencoding.value - == f.value - end - - local new_menu = {} - for _, format in pairs(jamendo.ALL_FORMATS) do - table.insert(new_menu, { format.display, setformat(format), - self:menu_item_radio(iscurr(format))}) - end - self.recreate_jamendo_formats = false - self.jamendo_formats_menu = { - "Format: " .. - jamendo.current_request_table.params.streamencoding.short_display, - new_menu } - end - return self.jamendo_formats_menu -end - -function awesompd:menu_jamendo_browse() - if self.recreate_jamendo_browse and self.browser - and self.current_track.unique_name then - local track = jamendo.get_track_by_link(self.current_track.unique_name) - local new_menu - if track then - local artist_link = - "http://www.jamendo.com/artist/" .. track.artist_link_name - local album_link = - "http://www.jamendo.com/album/" .. track.album_id - new_menu = { { "Artist's page" , - self:command_open_in_browser(artist_link) }, - { "Album's page" , - self:command_open_in_browser(album_link) } } - self.jamendo_browse_menu = { "Browse on Jamendo", new_menu } - else - self.jamendo_browse_menu = nil - end - end - return self.jamendo_browse_menu -end - -function awesompd:menu_jamendo_order() - if self.recreate_jamendo_order then - local setorder = - function(order) - return function() - jamendo.set_current_order(order) - self.recreate_menu = true - self.recreate_jamendo_order = true - end - end - - local iscurr = - function(o) - return jamendo.current_request_table.params.order.value - == o.value - end - - local new_menu = {} - for _, order in pairs(jamendo.ALL_ORDERS) do - table.insert(new_menu, { order.display, setorder(order), - self:menu_item_radio(iscurr(order))}) - end - self.recreate_jamendo_order = false - self.jamendo_order_menu = { - "Order: " .. - jamendo.current_request_table.params.order.short_display, - new_menu } - end - return self.jamendo_order_menu -end - -function awesompd:menu_jamendo_search_by(what) - return function() - local callback = - function(s) - local result = jamendo.search_by(what, s) - if result then - local track_count = table.getn(result.tracks) - self:add_jamendo_tracks(result.tracks) - self:add_hint(format("%s \"%s\" was found", - what.display, - result.search_res.name), - format("Added %s tracks to the playlist", - track_count)) - else - self:add_hint("Search failed", - format("%s \"%s\" was not found", - what.display, s)) - end - end - self:display_inputbox("Search music on Jamendo", - what.display, callback) - end -end - --- Checks if the current playlist has changed after the last check. -function awesompd:check_list() - local bus = io.popen(self:mpcquery() .. "playlist") - local info = bus:read("*all") - bus:close() - if info ~= self.list_line then - self.list_line = info - if string.len(info) > 0 then - self.list_array = self.split(string.sub(info,1,string.len(info))) - else - self.list_array = {} - end - self.recreate_menu = true - self.recreate_list = true - end -end - --- Checks if the collection of playlists changed after the last check. -function awesompd:check_playlists() - local bus = io.popen(self:mpcquery() .. "lsplaylists") - local info = bus:read("*all") - bus:close() - if info ~= self.playlists_line then - self.playlists_line = info - if string.len(info) > 0 then - self.playlists_array = self.split(info) - else - self.playlists_array = {} - end - self.recreate_menu = true - self.recreate_playlists = true - end -end - --- Changes the current server to the specified one. -function awesompd:change_server(server_number) - self.current_server = server_number - self:remove_hint() - self.recreate_menu = true - self.recreate_playback = true - self.recreate_list = true - self.recreate_playlists = true - self.recreate_servers = true - self:update_track() -end - -function awesompd:add_jamendo_tracks(track_table) - for i = 1,table.getn(track_table) do - self:command("add '" .. string.gsub(track_table[i].stream, '\\/', '/') .. "'") - end - self.recreate_menu = true - self.recreate_list = true -end - --- /// End of menu generation functions /// - -function awesompd:add_hint(hint_title, hint_text, hint_image) - self:remove_hint() - self.notification = naughty.notify({ title = hint_title - , text = awesompd.protect_string(hint_text) - , timeout = 5 - , position = "top_right" - , icon = hint_image - , icon_size = self.album_cover_size - }) -end - -function awesompd:remove_hint() - if self.notification ~= nil then - naughty.destroy(self.notification) - self.notification = nil - end -end - -function awesompd:notify_track() - if self:playing_or_paused() then - local caption = self.status_text - local nf_text = self.get_display_name(self.current_track) - local al_cover = nil - if self.show_album_cover then - nf_text = self.get_extended_info(self.current_track) - al_cover = self.current_track.album_cover - end - self:add_hint(caption, nf_text, al_cover) - end -end - -function awesompd:notify_state(state_changed) - state_array = { "Volume: " .. self.state_volume , - "Repeat: " .. self.state_repeat , - "Random: " .. self.state_random , - "Single: " .. self.state_single , - "Consume: " .. self.state_consume } - state_header = state_array[state_changed] - table.remove(state_array,state_changed) - full_state = state_array[1] - for i = 2, table.getn(state_array) do - full_state = full_state .. "\n" .. state_array[i] - end - self:add_hint(state_header, full_state) -end - -function awesompd:wrap_output(text) - return format('%s%s%s', - self.font, self.ldecorator, - awesompd.protect_string(text), self.rdecorator) -end - -function awesompd:mpcquery() - return "mpc -h " .. self.servers[self.current_server].server .. - " -p " .. self.servers[self.current_server].port .. " " -end - --- This function actually sets the text on the widget. -function awesompd:set_text(text) - self.widget.text = self:wrap_output(text) -end - -function awesompd.find_pattern(text, pattern, start) - return utf8sub(text, string.find(text, pattern, start)) -end - --- Scroll the given text by the current number of symbols. -function awesompd:scroll_text(text) - local result = text - if self.scrolling then - if self.output_size < utf8len(text) then - text = text .. " - " - if self.scroll_pos + self.output_size - 1 > utf8len(text) then - result = utf8sub(text, self.scroll_pos) - result = result .. utf8sub(text, 1, self.scroll_pos + self.output_size - 1 - utf8len(text)) - self.scroll_pos = self.scroll_pos + 1 - if self.scroll_pos > utf8len(text) then - self.scroll_pos = 1 - end - else - result = utf8sub(text, self.scroll_pos, self.scroll_pos + self.output_size - 1) - self.scroll_pos = self.scroll_pos + 1 - end - end - else - if self.output_size < utf8len(text) then - result = utf8sub(text, 1, self.output_size) - end - end - return result -end - --- This function is called every second. -function awesompd:update_widget() - self:set_text(self:scroll_text(self.text)) - self:check_notify() -end - --- This function is called by update_track each time content of --- the widget must be changed. -function awesompd:update_widget_text() - if self:playing_or_paused() then - self.text = self.get_display_name(self.current_track) - else - self.text = self.status - end -end - --- Checks if notification should be shown and shows if positive. -function awesompd:check_notify() - if self.to_notify then - self:notify_track() - self.to_notify = false - end -end - -function awesompd:notify_connect() - self:add_hint("Connected", "Connection established to " .. self.servers[self.current_server].server .. - " on port " .. self.servers[self.current_server].port) -end - -function awesompd:notify_disconnect() - self:add_hint("Disconnected", "Cannot connect to " .. self.servers[self.current_server].server .. - " on port " .. self.servers[self.current_server].port) -end - -function awesompd:update_track(file) - local file_exists = (file ~= nil) - if not file_exists then - file = io.popen(self:mpcquery()) - end - local track_line = file:read("*line") - local status_line = file:read("*line") - local options_line = file:read("*line") - if not file_exists then - file:close() - end - - if not track_line or string.len(track_line) == 0 then - if self.status ~= awesompd.DISCONNECTED then - self:notify_disconnect() - self.recreate_menu = true - self.status = awesompd.DISCONNECTED - self.current_track = { } - self:update_widget_text() - end - else - if self.status == awesompd.DISCONNECTED then - self:notify_connect() - self.recreate_menu = true - self:update_widget_text() - end - if string.find(track_line,"volume:") or string.find(track_line,"Updating DB") then - if self.status ~= awesompd.STOPPED then - self.status = awesompd.STOPPED - self.current_number = 0 - self.recreate_menu = true - self.recreate_playback = true - self.recreate_list = true - self.album_cover = nil - self.current_track = { } - self:update_widget_text() - end - self:update_state(track_line) - else - self:update_state(options_line) - local _, _, new_file, new_album = - string.find(self:command_read('current -f "%file%-<>-%album%"', "*line"), "(.+)%-<>%-(.*)") - if new_file ~= self.current_track.unique_name then - self.current_track = jamendo.get_track_by_link(new_file) - if not self.current_track then - self.current_track = { display_name = track_line, - album_name = new_album } - end - self.current_track.unique_name = new_file - if self.show_album_cover then - self.current_track.album_cover = self:get_cover(new_file) - end - self.to_notify = true - self.recreate_menu = true - self.recreate_playback = true - self.recreate_list = true - self.current_number = tonumber(self.find_pattern(status_line,"%d+")) - self:update_widget_text() - - -- If the track is not the last, asynchronously download - -- the cover for the next track. - if self.list_array and self.current_number ~= table.getn(self.list_array) then - -- Get the link (in case it is Jamendo stream) to the next track - local next_track = - self:command_read('playlist -f "%file%" | head -' .. - self.current_number + 1 .. ' | tail -1', "*line") - jamendo.try_get_cover_async(next_track) - end - end - local tmp_pst = string.find(status_line,"%d+%:%d+%/") - local progress = self.find_pattern(status_line,"%#%d+/%d+") .. " " .. string.sub(status_line,tmp_pst) - local new_status = awesompd.PLAYING - if string.find(status_line,"paused") then - new_status = awesompd.PAUSED - end - if new_status ~= self.status then - self.to_notify = true - self.recreate_list = true - self.status = new_status - self:update_widget_text() - end - self.status_text = self.status .. " " .. progress - end - end -end - -function awesompd:update_state(state_string) - self.state_volume = self.find_pattern(state_string,"%d+%% ") - if string.find(state_string,"repeat: on") then - self.state_repeat = self:check_set_state(self.state_repeat, "on") - else - self.state_repeat = self:check_set_state(self.state_repeat, "off") - end - if string.find(state_string,"random: on") then - self.state_random = self:check_set_state(self.state_random, "on") - else - self.state_random = self:check_set_state(self.state_random, "off") - end - if string.find(state_string,"single: on") then - self.state_single = self:check_set_state(self.state_single, "on") - else - self.state_single = self:check_set_state(self.state_single, "off") - end - if string.find(state_string,"consume: on") then - self.state_consume = self:check_set_state(self.state_consume, "on") - else - self.state_consume = self:check_set_state(self.state_consume, "off") - end -end - -function awesompd:check_set_state(statevar, val) - if statevar ~= val then - self.recreate_menu = true - self.recreate_options = true - end - return val -end - -function awesompd:run_prompt(welcome,hook) - awful.prompt.run({ prompt = welcome }, - self.promptbox[mouse.screen].widget, - hook) -end - --- Replaces control characters with escaped ones. --- for_menu - defines if the special escable table for menus should be --- used. -function awesompd.protect_string(str, for_menu) - if for_menu then - return utf8replace(str, awesompd.ESCAPE_MENU_SYMBOL_MAPPING) - else - return utf8replace(str, awesompd.ESCAPE_SYMBOL_MAPPING) - end -end - --- Displays an inputbox on the screen (looks like naughty with prompt). --- title_text - bold text on the first line --- prompt_text - preceding text on the second line --- hook - function that will be called with input data --- Use it like this: --- self:display_inputbox("Search music on Jamendo", "Artist", print) -function awesompd:display_inputbox(title_text, prompt_text, hook) - if self.inputbox then -- Inputbox already exists, replace it - keygrabber.stop() - self.inputbox.screen = nil - self.inputbox = nil - end - local width = 200 - local height = 30 - local border_color = beautiful.bg_focus or '#535d6c' - local margin = 5 - local wbox = wibox({ name = "awmpd_ibox", height = height , width = width, - border_color = border_color, border_width = 1 }) - self.inputbox = wbox - local ws = screen[mouse.screen].workarea - - wbox:geometry({ x = ws.width - width - 5, y = 25 }) - wbox.screen = mouse.screen - wbox.ontop = true - - local exe_callback = function(s) - hook(s) - wbox.screen = nil - self.inputbox = nil - end - local done_callback = function() - wbox.screen = nil - self.inputbox = nil - end - local wprompt = awful.widget.prompt({ layout = awful.widget.layout.horizontal.leftright }) - local wtbox = widget({ type = "textbox" }) - wtbox:margin({ right = margin, left = margin, bottom = margin, top = margin }) - wtbox.text = "" .. title_text .. "" - wbox.widgets = { wtbox, wprompt, layout = awful.widget.layout.vertical.flex } - awful.prompt.run( { prompt = " " .. prompt_text .. ": " }, wprompt.widget, - exe_callback, nil, nil, nil, done_callback) -end - --- Gets the cover for the given track. First looks in the Jamendo --- cache. If the track is not a Jamendo stream, looks in local --- folders. If there is no cover art either returns the default album --- cover. -function awesompd:get_cover(track) - return jamendo.try_get_cover(track) or - self:try_get_local_cover() or self.ICONS.DEFAULT_ALBUM_COVER -end - --- Tries to find an album cover for the track that is currently --- playing. -function awesompd:try_get_local_cover() - if self.mpd_config then - local result - -- First find the music directory in MPD configuration file - local _, _, music_folder = string.find( - self.pread('cat ' .. self.mpd_config .. ' | grep -v "#" | grep music_directory', "*line"), - 'music_directory%s+"(.+)"') - music_folder = music_folder .. "/" - - -- If the music_folder is specified with ~ at the beginning, - -- replace it with user home directory - if string.sub(music_folder, 1, 1) == "~" then - local user_folder = self.pread("echo ~", "*line") - music_folder = user_folder .. string.sub(music_folder, 2) - end - - -- Get the path to the file currently playing. - local current_file = self:command_read('current -f "%file%"') - local _, _, current_file_folder = string.find(current_file, '(.+%/).*') - - -- Check if the current file is not some kind of http stream or - -- Spotify track (like spotify:track:5r65GeuIoebfJB5sLcuPoC) - if not current_file_folder or string.match(current_file, "%w+://") then - return -- Let the default image to be the cover - end - - local folder = music_folder .. current_file_folder - - -- Get all images in the folder. Also escape occasional single - -- quotes in folder name. - local request = format("ls '%s' | grep -P '\.jpg\|\.png\|\.gif|\.jpeg'", - string.gsub(folder, "'", "'\\''")) - - local covers = self.pread(request, "*all") - local covers_table = self.split(covers) - - if covers_table.n > 0 then - result = folder .. covers_table[1] - if covers_table.n > 1 then - -- Searching for front cover with grep because Lua regular - -- expressions suck:[ - local front_cover = - self.pread('echo "' .. covers .. - '" | grep -i "cover\|front\|folder\|albumart" | head -n 1', "*line") - if front_cover then - result = folder .. front_cover - end - end - end - return result - end -end - --- /// Deprecated, left for some backward compatibility in --- configuration /// - -function awesompd:command_toggle() - return self:command_playpause() -end diff --git a/awesompd/icons/check_icon.png b/awesompd/icons/check_icon.png deleted file mode 100644 index dc5cba1287277f859cece13ee824cff04505493c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBh@j2|->Fp+6J-fkKict`Q~9`MJ5N zc_j?aMX8A;sVNHOnI#zt?w-B@;f;LaKt(>DE{-7|)Wq;QBO>@b3z`km+8Rs&h4)nzUcX~};;`b{3&)VX%&B)4i;3(^&${ye bya{8{KVI2gj>ldA&1LX(^>bP0l+XkK2sudM diff --git a/awesompd/icons/default_album_cover.png b/awesompd/icons/default_album_cover.png deleted file mode 100644 index b952ba4c1b323889c9a7ad2f0da2106159df7b0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18940 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 diff --git a/awesompd/icons/play_icon.png b/awesompd/icons/play_icon.png deleted file mode 100644 index 83230d13214f288943c4de91d96d8b74a64251ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBh@j2|-S?#UDi11BE0@Tq8=H^K)}k z^GX<;i&7IyQd1PlGfOfQ+&z5*!W;R-fr?x`T^vI!PAC8Q|KFZjwV|`o$)S|_pR?gn zJ%w8ePH_qu0vi(>E-M5v3m#VJN^Wd*`P%bD>VU^OZi$m$nUr@X9@!W8oMb$J3(S$PA gs7>V_fBw&8`!-it+JEQ0KOj3jUHx3vIVCg!09CF_#Q*>R diff --git a/awesompd/icons/prev_icon.png b/awesompd/icons/prev_icon.png deleted file mode 100644 index 8e1638154d8920e2ae45ab1205c8a4ce620aad79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBkFONggKMwe^kJKq1-8kcblJ{M_8s zyb>Unfx)>bHL)Z$MWH;iBtya7(>EYRFO?lA?(XU07-Dfc`Op9V_ROjcosCWoq0IlB z4XqAnyPaTV;Z0fOVr<9U@s%r#x#OD8=8P1TAfENy9cC=^#XF2z0we@XS_)sJt1;B= V6%q|+=Gh6d%hT1*Wt~$(697J!Kd}G+ diff --git a/awesompd/icons/radio_icon.png b/awesompd/icons/radio_icon.png deleted file mode 100644 index ffdf065b18858f0544eba1abcf47f9f9723cd9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBh@j2|+Pw?NYD~$r9Iy66gHf+|;}h z2Ir#G#FEq$h4Rdj3D4{CS0 zm#Q37; z`#pcjcWX5L63c{N^B<3x&NE?4$rfkFEL&z+*guix)i Zu$#+N94XdKsRcTo!PC{xWt~$(699`AapV91 diff --git a/awesompd/icons/stop_icon.png b/awesompd/icons/stop_icon.png deleted file mode 100644 index 6bef770ae510991bee43a7c9095ba3259017d5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBh@j2|i90cjJatKq1Kz*N775{M_8s zyb=cIqSVBa)D(sC%#sWRcTeAd@J2pypdwvQ7sn8b)5(AS|F>sWZRl)ta@feIlTowi u&*2j%PH=qwb)Y)&N<(5I!{ZC*eHauzb4hE=4%`LQ$l&Sf=d#Wzp$Pyv^gOEo diff --git a/awesompd/jamendo.lua b/awesompd/jamendo.lua deleted file mode 100644 index 9fa0428..0000000 --- a/awesompd/jamendo.lua +++ /dev/null @@ -1,524 +0,0 @@ ---------------------------------------------------------------------------- --- @author Alexander Yakushev --- @copyright 2011 Alexander Yakushev --- @release v1.1.5 ---------------------------------------------------------------------------- - --- Grab environment -local os = os -local awful = awful -local string = string -local table = table -local io = io -local pairs = pairs -local type = type -local assert = assert -local print = print -local tonumber = tonumber -local math = math -local tostring = tostring -local asyncshell = asyncshell - -module('jamendo') - --- UTILITY STUFF --- Checks whether file specified by filename exists. -local function file_exists(filename, mode) - mode = mode or 'r' - f = io.open(filename, mode) - if f then - f:close() - return true - else - return false - end -end - --- Global variables -FORMAT_MP3 = { display = "MP3 (128k)", - short_display = "MP3", - value = "mp31" } -FORMAT_OGG = { display = "Ogg Vorbis (q4)", - short_display = "Ogg", - value = "ogg2" } -ORDER_RATINGDAILY = { display = "Daily rating", - short_display = "daily rating", - value = "ratingday_desc" } -ORDER_RATINGWEEKLY = { display = "Weekly rating", - short_display = "weekly rating", - value = "ratingweek_desc" } -ORDER_RATINGTOTAL = { display = "All time rating", - short_display = "all time rating", - value = "ratingtotal_desc" } -ORDER_RANDOM = { display = "Random", - short_display = "random", - value = "random_desc" } -ORDER_RELEVANCE = { display = "None (consecutive)", - short_display = "none", - value = "searchweight_desc" } -SEARCH_ARTIST = { display = "Artist", - unit = "artist", - value = "artist_id" } -SEARCH_ALBUM = { display = "Album", - unit = "album", - value = "album_id" } -SEARCH_TAG = { display = "Tag", - unit = "tag", - value = "tag_id" } -ALL_FORMATS = { FORMAT_MP3, FORMAT_OGG } -ALL_ORDERS = { ORDER_RELEVANCE, ORDER_RANDOM, ORDER_RATINGDAILY, - ORDER_RATINGWEEKLY, ORDER_RATINGTOTAL } - -current_request_table = { unit = "track", - fields = {"id", "artist_url", "artist_name", "name", - "stream", "album_image", "album_name" }, - joins = { "track_album", "album_artist" }, - params = { streamencoding = FORMAT_MP3, - order = ORDER_RATINGWEEKLY, - n = 100 }} - --- Local variables -local jamendo_list = {} -local cache_file = awful.util.getdir ("cache").."/jamendo_cache" -local cache_header = "[version=1.1.0]" -local album_covers_folder = awful.util.getdir("cache") .. "/jamendo_covers/" -local default_mp3_stream = nil -local search_template = { fields = { "id", "name" }, - joins = {}, - params = { order = ORDER_RELEVANCE, - n = 1}} - --- DEPRECATED. Will be removed in the next major release. --- Returns default stream number for MP3 format. Requests API for it --- not more often than every hour. -local function get_default_mp3_stream() - if not default_mp3_stream or - (os.time() - default_mp3_stream.last_checked) > 3600 then - local trygetlink = - perform_request("echo $(curl -w %{redirect_url} " .. - "'http://api.jamendo.com/get2/stream/track/redirect/" .. - "?streamencoding="..FORMAT_MP3.value.."&id=729304')") - local _, _, prefix = string.find(trygetlink,"stream(%d+)\.jamendo\.com") - default_mp3_stream = { id = prefix, last_checked = os.time() } - end - return default_mp3_stream.id -end - --- Returns the track ID from the given link to Jamendo stream. If the --- given text is not the Jamendo stream returns nil. -function get_id_from_link(link) - local _, _, id = string.find(link,"storage%-new.newjamendo.com%?trackid=(%d+)") - return id -end - --- Returns link to music stream for the given track ID. Uses MP3 --- format and the default stream for it. -local function get_link_by_id(id) - -- This function is subject to change in the future. - return string.format("http://storage-new.newjamendo.com?trackid=%s&format=mp31&u=0", id) -end - --- -- Returns the album id for given music stream. --- function get_album_id_by_link(link) --- local id = get_id_from_link(link, true) --- if id and jamendo_list[id] then --- return jamendo_list[id].album_id --- end --- end - --- Returns the track table for the given music stream. -function get_track_by_link(link) - local id = get_id_from_link(link, true) - if id and jamendo_list[id] then - return jamendo_list[id] - end -end - --- If a track is actually a Jamendo stream, replace it with normal --- track name. -function replace_link(track_name) - local track = get_track_by_link(track_name) - if track then - return track.display_name - else - return track_name - end -end - --- Returns table of track IDs, names and other things based on the --- request table. -function return_track_table(request_table) - local req_string = form_request(request_table) - local response = perform_request(req_string) - if not response then - return nil -- Bad internet connection - end - parse_table = parse_json(response) - for i = 1, table.getn(parse_table) do - if parse_table[i].stream == "" then - -- Some songs don't have Ogg stream, use MP3 instead - parse_table[i].stream = get_link_by_id(parse_table[i].id) - end - _, _, parse_table[i].artist_link_name = - string.find(parse_table[i].artist_url, "\\/artist\\/(.+)") - -- Remove Jamendo escape slashes - parse_table[i].artist_name = - string.gsub(parse_table[i].artist_name, "\\/", "/") - parse_table[i].name = string.gsub(parse_table[i].name, "\\/", "/") - - parse_table[i].display_name = - parse_table[i].artist_name .. " - " .. parse_table[i].name - -- Do Jamendo a favor, extract album_id for the track yourself - -- from album_image link :) - local _, _, album_id = - string.find(parse_table[i].album_image, "\\/(%d+)\\/covers") - parse_table[i].album_id = album_id or 0 - -- Save fetched tracks for further caching - jamendo_list[parse_table[i].id] = parse_table[i] - end - save_cache() - return parse_table -end - --- Generates the request to Jamendo API based on provided request --- table. If request_table is nil, uses current_request_table instead. --- For all values that do not exist in request_table use ones from --- current_request_table. --- return - HTTP-request -function form_request(request_table) - local curl_str = "curl -A 'Mozilla/4.0' -fsm 5 \"%s\"" - local url = "http://api.jamendo.com/en/?m=get2%s%s" - request_table = request_table or current_request_table - - local fields = request_table.fields or current_request_table.fields - local joins = request_table.joins or current_request_table.joins - local unit = request_table.unit or current_request_table.unit - - -- Form field&joins string (like field1+field2+fieldN%2Fjoin+) - local fnj_string = "&m_params=" - for i = 1, table.getn(fields) do - fnj_string = fnj_string .. fields[i] .. "+" - end - fnj_string = string.sub(fnj_string,1,string.len(fnj_string)-1) - - fnj_string = fnj_string .. "%2F" .. unit .. "%2Fjson%2F" - for i = 1, table.getn(joins) do - fnj_string = fnj_string .. joins[i] .. "+" - end - fnj_string = fnj_string .. "%2F" - - local params = {} - -- If parameters where supplied in request_table, add them to the - -- parameters in current_request_table. - if request_table.params and - request_table.params ~= current_request_table.params then - -- First fill params with current_request_table parameters - for k, v in pairs(current_request_table.params) do - params[k] = v - end - -- Then add and overwrite them with request_table parameters - for k, v in pairs(request_table.params) do - params[k] = v - end - else -- Or just use current_request_table.params - params = current_request_table.params - end - -- Form parameter string (like param1=value1¶m2=value2) - local param_string = "" - for k, v in pairs(params) do - if type(v) == "table" then - v = v.value - end - v = string.gsub(v, " ", "+") - param_string = param_string .. "&" .. k .. "=" .. v - end - - return string.format(curl_str, string.format(url, fnj_string, param_string)) -end - --- Primitive function for parsing Jamendo API JSON response. Does not --- support arrays. Supports only strings and numbers as values. --- Provides basic safety (correctly handles special symbols like comma --- and curly brackets inside strings) --- text - JSON text -function parse_json(text) - local parse_table = {} - local block = {} - local i = 0 - local inblock = false - local instring = false - local curr_key = nil - local curr_val = nil - while i and i < string.len(text) do - if not inblock then -- We are not inside the block, find next { - i = string.find(text, "{", i+1) - inblock = true - block = {} - else - if not curr_key then -- We haven't found key yet - if not instring then -- We are not in string, check for more tags - local j = string.find(text, '"', i+1) - local k = string.find(text, '}', i+1) - if j and j < k then -- There are more tags in this block - i = j - instring = true - else -- Block is over, we found its ending - i = k - inblock = false - table.insert(parse_table, block) - end - else -- We are in string, find its ending - _, i, curr_key = string.find(text,'(.-[^%\\])"', i+1) - instring = false - end - else -- We have the key, let's find the value - if not curr_val then -- Value is not found yet - if not instring then -- Not in string, check if value is string - local j = string.find(text, '"', i+1) - local k = string.find(text, '[,}]', i+1) - if j and j < k then -- Value is string - i = j - instring = true - else -- Value is int - _, i, curr_val = string.find(text,'(%d+)', i+1) - end - else -- We are in string, find its ending - local j = string.find(text, '"', i+1) - if j == i+1 then -- String is empty - i = j - curr_val = "" - else - _, i, curr_val = string.find(text,'(.-[^%\\])"', i+1) - curr_val = utf8_codes_to_symbols(curr_val) - end - instring = false - end - else -- We have both key and value, add it to table - block[curr_key] = curr_val - curr_key = nil - curr_val = nil - end - end - end - end - return parse_table -end - --- Jamendo returns Unicode symbols as \uXXXX. Lua does not transform --- them into symbols so we need to do it ourselves. -function utf8_codes_to_symbols (s) - local hexnums = "[%dabcdefABCDEF]" - local pattern = string.format("\\u(%s%s%s%s?)", - hexnums, hexnums, hexnums, hexnums) - local decode = function(code) - code = tonumber(code, 16) - if code < 128 then -- one-byte symbol - return string.char(code) - elseif code < 2048 then -- two-byte symbol - -- Grab high and low bytes - local hi = math.floor(code / 64) - local lo = math.mod(code, 64) - -- Return symbol as \hi\lo - return string.char(hi + 192, lo + 128) - elseif code < 65536 then - -- Grab high, middle and low bytes - local hi = math.floor(code / 4096) - local leftover = code - hi * 4096 - local mi = math.floor(leftover / 64) - leftover = leftover - mi * 64 - local lo = math.mod(leftover, 64) - -- Return symbol as \hi\mi\lo - return string.char(hi + 224, mi + 160, lo + 128) - elseif code < 1114112 then - -- Grab high, highmiddle, lowmiddle and low bytes - local hi = math.floor(code / 262144) - local leftover = code - hi * 262144 - local hm = math.floor(leftover / 4096) - leftover = leftover - hm * 4096 - local lm = math.floor(leftover / 64) - local lo = math.mod(leftover, 64) - -- Return symbol as \hi\hm\lm\lo - return string.char(hi + 240, hm + 128, lm + 128, lo + 128) - else -- It is not Unicode symbol at all - return tostring(code) - end - end - return string.gsub(s, pattern, decode) -end - --- Retrieves mapping of track IDs to track names and album IDs to --- avoid redundant queries when Awesome gets restarted. -local function retrieve_cache() - local bus = io.open(cache_file) - local track = {} - if bus then - local header = bus:read("*line") - if header == cache_header then - for l in bus:lines() do - local _, _, id, artist_link_name, album_name, album_id, track_name = - string.find(l,"(%d+)-([^-]+)-([^-]+)-(%d+)-(.+)") - track = {} - track.id = id - track.artist_link_name = string.gsub(artist_link_name, '\\_', '-') - track.album_name = string.gsub(album_name, '\\_', '-') - track.album_id = album_id - track.display_name = track_name - jamendo_list[id] = track - end - else - -- We encountered an outdated version of the cache - -- file. Let's just remove it. - awful.util.spawn("rm -f " .. cache_file) - end - end -end - --- Saves track IDs to track names and album IDs mapping into the cache --- file. -function save_cache() - local bus = io.open(cache_file, "w") - bus:write(cache_header .. "\n") - for id,track in pairs(jamendo_list) do - bus:write(string.format("%s-%s-%s-%s-%s\n", id, - string.gsub(track.artist_link_name, '-', '\\_'), - string.gsub(track.album_name, '-', '\\_'), - track.album_id, track.display_name)) - end - bus:flush() - bus:close() -end - --- Retrieve cache on initialization -retrieve_cache() - --- Returns a filename of the album cover and formed wget request that --- downloads the album cover for the given track name. If the album --- cover already exists returns nil as the second argument. -function fetch_album_cover_request(track_id) - local track = jamendo_list[track_id] - local album_id = track.album_id - - if album_id == 0 then -- No cover for tracks without album! - return nil - end - local file_path = album_covers_folder .. album_id .. ".jpg" - - if not file_exists(file_path) then -- We need to download it - -- First check if cache directory exists - f = io.popen('test -d ' .. album_covers_folder .. ' && echo t') - if f:read("*line") ~= 't' then - awful.util.spawn("mkdir " .. album_covers_folder) - end - f:close() - - if not track.album_image then -- Wow! We have album_id, but - local a_id = tostring(album_id) --don't have album_image. Well, - local prefix = --it happens. - string.sub(a_id, 1, string.len(a_id) - 3) - track.album_image = - string.format("http://imgjam.com/albums/s%s/%s/covers/1.100.jpg", - prefix == "" and 0 or prefix, a_id) - end - - return file_path, string.format("wget %s -O %s 2> /dev/null", - track.album_image, file_path) - else -- Cover already downloaded, return its filename and nil - return file_path, nil - end -end - --- Returns a file containing an album cover for given track id. First --- searches in the cache folder. If file is not there, fetches it from --- the Internet and saves into the cache folder. -function get_album_cover(track_id) - local file_path, fetch_req = fetch_album_cover_request(track_id) - if fetch_req then - local f = io.popen(fetch_req) - f:close() - - -- Let's check if file is finally there, just in case - if not file_exists(file_path) then - return nil - end - end - return file_path -end - --- Same as get_album_cover, but downloads (if necessary) the cover --- asynchronously. -function get_album_cover_async(track_id) - local file_path, fetch_req = fetch_album_cover_request(track_id) - if fetch_req then - asyncshell.request(fetch_req) - end -end - --- Checks if track_name is actually a link to Jamendo stream. If true --- returns the file with album cover for the track. -function try_get_cover(track_name) - local id = get_id_from_link(track_name) - if id then - return get_album_cover(id) - end -end - --- Same as try_get_cover, but calls get_album_cover_async inside. -function try_get_cover_async(track_name) - local id = get_id_from_link(track_name) - if id then - return get_album_cover_async(id) - end -end - --- Returns the track table for given query and search method. --- what - search method - SEARCH_ARTIST, ALBUM or TAG --- s - string to search -function search_by(what, s) - -- Get a default request and set unit and query - local req = search_template - req.unit = what.unit - req.params.searchquery = s - local resp = perform_request(form_request(req)) - if resp then - local search_res = parse_json(resp)[1] - - if search_res then - -- Now when we got the search result, find tracks filtered by - -- this result. - local params = {} - params[what.value] = search_res.id - req = { params = params } - local track_table = return_track_table(req) - return { search_res = search_res, tracks = track_table } - end - end -end - --- Executes request_string with io.popen and returns the response. -function perform_request(reqest_string) - local bus = assert(io.popen(reqest_string,'r')) - local response = bus:read("*all") - bus:close() - -- Curl with popen can sometimes fail to fetch data when the - -- connection is slow. Let's try again if it fails. - if string.len(response) == 0 then - bus = assert(io.popen(reqest_string,'r')) - response = bus:read("*all") - bus:close() - -- If it still can't read anything, return nil - if string.len(response) ~= 0 then - return nil - end - end - return response -end - --- Sets default streamencoding in current_request_table. -function set_current_format(format) - current_request_table.params.streamencoding = format -end - --- Sets default order in current_request_table. -function set_current_order(order) - current_request_table.params.order = order -end diff --git a/awesompd/rcsample.lua b/awesompd/rcsample.lua deleted file mode 100644 index c5e0364..0000000 --- a/awesompd/rcsample.lua +++ /dev/null @@ -1,407 +0,0 @@ --- Standard awesome library -require("awful") -require("awful.autofocus") -require("awful.rules") --- Theme handling library -require("beautiful") --- Notification library -require("naughty") - --- Load Debian menu entries -require("debian.menu") - --- {{{ Variable definitions --- Themes define colours, icons, and wallpapers -beautiful.init("/usr/share/awesome/themes/default/theme.lua") - --- This is used later as the default terminal and editor to run. -terminal = "x-terminal-emulator" -editor = os.getenv("EDITOR") or "editor" -editor_cmd = terminal .. " -e " .. editor - --- Default modkey. --- Usually, Mod4 is the key with a logo between Control and Alt. --- If you do not like this or do not have such a key, --- I suggest you to remap Mod4 to another key using xmodmap or other tools. --- However, you can use another modifier like Mod1, but it may interact with others. -modkey = "Mod4" - --- Table of layouts to cover with awful.layout.inc, order matters. -layouts = -{ - awful.layout.suit.floating, - awful.layout.suit.tile, - awful.layout.suit.tile.left, - awful.layout.suit.tile.bottom, - awful.layout.suit.tile.top, - awful.layout.suit.fair, - awful.layout.suit.fair.horizontal, - awful.layout.suit.spiral, - awful.layout.suit.spiral.dwindle, - awful.layout.suit.max, - awful.layout.suit.max.fullscreen, - awful.layout.suit.magnifier -} --- }}} - --- {{{ Tags --- Define a tag table which hold all screen tags. -tags = {} -for s = 1, screen.count() do - -- Each screen has its own tag table. - tags[s] = awful.tag({ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, s, layouts[1]) -end --- }}} - --- {{{ Menu --- Create a laucher widget and a main menu -myawesomemenu = { - { "manual", terminal .. " -e man awesome" }, - { "edit config", editor_cmd .. " " .. awful.util.getdir("config") .. "/rc.lua" }, - { "restart", awesome.restart }, - { "quit", awesome.quit } -} - -mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon }, - { "Debian", debian.menu.Debian_menu.Debian }, - { "open terminal", terminal } - } - }) - -mylauncher = awful.widget.launcher({ image = image(beautiful.awesome_icon), - menu = mymainmenu }) --- }}} - --- {{{ Wibox --- Create a textclock widget -mytextclock = awful.widget.textclock({ align = "right" }) - --- Create a systray -mysystray = widget({ type = "systray" }) - --- BEGIN OF AWESOMPD WIDGET DECLARATION - - require('awesompd/awesompd') - - musicwidget = awesompd:create() -- Create awesompd widget - musicwidget.font = "Liberation Mono" -- Set widget font - musicwidget.scrolling = true -- If true, the text in the widget will be scrolled - musicwidget.output_size = 30 -- Set the size of widget in symbols - musicwidget.update_interval = 10 -- Set the update interval in seconds - - -- Set the folder where icons are located (change username to your login name) - musicwidget.path_to_icons = "/home/username/.config/awesome/icons" - - -- Set the default music format for Jamendo streams. You can change - -- this option on the fly in awesompd itself. - -- possible formats: awesompd.FORMAT_MP3, awesompd.FORMAT_OGG - musicwidget.jamendo_format = awesompd.FORMAT_MP3 - - -- Specify the browser you use so awesompd can open links from - -- Jamendo in it. - musicwidget.browser = "firefox" - - -- If true, song notifications for Jamendo tracks and local tracks - -- will also contain album cover image. - musicwidget.show_album_cover = true - - -- Specify how big in pixels should an album cover be. Maximum value - -- is 100. - musicwidget.album_cover_size = 50 - - -- This option is necessary if you want the album covers to be shown - -- for your local tracks. - musicwidget.mpd_config = "/home/username/.mpdconf" - - -- Specify decorators on the left and the right side of the - -- widget. Or just leave empty strings if you decorate the widget - -- from outside. - musicwidget.ldecorator = " " - musicwidget.rdecorator = " " - - -- Set all the servers to work with (here can be any servers you use) - musicwidget.servers = { - { server = "localhost", - port = 6600 }, - { server = "192.168.0.72", - port = 6600 } - } - - -- Set the buttons of the widget. Keyboard keys are working in the - -- entire Awesome environment. Also look at the line 352. - musicwidget:register_buttons({ { "", awesompd.MOUSE_LEFT, musicwidget:command_playpause() }, - { "Control", awesompd.MOUSE_SCROLL_UP, musicwidget:command_prev_track() }, - { "Control", awesompd.MOUSE_SCROLL_DOWN, musicwidget:command_next_track() }, - { "", awesompd.MOUSE_SCROLL_UP, musicwidget:command_volume_up() }, - { "", awesompd.MOUSE_SCROLL_DOWN, musicwidget:command_volume_down() }, - { "", awesompd.MOUSE_RIGHT, musicwidget:command_show_menu() }, - { "", "XF86AudioLowerVolume", musicwidget:command_volume_down() }, - { "", "XF86AudioRaiseVolume", musicwidget:command_volume_up() }, - { modkey, "Pause", musicwidget:command_playpause() } }) - - musicwidget:run() -- After all configuration is done, run the widget - --- END OF AWESOMPD WIDGET DECLARATION --- Don't forget to add the widget to the wibox. It is done on the line 216. - -mywibox = {} -mypromptbox = {} -mylayoutbox = {} -mytaglist = {} -mytaglist.buttons = awful.util.table.join( - awful.button({ }, 1, awful.tag.viewonly), - awful.button({ modkey }, 1, awful.client.movetotag), - awful.button({ }, 3, awful.tag.viewtoggle), - awful.button({ modkey }, 3, awful.client.toggletag), - awful.button({ }, 4, awful.tag.viewnext), - awful.button({ }, 5, awful.tag.viewprev) - ) -mytasklist = {} -mytasklist.buttons = awful.util.table.join( - awful.button({ }, 1, function (c) - if not c:isvisible() then - awful.tag.viewonly(c:tags()[1]) - end - client.focus = c - c:raise() - end), - awful.button({ }, 3, function () - if instance then - instance:hide() - instance = nil - else - instance = awful.menu.clients({ width=250 }) - end - end), - awful.button({ }, 4, function () - awful.client.focus.byidx(1) - if client.focus then client.focus:raise() end - end), - awful.button({ }, 5, function () - awful.client.focus.byidx(-1) - if client.focus then client.focus:raise() end - end)) - -for s = 1, screen.count() do - -- Create a promptbox for each screen - mypromptbox[s] = awful.widget.prompt({ layout = awful.widget.layout.horizontal.leftright }) - -- Create an imagebox widget which will contains an icon indicating which layout we're using. - -- We need one layoutbox per screen. - mylayoutbox[s] = awful.widget.layoutbox(s) - mylayoutbox[s]:buttons(awful.util.table.join( - awful.button({ }, 1, function () awful.layout.inc(layouts, 1) end), - awful.button({ }, 3, function () awful.layout.inc(layouts, -1) end), - awful.button({ }, 4, function () awful.layout.inc(layouts, 1) end), - awful.button({ }, 5, function () awful.layout.inc(layouts, -1) end))) - -- Create a taglist widget - mytaglist[s] = awful.widget.taglist(s, awful.widget.taglist.label.all, mytaglist.buttons) - - -- Create a tasklist widget - mytasklist[s] = awful.widget.tasklist(function(c) - return awful.widget.tasklist.label.currenttags(c, s) - end, mytasklist.buttons) - - -- Create the wibox - mywibox[s] = awful.wibox({ position = "top", screen = s }) - -- Add widgets to the wibox - order matters - mywibox[s].widgets = { - { - mylauncher, - mytaglist[s], - mypromptbox[s], - layout = awful.widget.layout.horizontal.leftright - }, - mylayoutbox[s], - mytextclock, - musicwidget.widget, -- Awesompd widget is added like this - s == 1 and mysystray or nil, - mytasklist[s], - layout = awful.widget.layout.horizontal.rightleft - } -end --- }}} - --- {{{ Mouse bindings -root.buttons(awful.util.table.join( - awful.button({ }, 3, function () mymainmenu:toggle() end), - awful.button({ }, 4, awful.tag.viewnext), - awful.button({ }, 5, awful.tag.viewprev) -)) --- }}} - --- {{{ Key bindings -globalkeys = awful.util.table.join( - awful.key({ modkey, }, "Left", awful.tag.viewprev ), - awful.key({ modkey, }, "Right", awful.tag.viewnext ), - awful.key({ modkey, }, "Escape", awful.tag.history.restore), - - awful.key({ modkey, }, "j", - function () - awful.client.focus.byidx( 1) - if client.focus then client.focus:raise() end - end), - awful.key({ modkey, }, "k", - function () - awful.client.focus.byidx(-1) - if client.focus then client.focus:raise() end - end), - awful.key({ modkey, }, "w", function () mymainmenu:show({keygrabber=true}) end), - - -- Layout manipulation - awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end), - awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end), - awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end), - awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end), - awful.key({ modkey, }, "u", awful.client.urgent.jumpto), - awful.key({ modkey, }, "Tab", - function () - awful.client.focus.history.previous() - if client.focus then - client.focus:raise() - end - end), - - -- Standard program - awful.key({ modkey, }, "Return", function () awful.util.spawn(terminal) end), - awful.key({ modkey, "Control" }, "r", awesome.restart), - awful.key({ modkey, "Shift" }, "q", awesome.quit), - - awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end), - awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end), - awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1) end), - awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1) end), - awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1) end), - awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1) end), - awful.key({ modkey, }, "space", function () awful.layout.inc(layouts, 1) end), - awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -1) end), - - -- Prompt - awful.key({ modkey }, "r", function () mypromptbox[mouse.screen]:run() end), - - awful.key({ modkey }, "x", - function () - awful.prompt.run({ prompt = "Run Lua code: " }, - mypromptbox[mouse.screen].widget, - awful.util.eval, nil, - awful.util.getdir("cache") .. "/history_eval") - end) -) - -clientkeys = awful.util.table.join( - awful.key({ modkey, }, "f", function (c) c.fullscreen = not c.fullscreen end), - awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end), - awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ), - awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end), - awful.key({ modkey, }, "o", awful.client.movetoscreen ), - awful.key({ modkey, "Shift" }, "r", function (c) c:redraw() end), - awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end), - awful.key({ modkey, }, "n", function (c) c.minimized = not c.minimized end), - awful.key({ modkey, }, "m", - function (c) - c.maximized_horizontal = not c.maximized_horizontal - c.maximized_vertical = not c.maximized_vertical - end) -) - --- Compute the maximum number of digit we need, limited to 9 -keynumber = 0 -for s = 1, screen.count() do - keynumber = math.min(9, math.max(#tags[s], keynumber)); -end - --- Bind all key numbers to tags. --- Be careful: we use keycodes to make it works on any keyboard layout. --- This should map on the top row of your keyboard, usually 1 to 9. -for i = 1, keynumber do - globalkeys = awful.util.table.join(globalkeys, - awful.key({ modkey }, "#" .. i + 9, - function () - local screen = mouse.screen - if tags[screen][i] then - awful.tag.viewonly(tags[screen][i]) - end - end), - awful.key({ modkey, "Control" }, "#" .. i + 9, - function () - local screen = mouse.screen - if tags[screen][i] then - awful.tag.viewtoggle(tags[screen][i]) - end - end), - awful.key({ modkey, "Shift" }, "#" .. i + 9, - function () - if client.focus and tags[client.focus.screen][i] then - awful.client.movetotag(tags[client.focus.screen][i]) - end - end), - awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9, - function () - if client.focus and tags[client.focus.screen][i] then - awful.client.toggletag(tags[client.focus.screen][i]) - end - end)) -end - -clientbuttons = awful.util.table.join( - awful.button({ }, 1, function (c) client.focus = c; c:raise() end), - awful.button({ modkey }, 1, awful.mouse.client.move), - awful.button({ modkey }, 3, awful.mouse.client.resize)) - --- Set keys --- Add this line before root.keys(globalkeys). -musicwidget:append_global_keys() - -root.keys(globalkeys) --- }}} - --- {{{ Rules -awful.rules.rules = { - -- All clients will match this rule. - { rule = { }, - properties = { border_width = beautiful.border_width, - border_color = beautiful.border_normal, - focus = true, - keys = clientkeys, - buttons = clientbuttons } }, - { rule = { class = "MPlayer" }, - properties = { floating = true } }, - { rule = { class = "pinentry" }, - properties = { floating = true } }, - { rule = { class = "gimp" }, - properties = { floating = true } }, - -- Set Firefox to always map on tags number 2 of screen 1. - -- { rule = { class = "Firefox" }, - -- properties = { tag = tags[1][2] } }, -} --- }}} - --- {{{ Signals --- Signal function to execute when a new client appears. -client.add_signal("manage", function (c, startup) - -- Add a titlebar - -- awful.titlebar.add(c, { modkey = modkey }) - - -- Enable sloppy focus - c:add_signal("mouse::enter", function(c) - if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier - and awful.client.focus.filter(c) then - client.focus = c - end - end) - - if not startup then - -- Set the windows at the slave, - -- i.e. put it at the end of others instead of setting it master. - -- awful.client.setslave(c) - - -- Put windows in a smart way, only if they does not set an initial position. - if not c.size_hints.user_position and not c.size_hints.program_position then - awful.placement.no_overlap(c) - awful.placement.no_offscreen(c) - end - end -end) - -client.add_signal("focus", function(c) c.border_color = beautiful.border_focus end) -client.add_signal("unfocus", function(c) c.border_color = beautiful.border_normal end) --- }}} diff --git a/awesompd/utf8.lua b/awesompd/utf8.lua deleted file mode 100644 index 72177c2..0000000 --- a/awesompd/utf8.lua +++ /dev/null @@ -1,158 +0,0 @@ --- Provides UTF-8 aware string functions implemented in pure lua: --- * string.utf8len(s) --- * string.utf8sub(s, i, j) --- --- All functions behave as their non UTF-8 aware counterparts with the exception --- that UTF-8 characters are used instead of bytes for all units. --- --- Note: all validations had been removed due to awesome usage specifics. ---[[ -Copyright (c) 2006-2007, Kyle Smith -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. -* Neither the name of the author nor the names of its contributors may be -used to endorse or promote products derived from this software without -specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---]] - --- ABNF from RFC 3629 --- --- UTF8-octets = *( UTF8-char ) --- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 --- UTF8-1 = %x00-7F --- UTF8-2 = %xC2-DF UTF8-tail --- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / --- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) --- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / --- %xF4 %x80-8F 2( UTF8-tail ) --- UTF8-tail = %x80-BF --- - --- returns the number of bytes used by the UTF-8 character at byte i in s --- also doubles as a UTF-8 character validator -function utf8charbytes (s, i) - -- argument defaults - i = i or 1 - local c = string.byte(s, i) - - -- determine bytes needed for character, based on RFC 3629 - if c > 0 and c <= 127 then - -- UTF8-1 - return 1 - elseif c >= 194 and c <= 223 then - -- UTF8-2 - local c2 = string.byte(s, i + 1) - return 2 - elseif c >= 224 and c <= 239 then - -- UTF8-3 - local c2 = s:byte(i + 1) - local c3 = s:byte(i + 2) - return 3 - elseif c >= 240 and c <= 244 then - -- UTF8-4 - local c2 = s:byte(i + 1) - local c3 = s:byte(i + 2) - local c4 = s:byte(i + 3) - return 4 - end -end - --- returns the number of characters in a UTF-8 string -function utf8len (s) - local pos = 1 - local bytes = string.len(s) - local len = 0 - - while pos <= bytes and len ~= chars do - local c = string.byte(s,pos) - len = len + 1 - - pos = pos + utf8charbytes(s, pos) - end - - if chars ~= nil then - return pos - 1 - end - - return len -end - --- functions identically to string.sub except that i and j are UTF-8 characters --- instead of bytes -function utf8sub (s, i, j) - j = j or -1 - - if i == nil then - return "" - end - - local pos = 1 - local bytes = string.len(s) - local len = 0 - - -- only set l if i or j is negative - local l = (i >= 0 and j >= 0) or utf8len(s) - local startChar = (i >= 0) and i or l + i + 1 - local endChar = (j >= 0) and j or l + j + 1 - - -- can't have start before end! - if startChar > endChar then - return "" - end - - -- byte offsets to pass to string.sub - local startByte, endByte = 1, bytes - - while pos <= bytes do - len = len + 1 - - if len == startChar then - startByte = pos - end - - pos = pos + utf8charbytes(s, pos) - - if len == endChar then - endByte = pos - 1 - break - end - end - - return string.sub(s, startByte, endByte) -end - --- replace UTF-8 characters based on a mapping table -function utf8replace (s, mapping) - local pos = 1 - local bytes = string.len(s) - local charbytes - local newstr = "" - - while pos <= bytes do - charbytes = utf8charbytes(s, pos) - local c = string.sub(s, pos, pos + charbytes - 1) - newstr = newstr .. (mapping[c] or c) - pos = pos + charbytes - end - - return newstr -end