xiami_sauce 0.0.1 → 0.0.2
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.
- checksums.yaml +7 -0
- data/.travis.yml +6 -0
- data/Gemfile +13 -2
- data/Guardfile +13 -0
- data/README.md +34 -17
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/bin/xsauce +10 -0
- data/lib/xiami_sauce/cli.rb +23 -0
- data/lib/xiami_sauce/downloader.rb +68 -0
- data/lib/xiami_sauce/track.rb +105 -0
- data/lib/xiami_sauce/track_list.rb +85 -0
- data/lib/xiami_sauce.rb +5 -2
- data/spec/spec_helper.rb +7 -0
- data/spec/xiami_sauce/downloader_spec.rb +5 -0
- data/spec/xiami_sauce/track_list_spec.rb +82 -0
- data/spec/xiami_sauce/track_spec.rb +20 -0
- data/xiami_sauce.gemspec +9 -4
- metadata +114 -26
- data/lib/xiami_sauce/version.rb +0 -3
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ca6f3d6701281dfc430d2fd2f17bdb58dc622c92
|
4
|
+
data.tar.gz: 97b99e4e6a7d83017b026415bf0ed1641cf6db5f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0831a00e09656a48b0d1b5619889c3c1f48e9a5960c994d8a337ee7307aeba267100ee2cabc8ee84b6e051aaeb67ffd6f0237bb1890d985b0903cd5f743f376b
|
7
|
+
data.tar.gz: 92ee592a6ecd6256f04d9907d760c5cf1f7f2bef11299467338deaaa4d0da58fde895c79d005dac83ca7d61db6129196f06d1d69094659462f32b2676324f48e
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,4 +1,15 @@
|
|
1
|
-
source '
|
1
|
+
source 'http://ruby.taobao.org'
|
2
|
+
# source 'https://rubygems.org'
|
2
3
|
|
3
|
-
# Specify your gem's dependencies in xiami_sauce.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
gem 'guard'
|
7
|
+
gem 'guard-bundler'
|
8
|
+
gem 'guard-rspec'
|
9
|
+
|
10
|
+
gem 'rb-fsevent', '>= 0.3.9'
|
11
|
+
gem 'rb-inotify', '>= 0.5.1'
|
12
|
+
|
13
|
+
# Notification System
|
14
|
+
gem 'terminal-notifier-guard', require: RUBY_PLATFORM.downcase.include?("darwin") ? 'terminal-notifier-guard' : nil
|
15
|
+
gem 'libnotify', require: RUBY_PLATFORM.downcase.include?("linux") ? 'libnotify' : nil
|
data/Guardfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
RVM_PREFIX = "env -u RUBYOPT rvm 1.8.7,1.9.3-p327,2.0.0 do"
|
2
|
+
BUNDLE_CMD = lambda { puts "Bundling gems..."; puts `#{RVM_PREFIX} bundle install` }
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch(/^.+\.gemspec/, &BUNDLE_CMD)
|
6
|
+
watch('Gemfile', &BUNDLE_CMD)
|
7
|
+
end
|
8
|
+
|
9
|
+
guard 'rspec', cli: "--color --format documentation" do
|
10
|
+
watch(%r{^spec/.+_spec\.rb$})
|
11
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
12
|
+
watch('spec/spec_helper.rb') { "spec" }
|
13
|
+
end
|
data/README.md
CHANGED
@@ -1,29 +1,46 @@
|
|
1
|
-
# XiamiSauce
|
1
|
+
# 虾米酱 - XiamiSauce
|
2
2
|
|
3
|
-
|
3
|
+
[](http://badge.fury.io/rb/xiami_sauce)
|
4
|
+
[](https://travis-ci.org/ranmocy/xiami_sauce)
|
5
|
+
[](https://gemnasium.com/ranmocy/xiami_sauce)
|
6
|
+
[](https://codeclimate.com/github/ranmocy/xiami_sauce)
|
4
7
|
|
5
|
-
|
8
|
+
虾米酱是一个虾米音乐下载器。封装成为一个 Ruby Gem 包。
|
6
9
|
|
7
|
-
|
10
|
+
XiamiSauce is a downloader of Xiami Music. Writed in Ruby, and presented as a Gem.
|
8
11
|
|
9
|
-
|
12
|
+
## Why
|
10
13
|
|
11
|
-
|
14
|
+
You know, if you know it.
|
12
15
|
|
13
|
-
|
16
|
+
## 依赖 - Dependency
|
14
17
|
|
15
|
-
|
18
|
+
虾米酱在 Ruby 1.9.2, 1.9.3, 2.0.0, jRuby, REE 环境下测试。
|
19
|
+
XiamiSauce will be tested under Ruby 1.9.2, 1.9.3, 2.0.0, jRuby, REE.
|
16
20
|
|
17
|
-
|
21
|
+
依赖 Thor, Nokogiri 的 Gem 包。
|
22
|
+
Depend on Gems Thor and Nokogiri.
|
18
23
|
|
19
|
-
##
|
24
|
+
## 安装 - Installation
|
20
25
|
|
21
|
-
|
26
|
+
一句话安装 - Install it with one-line code:
|
22
27
|
|
23
|
-
|
28
|
+
$ gem install xiami_sauce --no-ri --no-doc
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
## 使用方法 - Usage
|
31
|
+
|
32
|
+
$ xsauce http://www.xiami.com/artist/64360
|
33
|
+
$ xsauce http://www.xiami.com/album/355791
|
34
|
+
$ xsauce http://www.xiami.com/song/184616
|
35
|
+
|
36
|
+
虾米酱会按照歌曲信息下载到 `[artist]/[album]/[index].[song].mp3`。
|
37
|
+
|
38
|
+
XiamiSauce will download to `[artist]/[album]/[index].[song].mp3`.
|
39
|
+
|
40
|
+
## 参与 - Contributing
|
41
|
+
|
42
|
+
1. 派生 - Fork it
|
43
|
+
2. 新建特性分支 - Create your feature branch (`git checkout -b my-new-feature`)
|
44
|
+
3. 提交你的修改 - Commit your changes (`git commit -am 'Add some feature'`)
|
45
|
+
4. 推送特性分支 - Push to the branch (`git push origin my-new-feature`)
|
46
|
+
5. 发起合并请求 - Create new Pull Request
|
data/Rakefile
CHANGED
@@ -1 +1,32 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
# Rspec tasks
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
# Version tasks
|
8
|
+
require 'rake/version_task'
|
9
|
+
Rake::VersionTask.new
|
10
|
+
|
11
|
+
# include Rake::DSL if defined?(Rake::DSL)
|
12
|
+
|
13
|
+
RVM_PREFIX = "rvm 1.8.7,1.9.3-p327,2.0.0 do"
|
14
|
+
|
15
|
+
|
16
|
+
namespace :spec do
|
17
|
+
desc "Run on three Rubies"
|
18
|
+
task :platforms do
|
19
|
+
exit $?.exitstatus unless system "#{RVM_PREFIX} bundle install"
|
20
|
+
exit $?.exitstatus unless system "#{RVM_PREFIX} bundle exec rake spec"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
task default: 'spec:platforms'
|
25
|
+
|
26
|
+
desc 'Push everywhere!'
|
27
|
+
task :publish do
|
28
|
+
system %{git push github}
|
29
|
+
system %{git push github --tags}
|
30
|
+
system %{git push gitcafe}
|
31
|
+
system %{git push gitcafe --tags}
|
32
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
data/bin/xsauce
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module XiamiSauce
|
4
|
+
class CLI < Thor
|
5
|
+
default_task :download
|
6
|
+
|
7
|
+
desc 'download URL', 'download the music from a page url.'
|
8
|
+
def download(url=nil)
|
9
|
+
unless url
|
10
|
+
puts "Target Xiami URL[Song, Album, Artist, ShowCollect]:"
|
11
|
+
url = gets.chomp
|
12
|
+
end
|
13
|
+
puts "Preparing to download #{url}."
|
14
|
+
|
15
|
+
list = TrackList.new(url).download
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(url, *args)
|
19
|
+
download(url)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module XiamiSauce
|
5
|
+
class Downloader
|
6
|
+
RETRY_TIMES = 3
|
7
|
+
SLEEP_TIME = 3
|
8
|
+
|
9
|
+
attr_accessor :url, :file
|
10
|
+
|
11
|
+
def initialize(url, file)
|
12
|
+
@url = url
|
13
|
+
@file = file
|
14
|
+
end
|
15
|
+
|
16
|
+
def download
|
17
|
+
FileUtils.mkdir_p(@file.parent) unless @file.parent.exist?
|
18
|
+
|
19
|
+
puts "Downloading #{@file}."
|
20
|
+
return puts 'Skip.'.color(:yellow) if @file.exist?
|
21
|
+
|
22
|
+
retry_count = RETRY_TIMES
|
23
|
+
begin
|
24
|
+
raise unless(download_file url, @file)
|
25
|
+
rescue Exception => e
|
26
|
+
if retry_count == 0
|
27
|
+
puts 'Failed.'.color(:red)
|
28
|
+
else
|
29
|
+
puts "Retry remain #{retry_count -= 1} times.".color(:yellow)
|
30
|
+
sleep(SLEEP_TIME)
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
puts 'Done.'.color(:green)
|
36
|
+
sleep(SLEEP_TIME)
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_file file_path
|
40
|
+
/MPEG/.match(`file -b \"#{file_path}\"`)
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_downloader
|
44
|
+
return :curl if system('which curl >/dev/null')
|
45
|
+
return :wget if system('which wget >/dev/null')
|
46
|
+
raise 'No available downloader, currently support curl and wget.'
|
47
|
+
end
|
48
|
+
|
49
|
+
def download_file url, filename=nil
|
50
|
+
puts "[Debug] DownloadFile::Params #{url}, #{filename}" if $DEBUG
|
51
|
+
|
52
|
+
remote = url && url.to_s.gsub("\"", "\\\\\"")
|
53
|
+
local = filename && filename.to_s.gsub("\"", "\\\\\"")
|
54
|
+
puts "[Debug] DownloadFile::Prepare #{remote}, #{local}" if $DEBUG
|
55
|
+
|
56
|
+
case check_downloader
|
57
|
+
when :curl
|
58
|
+
command = local ? ("curl -\# \"%s\" > \"%s\"" % [remote, local]) : ("curl -O %s" % remote)
|
59
|
+
when :wget
|
60
|
+
command = local ? ("wget -O \"%s\" \"%s\"" % [local, remote]) : ("wget %s" % remote)
|
61
|
+
end
|
62
|
+
|
63
|
+
puts "[Debug] DownloadFile: #{command}" if $DEBUG
|
64
|
+
system(command) && check_file(local)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require 'net/http'
|
3
|
+
require 'nokogiri'
|
4
|
+
require_relative 'downloader'
|
5
|
+
|
6
|
+
module XiamiSauce
|
7
|
+
class Track
|
8
|
+
attr_accessor :id, :index, :name, :location, :url
|
9
|
+
attr_accessor :album_id, :album_cover, :album_name
|
10
|
+
attr_accessor :artist_id, :artist_name
|
11
|
+
# user_id is who use the widget, could be 0.
|
12
|
+
# attr_accessor :user_id, :width, :height, :widgetCode
|
13
|
+
|
14
|
+
# @todo I wish index could be fetch automatically from somewhere.
|
15
|
+
def initialize(song_url, index=nil)
|
16
|
+
@id = song_url.match(/song\/([0-9]+)/)[1]
|
17
|
+
@index = index.to_s
|
18
|
+
|
19
|
+
parse_info
|
20
|
+
end
|
21
|
+
|
22
|
+
def download(parent_path=nil)
|
23
|
+
file = Pathname('.').join((parent_path || album_name), url.split('/').last)
|
24
|
+
@downloader = Downloader.new(url, file)
|
25
|
+
@downloader.download
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_name
|
29
|
+
index_string = index ? index.to_s.rjust(2, '0') + '.' : ''
|
30
|
+
"[#{artist_name}-#{album_name}]#{index_string}#{name}.mp3"
|
31
|
+
end
|
32
|
+
|
33
|
+
def info_src
|
34
|
+
@info_src ||= "http://www.xiami.com/widget/xml-single/uid/0/sid/#{id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def parse_info
|
41
|
+
url_str = URI.parse(info_src)
|
42
|
+
site = Net::HTTP.new(url_str.host, url_str.port)
|
43
|
+
xml = site.get2(url_str.path,{'accept'=>'text/html'}).body
|
44
|
+
doc = Nokogiri::XML(xml)
|
45
|
+
|
46
|
+
doc.css('track *').each do |element|
|
47
|
+
instance_variable_set("@#{element.name}", element.content)
|
48
|
+
end
|
49
|
+
@name = @song_name
|
50
|
+
@url = sospa(@location)
|
51
|
+
end
|
52
|
+
|
53
|
+
def sospa(location)
|
54
|
+
totle = location.to_i
|
55
|
+
new_str = location[1..-1]
|
56
|
+
chu = (new_str.length.to_f / totle).floor
|
57
|
+
yu = new_str.length % totle
|
58
|
+
stor = []
|
59
|
+
|
60
|
+
i = 0
|
61
|
+
while i < yu do
|
62
|
+
index = (chu+1)*i
|
63
|
+
length = chu+1
|
64
|
+
stor[i] = new_str[index...index+length]
|
65
|
+
|
66
|
+
i+=1
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
i = yu
|
71
|
+
while i < totle do
|
72
|
+
index = chu*(i-yu)+(chu+1)*yu
|
73
|
+
length = chu
|
74
|
+
|
75
|
+
stor[i] = new_str[index...index+length]
|
76
|
+
|
77
|
+
i+=1
|
78
|
+
end
|
79
|
+
|
80
|
+
pin_str = ""
|
81
|
+
0.upto(stor[0].length-1) do |ii|
|
82
|
+
0.upto(stor.length-1) do |jj|
|
83
|
+
pin_str += stor[jj][ii...ii+1]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
pin_str = rtan(pin_str)
|
88
|
+
return_str = ""
|
89
|
+
|
90
|
+
0.upto(pin_str.length-1) do |iii|
|
91
|
+
if pin_str[iii...iii+1]=='^'
|
92
|
+
return_str<<"0"
|
93
|
+
else
|
94
|
+
return_str<<pin_str[iii...iii+1]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
return_str
|
99
|
+
end
|
100
|
+
|
101
|
+
def rtan(str)
|
102
|
+
CGI::unescape(str)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require_relative 'track'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module XiamiSauce
|
6
|
+
class URLParseError < StandardError; end
|
7
|
+
class DocParseError < StandardError; end
|
8
|
+
class InvalidURLType < StandardError; end
|
9
|
+
|
10
|
+
class TrackList
|
11
|
+
SONG_SELECTOR = nil
|
12
|
+
ALBUM_SELECTOR = "div#track.album_tracks table.track_list tr td.song_name"
|
13
|
+
ARTIST_SELECTOR = "div#artist_trends table.track_list td.song_name"
|
14
|
+
SHOWCOLLECT_SELECTOR = "div#list_collect div.quote_song_list li span.song_name"
|
15
|
+
|
16
|
+
attr_accessor :list
|
17
|
+
|
18
|
+
def initialize(url=nil)
|
19
|
+
@list = []
|
20
|
+
parse(url) if url
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse(url)
|
24
|
+
@uri, @type = parse_url(url)
|
25
|
+
@doc = parse_doc(@uri)
|
26
|
+
urls = parse_info_from_doc(selector(@type))
|
27
|
+
@list += urls.collect { |url| Track.new(url) }
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def download(target_path=nil)
|
32
|
+
path = (target_path && Pathname.new(target_path.to_s).exists?) || Pathname.new("./")
|
33
|
+
|
34
|
+
puts "Downloading #{@list.size} tracks..."
|
35
|
+
count = @list.uniq.inject(0) do |count, track|
|
36
|
+
count += 1 if track.download
|
37
|
+
end
|
38
|
+
puts "Downloaded #{count}/#{@list.size} tracks."
|
39
|
+
count
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def to_a
|
44
|
+
@list
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(method_name, *args)
|
48
|
+
@list.send(method_name, *args)
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def parse_url(url)
|
56
|
+
uri = URI.parse(url)
|
57
|
+
type = uri.path.split('/')[1].to_sym
|
58
|
+
[uri, type]
|
59
|
+
rescue Exception => e
|
60
|
+
raise URLParseError, e.message
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_doc(uri)
|
64
|
+
site = Net::HTTP.new(uri.host, uri.port)
|
65
|
+
xml = site.get2(uri.path, {'accept' => 'text/html', 'user-agent' => 'Mozilla/5.0'}).body
|
66
|
+
Nokogiri::HTML(xml)
|
67
|
+
rescue Exception => e
|
68
|
+
raise DocParseError, e.message
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_info_from_doc(selector)
|
72
|
+
return [@uri.to_s] unless selector
|
73
|
+
@doc.css(selector).collect { |element| element.css("a").first["href"] }
|
74
|
+
end
|
75
|
+
|
76
|
+
def selector(type)
|
77
|
+
selector_name = "#{type.to_s.upcase}_SELECTOR"
|
78
|
+
unless self.class.const_defined?(selector_name)
|
79
|
+
raise InvalidURLType, "#{type} isn't a supported URL type. Check your URL."
|
80
|
+
end
|
81
|
+
self.class.const_get(selector_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
data/lib/xiami_sauce.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# should define :subject as TrackList
|
4
|
+
shared_context "parse to doc" do |url|
|
5
|
+
before(:all) do
|
6
|
+
track_list = XiamiSauce::TrackList.new
|
7
|
+
@uri, @type = track_list.send(:parse_url, url)
|
8
|
+
track_list.instance_variable_set("@uri", @uri)
|
9
|
+
track_list.instance_variable_set("@type", @type)
|
10
|
+
sleep(2)
|
11
|
+
@doc = track_list.send(:parse_doc, @uri)
|
12
|
+
end
|
13
|
+
before(:each) do
|
14
|
+
subject { XiamiSauce::TrackList.new }
|
15
|
+
subject.instance_variable_set("@doc", @doc)
|
16
|
+
# subject.stubs(:parse_doc).returns(@doc)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# should define :subject as TrackList
|
21
|
+
shared_examples "a right parser" do |type, expected_result|
|
22
|
+
let(:selector) { subject.send(:selector, type) }
|
23
|
+
let(:parse_result) { subject.send(:parse_info_from_doc, selector) }
|
24
|
+
it { expect(parse_result).to be_kind_of Array }
|
25
|
+
it { expect(parse_result.first).to be_kind_of String }
|
26
|
+
it { expect(parse_result.size).to be == expected_result }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe XiamiSauce::TrackList do
|
30
|
+
context "to parse doc" do
|
31
|
+
let(:url) { 'http://www.xiami.com/song/1770450682' }
|
32
|
+
subject { XiamiSauce::TrackList.new }
|
33
|
+
|
34
|
+
it "should parse url" do
|
35
|
+
uri, type = subject.send(:parse_url, url)
|
36
|
+
uri.path.should == '/song/1770450682'
|
37
|
+
type.should == :song
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should parse doc" do
|
41
|
+
uri, type = subject.send(:parse_url, url)
|
42
|
+
subject.instance_variable_set("@uri", uri)
|
43
|
+
subject.instance_variable_set("@type", type)
|
44
|
+
doc = subject.send(:parse_doc, uri)
|
45
|
+
subject.instance_variable_set("@doc", doc)
|
46
|
+
subject.instance_variable_get("@doc").should be_kind_of Nokogiri::HTML::Document
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
context "should support type song" do
|
52
|
+
include_context "parse to doc", 'http://www.xiami.com/song/1770450682'
|
53
|
+
it_should_behave_like "a right parser", "song", 1
|
54
|
+
end
|
55
|
+
|
56
|
+
context "should support type album" do
|
57
|
+
include_context "parse to doc", 'http://www.xiami.com/album/462525'
|
58
|
+
it_should_behave_like "a right parser", "album", 7
|
59
|
+
end
|
60
|
+
|
61
|
+
context "should support type artist" do
|
62
|
+
include_context "parse to doc", 'http://www.xiami.com/artist/19775'
|
63
|
+
it_should_behave_like "a right parser", "artist", 10
|
64
|
+
end
|
65
|
+
|
66
|
+
context "should support type showcollect" do
|
67
|
+
include_context "parse to doc", 'http://www.xiami.com/song/showcollect/id/10100776'
|
68
|
+
it_should_behave_like "a right parser", "showcollect", 50
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
context "doing operations" do
|
73
|
+
let(:a_url) { 'http://www.xiami.com/album/355791' }
|
74
|
+
let(:a_list) { XiamiSauce::TrackList.new(a_url) }
|
75
|
+
let(:b_url) { 'http://www.xiami.com/album/362997' }
|
76
|
+
let(:b_list) { XiamiSauce::TrackList.new(b_url) }
|
77
|
+
|
78
|
+
it "should be able to add" do
|
79
|
+
(a_list << b_list).list.should be == (a_list.list << b_list.list)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XiamiSauce::Track do
|
4
|
+
let(:song_url) { "http://www.xiami.com/song/1770361274" }
|
5
|
+
let(:index) { 9 }
|
6
|
+
|
7
|
+
subject { XiamiSauce::Track.new(song_url, index) }
|
8
|
+
|
9
|
+
its('id') { should == '1770361274' }
|
10
|
+
its('index') { should == '9' }
|
11
|
+
its('name') { should == 'Kids (Unedited Version)' }
|
12
|
+
its('url') { should == 'http://f1.xiami.net/11768/454588/02_1770361274_2491919.mp3' }
|
13
|
+
its('album_id') { should == '454588' }
|
14
|
+
its('album_cover') { should == 'http://img.xiami.com/./images/album/img68/11768/4545881311563810_3.jpg' }
|
15
|
+
its('album_name') { should == 'The Way I Am' }
|
16
|
+
its('artist_id') { should == '11768' }
|
17
|
+
its('artist_name') { should == 'Eminem' }
|
18
|
+
|
19
|
+
its('file_name') { should == '[Eminem-The Way I Am]09.Kids (Unedited Version).mp3' }
|
20
|
+
end
|
data/xiami_sauce.gemspec
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'xiami_sauce/version'
|
5
3
|
|
6
4
|
Gem::Specification.new do |spec|
|
7
5
|
spec.name = "xiami_sauce"
|
8
|
-
spec.version =
|
6
|
+
spec.version = File.open('VERSION') { |f| f.read }
|
9
7
|
spec.authors = ["Wanzhang Sheng"]
|
10
8
|
spec.email = ["ranmocy@gmail.com"]
|
11
9
|
spec.description = %q{A Xiami Downloader.}
|
12
10
|
spec.summary = %q{A Xiami Downloader.}
|
13
|
-
spec.homepage = ""
|
11
|
+
spec.homepage = "http://ranmocy.github.com/xiami_sauce"
|
14
12
|
spec.license = "MIT"
|
15
13
|
|
16
14
|
spec.files = `git ls-files`.split($/)
|
@@ -18,6 +16,13 @@ Gem::Specification.new do |spec|
|
|
18
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
17
|
spec.require_paths = ["lib"]
|
20
18
|
|
19
|
+
spec.add_dependency 'thor', '~> 0.17.0'
|
20
|
+
spec.add_dependency 'nokogiri', '~> 1.5.0'
|
21
|
+
spec.add_dependency 'rainbow', '~> 1.1.0'
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
24
|
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency 'version'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'mocha', '~> 0.13.0'
|
23
28
|
end
|
metadata
CHANGED
@@ -1,92 +1,180 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xiami_sauce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Wanzhang Sheng
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
11
|
+
date: 2013-03-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.17.0
|
20
|
+
type: :runtime
|
15
21
|
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.17.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.5.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rainbow
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.1.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
16
56
|
name: bundler
|
17
57
|
requirement: !ruby/object:Gem::Requirement
|
18
58
|
requirements:
|
19
59
|
- - ~>
|
20
60
|
- !ruby/object:Gem::Version
|
21
61
|
version: '1.3'
|
22
|
-
none: false
|
23
62
|
type: :development
|
63
|
+
prerelease: false
|
24
64
|
version_requirements: !ruby/object:Gem::Requirement
|
25
65
|
requirements:
|
26
66
|
- - ~>
|
27
67
|
- !ruby/object:Gem::Version
|
28
68
|
version: '1.3'
|
29
|
-
none: false
|
30
69
|
- !ruby/object:Gem::Dependency
|
31
|
-
prerelease: false
|
32
70
|
name: rake
|
33
71
|
requirement: !ruby/object:Gem::Requirement
|
34
72
|
requirements:
|
35
|
-
- -
|
73
|
+
- - '>='
|
36
74
|
- !ruby/object:Gem::Version
|
37
75
|
version: '0'
|
38
|
-
none: false
|
39
76
|
type: :development
|
77
|
+
prerelease: false
|
40
78
|
version_requirements: !ruby/object:Gem::Requirement
|
41
79
|
requirements:
|
42
|
-
- -
|
80
|
+
- - '>='
|
43
81
|
- !ruby/object:Gem::Version
|
44
82
|
version: '0'
|
45
|
-
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: version
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: mocha
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.13.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.13.0
|
46
125
|
description: A Xiami Downloader.
|
47
126
|
email:
|
48
127
|
- ranmocy@gmail.com
|
49
|
-
executables:
|
128
|
+
executables:
|
129
|
+
- xsauce
|
50
130
|
extensions: []
|
51
131
|
extra_rdoc_files: []
|
52
132
|
files:
|
53
133
|
- .gitignore
|
134
|
+
- .travis.yml
|
54
135
|
- Gemfile
|
136
|
+
- Guardfile
|
55
137
|
- LICENSE.txt
|
56
138
|
- README.md
|
57
139
|
- Rakefile
|
140
|
+
- VERSION
|
141
|
+
- bin/xsauce
|
58
142
|
- lib/xiami_sauce.rb
|
59
|
-
- lib/xiami_sauce/
|
143
|
+
- lib/xiami_sauce/cli.rb
|
144
|
+
- lib/xiami_sauce/downloader.rb
|
145
|
+
- lib/xiami_sauce/track.rb
|
146
|
+
- lib/xiami_sauce/track_list.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
- spec/xiami_sauce/downloader_spec.rb
|
149
|
+
- spec/xiami_sauce/track_list_spec.rb
|
150
|
+
- spec/xiami_sauce/track_spec.rb
|
60
151
|
- xiami_sauce.gemspec
|
61
|
-
homepage:
|
152
|
+
homepage: http://ranmocy.github.com/xiami_sauce
|
62
153
|
licenses:
|
63
154
|
- MIT
|
155
|
+
metadata: {}
|
64
156
|
post_install_message:
|
65
157
|
rdoc_options: []
|
66
158
|
require_paths:
|
67
159
|
- lib
|
68
160
|
required_ruby_version: !ruby/object:Gem::Requirement
|
69
161
|
requirements:
|
70
|
-
- -
|
162
|
+
- - '>='
|
71
163
|
- !ruby/object:Gem::Version
|
72
|
-
segments:
|
73
|
-
- 0
|
74
164
|
version: '0'
|
75
|
-
hash: -3039331413833145677
|
76
|
-
none: false
|
77
165
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
166
|
requirements:
|
79
|
-
- -
|
167
|
+
- - '>='
|
80
168
|
- !ruby/object:Gem::Version
|
81
|
-
segments:
|
82
|
-
- 0
|
83
169
|
version: '0'
|
84
|
-
hash: -3039331413833145677
|
85
|
-
none: false
|
86
170
|
requirements: []
|
87
171
|
rubyforge_project:
|
88
|
-
rubygems_version:
|
172
|
+
rubygems_version: 2.0.0
|
89
173
|
signing_key:
|
90
|
-
specification_version:
|
174
|
+
specification_version: 4
|
91
175
|
summary: A Xiami Downloader.
|
92
|
-
test_files:
|
176
|
+
test_files:
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
- spec/xiami_sauce/downloader_spec.rb
|
179
|
+
- spec/xiami_sauce/track_list_spec.rb
|
180
|
+
- spec/xiami_sauce/track_spec.rb
|
data/lib/xiami_sauce/version.rb
DELETED