142 lines
3.7 KiB
Ruby
Executable File
142 lines
3.7 KiB
Ruby
Executable File
require 'cgi'
|
|
require 'open-uri'
|
|
require 'json/pure'
|
|
|
|
class SoundCloud
|
|
include Observable
|
|
LIMIT = 100
|
|
|
|
def initialize(client_id)
|
|
@cid = client_id
|
|
@playlist_pos = -1
|
|
@download_queue = []
|
|
@dthread = Thread.new do downloader end
|
|
end
|
|
|
|
def load_playlist(search = nil, offset = 0)
|
|
search = "" unless search && !search.empty?
|
|
if search =~ /\s*http(s)?:\/\/(www.)?soundcloud.com.*/
|
|
url = "http://api.soundcloud.com/resolve.json?url=%s&client_id=%s" % [CGI.escape(search), @cid]
|
|
else
|
|
url = "http://api.soundcloud.com/tracks.json?client_id=%s&filter=streamable&limit=%d&offset=%d&q=%s" \
|
|
% [@cid, LIMIT, offset, CGI.escape(search)]
|
|
end
|
|
c = open(url) do |io|
|
|
io.readlines
|
|
end.join
|
|
@tracks = JSON.parse c
|
|
@tracks = [@tracks] if @tracks.is_a? Hash
|
|
@tracks.map! do |t|
|
|
t["mpg123url"] = client_url t['stream_url']
|
|
t["download"] = client_url t['download_url']
|
|
t["duration"] = t["duration"].nil? ? 0 : t["duration"].to_i/1000
|
|
t["bpm"] = t["bpm"].nil? ? 0 : t["bpm"].to_i
|
|
t[:error] = "Not streamable" if t["stream_url"].nil?
|
|
t
|
|
end
|
|
changed
|
|
notify_observers :state =>:load, :tracks => @tracks
|
|
rescue => e
|
|
@error = {:error => e}
|
|
end
|
|
|
|
def shufflePlaylist
|
|
return unless @tracks.respond_to? "shuffle!"
|
|
@tracks.shuffle!
|
|
changed
|
|
notify_observers :state => :shuffle, :tracks => @tracks
|
|
end
|
|
|
|
def nextTrack
|
|
return @error unless @tracks
|
|
return if @tracks.empty? || @tracks.nil?
|
|
if @tracks.is_a? Hash
|
|
t = @tracks
|
|
else
|
|
@playlist_pos += 1
|
|
@playlist_pos -= @tracks.size if @playlist_pos >= @tracks.size
|
|
t = @tracks[@playlist_pos]
|
|
changed
|
|
notify_observers :state => :next, :position => @playlist_pos
|
|
end
|
|
t
|
|
end
|
|
|
|
def prevTrack
|
|
return @error unless @tracks
|
|
return if @tracks.empty?
|
|
if @tracks.is_a? Hash
|
|
t = @tracks
|
|
else
|
|
@playlist_pos -= 1
|
|
@playlist_pos += @tracks.size if @playlist_pos < 0
|
|
t = @tracks[@playlist_pos]
|
|
changed
|
|
notify_observers :state => :previous, :position => @playlist_pos
|
|
end
|
|
t
|
|
end
|
|
|
|
def download(target_dir)
|
|
return @error unless @tracks
|
|
return if @tracks.empty?
|
|
if @tracks.is_a? Hash
|
|
t = @tracks
|
|
else
|
|
t = @tracks[@playlist_pos]
|
|
end
|
|
unless t["download"].nil? || t["download"].size == 0
|
|
filename = "#{t["permalink"]}.#{t["original_format"]}"
|
|
path = File.join(target_dir, filename)
|
|
pair = [path, t['download']]
|
|
@download_queue << pair unless @download_queue.include? pair
|
|
@dthread.run
|
|
|
|
changed
|
|
notify_observers :state => :download, :count => @download_queue.size
|
|
else
|
|
changed
|
|
notify_observers :state => :download, :error => "Not downloadable"
|
|
end
|
|
end
|
|
|
|
# download thread
|
|
def downloader
|
|
loop do
|
|
size = @download_queue.size
|
|
while d = @download_queue.shift
|
|
path = d[0]
|
|
uri = d[1]
|
|
changed
|
|
notify_observers :state => :download, :name => path, :count => size
|
|
size = @download_queue.size
|
|
begin
|
|
path = File.expand_path path
|
|
file = File.new(path, "wb")
|
|
File.open(path, "wb") do |file|
|
|
file.print open(uri).read
|
|
end
|
|
changed
|
|
notify_observers :state => :download, :name => path, :count => size
|
|
rescue OpenURI::HTTPError => e
|
|
changed
|
|
notify_observers :state => :error, :error => e
|
|
ensure
|
|
file.close
|
|
end
|
|
end
|
|
sleep 5
|
|
end
|
|
rescue => e
|
|
changed
|
|
notify_observers :state => "error", :error => e
|
|
end
|
|
|
|
private
|
|
|
|
def client_url(url)
|
|
"#{url}?client_id=%s" % [@cid] if url
|
|
end
|
|
end
|
|
|