]> git.rmz.io Git - dotfiles.git/blobdiff - awesome/widgets/awesompd/jamendo.lua
awesome: add submodule for awesompd
[dotfiles.git] / awesome / widgets / awesompd / jamendo.lua
diff --git a/awesome/widgets/awesompd/jamendo.lua b/awesome/widgets/awesompd/jamendo.lua
deleted file mode 100644 (file)
index 3851e0a..0000000
+++ /dev/null
@@ -1,518 +0,0 @@
----------------------------------------------------------------------------
--- @author Alexander Yakushev <yakushev.alex@gmail.com>
--- @copyright 2011-2013 Alexander Yakushev
--- @release v1.2.4
----------------------------------------------------------------------------
-
--- Grab environment
-local awful = require('awful')
-
-local 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
-
-local function str_interpose(coll, sep)
-   if #coll == 0 then
-      return ""
-   end
-   local result = coll[1]
-   for i = 2, #coll do
-      result = result .. sep .. coll[i]
-   end
-   print(result)
-   return result
-end
-
--- Global variables
-jamendo.FORMAT_MP3 = { display = "MP3 (128k)",
-                       short_display = "MP3",
-                       value = "mp31" }
-jamendo.FORMAT_OGG = { display = "Ogg Vorbis (q4)",
-                       short_display = "Ogg",
-                       value = "ogg2" }
-jamendo.ORDER_RATINGDAILY = { display = "Daily rating",
-                              short_display = "daily rating",
-                              value = "ratingday_desc" }
-jamendo.ORDER_RATINGWEEKLY = { display = "Weekly rating",
-                               short_display = "weekly rating",
-                               value = "ratingweek_desc" }
-jamendo.ORDER_RATINGTOTAL = { display = "All time rating",
-                              short_display = "all time rating",
-                              value = "ratingtotal_desc" }
-jamendo.ORDER_RANDOM = { display = "Random",
-                         short_display = "random",
-                         value = "random_desc" }
-jamendo.ORDER_RELEVANCE = { display = "None (consecutive)",
-                            short_display = "none",
-                            value = "searchweight_desc" }
-jamendo.SEARCH_ARTIST = { display = "Artist",
-                          unit = "artist",
-                          value = "artist_id" }
-jamendo.SEARCH_ALBUM = { display = "Album",
-                         unit = "album",
-                         value = "album_id" }
-jamendo.SEARCH_TAG = { display = "Tag",
-                       unit = "tag",
-                       value = "tag_id" }
-jamendo.ALL_FORMATS = { jamendo.FORMAT_MP3, jamendo.FORMAT_OGG }
-jamendo.ALL_ORDERS = { ORDER_RELEVANCE, ORDER_RANDOM, ORDER_RATINGDAILY,
-                       ORDER_RATINGWEEKLY, ORDER_RATINGTOTAL }
-
-jamendo.current_request_table = { unit = "track",
-                                  fields = {"id", "artist_url", "artist_name", "name", 
-                                            "stream", "album_image", "album_name" },
-                                  joins = { "track_album", "album_artist" },
-                                  params = { streamencoding = jamendo.FORMAT_MP3,
-                                             order = jamendo.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 = 
-         jamendo.perform_request("echo $(curl -w %{redirect_url} " .. 
-                                 "'http://api.jamendo.com/get2/stream/track/redirect/" .. 
-                                 "?streamencoding="..jamendo.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 jamendo.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 jamendo.get_track_by_link(link)
-   local id = jamendo.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 jamendo.replace_link(track_name)
-   local track = jamendo.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 jamendo.return_track_table(request_table)
-   local req_string = jamendo.form_request(request_table)
-   local response = jamendo.perform_request(req_string)
-   if not response then
-      return nil -- Bad internet connection
-   end
-   local parse_table = jamendo.parse_json(response)
-   for i = 1, #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
-   jamendo.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 jamendo.form_request(request_table)
-   local curl_str = "curl -A 'Mozilla/4.0' -fsm 5 \"%s\""
-   local url = "http://api.jamendo.com/get2/%s/%s/json/%s/?%s"
-   request_table = request_table or jamendo.current_request_table
-   
-   local fields = request_table.fields or jamendo.current_request_table.fields
-   local joins = request_table.joins or jamendo.current_request_table.joins
-   local unit = request_table.unit or jamendo.current_request_table.unit
-   
-   -- Form fields string (like field1+field2+fieldN)
-   local f_string = str_interpose(fields, "+")
-   -- Form joins string
-   local j_string = str_interpose(joins, "+")
-
-   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 ~= jamendo.current_request_table.params then
-      -- First fill params with current_request_table parameters
-      for k, v in pairs(jamendo.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 = jamendo.current_request_table.params
-   end
-   -- Form parameter string (like param1=value1&param2=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, f_string, unit, j_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 jamendo.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 = jamendo.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 jamendo.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.fmod(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.fmod(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.fmod(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 jamendo.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 jamendo.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, #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 jamendo.get_album_cover(track_id)
-   local file_path, fetch_req = jamendo.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 jamendo.get_album_cover_async(track_id)
-   local file_path, fetch_req = jamendo.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 jamendo.try_get_cover(track_name)
-   local id = jamendo.get_id_from_link(track_name)
-   if id then
-      return jamendo.get_album_cover(id)
-   end
-end
-
--- Same as try_get_cover, but calls get_album_cover_async inside.
-function jamendo.try_get_cover_async(track_name)
-   local id = jamendo.get_id_from_link(track_name)
-   if id then
-      return jamendo.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 jamendo.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 = jamendo.perform_request(jamendo.form_request(req))
-   if resp then
-      local search_res = jamendo.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 = jamendo.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 jamendo.perform_request(request_string)
-   local bus = assert(io.popen(request_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 #response == 0 then
-      bus = assert(io.popen(request_string,'r'))
-      response = bus:read("*all")
-      bus:close()
-      -- If it still can't read anything, return nil
-      if #response ~= 0 then
-         return nil
-      end
-   end
-   return response
-end
-
--- Sets default streamencoding in current_request_table.
-function jamendo.set_current_format(format)
-   jamendo.current_request_table.params.streamencoding = format
-end
-
--- Sets default order in current_request_table.
-function jamendo.set_current_order(order)
-   jamendo.current_request_table.params.order = order
-end
-
-return jamendo