bremen 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Guardfile CHANGED
@@ -1,4 +1,3 @@
1
- notification :off
2
1
  guard 'minitest' do
3
2
  watch(%r|^spec/(.*)_spec\.rb|)
4
3
  watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Bremen
2
2
 
3
- TODO: Write a gem description
3
+ **Bremen** provides common search interface for some music websites. it supports YouTube, SoundCloud, MixCloud and Nicovideo
4
4
 
5
5
  ## Installation
6
6
 
@@ -16,9 +16,62 @@ Or install it yourself as:
16
16
 
17
17
  $ gem install bremen
18
18
 
19
+ ## Setting
20
+
21
+ As far as Soundcloud concerned, you need to set consumer key before using.
22
+
23
+ Bremen::Soundcloud.consumer_key = 'your_consumer_key'
24
+
19
25
  ## Usage
20
26
 
21
- TODO: Write usage instructions here
27
+ ### Retrieving a single track
28
+
29
+ call `.find` method with uid(unique key) or url.
30
+
31
+ Bremen::Youtube.find('XXXXXXXXXXX')
32
+ Bremen::Youtube.find('http://www.youtube.com/watch?v=XXXXXXXXXXX')
33
+ Bremen::Soundcloud.find('1111111')
34
+ Bremen::Soundcloud.find('http://soundcloud.com/author/title')
35
+ Bremen::Mixcloud.find('/author/title/')
36
+ Bremen::Mixcloud.find('http://www.mixcloud.com/author/title/')
37
+ Bremen::Nicovideo.find('sm1111111')
38
+ Bremen::Nicovideo.find('http://www.nicovideo.jp/watch/sm1111111')
39
+
40
+ ### Retrieving multiple tracks
41
+
42
+ call `.search` method with keyword.
43
+
44
+ Bremen::Youtube.find(keyword: 'Perfume')
45
+
46
+ #### Optional params
47
+
48
+ You can add optional parameters for filtering. But not supports all official API's filters.
49
+
50
+ Bremen::Youtube.find(keyword: 'KyaryPamyuPamyu', order: 'relevance', limit: 10)
51
+
52
+ ### Track object
53
+
54
+ Retrieving methods return Track object(s).
55
+
56
+ attribute | |
57
+ -----------|----------------------|
58
+ uid | unique key in a site |
59
+ url | |
60
+ title | |
61
+ author | |
62
+ length | duration of track |
63
+ created_at | released datetime |
64
+ updated_at | modified datetime |
65
+
66
+ ## API References
67
+
68
+ - [Reference Guide: Data API Protocol - YouTube — Google Developers](https://developers.google.com/youtube/2.0/reference#Searching_for_videos)
69
+ - [Docs - API - Reference - SoundCloud Developers](http://developers.soundcloud.com/docs/api/reference#tracks)
70
+ - [API documentation | Mixcloud](http://www.mixcloud.com/developers/documentation/#search)
71
+
72
+ ## Supported versions
73
+
74
+ - Ruby 1.9.3 or higher
22
75
 
23
76
  ## Contributing
24
77
 
data/bremen.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Bremen::VERSION
9
9
  gem.authors = ["itzki"]
10
10
  gem.email = ["itzki.h@gmail.com"]
11
- gem.description = %q{integrated searcher of audio track on music sites}
12
- gem.summary = %q{integrated searcher of audio track on music sites}
11
+ gem.description = %q{integrated searcher of audio tracks on music sites}
12
+ gem.summary = %q{Bremen provides common search interface for some music websites. it supports YouTube, SoundCloud, MixCloud and Nicovideo}
13
13
  gem.homepage = "https://github.com/itzki/bremen"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_development_dependency 'guard-minitest', ['>= 0']
21
21
  gem.add_development_dependency 'rb-fsevent', ['>= 0']
22
+ gem.add_development_dependency 'terminal-notifier-guard', ['>= 0']
22
23
  end
data/lib/bremen/base.rb CHANGED
@@ -9,14 +9,20 @@ module Bremen
9
9
  class << self
10
10
  attr_accessor :default_options
11
11
 
12
- def find options = {}
13
- convert_from_response(get(search_url(options)))
12
+ def find uid_or_url
13
+ convert_singly(get(find_url(uid_or_url)))
14
+ end
15
+
16
+ def search options = {}
17
+ convert_multiply(get(search_url(options)))
14
18
  end
15
19
 
16
20
  #abstract methods
21
+ def find_url uid_or_url; end
17
22
  def search_url options = {}; end
18
23
  private
19
- def convert_from_response response; end
24
+ def convert_singly response; end
25
+ def convert_multiply response; end
20
26
  end
21
27
  end
22
28
  end
@@ -3,13 +3,21 @@ require 'bremen/base'
3
3
 
4
4
  module Bremen
5
5
  class Mixcloud < Bremen::Base
6
- BASE_URL = 'http://api.mixcloud.com/search/'
6
+ BASE_URL = 'http://api.mixcloud.com/'
7
7
  self.default_options = {
8
8
  keyword: '',
9
9
  limit: 20,
10
10
  }
11
11
 
12
12
  class << self
13
+ def find_url uid_or_url
14
+ if uid_or_url.to_s.include?('www.mixcloud.com')
15
+ uid_or_url.sub('www.mixcloud.com', 'api.mixcloud.com')
16
+ else
17
+ "#{BASE_URL[0..-2]}#{uid_or_url}"
18
+ end
19
+ end
20
+
13
21
  def search_url options = {}
14
22
  options = default_options.merge(options)
15
23
  query = {
@@ -17,7 +25,7 @@ module Bremen
17
25
  limit: options[:limit],
18
26
  type: 'cloudcast',
19
27
  }
20
- "#{BASE_URL}?#{build_query(query)}"
28
+ "#{BASE_URL}search/?#{build_query(query)}"
21
29
  end
22
30
 
23
31
  def from_api hash = {}
@@ -34,7 +42,11 @@ module Bremen
34
42
  end
35
43
 
36
44
  private
37
- def convert_from_response response
45
+ def convert_singly response
46
+ from_api(JSON.parse(response))
47
+ end
48
+
49
+ def convert_multiply response
38
50
  JSON.parse(response)['data'].map{|t| from_api(t) }
39
51
  end
40
52
  end
@@ -3,7 +3,7 @@ require 'bremen/base'
3
3
 
4
4
  module Bremen
5
5
  class Nicovideo < Bremen::Base
6
- BASE_URL = 'http://www.nicovideo.jp/search/'
6
+ BASE_URL = 'http://www.nicovideo.jp/'
7
7
  self.default_options = {
8
8
  keyword: '',
9
9
  sort: 'f', #n(newer commented)/v(viewed)/r(most commented)/m(listed)/f(uploaded)/l(duration)
@@ -14,6 +14,14 @@ module Bremen
14
14
  }
15
15
 
16
16
  class << self
17
+ def find_url uid_or_url
18
+ unless uid_or_url.include?('www.nicovideo.jp')
19
+ "#{BASE_URL}watch/#{uid_or_url}"
20
+ else
21
+ uid_or_url
22
+ end
23
+ end
24
+
17
25
  def search_url options = {}
18
26
  options = default_options.merge(options)
19
27
  query = {
@@ -23,18 +31,33 @@ module Bremen
23
31
  l_range: options[:length],
24
32
  opt_md: options[:downloadable],
25
33
  }
26
- "#{BASE_URL}#{CGI.escape(options[:keyword])}?#{build_query(query)}"
34
+ "#{BASE_URL}search/#{CGI.escape(options[:keyword])}?#{build_query(query)}"
27
35
  end
28
36
 
29
37
  private
30
- def convert_from_response response
38
+ def convert_singly response
39
+ uid = response.scan(%r{<link rel="canonical" href="/watch/([^"]+)">}).flatten.first
40
+ title = CGI.unescape(response.scan(%r{<meta property="og:title" content="(.+)">}).flatten.first.to_s)
41
+ length = response.scan(%r{<meta property="video:duration" content="(\d+)">}).flatten.first.to_i
42
+ created_at = Time.parse(response.scan(%r{<meta property="video:release_date" content="(.+)">}).flatten.first.to_s)
43
+ new({
44
+ uid: uid,
45
+ url: "#{BASE_URL}watch/#{uid}",
46
+ title: title,
47
+ length: length,
48
+ created_at: created_at,
49
+ updated_at: created_at,
50
+ })
51
+ end
52
+
53
+ def convert_multiply response
31
54
  response.scan(%r{<div class="thumb_col_1">\n<!---->\n(.*?)\n<!---->\n</div></div>}m).flatten.map do |html|
32
55
  uid = html.scan(%r{<table [^>]+ summary="(.+)">}).flatten.first
33
56
  min, sec = html.scan(%r{<p class="vinfo_length"><span>([\d:]+)</span></p>}).flatten.first.to_s.split(':')
34
57
  created_at = Time.parse(html.scan(%r{<strong>(.+:\d\d)</strong>}).flatten.first.to_s.gsub(/\xE5\xB9\xB4|\xE6\x9C\x88|\xE6\x97\xA5/, ''))
35
58
  new({
36
59
  uid: uid,
37
- url: "http://www.nicovideo.jp/watch/#{uid}",
60
+ url: "#{BASE_URL}watch/#{uid}",
38
61
  title: CGI.unescape(html.scan(%r{<a [^>]+ class="watch" [^>]+>(.+)</a>}).flatten.first.to_s),
39
62
  length: min.to_i * 60 + sec.to_i,
40
63
  created_at: created_at,
@@ -3,7 +3,7 @@ require 'bremen/base'
3
3
 
4
4
  module Bremen
5
5
  class Soundcloud < Bremen::Base
6
- BASE_URL = 'http://api.soundcloud.com/tracks.json'
6
+ BASE_URL = 'http://api.soundcloud.com/'
7
7
  self.default_options = {
8
8
  keyword: '',
9
9
  order: 'created_at', #created_at/hotness
@@ -14,17 +14,28 @@ module Bremen
14
14
  class << self
15
15
  attr_accessor :consumer_key
16
16
 
17
- def search_url options = {}
17
+ def build_query options = {}
18
18
  raise %Q{"#{self.name}.consumer_key" must be set} unless consumer_key
19
+ super(options.merge(consumer_key: consumer_key))
20
+ end
21
+
22
+ def find_url uid_or_url
23
+ if uid_or_url.to_s =~ %r{\A\d+\Z}
24
+ "#{BASE_URL}tracks/#{uid_or_url}.json?#{build_query}"
25
+ else
26
+ "#{BASE_URL}resolve.json?#{build_query({url: uid_or_url})}"
27
+ end
28
+ end
29
+
30
+ def search_url options = {}
19
31
  options = default_options.merge(options)
20
32
  query = {
21
33
  q: options[:keyword],
22
34
  order: options[:order],
23
35
  limit: options[:limit],
24
36
  filter: options[:filter],
25
- consumer_key: consumer_key,
26
37
  }
27
- "#{BASE_URL}?#{build_query(query)}"
38
+ "#{BASE_URL}tracks.json?#{build_query(query)}"
28
39
  end
29
40
 
30
41
  def from_api hash = {}
@@ -40,7 +51,11 @@ module Bremen
40
51
  end
41
52
 
42
53
  private
43
- def convert_from_response response
54
+ def convert_singly response
55
+ from_api(JSON.parse(response))
56
+ end
57
+
58
+ def convert_multiply response
44
59
  JSON.parse(response).map{|t| from_api(t) }
45
60
  end
46
61
  end
@@ -1,3 +1,3 @@
1
1
  module Bremen
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -12,13 +12,21 @@ module Bremen
12
12
  }
13
13
 
14
14
  class << self
15
+ def build_query options = {}
16
+ super(options.merge(alt: 'json'))
17
+ end
18
+
19
+ def find_url uid_or_url
20
+ uid = uid_or_url.scan(%r{[?&]v=(.{11})}).flatten.first || uid_or_url
21
+ "#{BASE_URL}#{uid}?#{build_query}"
22
+ end
23
+
15
24
  def search_url options = {}
16
25
  options = default_options.merge(options)
17
26
  query = {
18
27
  vq: options[:keyword],
19
28
  orderby: options[:order],
20
29
  :"max-results" => options[:limit],
21
- alt: 'json',
22
30
  }
23
31
  "#{BASE_URL}-/#{options[:category]}/#{options[:tag]}/?#{build_query(query)}"
24
32
  end
@@ -37,7 +45,11 @@ module Bremen
37
45
  end
38
46
 
39
47
  private
40
- def convert_from_response response
48
+ def convert_singly response
49
+ from_api(JSON.parse(response)['entry'])
50
+ end
51
+
52
+ def convert_multiply response
41
53
  JSON.parse(response)['feed']['entry'].map{|t| from_api(t) }
42
54
  end
43
55
  end
@@ -2,6 +2,23 @@ $:.unshift(File.expand_path('../../', __FILE__))
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Bremen::Mixcloud do
5
+ describe '.find_url' do
6
+ subject{ Bremen::Mixcloud.find_url(uid_or_url) }
7
+ describe 'given id' do
8
+ let(:uid_or_url){ '/author/permalink/' }
9
+ it 'generate' do
10
+ subject.must_equal 'http://api.mixcloud.com/author/permalink/'
11
+ end
12
+ end
13
+
14
+ describe 'given url' do
15
+ let(:uid_or_url){ 'http://www.mixcloud.com/author/permalink/' }
16
+ it 'generate' do
17
+ subject.must_equal 'http://api.mixcloud.com/author/permalink/'
18
+ end
19
+ end
20
+ end
21
+
5
22
  describe '.search_url' do
6
23
  subject{ Bremen::Mixcloud.search_url(params) }
7
24
  describe 'only keyword' do
@@ -19,9 +36,17 @@ describe Bremen::Mixcloud do
19
36
  end
20
37
  end
21
38
 
22
- describe '.convert_from_response' do
23
- subject{ Bremen::Mixcloud.send(:convert_from_response, response) }
24
- let(:response){ fixture('mixcloud.json') }
39
+ describe '.convert_singly' do
40
+ subject{ Bremen::Mixcloud.send(:convert_singly, response) }
41
+ let(:response){ fixture('mixcloud_single.json') }
42
+ it 'convert successfully' do
43
+ subject.title.must_equal 'Title'
44
+ end
45
+ end
46
+
47
+ describe '.convert_multiply' do
48
+ subject{ Bremen::Mixcloud.send(:convert_multiply, response) }
49
+ let(:response){ fixture('mixcloud_multi.json') }
25
50
  it 'convert successfully' do
26
51
  subject.first.title.must_equal 'Title'
27
52
  end
@@ -2,6 +2,23 @@ $:.unshift(File.expand_path('../../', __FILE__))
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Bremen::Nicovideo do
5
+ describe '.find_url' do
6
+ subject{ Bremen::Nicovideo.find_url(uid_or_url) }
7
+ describe 'given id' do
8
+ let(:uid_or_url){ 'sm1111111' }
9
+ it 'generate' do
10
+ subject.must_equal 'http://www.nicovideo.jp/watch/sm1111111'
11
+ end
12
+ end
13
+
14
+ describe 'given url' do
15
+ let(:uid_or_url){ 'http://www.nicovideo.jp/watch/sm1111111' }
16
+ it 'generate' do
17
+ subject.must_equal 'http://www.nicovideo.jp/watch/sm1111111'
18
+ end
19
+ end
20
+ end
21
+
5
22
  describe '.search_url' do
6
23
  subject{ Bremen::Nicovideo.search_url(params) }
7
24
  describe 'only keyword' do
@@ -19,9 +36,17 @@ describe Bremen::Nicovideo do
19
36
  end
20
37
  end
21
38
 
22
- describe '.convert_from_response' do
23
- subject{ Bremen::Nicovideo.send(:convert_from_response, response) }
24
- let(:response){ fixture('nicovideo.html') }
39
+ describe '.convert_singly' do
40
+ subject{ Bremen::Nicovideo.send(:convert_singly, response) }
41
+ let(:response){ fixture('nicovideo_single.html') }
42
+ it 'convert successfully' do
43
+ subject.title.must_equal 'Title'
44
+ end
45
+ end
46
+
47
+ describe '.convert_multiply' do
48
+ subject{ Bremen::Nicovideo.send(:convert_multiply, response) }
49
+ let(:response){ fixture('nicovideo_multi.html') }
25
50
  it 'convert successfully' do
26
51
  subject.first.title.must_equal 'Title'
27
52
  end
@@ -2,34 +2,69 @@ $:.unshift(File.expand_path('../../', __FILE__))
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Bremen::Soundcloud do
5
- describe '.search_url' do
5
+ describe '.build_query' do
6
6
  describe 'not set consumer_key' do
7
7
  it 'raise error' do
8
8
  lambda{ Bremen::Soundcloud.search_url }.must_raise RuntimeError
9
9
  end
10
10
  end
11
+
11
12
  describe 'set consumer_key' do
12
13
  before{ Bremen::Soundcloud.consumer_key = 'CK' }
13
- subject{ Bremen::Soundcloud.search_url(params) }
14
- describe 'only keyword' do
15
- let(:params){ {keyword: 'searchword'} }
16
- it 'generate' do
17
- subject.must_equal 'http://api.soundcloud.com/tracks.json?q=searchword&order=created_at&limit=50&filter=&consumer_key=CK'
18
- end
14
+ subject{ Bremen::Soundcloud.build_query({a: 'b'}) }
15
+ it 'return query string' do
16
+ subject.must_equal 'a=b&consumer_key=CK'
17
+ end
18
+ end
19
+ end
20
+
21
+ describe '.find_url' do
22
+ before{ Bremen::Soundcloud.consumer_key = 'CK' }
23
+ subject{ Bremen::Soundcloud.find_url(uid_or_url) }
24
+ describe 'given id' do
25
+ let(:uid_or_url){ 100 }
26
+ it 'generate directly' do
27
+ subject.must_equal 'http://api.soundcloud.com/tracks/100.json?consumer_key=CK'
19
28
  end
29
+ end
20
30
 
21
- describe 'full params' do
22
- let(:params){ {keyword: 'searchword', order: 'hotness', limit: 1, filter: 'public'} }
23
- it 'generate' do
24
- subject.must_equal 'http://api.soundcloud.com/tracks.json?q=searchword&order=hotness&limit=1&filter=public&consumer_key=CK'
25
- end
31
+ describe 'given url' do
32
+ let(:uid_or_url){ 'http://soundcloud.com/author/permalink' }
33
+ it 'generate with resolve resource' do
34
+ subject.must_equal 'http://api.soundcloud.com/resolve.json?url=http%3A%2F%2Fsoundcloud.com%2Fauthor%2Fpermalink&consumer_key=CK'
26
35
  end
27
36
  end
28
37
  end
29
38
 
30
- describe '.convert_from_response' do
31
- subject{ Bremen::Soundcloud.send(:convert_from_response, response) }
32
- let(:response){ fixture('soundcloud.json') }
39
+ describe '.search_url' do
40
+ before{ Bremen::Soundcloud.consumer_key = 'CK' }
41
+ subject{ Bremen::Soundcloud.search_url(params) }
42
+ describe 'only keyword' do
43
+ let(:params){ {keyword: 'searchword'} }
44
+ it 'generate' do
45
+ subject.must_equal 'http://api.soundcloud.com/tracks.json?q=searchword&order=created_at&limit=50&filter=&consumer_key=CK'
46
+ end
47
+ end
48
+
49
+ describe 'full params' do
50
+ let(:params){ {keyword: 'searchword', order: 'hotness', limit: 1, filter: 'public'} }
51
+ it 'generate' do
52
+ subject.must_equal 'http://api.soundcloud.com/tracks.json?q=searchword&order=hotness&limit=1&filter=public&consumer_key=CK'
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '.convert_singly' do
58
+ subject{ Bremen::Soundcloud.send(:convert_singly, response) }
59
+ let(:response){ fixture('soundcloud_single.json') }
60
+ it 'convert successfully' do
61
+ subject.title.must_equal 'Title'
62
+ end
63
+ end
64
+
65
+ describe '.convert_multiply' do
66
+ subject{ Bremen::Soundcloud.send(:convert_multiply, response) }
67
+ let(:response){ fixture('soundcloud_multi.json') }
33
68
  it 'convert successfully' do
34
69
  subject.first.title.must_equal 'Title'
35
70
  end
@@ -2,6 +2,23 @@ $:.unshift(File.expand_path('../../', __FILE__))
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Bremen::Youtube do
5
+ describe '.find_url' do
6
+ subject{ Bremen::Youtube.find_url(uid_or_url) }
7
+ describe 'given id' do
8
+ let(:uid_or_url){ 'XXXXXXXXXXX' }
9
+ it 'generate' do
10
+ subject.must_equal 'http://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX?alt=json'
11
+ end
12
+ end
13
+
14
+ describe 'given url' do
15
+ let(:uid_or_url){ 'http://www.youtube.com/watch?v=XXXXXXXXXXX' }
16
+ it 'generate' do
17
+ subject.must_equal 'http://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX?alt=json'
18
+ end
19
+ end
20
+ end
21
+
5
22
  describe '.search_url' do
6
23
  subject{ Bremen::Youtube.search_url(params) }
7
24
  describe 'only keyword' do
@@ -19,9 +36,17 @@ describe Bremen::Youtube do
19
36
  end
20
37
  end
21
38
 
22
- describe '.convert_from_response' do
23
- subject{ Bremen::Youtube.send(:convert_from_response, response) }
24
- let(:response){ fixture('youtube.json') }
39
+ describe '.convert_singly' do
40
+ subject{ Bremen::Youtube.send(:convert_singly, response) }
41
+ let(:response){ fixture('youtube_single.json') }
42
+ it 'convert successfully' do
43
+ subject.title.must_equal 'Title'
44
+ end
45
+ end
46
+
47
+ describe '.convert_multiply' do
48
+ subject{ Bremen::Youtube.send(:convert_multiply, response) }
49
+ let(:response){ fixture('youtube_multi.json') }
25
50
  it 'convert successfully' do
26
51
  subject.first.title.must_equal 'Title'
27
52
  end
@@ -0,0 +1,43 @@
1
+ {
2
+ "listener_count": 111,
3
+ "name": "Title",
4
+ "tags": [
5
+ {
6
+ "url": "http://www.mixcloud.com/tag/tag1/",
7
+ "name": "Inna",
8
+ "key": "/tag/tag1/"
9
+ }
10
+ ],
11
+ "url": "http://www.mixcloud.com/author/title/",
12
+ "pictures": {
13
+ "medium": "http://images-mix.netdna-ssl.com/w/100/h/100/q/85/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
14
+ "extra_large": "http://images-mix.netdna-ssl.com/w/600/h/600/q/85/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
15
+ "large": "http://images-mix.netdna-ssl.com/w/300/h/300/q/85/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
16
+ "medium_mobile": "http://images-mix.netdna-ssl.com/w/80/h/80/q/75/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
17
+ "small": "http://images-mix.netdna-ssl.com/w/25/h/25/q/85/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
18
+ "thumbnail": "http://images-mix.netdna-ssl.com/w/50/h/50/q/85/upload/images/extaudio/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png"
19
+ },
20
+ "updated_time": "2012-01-01T00:00:00Z",
21
+ "play_count": 1111,
22
+ "comment_count": 1,
23
+ "percentage_music": 100,
24
+ "user": {
25
+ "url": "http://www.mixcloud.com/author/",
26
+ "username": "author",
27
+ "name": "Author",
28
+ "key": "/author/",
29
+ "pictures": {
30
+ "medium": "http://images-mix.netdna-ssl.com/w/100/h/100/q/85/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
31
+ "extra_large": "http://images-mix.netdna-ssl.com/w/600/h/600/q/85/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
32
+ "large": "http://images-mix.netdna-ssl.com/w/300/h/300/q/85/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
33
+ "medium_mobile": "http://images-mix.netdna-ssl.com/w/80/h/80/q/75/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
34
+ "small": "http://images-mix.netdna-ssl.com/w/25/h/25/q/85/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
35
+ "thumbnail": "http://images-mix.netdna-ssl.com/w/50/h/50/q/85/upload/images/profile/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx.png"
36
+ }
37
+ },
38
+ "key": "/author/title/",
39
+ "created_time": "2011-06-12T10:40:19Z",
40
+ "audio_length": 1111,
41
+ "slug": "title",
42
+ "favorite_count": 11
43
+ }
@@ -0,0 +1,91 @@
1
+ <html><head>
2
+ <meta name="keywords" content="">
3
+ <meta name="description" content="音ズレ修正">
4
+ <meta http-equiv="Pragma" content="no-cache">
5
+ <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate, post-check=0, pre-check=0">
6
+ <meta http-equiv="Expires" content="Thu, 01 Dec 1994 16:00:00 GMT">
7
+ <meta property="og:title" content="Title">
8
+ <meta property="og:description" content="This is a description.">
9
+ <meta property="og:type" content="video">
10
+ <meta property="og:video" content="http://ext.nicovideo.jp/thumb_watch/sm1111111?thumb_mode=swf&amp;ap=1&amp;c=1">
11
+ <meta property="og:video:width" content="485">
12
+ <meta property="og:video:height" content="385">
13
+ <meta property="og:video:type" content="application/x-shockwave-flash">
14
+ <meta property="og:site_name" content="Niconico">
15
+ <meta property="og:url" content="http://www.nicovideo.jp/watch/sm1111111">
16
+ <meta property="og:image" content="http://tn-skr2.smilevideo.jp/smile?i=1111111">
17
+ <meta property="og:locale:alternate" content="ja_JP"><meta property="og:locale:alternate" content="zh_TW"><meta property="og:locale" content="en_US">
18
+ <meta property="video:duration" content="1111">
19
+ <meta property="video:release_date" content="2012-01-01T00:00+0900">
20
+ <meta property="mixi:device-mobile" content="http://m.nicovideo.jp/watch/sm1111111?cp_webto=mixi_check_pc">
21
+ <meta property="mixi:device-docomo" content="http://m.nicovideo.jp/watch/sm1111111?uid=NULLGWDOCOMO&guid=ON&cp_webto=mixi_check_pc">
22
+ <link rel="alternate" media="handheld" href="http://nicomoba.jp/watch/sm1111111" />
23
+ <link rel="canonical" href="/watch/sm1111111">
24
+ <link rel="alternate" media="only screen and (max-width: 640px)" href="http://sp.nicovideo.jp/watch/sm1111111" />
25
+ <title>Title - niconico video Q</title>
26
+ </head>
27
+
28
+ <body>
29
+ <!--↓↓-->
30
+ <div style="width:144px; float:left; overflow:hidden;">
31
+ <p class="hLine"><img src="http://res.nimg.jp/img/watch/logout/icon_vinfo.png" alt="Video Info">Video Info</p>
32
+ <!--↓thumbnail↓-->
33
+ <div style="width:132px; border:solid 2px #C9CFCF; margin:4px;">
34
+ <p><img alt="" src="http://tn-skr2.smilevideo.jp/smile?i=1111111" class="img_std128"></p>
35
+ <p class="vinfo_length"><span>11:11</span></p>
36
+ </div>
37
+ <!--↑thumbnail↑-->
38
+ </div>
39
+ <div style="width:528px; float:left; overflow:hidden;">
40
+ <div style="padding:4px;">
41
+ <p class="font12" style="color:#696F6F; margin:0 0 4px;">
42
+ Uploaded on: <strong>Jan 1, 2012, 00:00</strong>
43
+ </p>
44
+ <!-- google_ad_section_start -->
45
+ <h1 itemprop="name">Title</h1>
46
+ <!-- google_ad_section_end -->
47
+ </div>
48
+ <table cellpadding="0" cellspacing="4" class="font12" style="clear:both;">
49
+ <tr>
50
+ <td><img src="http://res.nimg.jp/img/x.gif" alt="Views " class="icon_view"></td>
51
+ <td>Views:<strong>11,111</strong></td>
52
+ </tr>
53
+ <tr>
54
+ <td><img src="http://res.nimg.jp/img/x.gif" alt="Comments " class="icon_comment"></td>
55
+ <td>Comments:<strong>1,111</strong></td>
56
+ </tr>
57
+ <tr>
58
+ <td><img src="http://res.nimg.jp/img/x.gif" alt="Uploader" class="icon_owner"></td>
59
+ <td itemprop="author" itemscope itemtype="http://schema.org/Person">
60
+ Uploader: <strong itemprop="name">Author</strong>
61
+ </td>
62
+ </tr>
63
+ </table>
64
+ </div>
65
+
66
+
67
+
68
+ <!--↑↑-->
69
+
70
+ <!--↓動画説明文↓-->
71
+ <div class="mb8p4" style="clear:both;">
72
+ <div id="des_full">
73
+ <p class="font12" style="color:#494F4F; margin:0 0 4px;"><strong>Video Description:</strong></p>
74
+ <!-- google_ad_section_start -->
75
+ <p class="font12" style="margin-left:16px;" itemprop="description">This is a description.</p>
76
+ <!-- google_ad_section_end -->
77
+ </div>
78
+ </div>
79
+ <!--↑動画説明文↑-->
80
+
81
+ <!--↓↓-->
82
+ <div class="mb8p4">
83
+ <p class="font12" style="color:#494F4F; margin:0 0 4px;"><strong>Registered tags:</strong></p>
84
+ <p class="font12" style="margin-left:16px;"><span style="color:#999F9F;">There are no registered tags</span></p>
85
+ </div>
86
+ <div class="mb8p4" style="clear:both;"><p class="dotline_2"></p></div>
87
+ <p class="font12" style="padding:4px;">You will need to <strong>log in</strong> before you can view videos.</p>
88
+ <div id="suggest_login" class="clearfix"><a href="https://secure.nicovideo.jp/secure/register" class="registerButton button">Register New Account</a><a href="https://secure.nicovideo.jp/secure/login_form?next_url=%2Fwatch%2Fsm1111111&site=niconico&time=1111111111&hash_key=1f1f1f1f" class="loginButton button">To log in screen</a></div><div id="ext_login" class="clearfix"><a href="https://secure.nicovideo.jp/secure/login_ext/facebook?site=niconico&next_url=%2Fwatch%2Fsm1111111" id="Login_fb" title="Login using Facebook"><img src="http://res.nimg.jp/img/watch/logout/icon_facebook.png"><span>Login using Facebook</span></a><p>You can log in using Facebook→ </p></div>
89
+ <!--↑↑-->
90
+ </body>
91
+ </html>
@@ -0,0 +1,53 @@
1
+ {
2
+ "artwork_url": "http://i1.sndcdn.com/artworks-000011111111-1cfbic-large.jpg?5c687d0",
3
+ "attachments_uri": "http://api.soundcloud.com/tracks/11111111/attachments",
4
+ "bpm": null,
5
+ "comment_count": 0,
6
+ "commentable": true,
7
+ "created_at": "2012/01/01 00:08:00 +0000",
8
+ "description": "",
9
+ "download_count": 0,
10
+ "downloadable": false,
11
+ "duration": 111111,
12
+ "embeddable_by": "all",
13
+ "favoritings_count": 0,
14
+ "genre": "Garage",
15
+ "id": 11111111,
16
+ "isrc": "",
17
+ "key_signature": "",
18
+ "kind": "track",
19
+ "label_id": null,
20
+ "label_name": "",
21
+ "license": "all-rights-reserved",
22
+ "original_content_size": 1111111,
23
+ "original_format": "mp3",
24
+ "permalink": "permalink",
25
+ "permalink_url": "http://soundcloud.com/author/permalink",
26
+ "playback_count": 4,
27
+ "purchase_title": null,
28
+ "purchase_url": null,
29
+ "release": "",
30
+ "release_day": null,
31
+ "release_month": null,
32
+ "release_year": null,
33
+ "sharing": "public",
34
+ "state": "finished",
35
+ "stream_url": "http://api.soundcloud.com/tracks/11111111/stream",
36
+ "streamable": true,
37
+ "tag_list": "tag1 tag2 tag3",
38
+ "title": "Title",
39
+ "track_type": "",
40
+ "uri": "http://api.soundcloud.com/tracks/11111111",
41
+ "user": {
42
+ "avatar_url": "http://i1.sndcdn.com/avatars-000011111111-zzjvpd-large.jpg?5c687d0",
43
+ "id": 11111111,
44
+ "kind": "user",
45
+ "permalink": "author",
46
+ "permalink_url": "http://soundcloud.com/author",
47
+ "uri": "http://api.soundcloud.com/users/11111111",
48
+ "username": "Author"
49
+ },
50
+ "user_id": 11111111,
51
+ "video_url": null,
52
+ "waveform_url": "http://w1.sndcdn.com/XXXXXXXXXXXX_m.png"
53
+ }
File without changes
@@ -0,0 +1,262 @@
1
+ {
2
+ "version":"1.0",
3
+ "encoding":"UTF-8",
4
+ "entry":{
5
+ "xmlns":"http://www.w3.org/2005/Atom",
6
+ "xmlns$media":"http://search.yahoo.com/mrss/",
7
+ "xmlns$gd":"http://schemas.google.com/g/2005",
8
+ "xmlns$yt":"http://gdata.youtube.com/schemas/2007",
9
+ "gd$etag":"W/\"CEQDQX47eCp7I2A9WhNXF04.\"",
10
+ "id":{
11
+ "$t":"tag:youtube.com,2008:video:XXXXXXXXXXX"
12
+ },
13
+ "published":{
14
+ "$t":"2012-01-01T00:00:00.000Z"
15
+ },
16
+ "updated":{
17
+ "$t":"2012-01-01T00:00:00.000Z"
18
+ },
19
+ "category":[
20
+ {
21
+ "scheme":"http://schemas.google.com/g/2005#kind",
22
+ "term":"http://gdata.youtube.com/schemas/2007#video"
23
+ },
24
+ {
25
+ "scheme":"http://gdata.youtube.com/schemas/2007/categories.cat",
26
+ "term":"Music",
27
+ "label":"Music"
28
+ }
29
+ ],
30
+ "title":{
31
+ "$t":"Title"
32
+ },
33
+ "content":{
34
+ "type":"application/x-shockwave-flash",
35
+ "src":"https://www.youtube.com/v/XXXXXXXXXXX?version=3&f=videos&app=youtube_gdata"
36
+ },
37
+ "link":[
38
+ {
39
+ "rel":"http://gdata.youtube.com/schemas/2007#video.in-response-to",
40
+ "type":"application/atom+xml",
41
+ "href":"https://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX"
42
+ },
43
+ {
44
+ "rel":"alternate",
45
+ "type":"text/html",
46
+ "href":"https://www.youtube.com/watch?v=XXXXXXXXXXX&feature=youtube_gdata"
47
+ },
48
+ {
49
+ "rel":"http://gdata.youtube.com/schemas/2007#video.responses",
50
+ "type":"application/atom+xml",
51
+ "href":"https://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX/responses"
52
+ },
53
+ {
54
+ "rel":"http://gdata.youtube.com/schemas/2007#video.related",
55
+ "type":"application/atom+xml",
56
+ "href":"https://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX/related"
57
+ },
58
+ {
59
+ "rel":"http://gdata.youtube.com/schemas/2007#mobile",
60
+ "type":"text/html",
61
+ "href":"https://m.youtube.com/details?v=XXXXXXXXXXX"
62
+ },
63
+ {
64
+ "rel":"http://gdata.youtube.com/schemas/2007#uploader",
65
+ "type":"application/atom+xml",
66
+ "href":"https://gdata.youtube.com/feeds/api/users/XXX-XXXXXXXXXXXXXXXXXX"
67
+ },
68
+ {
69
+ "rel":"self",
70
+ "type":"application/atom+xml",
71
+ "href":"https://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX"
72
+ }
73
+ ],
74
+ "author":[
75
+ {
76
+ "name":{
77
+ "$t":"pogobat"
78
+ },
79
+ "uri":{
80
+ "$t":"https://gdata.youtube.com/feeds/api/users/Author"
81
+ },
82
+ "yt$userId":{
83
+ "$t":"XXX-XXXXXXXXXXXXXXXXXX"
84
+ }
85
+ }
86
+ ],
87
+ "yt$accessControl":[
88
+ {
89
+ "action":"comment",
90
+ "permission":"allowed"
91
+ },
92
+ {
93
+ "action":"commentVote",
94
+ "permission":"allowed"
95
+ },
96
+ {
97
+ "action":"videoRespond",
98
+ "permission":"allowed"
99
+ },
100
+ {
101
+ "action":"rate",
102
+ "permission":"allowed"
103
+ },
104
+ {
105
+ "action":"embed",
106
+ "permission":"allowed"
107
+ },
108
+ {
109
+ "action":"list",
110
+ "permission":"allowed"
111
+ },
112
+ {
113
+ "action":"autoPlay",
114
+ "permission":"allowed"
115
+ },
116
+ {
117
+ "action":"syndicate",
118
+ "permission":"allowed"
119
+ }
120
+ ],
121
+ "gd$comments":{
122
+ "gd$feedLink":{
123
+ "rel":"http://gdata.youtube.com/schemas/2007#comments",
124
+ "href":"https://gdata.youtube.com/feeds/api/videos/XXXXXXXXXXX/comments",
125
+ "countHint":11111
126
+ }
127
+ },
128
+ "media$group":{
129
+ "media$category":[
130
+ {
131
+ "$t":"Music",
132
+ "label":"Music",
133
+ "scheme":"http://gdata.youtube.com/schemas/2007/categories.cat"
134
+ }
135
+ ],
136
+ "media$content":[
137
+ {
138
+ "url":"https://www.youtube.com/v/XXXXXXXXXXX?version=3&f=videos&app=youtube_gdata",
139
+ "type":"application/x-shockwave-flash",
140
+ "medium":"video",
141
+ "isDefault":"true",
142
+ "expression":"full",
143
+ "duration":111,
144
+ "yt$format":5
145
+ },
146
+ {
147
+ "url":"rtsp://v8.cache6.c.youtube.com/CiILENy73wIaGQkzQ5_8oAjEHhMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
148
+ "type":"video/3gpp",
149
+ "medium":"video",
150
+ "expression":"full",
151
+ "duration":111,
152
+ "yt$format":1
153
+ },
154
+ {
155
+ "url":"rtsp://v4.cache4.c.youtube.com/CiILENy73wIaGQkzQ5_8oAjEHhMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
156
+ "type":"video/3gpp",
157
+ "medium":"video",
158
+ "expression":"full",
159
+ "duration":111,
160
+ "yt$format":6
161
+ }
162
+ ],
163
+ "media$credit":[
164
+ {
165
+ "$t":"Author",
166
+ "role":"uploader",
167
+ "scheme":"urn:youtube",
168
+ "yt$display":"Author",
169
+ "yt$type":"partner"
170
+ }
171
+ ],
172
+ "media$description":{
173
+ "$t":"This is a description.",
174
+ "type":"plain"
175
+ },
176
+ "media$keywords":{
177
+
178
+ },
179
+ "media$license":{
180
+ "$t":"youtube",
181
+ "type":"text/html",
182
+ "href":"http://www.youtube.com/t/terms"
183
+ },
184
+ "media$player":{
185
+ "url":"https://www.youtube.com/watch?v=XXXXXXXXXXX&feature=youtube_gdata_player"
186
+ },
187
+ "media$thumbnail":[
188
+ {
189
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/default.jpg",
190
+ "height":90,
191
+ "width":120,
192
+ "time":"00:03:16",
193
+ "yt$name":"default"
194
+ },
195
+ {
196
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/mqdefault.jpg",
197
+ "height":180,
198
+ "width":320,
199
+ "yt$name":"mqdefault"
200
+ },
201
+ {
202
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/hqdefault.jpg",
203
+ "height":360,
204
+ "width":480,
205
+ "yt$name":"hqdefault"
206
+ },
207
+ {
208
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/1.jpg",
209
+ "height":90,
210
+ "width":120,
211
+ "time":"00:01:11",
212
+ "yt$name":"start"
213
+ },
214
+ {
215
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/2.jpg",
216
+ "height":90,
217
+ "width":120,
218
+ "time":"00:01:11",
219
+ "yt$name":"middle"
220
+ },
221
+ {
222
+ "url":"http://i.ytimg.com/vi/XXXXXXXXXXX/3.jpg",
223
+ "height":90,
224
+ "width":120,
225
+ "time":"00:01:11",
226
+ "yt$name":"end"
227
+ }
228
+ ],
229
+ "media$title":{
230
+ "$t":"Title",
231
+ "type":"plain"
232
+ },
233
+ "yt$duration":{
234
+ "seconds":"111"
235
+ },
236
+ "yt$uploaded":{
237
+ "$t":"2012-01-01T00:00:00.000Z"
238
+ },
239
+ "yt$uploaderId":{
240
+ "$t":"XXXXX-XXXXXXXXXXXXXXXXXX"
241
+ },
242
+ "yt$videoid":{
243
+ "$t":"XXXXXXXXXXX"
244
+ }
245
+ },
246
+ "gd$rating":{
247
+ "average":5.0000000,
248
+ "max":5,
249
+ "min":1,
250
+ "numRaters":11111,
251
+ "rel":"http://schemas.google.com/g/2005#overall"
252
+ },
253
+ "yt$statistics":{
254
+ "favoriteCount":"0",
255
+ "viewCount":"11111111"
256
+ },
257
+ "yt$rating":{
258
+ "numDislikes":"1111",
259
+ "numLikes":"11111"
260
+ }
261
+ }
262
+ }
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'minitest/autorun'
2
+ require 'minitest/pride'
2
3
  require 'bremen'
3
4
 
4
5
  def fixture path
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bremen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-19 00:00:00.000000000 Z
12
+ date: 2012-12-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: guard-minitest
@@ -43,7 +43,23 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
- description: integrated searcher of audio track on music sites
46
+ - !ruby/object:Gem::Dependency
47
+ name: terminal-notifier-guard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: integrated searcher of audio tracks on music sites
47
63
  email:
48
64
  - itzki.h@gmail.com
49
65
  executables: []
@@ -70,10 +86,14 @@ files:
70
86
  - spec/bremen/nicovideo_spec.rb
71
87
  - spec/bremen/soundcloud_spec.rb
72
88
  - spec/bremen/youtube_spec.rb
73
- - spec/fixtures/mixcloud.json
74
- - spec/fixtures/nicovideo.html
75
- - spec/fixtures/soundcloud.json
76
- - spec/fixtures/youtube.json
89
+ - spec/fixtures/mixcloud_multi.json
90
+ - spec/fixtures/mixcloud_single.json
91
+ - spec/fixtures/nicovideo_multi.html
92
+ - spec/fixtures/nicovideo_single.html
93
+ - spec/fixtures/soundcloud_multi.json
94
+ - spec/fixtures/soundcloud_single.json
95
+ - spec/fixtures/youtube_multi.json
96
+ - spec/fixtures/youtube_single.json
77
97
  - spec/spec_helper.rb
78
98
  homepage: https://github.com/itzki/bremen
79
99
  licenses: []
@@ -98,14 +118,19 @@ rubyforge_project:
98
118
  rubygems_version: 1.8.23
99
119
  signing_key:
100
120
  specification_version: 3
101
- summary: integrated searcher of audio track on music sites
121
+ summary: Bremen provides common search interface for some music websites. it supports
122
+ YouTube, SoundCloud, MixCloud and Nicovideo
102
123
  test_files:
103
124
  - spec/bremen/mixcloud_spec.rb
104
125
  - spec/bremen/nicovideo_spec.rb
105
126
  - spec/bremen/soundcloud_spec.rb
106
127
  - spec/bremen/youtube_spec.rb
107
- - spec/fixtures/mixcloud.json
108
- - spec/fixtures/nicovideo.html
109
- - spec/fixtures/soundcloud.json
110
- - spec/fixtures/youtube.json
128
+ - spec/fixtures/mixcloud_multi.json
129
+ - spec/fixtures/mixcloud_single.json
130
+ - spec/fixtures/nicovideo_multi.html
131
+ - spec/fixtures/nicovideo_single.html
132
+ - spec/fixtures/soundcloud_multi.json
133
+ - spec/fixtures/soundcloud_single.json
134
+ - spec/fixtures/youtube_multi.json
135
+ - spec/fixtures/youtube_single.json
111
136
  - spec/spec_helper.rb