zhangmen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.project ADDED
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>zhangmen</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>com.aptana.ide.core.unifiedBuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>com.aptana.ruby.core.rubynature</nature>
16
+ </natures>
17
+ </projectDescription>
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'http://rubygems.org'
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem 'activesupport', '>= 2.3.5'
5
+ gem 'json'
6
+ gem 'mechanize'
7
+ gem 'nokogiri'
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem 'rdoc', '>= 3.6.0'
13
+ gem 'rspec', '~> 2.6.0'
14
+ gem 'bundler', '~> 1.0.0'
15
+ gem 'jeweler', '~> 1.6.2'
16
+ gem 'rcov', '>= 0'
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.6.2)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ json (1.5.2)
11
+ mechanize (1.0.0)
12
+ nokogiri (>= 1.2.1)
13
+ nokogiri (1.4.5)
14
+ rake (0.9.2)
15
+ rcov (0.9.9)
16
+ rdoc (3.6.1)
17
+ rspec (2.6.0)
18
+ rspec-core (~> 2.6.0)
19
+ rspec-expectations (~> 2.6.0)
20
+ rspec-mocks (~> 2.6.0)
21
+ rspec-core (2.6.4)
22
+ rspec-expectations (2.6.0)
23
+ diff-lcs (~> 1.1.2)
24
+ rspec-mocks (2.6.0)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bundler (~> 1.0.0)
31
+ jeweler (~> 1.6.2)
32
+ json
33
+ mechanize
34
+ nokogiri
35
+ rcov
36
+ rdoc (>= 3.6.0)
37
+ rspec (~> 2.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Victor Costan
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,42 @@
1
+ = zhangmen
2
+
3
+ Fetches music from the Baidu streaming music player.
4
+
5
+ == Usage
6
+
7
+ The code currently requires the string encoding support in Ruby 1.9. Use MRI 1.9.2 or Rubinius / Jruby in 1.9 mode.
8
+
9
+ rvm use 1.9.2
10
+
11
+ First get the directory containing all the categories and playlists.
12
+
13
+ zhangmen list
14
+
15
+ Then choose a playlist and download all its songs.
16
+
17
+ zhangmen fetch 602
18
+
19
+ Or, if you're feeling undecided, grab the entire library.
20
+
21
+ zhangmen all
22
+
23
+ The songs will be downloaded in the current directory. One directory will be made for each artist, and all that artist's songs will be saved there.
24
+
25
+ == Known Issues
26
+
27
+ Some songs might not download. The Flash player skips over those songs too, so it seems to be a server issue.
28
+
29
+ == Contributing to zhangmen
30
+
31
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
32
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
33
+ * Fork the project
34
+ * Start a feature/bugfix branch
35
+ * Commit and push until you are happy with your contribution
36
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
37
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
38
+
39
+ == Copyright
40
+
41
+ Copyright (c) 2011 Victor Costan. See LICENSE.txt for further details.
42
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "zhangmen"
18
+ gem.homepage = "http://github.com/pwnall/zhangmen"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{CLI and library for downloading music from Baidu}
21
+ gem.description = %Q{CLI and library for downloading music from Baidu}
22
+ gem.email = "victor@costan.us"
23
+ gem.authors = ["Victor Costan"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "zhangmen #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/zhangmen ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'zhangmen'
5
+
6
+ Zhangmen::Cli.new.run ARGV
@@ -0,0 +1,120 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+
4
+ # :nodoc: namespace
5
+ module Zhangmen
6
+
7
+ # Command-line interface.
8
+ class Cli
9
+ def scan_categories
10
+ category_id = 1
11
+ empty_categories = 0
12
+
13
+ loop do
14
+ begin
15
+ playlists = @client.category category_id
16
+ save_client_cache
17
+ if playlists.empty?
18
+ empty_categories += 1
19
+ break if empty_categories == 5
20
+ else
21
+ empty_categories = 0
22
+ yield category_id, playlists
23
+ end
24
+ category_id += 1
25
+ rescue Exception => e
26
+ puts "#{e.class.name}: #{e}"
27
+ puts e.backtrace.join("\n")
28
+ break
29
+ end
30
+ end
31
+ category_id
32
+ end
33
+
34
+ def categories
35
+ scan_categories do |id, playlists|
36
+ puts "Category #{id}"
37
+ playlists.each do |list|
38
+ puts " #{list[:name]} - #{list[:id]} - #{list[:song_count]} songs"
39
+ end
40
+ end
41
+ end
42
+
43
+ def playlist(list_id)
44
+ songs = @client.playlist :id => list_id
45
+ # save_client_cache
46
+
47
+ songs.each do |song|
48
+ print "#{song[:author]} - #{song[:title]} ... "
49
+ bits = @client.song song
50
+ if bits
51
+ FileUtils.mkdir_p song[:author]
52
+ filename = File.join song[:author],
53
+ "#{song[:author]} - #{song[:title]}.mp3"
54
+ File.open filename, 'w' do |f|
55
+ f.write bits
56
+ end
57
+ print "ok\n"
58
+ else
59
+ print "FAIL\n"
60
+ end
61
+ end
62
+ end
63
+
64
+ def all
65
+ scan_categories do |id, playlists|
66
+ puts "Category #{id}"
67
+ playlists.each do |list|
68
+ puts " #{list[:name]} - #{list[:id]} - #{list[:song_count]} songs"
69
+ playlist list[:id]
70
+ end
71
+ end
72
+ end
73
+
74
+ # Points to the client's metadata cache file.
75
+ def client_cache_path
76
+ File.expand_path '~/.zhangmen_cache'
77
+ end
78
+
79
+ # Last snapshot of the client's metadata cache.
80
+ def client_cache
81
+ if File.exist? client_cache_path
82
+ YAML.load(File.read(client_cache_path)) || {} rescue {}
83
+ else
84
+ {}
85
+ end
86
+ end
87
+
88
+ # Saves a new snapshot of the client's metadata cache.
89
+ def client_cache=(new_contents)
90
+ begin
91
+ File.open(client_cache_path, 'w') do |f|
92
+ YAML.dump new_contents, f
93
+ end
94
+ rescue ArgumentError => e
95
+ # Encoding error; bummer, can't cache
96
+ end
97
+ end
98
+
99
+ # Saves a new snapshot of the client's metadata cache.
100
+ def save_client_cache
101
+ self.client_cache = @client.cache
102
+ end
103
+
104
+ # Runs a command.
105
+ def run(args)
106
+ @client = Zhangmen::Client.new
107
+ @client.cache = client_cache
108
+
109
+ case args[0]
110
+ when 'list'
111
+ categories
112
+ when 'fetch'
113
+ args[1..-1].each { |arg| playlist arg }
114
+ when 'all'
115
+ all
116
+ end
117
+ end
118
+ end # class Zhangmen::Cli
119
+
120
+ end # namespace Zhangmen
@@ -0,0 +1,154 @@
1
+ require 'cgi'
2
+
3
+ require 'mechanize'
4
+ require 'nokogiri'
5
+
6
+ # :nodoc: namespace
7
+ module Zhangmen
8
+
9
+ # Wraps a client session for accessing Baidu's streaming music service.
10
+ class Client
11
+ def initialize
12
+ @mech = mechanizer
13
+ @cache = {}
14
+ end
15
+
16
+ # Cache of HTTP requests / responses.
17
+ #
18
+ # The cache covers all the song metadata, but not actual song data.
19
+ attr_accessor :cache
20
+
21
+ # Fetches a collection of playlists by the category ID.
22
+ #
23
+ # Args:
24
+ # category_id:: right now, categories seem to be numbers, so enumerating
25
+ # from 1 onwards should be good
26
+ #
27
+ # Returns an array of playlists.
28
+ def category(category_id)
29
+ result = op 3, :list_cat => category_id
30
+ result.css('data').map do |playlist_node|
31
+ {
32
+ :id => playlist_node.css('id').inner_text,
33
+ :name => playlist_node.css('name').inner_text.encode('UTF-8'),
34
+ :song_count => playlist_node.css('tcount').inner_text.to_i
35
+ }
36
+ end
37
+ end
38
+
39
+ # Fetches a playlist.
40
+ #
41
+ # Args:
42
+ # list:: a playlist obtained by calling category
43
+ #
44
+ # Returns an array of songs.
45
+ def playlist(list)
46
+ result = op 22, :listid => list[:id]
47
+ native_encoding = result.document.encoding
48
+
49
+ count = result.css('count').inner_text.to_i
50
+ result.css('data').map do |song_node|
51
+ raw_name = song_node.css('name').inner_text
52
+ if match = /^(.*)\$\$(.*)\$\$\$\$/.match(raw_name)
53
+ title = match[1].encode('UTF-8')
54
+ author = match[2].encode('UTF-8')
55
+ else
56
+ author = title = raw_name.encode('UTF-8')
57
+ end
58
+
59
+ {
60
+ :raw_name => raw_name.encode(native_encoding),
61
+ :title => title, :author => author,
62
+ :id => song_node.css('id').inner_text
63
+ }
64
+ end
65
+ end
66
+
67
+ # Fetches the MP3 contents of a song.
68
+ #
69
+ # Args:
70
+ # song:: a song obtained by calling playlist
71
+ #
72
+ # Returns the MP3 bits.
73
+ def song(entry)
74
+ song_sources(entry).each do |src|
75
+ 3.times do
76
+ begin
77
+ result = @mech.get src[:url]
78
+ next unless result.kind_of?(Mechanize::File)
79
+ bits = result.body
80
+ if bits[-256, 3] == 'TAG' || bits[0, 3] == 'ID3'
81
+ return bits
82
+ else
83
+ break
84
+ end
85
+ rescue EOFError
86
+ # Server hung up on us. Try again in case the error is temporary.
87
+ rescue Timeout::Error
88
+ # Server hung up on us. Try again in case the error is temporary.
89
+ rescue Mechanize::ResponseCodeError
90
+ # 500-ish response. Try again in case the error is temporary.
91
+ end
92
+ end
93
+ end
94
+ nil
95
+ end
96
+
97
+ # Fetches the MP3 download locations for a song.
98
+ #
99
+ # Args:
100
+ # song:: a song obtained by calling playlist
101
+ #
102
+ # Returns
103
+ def song_sources(entry)
104
+ result = op 12, :count => 1, :mtype => 1, :title => entry[:raw_name],
105
+ :url => '', :listenreelect => 0
106
+ result.css('url').map do |url_node|
107
+ filename = url_node.css('decode').inner_text
108
+ encoded_url = url_node.css('encode').inner_text
109
+ url = File.join File.dirname(encoded_url), filename
110
+ {
111
+ :url => url,
112
+ :type => url_node.css('type').inner_text.to_i,
113
+ :lyrics_id => url_node.css('lrid').inner_text.to_i,
114
+ :flag => url_node.css('flag').inner_text
115
+ }
116
+ end
117
+ end
118
+
119
+ # Performs a numbered operation.
120
+ #
121
+ # Args:
122
+ # opcode:: operation number (e.g. 22 for playlist fetch)
123
+ # args:: operation arguments (e.g. :listid => number for playlist ID)
124
+ #
125
+ # Returns a Nokogiri root node.
126
+ def op(opcode, args)
127
+ url = op_url(opcode, args)
128
+ cache_key = url.to_s
129
+ @cache[cache_key] ||= @mech.get(url).body
130
+ Nokogiri.XML(@cache[cache_key]).root
131
+ end
132
+
133
+ # The fetch URL for an XML opcode.
134
+ def op_url(opcode, args)
135
+ query = { :op => opcode }.merge(args).
136
+ map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.
137
+ join('&') + '&.r=' + ('%.16f' % Kernel.rand)
138
+ URI.parse "http://#{hostname}/x?#{query}"
139
+ end
140
+
141
+ # The service hostname.
142
+ def hostname
143
+ 'box.zhangmen.baidu.com'
144
+ end
145
+
146
+ # Mechanize instance customized to maximize fetch success.
147
+ def mechanizer
148
+ mech = Mechanize.new
149
+ mech.user_agent_alias = 'Linux Firefox'
150
+ mech
151
+ end
152
+ end # class Zhangmen::Client
153
+
154
+ end # namespace Zhangmen
data/lib/zhangmen.rb ADDED
@@ -0,0 +1,7 @@
1
+ # Documentation here.
2
+ module Zhangmen
3
+
4
+ end # namespace Zhangmen
5
+
6
+ require 'zhangmen/client.rb'
7
+ require 'zhangmen/cli.rb'
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'zhangmen'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,108 @@
1
+ # :encoding: UTF-8
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+
4
+ describe Zhangmen::Client do
5
+ let(:client) { Zhangmen::Client.new }
6
+
7
+ describe 'op_url' do
8
+ it 'encodes everything correctly' do
9
+ Kernel.should_receive(:rand).and_return(0.42)
10
+ client.op_url(22, :listid => 600).to_s.should ==
11
+ 'http://box.zhangmen.baidu.com/x?op=22&listid=600&.r=0.42' + '0' * 14
12
+ end
13
+ end
14
+
15
+ describe 'op' do
16
+ describe 'with good known arguments' do
17
+ let(:result) { client.op 22, :listid => 600 }
18
+
19
+ it 'returns a Nokogiri root node' do
20
+ result.should be_kind_of(Nokogiri::XML::Element)
21
+ end
22
+
23
+ it 'returns a <result> root node' do
24
+ result.name.should == 'result'
25
+ end
26
+ end
27
+ end
28
+
29
+ describe 'category' do
30
+ describe '1' do
31
+ let(:category) { client.category 1 }
32
+
33
+ it 'should have a non-trivial number of playlists' do
34
+ category.length.should > 10
35
+ end
36
+
37
+ it 'should have a non-empty name for each playlist' do
38
+ category.map { |pl| pl[:name] }.any?(&:empty?).should be_false
39
+ end
40
+
41
+ it 'should have a non-empty download id for each playlist' do
42
+ category.map { |pl| pl[:id] }.any?(&:empty?).should be_false
43
+ end
44
+
45
+ it 'should have a non-empty song count for each playlist' do
46
+ category.map { |pl| pl[:song_count] }.any? { |count| count <= 0 }.
47
+ should be_false
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'playlist' do
53
+ describe 'first one in category 1' do
54
+ let(:playlist) { client.playlist client.category(1).first }
55
+
56
+ it 'should have a non-trivial number of songs' do
57
+ playlist.length.should > 10
58
+ end
59
+
60
+ it 'should have a non-empty raw name in each song' do
61
+ playlist.map { |song| song[:raw_name] }.any?(&:empty?).should be_false
62
+ end
63
+
64
+ it 'should have a non-empty author in each song' do
65
+ playlist.map { |song| song[:author] }.any?(&:empty?).should be_false
66
+ end
67
+
68
+ it 'should have a non-empty title in each song' do
69
+ playlist.map { |song| song[:title] }.any?(&:empty?).should be_false
70
+ end
71
+
72
+ it 'should have a non-empty download id in each song' do
73
+ playlist.map { |song| song[:id] }.any?(&:empty?).should be_false
74
+ end
75
+ end
76
+ end
77
+
78
+ describe 'song_sources' do
79
+ describe 'first song in first playlits in category 1' do
80
+ let(:sources) do
81
+ client.song_sources client.playlist(client.category(1).first)[0]
82
+ end
83
+
84
+ it 'should have a positive number of sources' do
85
+ sources.length.should > 0
86
+ end
87
+
88
+ it 'should have a url key in each source' do
89
+ sources.map { |src| src[:url] }.any?(&:empty?).should be_false
90
+ end
91
+ end
92
+ end
93
+
94
+ describe 'song' do
95
+ describe 'some popular song' do
96
+ let(:entry) { client.playlist(client.category(5).first).first }
97
+ let(:bits) { client.song entry }
98
+
99
+ it 'should be a string' do
100
+ bits.should respond_to(:to_str)
101
+ end
102
+
103
+ it 'should be more than 1Mb in size' do
104
+ bits.length.should >= 2 ** 20
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Zhangmen do
4
+ pending "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/zhangmen.gemspec ADDED
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{zhangmen}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Victor Costan}]
12
+ s.date = %q{2011-07-09}
13
+ s.description = %q{CLI and library for downloading music from Baidu}
14
+ s.email = %q{victor@costan.us}
15
+ s.executables = [%q{zhangmen}]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".project",
23
+ ".rspec",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/zhangmen",
31
+ "lib/zhangmen.rb",
32
+ "lib/zhangmen/cli.rb",
33
+ "lib/zhangmen/client.rb",
34
+ "spec/spec_helper.rb",
35
+ "spec/zhangmen/client_spec.rb",
36
+ "spec/zhangmen_spec.rb",
37
+ "zhangmen.gemspec"
38
+ ]
39
+ s.homepage = %q{http://github.com/pwnall/zhangmen}
40
+ s.licenses = [%q{MIT}]
41
+ s.require_paths = [%q{lib}]
42
+ s.rubygems_version = %q{1.8.5}
43
+ s.summary = %q{CLI and library for downloading music from Baidu}
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<json>, [">= 0"])
50
+ s.add_runtime_dependency(%q<mechanize>, [">= 0"])
51
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
52
+ s.add_development_dependency(%q<rdoc>, [">= 3.6.0"])
53
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
54
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
55
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
56
+ s.add_development_dependency(%q<rcov>, [">= 0"])
57
+ else
58
+ s.add_dependency(%q<json>, [">= 0"])
59
+ s.add_dependency(%q<mechanize>, [">= 0"])
60
+ s.add_dependency(%q<nokogiri>, [">= 0"])
61
+ s.add_dependency(%q<rdoc>, [">= 3.6.0"])
62
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
63
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
65
+ s.add_dependency(%q<rcov>, [">= 0"])
66
+ end
67
+ else
68
+ s.add_dependency(%q<json>, [">= 0"])
69
+ s.add_dependency(%q<mechanize>, [">= 0"])
70
+ s.add_dependency(%q<nokogiri>, [">= 0"])
71
+ s.add_dependency(%q<rdoc>, [">= 3.6.0"])
72
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ end
77
+ end
78
+
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zhangmen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Victor Costan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-09 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &26919700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *26919700
25
+ - !ruby/object:Gem::Dependency
26
+ name: mechanize
27
+ requirement: &26919100 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *26919100
36
+ - !ruby/object:Gem::Dependency
37
+ name: nokogiri
38
+ requirement: &26918520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *26918520
47
+ - !ruby/object:Gem::Dependency
48
+ name: rdoc
49
+ requirement: &26917940 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 3.6.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *26917940
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &26917340 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 2.6.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *26917340
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: &26916760 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *26916760
80
+ - !ruby/object:Gem::Dependency
81
+ name: jeweler
82
+ requirement: &26916200 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: 1.6.2
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *26916200
91
+ - !ruby/object:Gem::Dependency
92
+ name: rcov
93
+ requirement: &26915680 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *26915680
102
+ description: CLI and library for downloading music from Baidu
103
+ email: victor@costan.us
104
+ executables:
105
+ - zhangmen
106
+ extensions: []
107
+ extra_rdoc_files:
108
+ - LICENSE.txt
109
+ - README.rdoc
110
+ files:
111
+ - .document
112
+ - .project
113
+ - .rspec
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - LICENSE.txt
117
+ - README.rdoc
118
+ - Rakefile
119
+ - VERSION
120
+ - bin/zhangmen
121
+ - lib/zhangmen.rb
122
+ - lib/zhangmen/cli.rb
123
+ - lib/zhangmen/client.rb
124
+ - spec/spec_helper.rb
125
+ - spec/zhangmen/client_spec.rb
126
+ - spec/zhangmen_spec.rb
127
+ - zhangmen.gemspec
128
+ homepage: http://github.com/pwnall/zhangmen
129
+ licenses:
130
+ - MIT
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ segments:
142
+ - 0
143
+ hash: 1290430065827447959
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 1.8.5
153
+ signing_key:
154
+ specification_version: 3
155
+ summary: CLI and library for downloading music from Baidu
156
+ test_files: []