gmusic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Tower He
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ = GMusic
2
+
3
+ GMusic provides APIs to search songs from http://music.g.cn. There are three query conditions supported by GMusic, which are title, artist and album of a song. GMusic delivers query requests to Google and collects the search result returned from Google.
4
+
5
+ == SYNOPSIS:
6
+
7
+ require 'gmusic'
8
+
9
+ result = GMusic.search(:title => 'heal the world', :artist => 'michael jackson')
10
+
11
+ puts result.class
12
+ # => Array
13
+
14
+ result.first.keys
15
+ # => [:artist, :album, :format, :size, :url, :id, :lyrics, :title]
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * jeweler (1.4.0) http://technicalpickles.github.com/jeweler
20
+ * hpricot (0.8.2) http://github.com/whymirror/hpricot
21
+
22
+ == INSTALL:
23
+
24
+ gem install gmusic
25
+
26
+ == COPYRIGHT:
27
+
28
+ Copyright (c) 2010 Tower He. See LICENSE for details.
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "gmusic"
8
+ gem.summary = %Q{Search songs from http://music.g.cn}
9
+ gem.description = %Q{gmusic provides APIs to search songs from http://music.g.cn. There are three query conditions supported by gmusic, which are title, artist and album of a song. gmusic delivers query requests to Google and collects the search result returned from Google. }
10
+ gem.email = "towerhe@gmail.com"
11
+ gem.homepage = "http://github.com/towerhe/gmusic"
12
+ gem.authors = ["Tower He"]
13
+ gem.add_dependency "hpricot", ">=0.8.2"
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+
16
+ gem.files = FileList['lib/**/*.rb', 'spec/**/*.rb', '[A-Z]*'].to_a
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ task :default => :spec
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "gmusic #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'hpricot'
3
+ require 'open-uri'
4
+
5
+ require 'gmusic/parser'
6
+
7
+ module GMusic
8
+ class << self
9
+ VALID_CONDITIONS = [ :title, :artist ]
10
+
11
+ SEARCH_URL_TEMPLATE = %Q(http://www.google.cn/music/search?q=%s&aq=f)
12
+ DOWNLOAD_INFO_URL_TEMPLATE = %Q(http://www.google.cn/music/top100/musicdownload?id=%s)
13
+ LYRICS_URL_TEMPLATE = %Q(http://www.google.cn/music/top100/lyrics?id=%s)
14
+
15
+
16
+ def search(conditions)
17
+ validate_conditions(conditions)
18
+ query = construct_query(conditions)
19
+ songs = SongListParser.parse(SEARCH_URL_TEMPLATE % query)
20
+
21
+ matched = []
22
+ songs.each do |song|
23
+ if matched?(song, conditions)
24
+ song.merge!(DownloadInfoParser.parse(DOWNLOAD_INFO_URL_TEMPLATE % song[:id]))
25
+ song.merge!(LyricsParser.parse(LYRICS_URL_TEMPLATE % song[:id]))
26
+ matched << song
27
+ end
28
+ end
29
+ matched
30
+ end
31
+
32
+ private
33
+ def validate_conditions(conditions)
34
+ raise_argument_error if conditions.empty? or (conditions.keys & VALID_CONDITIONS).empty?
35
+ end
36
+
37
+ def construct_query(conditions)
38
+ query = ''
39
+ VALID_CONDITIONS.each do |c|
40
+ query << (query.empty? ? '' : '+') << "\"#{conditions[c]}\"" if conditions.has_key? c
41
+ end
42
+ URI.escape query
43
+ end
44
+
45
+ def raise_argument_error
46
+ raise ArgumentError.new('must specify at least one condition')
47
+ end
48
+
49
+ def matched?(result, conditions)
50
+ if conditions.has_key?(:title) and not result[:title].downcase.eql?(conditions[:title].downcase)
51
+ return false
52
+ end
53
+ if conditions.has_key?(:artist) and not result[:artist].downcase.include?(conditions[:artist].downcase)
54
+ return false
55
+ end
56
+ return true
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,53 @@
1
+ module GMusic
2
+ module SongListParser
3
+ class << self
4
+ def parse url
5
+ songs = []
6
+ doc = Hpricot(open(url))
7
+ (doc/'#song_list > tr').each do |tr|
8
+ begin
9
+ song = {}
10
+ id = tr.attributes["id"].gsub(/row/, '')
11
+ next if id.include?('snippetRow')
12
+ song.merge!({ :id => id })
13
+ song.merge!({ :title => (tr/'td.Title > a > b').first.inner_html })
14
+ song.merge!({ :artist => (tr/'td.Artist > a').first.inner_html })
15
+ song.merge!({ :album => (tr/'td.Album > a').first.inner_html })
16
+ songs << song
17
+ rescue
18
+ end
19
+ end
20
+ songs
21
+ end
22
+ end
23
+ end
24
+
25
+ module LyricsParser
26
+ class << self
27
+ def parse url
28
+ doc = Hpricot(open(url))
29
+ begin
30
+ { :lyrics => (doc/'#lyrics').first.inner_html }
31
+ rescue
32
+ nil
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module DownloadInfoParser
39
+ class << self
40
+ def parse url
41
+ result = {}
42
+ doc = Hpricot(open(url))
43
+ begin
44
+ result.merge!({ :size => (doc/'tr.meta-data-tr > td.td-size').first.inner_html })
45
+ result.merge!({ :format => (doc/'tr.meta-data-tr > td.td-format').first.inner_html })
46
+ result.merge!({ :url => (doc/'div.download > a').first.attributes['href'] })
47
+ rescue
48
+ nil
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Gmusic" do
4
+ it "should raise ArgumentError if the argument 'conditions' is empty" do
5
+ lambda { GMusic.search({}) }.should raise_error ArgumentError
6
+ end
7
+
8
+ it "should raise ArgumentError if there is no valid condition specified" do
9
+ lambda { GMusic.search(:invalid => 'test') }.should raise_error ArgumentError
10
+ end
11
+
12
+ it "should return an empty array if nothing is matched" do
13
+ GMusic::SongListParser.should_receive(:parse).and_return([])
14
+ GMusic.search(:title => 'inexistence').empty?.should be_true
15
+ end
16
+
17
+ it "should only return the full matched item" do
18
+ parsed = GMusic::SongListParser.parse path_of('g-search-alreadygone.html')
19
+ dowload_info = GMusic::DownloadInfoParser.parse path_of('g-download.html')
20
+ lyrics = GMusic::LyricsParser.parse path_of('g-lyrics.html')
21
+
22
+ GMusic::SongListParser.should_receive(:parse).and_return(parsed)
23
+ GMusic::DownloadInfoParser.should_receive(:parse).and_return(dowload_info)
24
+ GMusic::LyricsParser.should_receive(:parse).and_return(lyrics)
25
+ songs = GMusic.search(:title => 'already gone', :artist => 'kelly clarkson')
26
+
27
+ songs.size.should == 1
28
+ end
29
+
30
+ it "should return 5 full matched items" do
31
+ parsed = GMusic::SongListParser.parse path_of('g-search-thankyou.html')
32
+ dowload_info = GMusic::DownloadInfoParser.parse path_of('g-download.html')
33
+ lyrics = GMusic::LyricsParser.parse path_of('g-lyrics.html')
34
+
35
+ GMusic::SongListParser.should_receive(:parse).and_return(parsed)
36
+ GMusic::DownloadInfoParser.should_receive(:parse).exactly(5).times.and_return(dowload_info)
37
+ GMusic::LyricsParser.should_receive(:parse).exactly(5).times.and_return(lyrics)
38
+
39
+ songs = GMusic.search(:title => 'thank you', :artist => 'dido')
40
+
41
+ songs.size.should == 5
42
+ end
43
+ end
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module GMusic
4
+ describe SongListParser do
5
+ it "should return an empty array if no record has been found" do
6
+ songs = SongListParser.parse path_of('g-empty.html')
7
+ songs.empty?.should be_true
8
+ end
9
+
10
+ it "should parse all the songs with id, title, artist and album" do
11
+ songs = SongListParser.parse path_of('g-search-thankyou.html')
12
+
13
+ songs.should be_instance_of(Array)
14
+ songs.size.should == 20
15
+
16
+ songs.each do |s|
17
+ s.has_key?(:id).should be_true
18
+ s.has_key?(:title).should be_true
19
+ s.has_key?(:artist).should be_true
20
+ s.has_key?(:album).should be_true
21
+ end
22
+ end
23
+ end
24
+
25
+ describe LyricsParser do
26
+ it "should return nil if no record has been found" do
27
+ result = LyricsParser.parse path_of('g-empty.html')
28
+ result.should be_nil
29
+ end
30
+
31
+ it "should parse the lyrics" do
32
+ lyrics = <<-LYRICS
33
+ 歌手:
34
+ &#20975;&#33673; &#20811;&#33713;&#20811;&#26862;(Kelly Clarkson)
35
+ <br>
36
+ 专辑:&nbsp;
37
+ All I Ever Wanted
38
+ <br>
39
+ 公司:
40
+ RCA&#21809;&#29255;(&#32034;&#23612;BMG)
41
+ <br>
42
+ <br>
43
+ Remember all the things we wanted<br> Now all our memories, they&#39;re haunted<br> We were always meant to say goodbye<br> Even with our fists held high<br> It never would&#39;ve worked out right<br> We were never meant for do or die<br> I didn&#39;t want us to burn out<br> I didn&#39;t come here to hold you<br> Now I can&#39;t stop<br> I want you to know that it doesn&#39;t matter<br> Where we take this road<br> Someone&#39;s gotta go<br> And I want you to know<br> You couldn&#39;t have loved me better<br> But I want you to move on<br> So I&#39;m already gone<br> Looking at you makes it harder<br> But I know that you&#39;ll find another<br> That doesn&#39;t always make you want to cry<br> Started with a perfect kiss<br> Then we could feel the poison set in<br> Perfect couldn&#39;t keep this love alive<br> You know that I love you so<br> I love you enough to let you go<br> I want you to know that it doesn&#39;t matter<br> Where we take this road<br> Someone&#39;s gotta go<br> And I want you to know<br> You couldn&#39;t have loved me better<br> But I want you to move on<br> So I&#39;m already gone<br> I&#39;m already gone, already gone<br> You can&#39;t make it feel right<br> When you know that it&#39;s wrong<br> I&#39;m already gone, already gone<br> There&#39;s no moving on<br> So I&#39;m already gone<br> Remember all the things we wanted<br> Now all our memories, they&#39;re haunted<br> We were always meant to say goodbye<br> I want you to know that it doesn&#39;t matter<br> Where we take this road<br> Someone&#39;s gotta go<br> And I want you to know<br> You couldn&#39;t have loved me better<br> But I want you to move on<br> So I&#39;m already gone<br> I&#39;m already gone, already gone<br> You can&#39;t make it feel right<br> When you know that it&#39;s wrong<br> I&#39;m already gone, already gone<br> There&#39;s no moving on<br> So I&#39;m already gone<br>
44
+ LYRICS
45
+
46
+ result = LyricsParser.parse path_of('g-lyrics.html')
47
+
48
+ result[:lyrics].should == lyrics.strip.gsub(/<br>/, '<br />')
49
+ end
50
+ end
51
+
52
+ describe DownloadInfoParser do
53
+ it "should return nil if no record has been found" do
54
+ result = DownloadInfoParser.parse path_of('g-empty.html')
55
+ result.should be_nil
56
+ end
57
+
58
+ it "should parse the download link" do
59
+ result = DownloadInfoParser.parse path_of('g-download.html')
60
+
61
+ result[:size].should == '6.7&nbsp;MB'
62
+ result[:format].should == 'MP3'
63
+ # &amp; is decoded to &
64
+ result[:url].should == '/music/top100/url?q=http%3A%2F%2Ffile3.top100.cn%2F201002182021%2F6606E229A301A32E3EFEDD028337EA8C%2FSpecial_120398%2FAlready%2520Gone.mp3&ct=rdl&cad=dl&ei=uTB9S5CgOZjssQK5t5X8AQ&sig=9BD4F31BB70F31BAB6AD4AECA3C5200D'
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'gmusic'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
10
+
11
+ def path_of(file)
12
+ File.join(File.dirname(__FILE__), file)
13
+ end
14
+
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gmusic
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Tower He
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-02-22 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: hpricot
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 8
30
+ - 2
31
+ version: 0.8.2
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 9
45
+ version: 1.2.9
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: "gmusic provides APIs to search songs from http://music.g.cn. There are three query conditions supported by gmusic, which are title, artist and album of a song. gmusic delivers query requests to Google and collects the search result returned from Google. "
49
+ email: towerhe@gmail.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE
56
+ - README.rdoc
57
+ files:
58
+ - LICENSE
59
+ - README.rdoc
60
+ - Rakefile
61
+ - VERSION
62
+ - lib/gmusic.rb
63
+ - lib/gmusic/parser.rb
64
+ - spec/gmusic_spec.rb
65
+ - spec/parser_spec.rb
66
+ - spec/spec_helper.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/towerhe/gmusic
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.6
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Search songs from http://music.g.cn
97
+ test_files:
98
+ - spec/gmusic_spec.rb
99
+ - spec/parser_spec.rb
100
+ - spec/spec_helper.rb