booru 0.0.1

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.
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Client
4
+ include Error
5
+ include Request
6
+
7
+ include API
8
+ include API::Artist
9
+ include API::Comment
10
+ include API::Favorite
11
+ include API::Note
12
+ include API::Pool
13
+ include API::Post
14
+ include API::Tag
15
+ include API::User
16
+ include API::Wiki
17
+
18
+ def initialize(user=nil, pass=nil, format='json')
19
+ @user = user
20
+ @pass = pass ? hash_password(pass) : nil
21
+ @format = format.to_sym
22
+ end
23
+
24
+ def hash_password(pass)
25
+ Digest::SHA1.hexdigest(pass)
26
+ end
27
+
28
+ def url_file_segment(url, md5)
29
+ CGI.unescape(url)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Behoimi < Client
4
+ attr_accessor :host, :referer
5
+
6
+ def initialize(user = nil, pass = nil)
7
+ @host = "http://behoimi.org"
8
+ @referer = "http://behoimi.org/post/show"
9
+ super
10
+ end
11
+
12
+ def hash_password(pass)
13
+ salt = 'meganekko-heaven'
14
+ salted_pass = "#{salt}--#{pass}--"
15
+ Digest::SHA1.hexdigest(salted_pass)
16
+ super
17
+ end
18
+
19
+ def url_file_segment(url, md5)
20
+ strings = ["http://behoimi.org/"]
21
+ strings.each do |str|
22
+ url = url.gsub(str, "")
23
+ end
24
+ url = url.gsub(/data\/.{2}\/.{2}\//,"")
25
+ super
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Danbooru < Client
4
+ attr_accessor :host, :referer
5
+
6
+ def initialize(user = nil, pass = nil)
7
+ @host = "http://danbooru.donmai.us"
8
+ @referer = "http://danbooru.donmai.us"
9
+ super
10
+ end
11
+
12
+ def hash_password(pass)
13
+ salt = 'choujin-steiner'
14
+ salted_pass = "#{salt}--#{pass}--"
15
+ Digest::SHA1.hexdigest(salted_pass)
16
+ super
17
+ end
18
+
19
+ def url_file_segment(url, md5)
20
+ url = url.gsub(/http:\/\/.+\/.+\//, "")
21
+ super
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Konachan < Client
4
+ attr_accessor :host, :referer
5
+
6
+ def initialize(user = nil, pass = nil)
7
+ @host = "http://konachan.com"
8
+ @referer = "http://konachan.com"
9
+ super
10
+ end
11
+
12
+ def hash_password(pass)
13
+ salt = 'So-I-Heard-You-Like-Mupkids-?'
14
+ salted_pass = "#{salt}--#{pass}--"
15
+ Digest::SHA1.hexdigest(salted_pass)
16
+ super
17
+ end
18
+
19
+ def url_file_segment(url, md5)
20
+ url = url.gsub(/http:\/\/.+\/image\/#{md5}\//, "")
21
+ super
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Yandere < Client
4
+ attr_accessor :host, :referer
5
+
6
+ def initialize(user = nil, pass = nil)
7
+ @host = "https://yande.re"
8
+ @referer = "https://yande.re"
9
+ super
10
+ end
11
+
12
+ def hash_password(pass)
13
+ salt = 'choujin-steiner'
14
+ salted_pass = "#{salt}--#{pass}--"
15
+ Digest::SHA1.hexdigest(salted_pass)
16
+ super
17
+ end
18
+
19
+ def url_file_segment(url, md5)
20
+ url = url.gsub(/https:\/\/.+\/image\/#{md5}\//, "")
21
+ super
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,95 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ class Downloader
4
+ include Utils
5
+
6
+ def initialize(options = {})
7
+ board = options[:board].downcase.to_sym
8
+ user = options[:user]
9
+ pass = options[:pass]
10
+ limit = options[:max]
11
+ @tags = sanitize(options[:tags])
12
+ @pool = sanitize(options[:pool])
13
+ @store = options[:output]
14
+ @user_agent = 'Mozilla/5.0'
15
+
16
+ @client = case board
17
+ when :konachan
18
+ Konachan.new(user,pass)
19
+ when :behoimi
20
+ Behoimi.new(user, pass)
21
+ when :yandere
22
+ Yandere.new(user, pass)
23
+ when :danbooru
24
+ Danbooru.new(user, pass)
25
+ else
26
+ puts "Image board unsupported. Exiting."
27
+ exit 1
28
+ end
29
+
30
+ @curr = 1
31
+ @page = 1
32
+ fetch_posts(@page)
33
+
34
+ # TODO Figure out a way to get total count
35
+ if limit
36
+ @total = limit
37
+ else
38
+ @total = 100
39
+ end
40
+ #@pages = @total.to_i/100 + 1
41
+ FileUtils.mkdir_p File.expand_path(@store)
42
+ end
43
+
44
+ def download
45
+ while @posts.length > 0 do
46
+ download_posts
47
+ paginate
48
+ end
49
+ puts "No more results for #{@tags}"
50
+ end
51
+
52
+ private
53
+
54
+ def paginate
55
+ @page += 1
56
+ puts "Fetching page #{@page} of #{@pages}"
57
+ fetch_posts(@page)
58
+ end
59
+
60
+ # Returns an array of posts
61
+ def fetch_posts(page)
62
+ params = {:page => page, :tags => @tags}
63
+ @posts = @client.list_posts(params)
64
+ # TODO Implement download by pool
65
+ #@posts = @client.list_pool_posts(params)
66
+ end
67
+
68
+ def download_posts
69
+ @posts.each do |post|
70
+ file_url = post['file_url']
71
+ md5 = post['md5']
72
+ tags = post['tags']
73
+
74
+ filename = @client.url_file_segment(file_url, md5)
75
+ filename = reduce_filename(filename, 100)
76
+ filename = clean_filename(filename)
77
+ filename = File.join(@store, filename)
78
+
79
+ if File.exist?(filename) && md5 == get_md5_hash(filename)
80
+ puts "(#{@curr}/#{@total}): File already exists, skipping."
81
+ else
82
+ puts "(#{@curr}/#{@total}): Saving..."
83
+ transfer(file_url, filename, @user_agent, @client.referer)
84
+ puts "Saved."
85
+ end
86
+
87
+ @curr += 1
88
+ if @curr > @total
89
+ puts "Maximum downloads reached."
90
+ exit
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ module Error
4
+ # Custom error class for rescuing all Moebooru errors
5
+ class BooruError < StandardError; end
6
+
7
+ # Raised when Booru returns the HTTP status code 420
8
+ # Reason: Record could not be saved
9
+ class InvalidRecordError < BooruError; end
10
+ # Raised when Booru returns the HTTP status code 421
11
+ # Reason: User is throttled, try again later
12
+ class UserThrottled < BooruError; end
13
+ # Raised when Booru returns the HTTP status code 422
14
+ # Reason: The resource is locked and cannot be modified
15
+ class LockedError < BooruError; end
16
+ # Raised when Booru returns the HTTP status code 423
17
+ # Reason: Resource already exists
18
+ class AlreadyExistsError < BooruError; end
19
+ # Raised when Booru returns the HTTP status code 424
20
+ # Reason: The given parameters were invalid
21
+ class InvalidParametersError < BooruError; end
22
+
23
+ # Raised when Booru returns the HTTP status code 403
24
+ class ForbiddenError < StandardError; end
25
+ # Raised when Booru returns the HTTP status code 404
26
+ class NotFoundError < StandardError; end
27
+ # Raised when Booru returns the HTTP status code 500
28
+ class InternalServerError < StandardError; end
29
+ # Raised when Booru returns the HTTP status code 503
30
+ class UnavailableError < StandardError; end
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ module Request
4
+
5
+ HEADERS = {
6
+ 'User-Agent' => 'Mozilla/5.0',
7
+ }
8
+
9
+ def get(path, options={})
10
+ request(:get, path, HEADERS.merge(options))
11
+ end
12
+
13
+ def post(path, options = {})
14
+ request(:post, path, HEADERS.merge(options))
15
+ end
16
+
17
+ # Perform an HTTP request
18
+ def request(method, path, options)
19
+ case method
20
+ when :get
21
+ response = RestClient.get("#{@host}#{path}", options)
22
+ when :post
23
+ response = RestClient.post("#{@host}#{path}", options)
24
+ end
25
+ raise_errors(response)
26
+ response
27
+ end
28
+
29
+ private
30
+
31
+ def raise_errors(response)
32
+ case response.code.to_i
33
+ when 403
34
+ raise Booru::Error::AccessDeniedError
35
+ when 404
36
+ raise Booru::Error::NotFoundError
37
+ when 420
38
+ raise Booru::Error::InvalidRecordError
39
+ when 421
40
+ raise Booru::Error::UserThrottledError
41
+ when 422
42
+ raise Booru::Error::ResourceLockedError
43
+ when 423
44
+ raise Booru::Error::ResourceAlreadyExistsError
45
+ when 424
46
+ raise Booru::Error::InvalidParametersError
47
+ when 500
48
+ raise Booru::Error::InternalServerError
49
+ when 503
50
+ raise Booru::Error::UnavailableError
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,80 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ module Utils
4
+
5
+ def clean_filename(filename)
6
+ filename.gsub(' ', '_').gsub(/[^a-zA-Z0-9_.]+/, '-')
7
+ end
8
+
9
+ # TODO Rather than just reduce filename size by removing
10
+ # characters, try to not to clip tags in the filename
11
+ def reduce_filename(filename, max = 255)
12
+ if filename.length > max
13
+ suffix = File.extname(filename)
14
+ basename = filename.chomp(suffix)
15
+
16
+ size = max.to_i - suffix.length
17
+ shortname = basename[0..size]
18
+
19
+ [shortname, suffix].join('')
20
+ else
21
+ filename
22
+ end
23
+ end
24
+
25
+ def sanitize(values)
26
+ values = values.gsub(" ", "+") if values.is_a?(String)
27
+ if values.is_a?(Array)
28
+ values.each { |value| value.gsub(' ', '_') }
29
+ values = values.join("+")
30
+ end
31
+ values
32
+ end
33
+
34
+ def get_md5_hash(file)
35
+ string = File.read(file)
36
+ hash = Digest::MD5.hexdigest(string)
37
+ end
38
+
39
+ # Cross-platform 'which'
40
+ # http://stackoverflow.com/a/5471032
41
+ def which(cmd)
42
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
43
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
44
+ exts.each { |ext|
45
+ exe = "#{path}/#{cmd}#{ext}"
46
+ return exe if File.executable? exe
47
+ }
48
+ end
49
+ return nil
50
+ end
51
+
52
+ # File download wrapper
53
+ def transfer(url, output, user_agent, referer)
54
+ if which('wget')
55
+ wget(url, output, user_agent, referer)
56
+ elsif which('curl')
57
+ curl(url, output, user_agent, referer)
58
+ else
59
+ write(url, output, user_agent, referer)
60
+ end
61
+ end
62
+
63
+ def wget(url, output, user_agent, referer)
64
+ command = "wget -nv #{url} -O #{output} "\
65
+ "--user-agent=#{user_agent} --referer=#{referer}"
66
+ system(command)
67
+ end
68
+
69
+ def curl(url, output, user_agent, referer)
70
+ command = "curl -A #{user_agent} -e #{referer} --progress-bar "\
71
+ "-o #{output} #{url}"
72
+ system(command)
73
+ end
74
+
75
+ def write(url, output, user_agent, referer)
76
+ data = open(url, "User-Agent" => user_agent, "Referer" => referer).read
77
+ File.open(output, "wb") { |file| file.write(data) }
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Booru
3
+ VERSION = "0.0.1"
4
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: booru
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Palma
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: trollop
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: nori
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
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
+ - !ruby/object:Gem::Dependency
63
+ name: rest-client
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: A Moebooru API client and image downloader for Danbooru style image boards.
79
+ email: requiem.der.seele@gmail.com
80
+ executables:
81
+ - booru.rb
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE
88
+ - README.md
89
+ - Rakefile
90
+ - bin/booru.rb
91
+ - booru.gemspec
92
+ - fixtures/konachan/post_index.json
93
+ - fixtures/yandere/post_index.json
94
+ - lib/booru.rb
95
+ - lib/booru/api.rb
96
+ - lib/booru/api/artist.rb
97
+ - lib/booru/api/comment.rb
98
+ - lib/booru/api/favorite.rb
99
+ - lib/booru/api/forum.rb
100
+ - lib/booru/api/note.rb
101
+ - lib/booru/api/pool.rb
102
+ - lib/booru/api/post.rb
103
+ - lib/booru/api/tag.rb
104
+ - lib/booru/api/user.rb
105
+ - lib/booru/api/wiki.rb
106
+ - lib/booru/client.rb
107
+ - lib/booru/client/behoimi.rb
108
+ - lib/booru/client/danbooru.rb
109
+ - lib/booru/client/konachan.rb
110
+ - lib/booru/client/yandere.rb
111
+ - lib/booru/downloader.rb
112
+ - lib/booru/error.rb
113
+ - lib/booru/request.rb
114
+ - lib/booru/utils.rb
115
+ - lib/booru/version.rb
116
+ homepage: https://github.com/mistofvongola/booru
117
+ licenses: []
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 1.8.23
137
+ signing_key:
138
+ specification_version: 3
139
+ summary: A Ruby client for the Moebooru API.
140
+ test_files: []