stashboxr 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/lib/stashboxr.rb +253 -0
- data/test/helper.rb +10 -0
- data/test/test_stashboxr.rb +6 -0
- metadata +101 -0
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "stashboxr"
|
8
|
+
gem.summary = %Q{A ruby api for the stashbox.org website}
|
9
|
+
gem.description = %Q{Upload files to stashbox.org and manage their metadata. User accounts or anonymous uploads!}
|
10
|
+
gem.email = "jphastings@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/jphastings/StashboxR"
|
12
|
+
gem.authors = ["JP Hastings-Spital"]
|
13
|
+
gem.add_dependency "mechanize",'>= 1.0.0'
|
14
|
+
gem.add_development_dependency "Shoulda"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/stashboxr.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mechanize'
|
3
|
+
|
4
|
+
# Allows percentages to be inspected and stringified in human
|
5
|
+
# form "33.3%", but kept in a float format for mathmatics
|
6
|
+
class Percentage < DelegateClass(Float)
|
7
|
+
def to_s(decimalplaces = 0)
|
8
|
+
(((self * 10**(decimalplaces+2)).round)/10**decimalplaces).to_s+"%"
|
9
|
+
end
|
10
|
+
alias :inspect :to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
# A library for dealing with files on http://stashbox.org
|
14
|
+
class Stashboxr
|
15
|
+
# Settings for the browser emulation
|
16
|
+
Agent = Mechanize.new { |agent| agent.user_agent = 'Stashboxr' }
|
17
|
+
|
18
|
+
# Log into stashbox
|
19
|
+
def self.login(username,password)
|
20
|
+
res = Agent.post('http://stashbox.org/',{
|
21
|
+
:dologin => 1,
|
22
|
+
:user => username,
|
23
|
+
:password => password
|
24
|
+
})
|
25
|
+
|
26
|
+
raise RuntimeError, "Log in failed" if res.code != "200"
|
27
|
+
@@username = username
|
28
|
+
end
|
29
|
+
|
30
|
+
# Log out of stashbox
|
31
|
+
def self.logout
|
32
|
+
Agent.get('http://stashbox.org/logout.php')
|
33
|
+
@@username = nil
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Is someone logged in?
|
38
|
+
def self.loggedin?
|
39
|
+
!@@username.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Username of current logged in user (nil if not logged in)
|
43
|
+
def self.username
|
44
|
+
@@username
|
45
|
+
end
|
46
|
+
|
47
|
+
# Search stashbox.org - the site has this advice for formatting searches:
|
48
|
+
#
|
49
|
+
# You can use any number of terms in your search, all of which will be required in the results. You can also use the following search types (using "type:*the_type*" format): archives, audio, code, documents, images, movies
|
50
|
+
#
|
51
|
+
# Search types can be combined with normal search keywords to refine your results.
|
52
|
+
#
|
53
|
+
# In addition, remember you can search for both full and partial mimetypes, ie _application/pdf_.
|
54
|
+
def self.search(q)
|
55
|
+
res = Nokogiri::XML(Agent.get("http://stashbox.org/browse.php?wants_rss=1&q=#{URI.encode(q)}").body)
|
56
|
+
|
57
|
+
res.search('//item').collect do |item|
|
58
|
+
File.new(item.search('link').inner_text)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class File
|
63
|
+
attr_reader :filename, :url, :unsaved
|
64
|
+
attr_reader :know_code, :uploaded_on, :size, :views, :rating
|
65
|
+
attr_accessor :title, :description
|
66
|
+
attr_accessor :autosave
|
67
|
+
|
68
|
+
SIZES = {
|
69
|
+
'' => 9.765625e-4,
|
70
|
+
'K' => 1,
|
71
|
+
'M' => 1024
|
72
|
+
}
|
73
|
+
|
74
|
+
# Upload a file to stashbox and return a Stashbox::File object referencing it (which can be used to edit the metadata)
|
75
|
+
def self.upload(local_file,is_public = true)
|
76
|
+
# Does file exist?
|
77
|
+
raise RuntimeError, "This file does not exist - I can't upload it!" if !::File.exists? local_file
|
78
|
+
# Upload the file
|
79
|
+
res = Agent.post('http://stashbox.org/upload.php',{
|
80
|
+
:upload_type => "local_file",
|
81
|
+
:wants_rss => 1,
|
82
|
+
:file => open(local_file),
|
83
|
+
:is_public => is_public
|
84
|
+
})
|
85
|
+
body = Nokogiri::XML(res.body)
|
86
|
+
|
87
|
+
case res.code.to_i
|
88
|
+
when 200
|
89
|
+
new(body.search('//url').inner_text)
|
90
|
+
else
|
91
|
+
reason = body.search('//error').inner_text rescue nil
|
92
|
+
raise RuntimeError, "The upload wasn't allowed"<<((reason.nil?) ? "" : " because '#{reason}'")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(url)
|
97
|
+
if (url =~ /(?:http:\/\/)?(?:stashbox\.org)?(?:\/v)?\/?([0-9]+\/(.+))$/)
|
98
|
+
@stashboxid = $1
|
99
|
+
@filename = $2
|
100
|
+
else
|
101
|
+
raise RuntimeError, "That isn't a valid stashbox URL"
|
102
|
+
end
|
103
|
+
|
104
|
+
@autosave = true
|
105
|
+
@saved = true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Has metadata been loaded
|
109
|
+
def has_metadata?; @metadata; end
|
110
|
+
# If you make alterations to the file's metadata with autosave turned off, this will become true until to #save it.
|
111
|
+
def autosave=(pref); @autosave = (pref != false); end
|
112
|
+
# Returns true if there are unsaved changes to the metadata for the file
|
113
|
+
def unsaved?; !@saved; end
|
114
|
+
def url; "http://stashbox.org/#{@stashboxid}"; end
|
115
|
+
def tags; refresh if not @metadata; @tags; end
|
116
|
+
def title; refresh if not @metadata; @title; end
|
117
|
+
def description; refresh if not @metadata; @description; end
|
118
|
+
def uploaded_on; refresh if not @metadata; @uploaded_on; end
|
119
|
+
def size; refresh if not @metadata; @size; end
|
120
|
+
def views; refresh if not @metadata; @views; end
|
121
|
+
def rating; refresh if not @metadata; @rating; end
|
122
|
+
def sfw?; refresh if not @metadata; @sfw; end
|
123
|
+
def public?; refresh if not @metadata; @public; end
|
124
|
+
|
125
|
+
# Add tags to the file on stashbox
|
126
|
+
def add_tag(new_tags)
|
127
|
+
# TODO: Parse input tags?
|
128
|
+
@tags = (tags + parse_tags(new_tags)).uniq
|
129
|
+
@saved = false
|
130
|
+
save if @autosave
|
131
|
+
end
|
132
|
+
alias :add_tags :add_tag
|
133
|
+
|
134
|
+
# Remove tags from the file on stashbox
|
135
|
+
def remove_tag(new_tags)
|
136
|
+
@tags = (tags - parse_tags(new_tags)).uniq
|
137
|
+
@saved = false
|
138
|
+
save if @autosave
|
139
|
+
end
|
140
|
+
alias :remove_tags :remove_tag
|
141
|
+
|
142
|
+
# Reset the tags for this file to the ones in the given array
|
143
|
+
def set_tags(new_tags)
|
144
|
+
@tags = parse_tags(new_tags)
|
145
|
+
@saved = false
|
146
|
+
save if @autosave
|
147
|
+
end
|
148
|
+
|
149
|
+
def title=(title)
|
150
|
+
@title = title
|
151
|
+
@saved = false
|
152
|
+
save if @autosave
|
153
|
+
end
|
154
|
+
|
155
|
+
def description=(description)
|
156
|
+
@description = description
|
157
|
+
@saved = false
|
158
|
+
save if @autosave
|
159
|
+
end
|
160
|
+
|
161
|
+
def sfw=(sfw)
|
162
|
+
@sfw = (sfw == true)
|
163
|
+
@saved = false
|
164
|
+
save if @autosave
|
165
|
+
end
|
166
|
+
|
167
|
+
def public=(ispublic)
|
168
|
+
@public = (ispublic == true)
|
169
|
+
@saved = false
|
170
|
+
save if @autosave
|
171
|
+
end
|
172
|
+
|
173
|
+
# Save the metadata to Stashboxr. Called automatically with any changes by default.
|
174
|
+
def save
|
175
|
+
res = Agent.post("http://stashbox.org/manage_file/"<<@stashboxid,{
|
176
|
+
:tags => @tags * " ",
|
177
|
+
:meta_title => @title,
|
178
|
+
:meta_description => @details,
|
179
|
+
:is_nsfw => !@sfw,
|
180
|
+
:is_public => @public
|
181
|
+
})
|
182
|
+
|
183
|
+
raise RuntimeError, "You don't have permission to edit this file" if res.body =~ /don't have permission/
|
184
|
+
|
185
|
+
return res.code == "200"
|
186
|
+
end
|
187
|
+
|
188
|
+
# Delete this file (permanently!) from stashbox.org
|
189
|
+
def delete
|
190
|
+
res = Agent.post("http://stashbox.org/delete_upload/"<<@stashboxid)
|
191
|
+
|
192
|
+
raise RuntimeError, "You don't have permission to delete this file" if res.body =~ /don't have permission/
|
193
|
+
|
194
|
+
return res.code == "200"
|
195
|
+
end
|
196
|
+
|
197
|
+
def inspect
|
198
|
+
keys = [@public ? "public" : "private"]
|
199
|
+
keys.push("nsfw") if !@sfw and @metadata
|
200
|
+
"<Stash: #{@filename} (#{keys * ", "})>"
|
201
|
+
end
|
202
|
+
|
203
|
+
# Grab metadata from stashbox.org
|
204
|
+
def refresh
|
205
|
+
# Get extended information
|
206
|
+
doc = Agent.get "http://stashbox.org/v/"<<@stashboxid
|
207
|
+
|
208
|
+
doc.search("//div[@class='subsection']").each do |sub|
|
209
|
+
value = sub.search("div[@class='value']").inner_text.strip
|
210
|
+
case sub.search("div[@class='label']").inner_text
|
211
|
+
when "Size"
|
212
|
+
@size = value[/^(\d+\.\d+)\ ([K|M]?)B$/,1].to_f * SIZES[$2]
|
213
|
+
when "Uploaded On"
|
214
|
+
@uploaded_on = Time.parse(value)
|
215
|
+
when "Views"
|
216
|
+
@size = value[/^(\d+)\ \(/,1].to_i
|
217
|
+
when "Rating"
|
218
|
+
@rating = Percentage.new(value[/(\d+\.\d+)\/5\ $/,1].to_f/5.0)
|
219
|
+
when "Tags"
|
220
|
+
@tags = sub.search("div[@class='value']/a").collect do |tag|
|
221
|
+
tag.inner_text
|
222
|
+
end
|
223
|
+
when "Title"
|
224
|
+
if sub.search("div[@class='value']/i").inner_text == ""
|
225
|
+
@title = value
|
226
|
+
else
|
227
|
+
@title = nil
|
228
|
+
end
|
229
|
+
when "KnowCode"
|
230
|
+
@know_code = value
|
231
|
+
when "Description"
|
232
|
+
if sub.search("div[@class='value']/i").inner_text == ""
|
233
|
+
@description = value
|
234
|
+
else
|
235
|
+
@description = nil
|
236
|
+
end
|
237
|
+
when "Is this file work safe?"
|
238
|
+
@sfw = (value == "Yes")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
@metadata = true
|
242
|
+
return
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
def parse_tags(new_tags)
|
247
|
+
[new_tags].flatten.collect do |tag|
|
248
|
+
tag.downcase.gsub(/[^a-z0-9_-]/,"")
|
249
|
+
tag = nil if tag.empty?
|
250
|
+
end.compact.uniq
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stashboxr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- JP Hastings-Spital
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-08 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: mechanize
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: Shoulda
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Upload files to stashbox.org and manage their metadata. User accounts or anonymous uploads!
|
52
|
+
email: jphastings@gmail.com
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- Rakefile
|
61
|
+
- VERSION
|
62
|
+
- lib/stashboxr.rb
|
63
|
+
- test/helper.rb
|
64
|
+
- test/test_stashboxr.rb
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/jphastings/StashboxR
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --charset=UTF-8
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.3.7
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: A ruby api for the stashbox.org website
|
99
|
+
test_files:
|
100
|
+
- test/helper.rb
|
101
|
+
- test/test_stashboxr.rb
|