spotify-to-mp3 0.3.1 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -4,25 +4,53 @@ A simple command line utility to download MP3 files of Spotify tracks
4
4
 
5
5
  ## Installation
6
6
 
7
+ ### Mac OS X (Lion)
8
+
7
9
  $ gem install spotify-to-mp3
8
10
 
11
+ ### Linux (Ubuntu 11.04)
12
+
13
+ Make sure you have rubygems installed and configured:
14
+
15
+ $ sudo apt-get install rubygems1.8
16
+ $ echo 'PATH=$PATH:/var/lib/gems/1.8/bin' | sudo tee /etc/profile.d/rubygems1.8.sh >/dev/null
17
+
18
+ <span></span>
19
+
20
+ $ sudo gem install spotify-to-mp3
21
+
22
+ ### Windows (not tested)
23
+
24
+ [Install Ruby](http://rubyinstaller.org/)
25
+
26
+ > gem install spotify-to-mp3
27
+
9
28
  ## Usage
10
29
 
11
- 1. Drag Spotify songs to a file (eg, songs.txt). It will look like this:
30
+ 1. Drag Spotify songs to a file (eg, songs.txt). Plain song names are also
31
+ accepted. It may look like this:
12
32
 
13
33
  http://open.spotify.com/track/1JqTcOjOn7gEpeC0JcRVPa
14
- http://open.spotify.com/track/4pQIl2gJyRDKiwIhEOc1gW
15
- http://open.spotify.com/track/2rREB1sRvcb5i6AZRhOhey
16
- http://open.spotify.com/track/6UOvd5f3oyfIGW3GA8dn89
34
+ spotify:track:1fE3ddAlmjJ99IIfLgZjTy
35
+ The Drums - Money
17
36
 
18
37
  2. Download songs. Errors will appear in red (eg, when a song is not found)
19
38
 
20
39
  $ spotify-to-mp3 songs.txt
21
40
 
41
+ ## Changelog
42
+
43
+ 2011-09-26
44
+
45
+ - Both Spotify URLs and plain song names are accepted
46
+
22
47
  ## TODO
23
48
 
24
49
  - Consider multiple artists songs
25
- - Show download progress
26
50
  - Estimated download time
27
51
  - Per song
28
52
  - Total
53
+ - Distinguish internal errors from user errors
54
+ - Filter Grooveshark results by artist, title and length
55
+ - Split code in multiple files
56
+ - --sync option to add/remove \*.mp3 files to match songs file
data/bin/spotify-to-mp3 CHANGED
@@ -6,39 +6,154 @@ require 'open-uri'
6
6
  require 'colorize'
7
7
  require 'cgi'
8
8
 
9
- class SpotifyToMp3
10
- def initialize(log)
11
- @grooveshark = Grooveshark.new
12
- @spotify = Spotify.new
13
- @log = log
9
+ class App
10
+ def initialize
11
+ @context = {
12
+ :grooveshark => Grooveshark.new,
13
+ :spotify => Spotify.new
14
+ }
14
15
  end
15
16
 
16
- def download_song(spotify_uri)
17
- log("Getting song info for #{spotify_uri} from Spotify")
18
- track = @spotify.track(spotify_uri)
19
- filename = File.basename("#{track.artist()} - #{track.name()}.mp3".tr('/', '-'))
20
- if (File.exists?(filename))
21
- log("File #{filename} already exists, skipping")
22
- return
23
- end
24
- log("Getting download URL for \"#{track.artist()} - #{track.name()}\" from Grooveshark")
25
- url = @grooveshark.song_url(track.artist(), track.name())
26
- log("Downloading song")
27
- download_url(url, filename)
28
- log("Song downloaded to #{filename}")
17
+ def run
18
+ begin
19
+ file = ARGV.first
20
+ raise "No songs file specified. Usage: #{$0} file" if file.nil?
21
+
22
+ File.open(file).each_line{|song_id|
23
+ song_id.strip!
24
+ next if song_id.empty?
25
+ begin
26
+ song = UnresolvedSong.new(@context, song_id)
27
+ print "Resolving \"#{song}\" "
28
+ song = song.resolve
29
+ print "-> Searching \"#{song}\" on Grooveshark "
30
+ song = song.from_grooveshark
31
+ print "-> Downloading \"#{song}\" "
32
+ if song.downloaded
33
+ puts "Already exists, skipping".green
34
+ else
35
+ song.download
36
+ puts "Done".green
37
+ end
38
+ rescue
39
+ puts "#{$!}".red
40
+ end
41
+ }
42
+ rescue
43
+ puts "#{$!}".red
44
+ end
29
45
  end
30
46
 
31
47
  private
32
48
 
33
- def log(message)
34
- @log.call(message)
49
+ class UnresolvedSong
50
+ def initialize(context, id)
51
+ @context = context
52
+ @id = id
53
+ end
54
+
55
+ def resolve
56
+ if @id.start_with?('http://open.spotify.com/track/', 'spotify:track:')
57
+ track = @context[:spotify].track(@id)
58
+ source = SpotifySource.new(@context, track)
59
+ else
60
+ source = PlainSource.new(@context, @id)
61
+ end
62
+ ResolvedSong.new(@context, source)
63
+ end
64
+
65
+ def to_s
66
+ @id
67
+ end
68
+ end
69
+
70
+ class Source
71
+ def grooveshark_query
72
+ raise "Not implemented"
73
+ end
74
+
75
+ def to_s
76
+ raise "Not implemented"
77
+ end
78
+ end
79
+
80
+ class SpotifySource < Source
81
+ def initialize(context, track)
82
+ @context = context
83
+ @track = track
84
+ end
85
+
86
+ def grooveshark_query
87
+ "artist:\"#{@track.artist}\" title:\"#{@track.name}\""
88
+ end
89
+
90
+ def to_s
91
+ "#{@track.artist} - #{@track.name}"
92
+ end
93
+ end
94
+
95
+ class PlainSource < Source
96
+ attr_reader :grooveshark_query, :to_s
97
+
98
+ def initialize(context, id)
99
+ parts = id.split(' - ', 2)
100
+ if parts.length < 2
101
+ @grooveshark_query = id
102
+ @to_s = id
103
+ else
104
+ artist, name = parts
105
+ @grooveshark_query = "artist:\"#{artist}\" title:\"#{name}\""
106
+ @to_s = "#{artist} - #{name}"
107
+ end
108
+ end
109
+ end
110
+
111
+ class ResolvedSong
112
+ def initialize(context, source)
113
+ @context = context
114
+ @source = source
115
+ end
116
+
117
+ def from_grooveshark
118
+ GroovesharkSong.new(@context, @source.grooveshark_query)
119
+ end
120
+
121
+ def to_s
122
+ "#{@source}"
123
+ end
35
124
  end
36
125
 
37
- def download_url(url, path)
38
- content = open(url).read
39
- file = open(path, 'wb')
40
- file.write(content)
41
- file.close
126
+ class GroovesharkSong
127
+ def initialize(context, query)
128
+ @context = context
129
+ @raw_grooveshark_song = @context[:grooveshark].song(query)
130
+ end
131
+
132
+ def downloaded
133
+ File.exists?(filename)
134
+ end
135
+
136
+ def download
137
+ download_url(@context[:grooveshark].song_url(@raw_grooveshark_song))
138
+ end
139
+
140
+ def to_s
141
+ "#{@raw_grooveshark_song.artist} - #{@raw_grooveshark_song.name}"
142
+ end
143
+
144
+ private
145
+
146
+ def filename
147
+ "#{@raw_grooveshark_song.artist} - #{@raw_grooveshark_song.name}.mp3"
148
+ .tr('/', '-') # / is not allowed in file names
149
+ end
150
+
151
+ def download_url(url)
152
+ content = open(url).read
153
+ file = open(filename, 'wb')
154
+ file.write(content)
155
+ file.close
156
+ end
42
157
  end
43
158
 
44
159
  class Grooveshark
@@ -46,11 +161,15 @@ class SpotifyToMp3
46
161
  @client = ::Grooveshark::Client.new
47
162
  end
48
163
 
49
- def song_url(artist, title)
50
- song = @client.search_songs("artist:\"#{artist}\" title:\"#{title}\"").first
51
- raise "Song not found" if song.nil?
164
+ def song_url(song)
52
165
  @client.get_song_url(song)
53
166
  end
167
+
168
+ def song(query)
169
+ song = @client.search_songs(query).first
170
+ raise "Song not found" if song.nil?
171
+ song
172
+ end
54
173
  end
55
174
 
56
175
  class Spotify
@@ -61,36 +180,14 @@ class SpotifyToMp3
61
180
  end
62
181
 
63
182
  class Track
64
- def initialize(json)
65
- @json = json
66
- end
183
+ attr_reader :name, :artist
67
184
 
68
- def name
69
- @json['track']['name']
70
- end
71
-
72
- def artist
73
- @json['track']['artists'].first['name']
185
+ def initialize(json)
186
+ @name = json['track']['name']
187
+ @artist = json['track']['artists'].first['name']
74
188
  end
75
189
  end
76
190
  end
77
191
  end
78
192
 
79
- begin
80
- spotifyToMp3 = SpotifyToMp3.new(lambda { |message| puts message })
81
-
82
- file = ARGV.first
83
- raise "No songs file specified. Usage: #{$0} file" if file.nil?
84
-
85
- File.open(file).each_line{|spotify_uri|
86
- spotify_uri.strip!
87
- next if spotify_uri.empty?
88
- begin
89
- spotifyToMp3.download_song(spotify_uri)
90
- rescue
91
- puts "ERROR: #{$!}".red
92
- end
93
- }
94
- rescue
95
- puts "ERROR: #{$!}".red
96
- end
193
+ App.new.run
@@ -2,7 +2,7 @@ Gem::Specification.new do |s|
2
2
  s.name = 'spotify-to-mp3'
3
3
  s.summary = 'Spotify to MP3'
4
4
  s.description = 'Download MP3 files of Spotify tracks'
5
- s.version = '0.3.1'
5
+ s.version = '0.4'
6
6
  s.author = 'Francesc Rosàs'
7
7
  s.email = 'francescrosasbosque@gmail.com'
8
8
  s.homepage = 'https://github.com/frosas/spotify-to-mp3'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spotify-to-mp3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: '0.4'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-20 00:00:00.000000000Z
12
+ date: 2011-10-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: grooveshark
16
- requirement: &70186426570380 !ruby/object:Gem::Requirement
16
+ requirement: &70201790964320 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70186426570380
24
+ version_requirements: *70201790964320
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: colorize
27
- requirement: &70186426569600 !ruby/object:Gem::Requirement
27
+ requirement: &70201790963620 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70186426569600
35
+ version_requirements: *70201790963620
36
36
  description: Download MP3 files of Spotify tracks
37
37
  email: francescrosasbosque@gmail.com
38
38
  executables: