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 +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/gmusic.rb +59 -0
- data/lib/gmusic/parser.rb +53 -0
- data/spec/gmusic_spec.rb +43 -0
- data/spec/parser_spec.rb +67 -0
- data/spec/spec_helper.rb +14 -0
- metadata +100 -0
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.
|
data/README.rdoc
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/gmusic.rb
ADDED
@@ -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
|
data/spec/gmusic_spec.rb
ADDED
@@ -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
|
data/spec/parser_spec.rb
ADDED
@@ -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
|
+
凯莉 克莱克森(Kelly Clarkson)
|
35
|
+
<br>
|
36
|
+
专辑:
|
37
|
+
All I Ever Wanted
|
38
|
+
<br>
|
39
|
+
公司:
|
40
|
+
RCA唱片(索尼BMG)
|
41
|
+
<br>
|
42
|
+
<br>
|
43
|
+
Remember all the things we wanted<br> Now all our memories, they're haunted<br> We were always meant to say goodbye<br> Even with our fists held high<br> It never would've worked out right<br> We were never meant for do or die<br> I didn't want us to burn out<br> I didn't come here to hold you<br> Now I can't stop<br> I want you to know that it doesn't matter<br> Where we take this road<br> Someone's gotta go<br> And I want you to know<br> You couldn't have loved me better<br> But I want you to move on<br> So I'm already gone<br> Looking at you makes it harder<br> But I know that you'll find another<br> That doesn'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'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't matter<br> Where we take this road<br> Someone's gotta go<br> And I want you to know<br> You couldn't have loved me better<br> But I want you to move on<br> So I'm already gone<br> I'm already gone, already gone<br> You can't make it feel right<br> When you know that it's wrong<br> I'm already gone, already gone<br> There's no moving on<br> So I'm already gone<br> Remember all the things we wanted<br> Now all our memories, they're haunted<br> We were always meant to say goodbye<br> I want you to know that it doesn't matter<br> Where we take this road<br> Someone's gotta go<br> And I want you to know<br> You couldn't have loved me better<br> But I want you to move on<br> So I'm already gone<br> I'm already gone, already gone<br> You can't make it feel right<br> When you know that it's wrong<br> I'm already gone, already gone<br> There's no moving on<br> So I'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 MB'
|
62
|
+
result[:format].should == 'MP3'
|
63
|
+
# & 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|