diff options
-rw-r--r-- | awesompd.lua | 179 | ||||
-rw-r--r-- | jamendo.lua | 237 |
2 files changed, 261 insertions, 155 deletions
diff --git a/awesompd.lua b/awesompd.lua index 8c6d382..8243542 100644 --- a/awesompd.lua +++ b/awesompd.lua @@ -5,6 +5,8 @@ --------------------------------------------------------------------------- require('utf8') +require('jamendo') + local naughty = naughty local awful = awful @@ -78,7 +80,6 @@ function awesompd:create() instance.status_text = "Stopped" instance.to_notify = false instance.connected = true - instance.jamendo_list = {} instance.recreate_menu = true instance.recreate_playback = true @@ -95,7 +96,6 @@ function awesompd:create() instance.output_size = 30 instance.update_interval = 10 instance.path_to_icons = "" - instance.filename = awful.util.getdir ("cache").."/jamendo_cache" instance.ldecorator = " " instance.rdecorator = " " @@ -112,7 +112,6 @@ end -- Registers timers for the widget function awesompd:run() enable_dbg = self.debug_mode - self:retrieve_cache() self:update_track() self:check_playlists() self.load_icons(self.path_to_icons) @@ -301,43 +300,31 @@ function awesompd:command_show_menu() end end -function awesompd:add_tracks_from_jamendo(parse_table,format) - if (table.getn(parse_table) > 0) then - local trygetlink = - assert(io.popen("echo $(curl -w %{redirect_url} " .. - "'http://api.jamendo.com/get2/stream/track/redirect/" .. - "?streamencoding="..format.."&id=729304')"),'r'):read("*lines") - local _, _, prefix = string.find(trygetlink,"stream(%d+)\.jamendo\.com") - for i = 1,table.getn(parse_table) do - track_link = "http://stream" .. prefix .. ".jamendo.com/stream/" - .. parse_table[i].id .."/".. format .."/" - self:command("add " .. track_link) - self.jamendo_list[parse_table[i].id] = - parse_table[i].artist .. " - " .. parse_table[i].track - end - end -end +-- function awesompd:add_tracks_from_jamendo(parse_table,format) +-- if (table.getn(parse_table) > 0) then +-- local trygetlink = +-- assert(io.popen("echo $(curl -w %{redirect_url} " .. +-- "'http://api.jamendo.com/get2/stream/track/redirect/" .. +-- "?streamencoding="..format.."&id=729304')"),'r'):read("*lines") +-- local _, _, prefix = string.find(trygetlink,"stream(%d+)\.jamendo\.com") +-- for i = 1,table.getn(parse_table) do +-- track_link = "http://stream" .. prefix .. ".jamendo.com/stream/" +-- .. parse_table[i].id .."/".. format .."/" +-- self:command("add " .. track_link) +-- self.jamendo_list[parse_table[i].id] = +-- parse_table[i].artist .. " - " .. parse_table[i].track +-- end +-- end +-- end function awesompd:add_jamendo_top(format) return function () - local top_list = "curl -A 'Mozilla/4.0' -fsm 5 \"http://api.jamendo.com/get2/" .. - "id+name+url+stream+album_name+album_url+album_id+artist_id+artist_name" .. - "/track/jsonpretty/track_album+album_artist/?n=100&order=ratingweek_desc\"" - local bus = assert(io.popen(top_list, 'r')) - local r = bus:read("*all") - bus:close() - local parse_table = {} - string.gsub(r, "\"id\":(%d+),%s+\"name\":\"([^\"]+)[^%}]*\"artist_name\":\"([^\"]+)\"", - function(_id,_track,_artist) - table.insert(parse_table, - { id = _id, - track = (_track or ""), - artist = (_artist or "")}) - end) - self:add_tracks_from_jamendo(parse_table,format) + local track_table = jamendo.return_track_table() + for i = 1,table.getn(track_table) do + self:command("add " .. track_table[i].stream) + end self.recreate_menu = true self.recreate_list = true - self:save_cache() end end @@ -384,7 +371,7 @@ function awesompd:get_list_menu() local end_num = (self.current_number + 15 < total_count ) and self.current_number + 15 or total_count for i = start_num, end_num do if (string.find(self.list_array[i],"jamendo.com")) then - table.insert(new_menu, { self.jamendo_list[awesompd.get_id_from_link(self.list_array[i])], + table.insert(new_menu, { jamendo.get_name_by_link(self.list_array[i]), self:command_play_specific(i), self.current_number == i and (self.status == "Playing" and self.ICONS.PLAY or self.ICONS.PAUSE) @@ -550,34 +537,6 @@ function awesompd:wrap_output(text) awesompd.protect_string(text), self.rdecorator) end --- Retrieves mapping of track IDs to track names to avoid redundant --- queries when Awesome gets restarted. -function awesompd:retrieve_cache() - local bus = io.open(self.filename) - if bus then - for l in bus:lines() do - local _, _, id, track = string.find(l,"(%d+)-(.+)") - self.jamendo_list[id] = track - end - end -end - --- Saves track IDs to track names mapping into the cache file. -function awesompd:save_cache() - local bus = io.open(self.filename, "w") - for id,name in pairs(self.jamendo_list) do - bus:write(id.."-"..name.."\n") - end - bus:flush() - bus:close() -end - --- Returns the track ID from the given link to Jamendo stream. -function awesompd.get_id_from_link(link) - local _, _, id = string.find(link,"stream/(%d+)") - return id -end - function awesompd.split (s,t) local l = {n=0} local f = function (s) @@ -687,7 +646,7 @@ function awesompd:update_track(file) local new_track = track_line if new_track ~= self.unique_text then if (string.find(new_track,"jamendo.com")) then - self.text = self.jamendo_list[awesompd.get_id_from_link(new_track)] + self.text = jamendo.get_name_by_link(new_track) else self.text = new_track end @@ -762,93 +721,3 @@ function awesompd.protect_string(str, for_menu) return utf8replace(str, awesompd.ESCAPE_SYMBOL_MAPPING) end 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 awesompd.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, find 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 = awesompd.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 awesompd.utf8_codes_to_symbols (s) - local hexnums = "[%dabcdefABCDEF]" - local pattern = string.format("\\u(%s%s%s%s?)", - hexnums, hexnums, hexnums, hexnums) - print("Pattern is : " .. pattern) - local decode = function(code) - code = tonumber(code, 16) - -- Grab high and low byte - local hi = math.floor(code / 256) * 4 + 192 - local lo = math.mod(code, 256) - -- Reduce low byte to 64, add overflow to high - local oflow = math.floor(lo / 64) - hi = hi + oflow - lo = math.mod(code, 64) + 128 - -- Return symbol as \hi\lo - return string.char(hi, lo) - end - return string.gsub(s, pattern, decode) -end diff --git a/jamendo.lua b/jamendo.lua new file mode 100644 index 0000000..1f4a915 --- /dev/null +++ b/jamendo.lua @@ -0,0 +1,237 @@ +module('jamendo', package.seeall) + +-- Grab environment +local os = os + +-- 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" } +SEARCH_ARTIST = { display = "Artist", + value = "artist" } +SEARCH_ALBUM = { display = "Album", + value = "album" } +SEARCH_TAG = { display = "Tag", + value = "tag_idstr" } + +current_request_table = { format = FORMAT_MP3, + order = ORDER_RATINGWEEKLY } + +-- Local variables +local jamendo_list = {} +local cache_file = awful.util.getdir ("cache").."/jamendo_cache" +local default_mp3_stream = nil + +-- Returns default stream number for MP3 format. Requests API for it +-- not more often than every hour. +function get_default_mp3_stream() + if not default_mp3_stream or + (os.time() - default_mp3_stream.last_checked) > 3600 then + local trygetlink = + assert(io.popen("echo $(curl -w %{redirect_url} " .. + "'http://api.jamendo.com/get2/stream/track/redirect/" .. + "?streamencoding="..format.."&id=729304')",'r')):read("*line") + 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. +function get_id_from_link(link) + local _, _, id = string.find(link,"stream/(%d+)") + return id +end + +-- Returns link to music stream for the given track ID. Uses MP3 +-- format and the default stream for it. +function get_link_by_id(id) + return string.format("http://stream%s.jamendo.com/stream/%s/mp31/", + get_default_mp3_stream(), id) +end + +-- Returns track name for given music stream. +function get_name_by_link(link) + return jamendo_list[get_id_from_link(link)] +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 bus = assert(io.popen(req_string, 'r')) + local response = bus:read("*all") + bus:close() + 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].display_name = + parse_table[i].artist_name .. " - " .. parse_table[i].name + -- Save fetched tracks for further caching + jamendo_list[parse_table[i].id] = parse_table[i].display_name + 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. +function form_request(request_table) + local curl_str = 'echo $(curl -w %%{redirect_url} ' .. + '"http://api.jamendo.com/get2/id+artist_name+name+stream/' .. + 'track/json/track_album+album_artist/?n=100&order=%s&streamencoding=%s")' + if request_table then + local format = request_table.format or current_request_table.format + local order = request_table.order or current_request_table.order + return string.format(curl_str, order.value, format.value) + else + print("Request : " .. string.format(curl_str, + current_request_table.order.value, + current_request_table.format.value)) + return string.format(curl_str, + current_request_table.order.value, + current_request_table.format.value) + end +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, find 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) + -- Grab high and low byte + local hi = math.floor(code / 256) * 4 + 192 + local lo = math.mod(code, 256) + -- Reduce low byte to 64, add overflow to high + local oflow = math.floor(lo / 64) + hi = hi + oflow + lo = math.mod(code, 64) + 128 + -- Return symbol as \hi\lo + return string.char(hi, lo) + end + return string.gsub(s, pattern, decode) +end + +-- Retrieves mapping of track IDs to track names to avoid redundant +-- queries when Awesome gets restarted. +function retrieve_cache() + local bus = io.open(cache_file) + if bus then + for l in bus:lines() do + local _, _, id, track = string.find(l,"(%d+)-(.+)") + jamendo_list[id] = track + end + end +end + +-- Saves track IDs to track names mapping into the cache file. +function save_cache() + local bus = io.open(cache_file, "w") + for id,name in pairs(jamendo_list) do + bus:write(id.."-"..name.."\n") + end + bus:flush() + bus:close() +end + +-- Retrieve cache on initialization +retrieve_cache() |