digitalpardoe-rflickr 1.0.0.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/README +22 -0
- data/lib/flickr.rb +39 -0
- data/lib/flickr/auth.rb +96 -0
- data/lib/flickr/base.rb +813 -0
- data/lib/flickr/blogs.rb +50 -0
- data/lib/flickr/contacts.rb +68 -0
- data/lib/flickr/favorites.rb +79 -0
- data/lib/flickr/groups.rb +102 -0
- data/lib/flickr/interestingness.rb +39 -0
- data/lib/flickr/licenses.rb +43 -0
- data/lib/flickr/notes.rb +44 -0
- data/lib/flickr/people.rb +99 -0
- data/lib/flickr/photos.rb +305 -0
- data/lib/flickr/photosets.rb +124 -0
- data/lib/flickr/pools.rb +90 -0
- data/lib/flickr/reflection.rb +109 -0
- data/lib/flickr/tags.rb +79 -0
- data/lib/flickr/transform.rb +29 -0
- data/lib/flickr/upload.rb +225 -0
- data/lib/flickr/urls.rb +69 -0
- metadata +73 -0
data/README
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
To use this version of rFlickr, first you will need to install
|
2
|
+
the RubyGem you can do by (from a command line):
|
3
|
+
|
4
|
+
$ gem sources -a http://gems.github.com (you only have to do this once)
|
5
|
+
$ sudo gem install digitalpardoe-rflickr
|
6
|
+
|
7
|
+
Then you'll need to get a Flickr API key as detailed here:
|
8
|
+
|
9
|
+
http://www.flickr.com/services/api/misc.api_keys.html
|
10
|
+
|
11
|
+
You'll need to authorize your application's API key from each
|
12
|
+
Flickr user account which wants to use it.
|
13
|
+
|
14
|
+
A short guide to get you started can be found here:
|
15
|
+
|
16
|
+
http://digitalpardoe.co.uk/blog/show/87
|
17
|
+
|
18
|
+
The guide is a little out of date in it's gem installation technique,
|
19
|
+
use the instructions above instead.
|
20
|
+
|
21
|
+
This project is still a work in progress, bear with me whilst it is
|
22
|
+
set up.
|
data/lib/flickr.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# rFlickr: A Ruby based Flickr API implementation.
|
2
|
+
# Copyright (C) 2009, Alex Pardoe (digital:pardoe)
|
3
|
+
#
|
4
|
+
# Derrived from work by Trevor Schroeder, see here:
|
5
|
+
# http://rubyforge.org/projects/rflickr/.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License
|
9
|
+
# as published by the Free Software Foundation; either version 2
|
10
|
+
# of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
20
|
+
|
21
|
+
require 'flickr/auth'
|
22
|
+
require 'flickr/base'
|
23
|
+
require 'flickr/blogs'
|
24
|
+
require 'flickr/contacts'
|
25
|
+
require 'flickr/favorites'
|
26
|
+
require 'flickr/groups'
|
27
|
+
require 'flickr/licenses'
|
28
|
+
require 'flickr/notes'
|
29
|
+
require 'flickr/people'
|
30
|
+
require 'flickr/photos'
|
31
|
+
require 'flickr/photosets'
|
32
|
+
require 'flickr/pools'
|
33
|
+
require 'flickr/reflection'
|
34
|
+
require 'flickr/test'
|
35
|
+
require 'flickr/transform'
|
36
|
+
require 'flickr/upload'
|
37
|
+
require 'flickr/urls'
|
38
|
+
require 'flickr/tags'
|
39
|
+
require 'flickr/interestingness'
|
data/lib/flickr/auth.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# rFlickr: A Ruby based Flickr API implementation.
|
4
|
+
# Copyright (C) 2009, Alex Pardoe (digital:pardoe)
|
5
|
+
#
|
6
|
+
# Derrived from work by Trevor Schroeder, see here:
|
7
|
+
# http://rubyforge.org/projects/rflickr/.
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or
|
10
|
+
# modify it under the terms of the GNU General Public License
|
11
|
+
# as published by the Free Software Foundation; either version 2
|
12
|
+
# of the License, or (at your option) any later version.
|
13
|
+
#
|
14
|
+
# This program is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with this program; if not, write to the Free Software
|
21
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
22
|
+
|
23
|
+
require 'flickr/base'
|
24
|
+
|
25
|
+
class Flickr::Auth < Flickr::APIBase
|
26
|
+
attr_accessor :cache_file, :token
|
27
|
+
|
28
|
+
def clear_cache
|
29
|
+
@token = nil
|
30
|
+
@frob = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(flickr,cache_file=nil)
|
34
|
+
super(flickr)
|
35
|
+
@frob = nil
|
36
|
+
@token = nil
|
37
|
+
@cache_file = cache_file
|
38
|
+
if @cache_file && File.exists?(@cache_file)
|
39
|
+
load_token
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def login_link(perms='delete')
|
44
|
+
args={ 'api_key' => @flickr.api_key, 'perms' => perms}
|
45
|
+
args['frob'] = self.frob
|
46
|
+
args['api_sig'] = @flickr.sign(args)
|
47
|
+
return "http://flickr.com/services/auth/?"+
|
48
|
+
args.to_a.map{|arr| arr.join('=')}.join('&')
|
49
|
+
end
|
50
|
+
|
51
|
+
def frob=(frob) @frob = frob end
|
52
|
+
def frob() return @frob || getFrob end
|
53
|
+
|
54
|
+
def cache_token
|
55
|
+
File.open(@cache_file,'w'){ |f| f.write @token.to_xml } if token
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_token
|
59
|
+
token = nil
|
60
|
+
File.open(@cache_file,'r'){ |f| token = f.read }
|
61
|
+
# Dirt stupid check to see if it's probably XML or
|
62
|
+
# not. If it is, then we don't call checkToken.
|
63
|
+
#
|
64
|
+
# Backwwards compatible with old token storage.
|
65
|
+
@token = token.include?('<') ?
|
66
|
+
Flickr::Token.from_xml(REXML::Document.new(token)) :
|
67
|
+
@token = checkToken(token)
|
68
|
+
end
|
69
|
+
|
70
|
+
def getToken(frob=nil)
|
71
|
+
frob ||= @frob
|
72
|
+
res=@flickr.call_unauth_method('flickr.auth.getToken',
|
73
|
+
'frob'=>frob)
|
74
|
+
@token = Flickr::Token.from_xml(res)
|
75
|
+
end
|
76
|
+
|
77
|
+
def getFullToken(mini_token)
|
78
|
+
res = flickr.call_unauth_method('flickr.auth.getFullToken',
|
79
|
+
'mini_token' => mini_token)
|
80
|
+
@token = Flickr::Token.from_xml(res)
|
81
|
+
end
|
82
|
+
|
83
|
+
def getFrob
|
84
|
+
doc = @flickr.call_unauth_method('flickr.auth.getFrob')
|
85
|
+
@frob = doc.elements['/frob'].text
|
86
|
+
return @frob
|
87
|
+
end
|
88
|
+
|
89
|
+
def checkToken(token=nil)
|
90
|
+
token ||= @token
|
91
|
+
token = token.token if token.class == Flickr::Token
|
92
|
+
res = @flickr.call_unauth_method('flickr.auth.checkToken',
|
93
|
+
'auth_token' => token)
|
94
|
+
@token = Flickr::Token.from_xml(res)
|
95
|
+
end
|
96
|
+
end
|
data/lib/flickr/base.rb
ADDED
@@ -0,0 +1,813 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# rFlickr: A Ruby based Flickr API implementation.
|
4
|
+
# Copyright (C) 2009, Alex Pardoe (digital:pardoe)
|
5
|
+
#
|
6
|
+
# Derrived from work by Trevor Schroeder, see here:
|
7
|
+
# http://rubyforge.org/projects/rflickr/.
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or
|
10
|
+
# modify it under the terms of the GNU General Public License
|
11
|
+
# as published by the Free Software Foundation; either version 2
|
12
|
+
# of the License, or (at your option) any later version.
|
13
|
+
#
|
14
|
+
# This program is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with this program; if not, write to the Free Software
|
21
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
22
|
+
|
23
|
+
## Structure this class hierarchy the way the Flickr API is structured.
|
24
|
+
## Flickr::Auth and so on. At least think about whether it makes sense or
|
25
|
+
## not.
|
26
|
+
|
27
|
+
require 'xmlrpc/client'
|
28
|
+
require 'md5'
|
29
|
+
require 'rexml/document'
|
30
|
+
require 'parsedate'
|
31
|
+
|
32
|
+
class Flickr
|
33
|
+
API_KEY=''
|
34
|
+
SHARED_SECRET=''
|
35
|
+
|
36
|
+
attr_reader :api_key
|
37
|
+
attr_accessor :async, :debug, :caching, :auth_mode
|
38
|
+
|
39
|
+
############################### CACHE ACCESSORS ###########################
|
40
|
+
def ticket_cache_lookup(id) @ticket_by_id[id] if @caching end
|
41
|
+
|
42
|
+
def ticket_cache_store(ticket)
|
43
|
+
@ticket_by_id[ticket.id] = ticket if @caching
|
44
|
+
end
|
45
|
+
|
46
|
+
def person_cache_lookup(nsid) @person_by_nsid[nsid] if @caching end
|
47
|
+
|
48
|
+
def person_cache_store(person)
|
49
|
+
@person_by_nsid[person.nsid] = person if @caching
|
50
|
+
end
|
51
|
+
|
52
|
+
def photo_cache_lookup(id) @photo_by_id[id] if @caching end
|
53
|
+
|
54
|
+
def photo_cache_store(photo)
|
55
|
+
@photo_by_id[photo.id] = photo if @caching
|
56
|
+
end
|
57
|
+
|
58
|
+
def license_cache_lookup() @license_cache if @caching end
|
59
|
+
|
60
|
+
def license_cache_store(licenses)
|
61
|
+
@license_cache = licenses if @caching
|
62
|
+
end
|
63
|
+
|
64
|
+
def blog_cache_lookup() @blog_cache if @caching end
|
65
|
+
|
66
|
+
def blog_cache_store(blogs) @blog_cache = blogs if @caching end
|
67
|
+
|
68
|
+
def photoset_cache_lookup(id) @photoset_by_id[id] if @caching end
|
69
|
+
|
70
|
+
def photoset_cache_store(set)
|
71
|
+
@photoset_by_id[set.id] = set if @caching
|
72
|
+
end
|
73
|
+
|
74
|
+
def photopool_cache_lookup(id) @photopool_by_id[id] if @caching end
|
75
|
+
|
76
|
+
def photopool_cache_store(pool)
|
77
|
+
@photopool_by_id[pool.id] = pool if @caching
|
78
|
+
end
|
79
|
+
|
80
|
+
def group_cache_lookup(id) @group_by_id[id] if @caching end
|
81
|
+
|
82
|
+
def group_cache_store(group)
|
83
|
+
@group_by_id[group.id] = group if @caching
|
84
|
+
end
|
85
|
+
############################################################################
|
86
|
+
|
87
|
+
def debug(*args) $stderr.puts(sprintf(*args)) if @debug end
|
88
|
+
|
89
|
+
def Flickr.todo
|
90
|
+
[
|
91
|
+
'Refactor, especially more Class.from_xml methods',
|
92
|
+
'More logical OO design, wrap the API methods to make transparent',
|
93
|
+
'Class & method documentation',
|
94
|
+
'Unit tests',
|
95
|
+
'Implement missing methods (see flickr.reflection.missing_methods)'
|
96
|
+
]
|
97
|
+
end
|
98
|
+
def todo()
|
99
|
+
Flickr.todo+reflection.missing_methods.map{|m| 'Implement '+m}
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def initialize(token_cache=nil,api_key=API_KEY,
|
104
|
+
shared_secret=SHARED_SECRET,
|
105
|
+
endpoint='http://www.flickr.com/services/xmlrpc/')
|
106
|
+
@async = false
|
107
|
+
@caching = true
|
108
|
+
@auth_mode = true
|
109
|
+
@api_key=api_key
|
110
|
+
@shared_secret=shared_secret
|
111
|
+
@token_cache = token_cache
|
112
|
+
@endpoint=endpoint
|
113
|
+
proto,host,port,path,user,pass=parse_url(@endpoint)
|
114
|
+
raise ProtoUnknownError.new("Unhandled protocol '#{proto}'") if
|
115
|
+
proto.downcase != 'http'
|
116
|
+
@client=XMLRPC::Client.new(host,path,port)
|
117
|
+
clear_cache
|
118
|
+
end
|
119
|
+
|
120
|
+
def clear_cache()
|
121
|
+
@auth = nil
|
122
|
+
@blogs = nil
|
123
|
+
@contacts = nil
|
124
|
+
@favorites = nil
|
125
|
+
@groups = nil
|
126
|
+
@interestingness = nil
|
127
|
+
@reflection = nil
|
128
|
+
@people = nil
|
129
|
+
@photos = nil
|
130
|
+
@photosets = nil
|
131
|
+
@test = nil
|
132
|
+
@urls = nil
|
133
|
+
|
134
|
+
@ticket_by_id = {}
|
135
|
+
@person_by_nsid = {}
|
136
|
+
@photo_by_id = {}
|
137
|
+
@photoset_by_id = {}
|
138
|
+
@photopool_by_id = {}
|
139
|
+
@group_by_id = {}
|
140
|
+
@license_cache = nil
|
141
|
+
@blog_cache = nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def auth() @auth ||= Auth.new(self,@token_cache) end
|
145
|
+
def blogs() @blogs ||= Blogs.new(self) end
|
146
|
+
def contacts() @contacts ||= Contacts.new(self) end
|
147
|
+
def favorites() @favorites ||= Favorites.new(self) end
|
148
|
+
def groups() @groups ||= Groups.new(self) end
|
149
|
+
def people() @people ||= People.new(self) end
|
150
|
+
def photos() @photos ||= Photos.new(self) end
|
151
|
+
def photosets() @photosets ||= PhotoSets.new(self) end
|
152
|
+
def reflection() @reflection ||= Reflection.new(self) end
|
153
|
+
def test() @test ||= Test.new(self) end
|
154
|
+
def urls() @urls ||= Urls.new(self) end
|
155
|
+
def tags() @tags ||= Tags.new(self) end
|
156
|
+
def interestingness() @interestingness ||= Interestingness.new(self) end
|
157
|
+
|
158
|
+
def call_method(method,args={})
|
159
|
+
@auth_mode ? call_auth_method(method,args) :
|
160
|
+
call_unauth_method(method,args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def call_unauth_method(method,args={})
|
164
|
+
debug('%s(%s)', method, args.inspect)
|
165
|
+
tries = 3
|
166
|
+
args = args.dup
|
167
|
+
args['api_key'] = @api_key
|
168
|
+
api_sig=sign(args)
|
169
|
+
args['api_sig']=api_sig
|
170
|
+
begin
|
171
|
+
tries -= 1;
|
172
|
+
str = @async ? @client.call_async(method,args) :
|
173
|
+
@client.call(method,args)
|
174
|
+
debug('RETURN: %s',str)
|
175
|
+
return REXML::Document.new(str)
|
176
|
+
rescue Timeout::Error => te
|
177
|
+
$stderr.puts "Timed out, will try #{tries} more times."
|
178
|
+
if tries > 0
|
179
|
+
retry
|
180
|
+
else
|
181
|
+
raise te
|
182
|
+
end
|
183
|
+
rescue REXML::ParseException => pe
|
184
|
+
return REXML::Document.new('<rsp>'+str+'</rsp>').
|
185
|
+
elements['/rsp']
|
186
|
+
rescue XMLRPC::FaultException => fe
|
187
|
+
$stderr.puts "ERR: #{fe.faultString} (#{fe.faultCode})"
|
188
|
+
raise fe
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def call_auth_method(method,args={})
|
193
|
+
at = args['auth_token']
|
194
|
+
args['auth_token'] ||= auth.token.token
|
195
|
+
res = call_unauth_method(method,args)
|
196
|
+
args.delete('auth_token') unless at
|
197
|
+
return res
|
198
|
+
end
|
199
|
+
|
200
|
+
def sign(args)
|
201
|
+
return MD5.md5(@shared_secret+args.sort.flatten.join).to_s
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_url(url)
|
205
|
+
url =~ /([^:]+):\/\/([^\/]*)(.*)/
|
206
|
+
proto = $1.to_s
|
207
|
+
hostplus = $2.to_s
|
208
|
+
path = $3.to_s
|
209
|
+
|
210
|
+
hostplus =~ /(?:(.*)@)?(.*)/
|
211
|
+
userpass = $1
|
212
|
+
hostport = $2
|
213
|
+
user,pass = userpass.to_s.split(':',2)
|
214
|
+
host,port = hostport.to_s.split(':',2)
|
215
|
+
port = port ? port.to_i : 80
|
216
|
+
|
217
|
+
return proto,host,port,path,user,pass
|
218
|
+
end
|
219
|
+
|
220
|
+
def mysql_datetime(time) time.strftime('%Y-%m-%d %H:%M:%S') end
|
221
|
+
def mysql_date(time) time.strftime('%Y-%m-%d') end
|
222
|
+
end
|
223
|
+
|
224
|
+
class Flickr::APIBase
|
225
|
+
attr_reader :flickr
|
226
|
+
|
227
|
+
def initialize(flickr) @flickr = flickr end
|
228
|
+
end
|
229
|
+
|
230
|
+
class Flickr::Token
|
231
|
+
attr_reader :token, :perms, :user
|
232
|
+
|
233
|
+
def initialize(token, perms, user)
|
234
|
+
@token = token
|
235
|
+
@perms = perms
|
236
|
+
@user = user
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.from_xml(xml, flickr=nil)
|
240
|
+
token = xml.elements['/auth/token'].text
|
241
|
+
perms = xml.elements['/auth/perms'].text.intern
|
242
|
+
user = xml.elements['/auth/user']
|
243
|
+
nsid = user.attributes['nsid']
|
244
|
+
username = user.attributes['username']
|
245
|
+
fullname = user.attributes['fullname']
|
246
|
+
|
247
|
+
p = flickr.person_cache_lookup(nsid) if flickr
|
248
|
+
p ||= Flickr::Person.new(flickr,nsid,username)
|
249
|
+
p.realname=fullname
|
250
|
+
flickr.person_cache_store(p) if flickr
|
251
|
+
|
252
|
+
return Flickr::Token.new(token,perms,p)
|
253
|
+
end
|
254
|
+
|
255
|
+
def to_xml
|
256
|
+
return "<auth><token>#{self.token}</token>"+
|
257
|
+
"<perms>#{self.perms}</perms>"+
|
258
|
+
"<user nsid=\"#{self.user.nsid}\" "+
|
259
|
+
"username=\"#{self.user.username}\" "+
|
260
|
+
"fullname=\"#{self.user.realname}\" /></auth>"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class Flickr::Blog
|
265
|
+
attr_reader :id, :name, :needspassword, :url
|
266
|
+
|
267
|
+
def initialize(id,name,needspassword,url)
|
268
|
+
@id = id
|
269
|
+
@name = name
|
270
|
+
@needspassword = needspassword
|
271
|
+
@url = url
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class Flickr::Person
|
276
|
+
attr_accessor :nsid, :username, :realname, :mbox_sha1sum, :location,
|
277
|
+
:photosurl, :profileurl, :photos_firstdate, :photos_firstdatetaken,
|
278
|
+
:photos_count, :info_fetched, :isadmin, :ispro, :iconserver,
|
279
|
+
:bandwidth_max, :bandwidth_used, :filesize_max, :upload_fetched,
|
280
|
+
:friend, :family, :ignored
|
281
|
+
|
282
|
+
def initialize(flickr, nsid, username)
|
283
|
+
@flickr = flickr
|
284
|
+
@nsid = nsid
|
285
|
+
@username = username
|
286
|
+
@info_fetched = false
|
287
|
+
@upload_fetched = false
|
288
|
+
end
|
289
|
+
|
290
|
+
def full_info()
|
291
|
+
self.info_fetched ? self : @flickr.people.getInfo(self)
|
292
|
+
end
|
293
|
+
def upload_status() self.upload_fetched ? self :
|
294
|
+
@flickr.people.getUploadStatus(self) end
|
295
|
+
|
296
|
+
|
297
|
+
# I think this will define a class method. You can't use
|
298
|
+
# Flickr::Person.from_xml and if you just say Person.from_xml, it
|
299
|
+
# can't resolve Flickr::Person::Person
|
300
|
+
def self.from_xml(xml,flickr=nil)
|
301
|
+
els = xml.elements
|
302
|
+
att = xml.root.attributes
|
303
|
+
|
304
|
+
nsid = cond_attr(att,'nsid')
|
305
|
+
username = cond_text(els,'/person/username')
|
306
|
+
|
307
|
+
p = flickr.person_cache_lookup(nsid) if flickr
|
308
|
+
p ||= Flickr::Person.new(flickr,nsid,username)
|
309
|
+
|
310
|
+
p.username = username
|
311
|
+
p.isadmin = cond_attr(att,'isadmin') &&
|
312
|
+
cond_attr(att,'isadmin') == '1'
|
313
|
+
p.ispro = cond_attr(att,'ispro') &&
|
314
|
+
cond_attr(att,'ispro') == '1'
|
315
|
+
p.iconserver = cond_attr(att,'iconserver') &&
|
316
|
+
cond_attr(att,'iconserver').to_i
|
317
|
+
p.realname = cond_text(els,'/person/realname')
|
318
|
+
p.mbox_sha1sum = cond_text(els,'/person/mbox_sha1sum')
|
319
|
+
p.location = cond_text(els,'/person/location')
|
320
|
+
p.photosurl = cond_text(els,'/person/photosurl')
|
321
|
+
p.profileurl = cond_text(els,'/person/profileurl')
|
322
|
+
tstr = cond_text(els,'/person/photos/firstdate')
|
323
|
+
p.photos_firstdate = Time.at(tstr.to_i) if tstr
|
324
|
+
tstr = cond_text(els, '/person/photos/firstdatetaken')
|
325
|
+
p.photos_firstdatetaken = Time.gm(*ParseDate.parsedate(tstr)) if
|
326
|
+
tstr
|
327
|
+
p.photos_count = cond_text(els,'/person/photos/count')
|
328
|
+
p.photos_count = p.photos_count if p.photos_count
|
329
|
+
|
330
|
+
p.info_fetched = true if p.photos_count
|
331
|
+
|
332
|
+
if els['/user/bandwidth']
|
333
|
+
att = els['/user/bandwidth'].attributes
|
334
|
+
p.bandwidth_max = cond_attr(att,'max') &&
|
335
|
+
cond_attr(att,'max').to_i
|
336
|
+
p.bandwidth_used = cond_attr(att,'used') &&
|
337
|
+
cond_attr(att,'used').to_i
|
338
|
+
end
|
339
|
+
if els['/user/filesize']
|
340
|
+
att = els['/user/filesize'].attributes
|
341
|
+
p.filesize_max = cond_attr(att,'max') &&
|
342
|
+
cond_attr(att,'max').to_i
|
343
|
+
end
|
344
|
+
|
345
|
+
p.upload_fetched = true if p.bandwidth_max
|
346
|
+
|
347
|
+
flickr.person_cache_store(p) if flickr
|
348
|
+
return p
|
349
|
+
end
|
350
|
+
|
351
|
+
private
|
352
|
+
def self.cond_text(elements,index)
|
353
|
+
elements[index] ? elements[index].text : nil
|
354
|
+
end
|
355
|
+
|
356
|
+
def self.cond_attr(attributes,name) attributes[name] end
|
357
|
+
end
|
358
|
+
|
359
|
+
class Flickr::Size
|
360
|
+
attr_reader :label,:width,:height,:source,:url
|
361
|
+
|
362
|
+
def initialize(label,width,height,source,url)
|
363
|
+
@label = label
|
364
|
+
@width = width
|
365
|
+
@height = height
|
366
|
+
@source = source
|
367
|
+
@url = url
|
368
|
+
end
|
369
|
+
|
370
|
+
def self.from_xml(xml)
|
371
|
+
att = xml.attributes
|
372
|
+
return Flickr::Size.new(att['label'],att['width'].to_i,
|
373
|
+
att['height'].to_i,att['source'],att['url'])
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
class Flickr::Photo
|
378
|
+
attr_accessor :id, :owner_id, :secret, :server, :title, :ispublic,
|
379
|
+
:isfriend, :isfamily, :ownername, :dateadded,
|
380
|
+
:license_id, :description, :dates, :taken,
|
381
|
+
:lastupdate, :takengranularity, :cancomment, :canaddmeta,
|
382
|
+
:comments, :rotation, :notes, :urls, :permaddmeta,
|
383
|
+
:permcomment, :originalformat
|
384
|
+
|
385
|
+
attr_reader :flickr
|
386
|
+
|
387
|
+
def owner() @owner ||= @flickr.people.getInfo(owner_id) end
|
388
|
+
def sizes() @sizes || @flickr.photos.getSizes(self).sizes end
|
389
|
+
def sizes=(sizes) @sizes = sizes end
|
390
|
+
|
391
|
+
def max_size
|
392
|
+
sizes[:Original] || sizes[:Large] || sizes[:Medium] ||
|
393
|
+
sizes[:Small]
|
394
|
+
end
|
395
|
+
|
396
|
+
def initialize(flickr,id)
|
397
|
+
@flickr = flickr
|
398
|
+
@id = id
|
399
|
+
end
|
400
|
+
|
401
|
+
def exif() @exif ||= @flickr.photos.getExif(self) end
|
402
|
+
def exif=(set) @exif = set end
|
403
|
+
|
404
|
+
def tags() @tags ||= @flickr.tags.getListPhoto(self) end
|
405
|
+
def tags=(set) @tags = set end
|
406
|
+
|
407
|
+
def license() @flickr.photos.licenses.getInfo[@license_id] end
|
408
|
+
|
409
|
+
def contexts() @contexts ||= @flickr.photos.getAllContexts(self) end
|
410
|
+
|
411
|
+
def url(size=nil)
|
412
|
+
base = 'http://static.flickr.com'
|
413
|
+
ext = (size == 'o') ? self.originalformat : 'jpg'
|
414
|
+
return size ?
|
415
|
+
"#{base}/#@server/#{@id}_#{@secret}_#{size}.#{ext}" :
|
416
|
+
"#{base}/#@server/#{@id}_#{@secret}.jpg"
|
417
|
+
end
|
418
|
+
|
419
|
+
def delete() @flickr.photos.delete(self) end
|
420
|
+
|
421
|
+
def self.from_xml(xml,flickr=nil)
|
422
|
+
att = xml.attributes
|
423
|
+
phid = att['id']
|
424
|
+
|
425
|
+
photo = flickr.photo_cache_lookup(phid) if flickr
|
426
|
+
photo ||= Flickr::Photo.new(flickr,phid)
|
427
|
+
|
428
|
+
photo.owner_id ||= att['owner'] || (xml.elements['owner'] &&
|
429
|
+
xml.elements['owner'].attributes['nsid'])
|
430
|
+
photo.secret = att['secret'] if att['secret']
|
431
|
+
photo.originalformat = att['originalformat'] if
|
432
|
+
att['originalformat']
|
433
|
+
photo.server = att['server'].to_i if att['server']
|
434
|
+
photo.title = att['title'] || cond_text(xml.elements,'title')
|
435
|
+
photo.license_id = att['license']
|
436
|
+
photo.rotation = att['rotation'].to_i if att['rotation']
|
437
|
+
|
438
|
+
photo.ispublic = (att['ispublic'].to_i == 1) if att['ispublic']
|
439
|
+
photo.isfriend = (att['isfriend'].to_i == 1) if att['isfriend']
|
440
|
+
photo.isfamily = (att['isfamily'].to_i == 1) if att['isfamily']
|
441
|
+
photo.ownername = att['ownername'] || (xml.elements['owner'] &&
|
442
|
+
xml.elements['owner'].attributes['username'])
|
443
|
+
photo.description = cond_text(xml.elements,'description')
|
444
|
+
photo.dateadded = Time.at(att['dateadded'].to_i) if
|
445
|
+
att['dateadded']
|
446
|
+
if xml.elements['exif']
|
447
|
+
list = []
|
448
|
+
xml.elements.each('exif') do |el|
|
449
|
+
exif = Flickr::Exif.from_xml(el)
|
450
|
+
list << exif
|
451
|
+
end
|
452
|
+
photo.exif = list
|
453
|
+
end
|
454
|
+
if xml.elements['visibility']
|
455
|
+
att = xml.elements['visibility'].attributes
|
456
|
+
photo.ispublic = (att['ispublic'].to_i == 1)
|
457
|
+
photo.isfriend = (att['isfriend'].to_i == 1)
|
458
|
+
photo.isfamily = (att['isfamily'].to_i == 1)
|
459
|
+
end
|
460
|
+
if xml.elements['dates']
|
461
|
+
att = xml.elements['dates'].attributes
|
462
|
+
dates = {}
|
463
|
+
dates[:posted] = Time.at(att['posted'].to_i)
|
464
|
+
dates[:taken] = Time.gm(*ParseDate.parsedate(att['taken']))
|
465
|
+
dates[:lastupdate] = Time.at(att['lastupdate'].to_i)
|
466
|
+
dates[:takengranularity] = att['takengranularity'].to_i
|
467
|
+
photo.dates = dates
|
468
|
+
end
|
469
|
+
if xml.elements['editability']
|
470
|
+
att = xml.elements['editability'].attributes
|
471
|
+
photo.cancomment = (att['cancomment'].to_i == 1)
|
472
|
+
photo.canaddmeta = (att['canaddmeta'].to_i == 1)
|
473
|
+
end
|
474
|
+
photo.comments = cond_text(xml.elements,'comments')
|
475
|
+
photo.comments &&= photo.comments.to_i
|
476
|
+
if xml.elements['notes']
|
477
|
+
notes = []
|
478
|
+
xml.elements['notes'].each_element do |el|
|
479
|
+
notes << Flickr::Note.from_xml(el,photo)
|
480
|
+
end
|
481
|
+
photo.notes = notes
|
482
|
+
end
|
483
|
+
if xml.elements['tags']
|
484
|
+
tags = []
|
485
|
+
xml.elements['tags'].each_element do |el|
|
486
|
+
tags << Flickr::Tag.from_xml(el,photo)
|
487
|
+
end
|
488
|
+
photo.tags = tags
|
489
|
+
end
|
490
|
+
if xml.elements['urls']
|
491
|
+
urls = {}
|
492
|
+
xml.elements['urls'].each_element do |el|
|
493
|
+
att = el.attributes
|
494
|
+
urls[att['type'].intern] = el.text
|
495
|
+
end
|
496
|
+
photo.urls = urls
|
497
|
+
end
|
498
|
+
|
499
|
+
flickr.photo_cache_store(photo) if flickr
|
500
|
+
return photo
|
501
|
+
end
|
502
|
+
|
503
|
+
private
|
504
|
+
def self.cond_text(elements,index)
|
505
|
+
elements[index] ? elements[index].text : nil
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
class Flickr::Exif
|
510
|
+
attr_reader :tagspace,:tagspaceid,:tag,:label
|
511
|
+
attr_accessor :raw,:clean
|
512
|
+
def initialize(tagspace,tagspaceid,tag,label)
|
513
|
+
@tagspace = tagspace
|
514
|
+
@tagspaceid = tagspaceid
|
515
|
+
@tag = tag
|
516
|
+
@label = label
|
517
|
+
end
|
518
|
+
|
519
|
+
def self.from_xml(element)
|
520
|
+
att = element.attributes
|
521
|
+
exif = Flickr::Exif.new(att['tagspace'],att['tagspaceid'].to_i,
|
522
|
+
att['tag'],att['label'])
|
523
|
+
exif.raw=element.elements['raw'].text if element.elements['raw']
|
524
|
+
exif.clean=element.elements['clean'].text if
|
525
|
+
element.elements['clean']
|
526
|
+
return exif
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
class Flickr::PhotoList < Array
|
531
|
+
attr_reader :page,:pages,:perpage,:total
|
532
|
+
|
533
|
+
def initialize(page,pages,perpage,total)
|
534
|
+
@page = page
|
535
|
+
@pages = pages
|
536
|
+
@perpage = perpage
|
537
|
+
@total = total
|
538
|
+
end
|
539
|
+
|
540
|
+
def self.from_xml(xml,flickr=self)
|
541
|
+
att = xml.root.attributes
|
542
|
+
list = Flickr::PhotoList.new(att['page'].to_i,att['pages'].to_i,
|
543
|
+
att['perpage'].to_i,att['total'].to_i)
|
544
|
+
xml.elements['/photos'].each_element do |e|
|
545
|
+
list << Flickr::Photo.from_xml(e,flickr)
|
546
|
+
end
|
547
|
+
return list
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
class Flickr::Category
|
552
|
+
attr_reader :name, :path, :pathids, :groups, :subcats, :id
|
553
|
+
|
554
|
+
def initialize(name,path,pathids)
|
555
|
+
@name = name
|
556
|
+
@path = path
|
557
|
+
@pathids = pathids
|
558
|
+
@groups = []
|
559
|
+
@subcats = []
|
560
|
+
@id = pathids.split('/').last
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
class Flickr::SubCategory
|
565
|
+
attr_reader :name, :id, :count
|
566
|
+
|
567
|
+
def initialize(name,id,count)
|
568
|
+
@name = name
|
569
|
+
@id = id
|
570
|
+
@count = count
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
class Flickr::Group
|
575
|
+
# The privacy attribute is 1 for private groups, 2 for invite-only public
|
576
|
+
# groups and 3 for open public groups.
|
577
|
+
PRIVACY = [nil,:private,:invite,:public]
|
578
|
+
|
579
|
+
attr_accessor :nsid, :name, :members, :online, :chatnsid, :inchat,
|
580
|
+
:description, :privacy, :eighteenplus, :fully_fetched, :admin,
|
581
|
+
:photo_count, :iconserver
|
582
|
+
|
583
|
+
def initialize(flickr,nsid, name=nil, members=nil, online=nil,
|
584
|
+
chatnsid=nil, inchat=nil)
|
585
|
+
@flickr = flickr
|
586
|
+
@nsid = nsid
|
587
|
+
@name = name
|
588
|
+
@members = members
|
589
|
+
@online = online
|
590
|
+
@chatnsid = chatnsid
|
591
|
+
@inchat = inchat
|
592
|
+
@fully_fetched = false
|
593
|
+
end
|
594
|
+
|
595
|
+
def full_info
|
596
|
+
self.fully_fetched ? self : @flickr.groups.getInfo(self)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
class Flickr::GroupList < Array
|
601
|
+
attr_reader :page,:pages,:perpage,:total
|
602
|
+
|
603
|
+
def initialize(page,pages,perpage,total)
|
604
|
+
@page = page
|
605
|
+
@pages = pages
|
606
|
+
@perpage = perpage
|
607
|
+
@total = total
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
class Flickr::Context
|
612
|
+
attr_reader :prev_id,:prev_secret,:prev_title,:prev_url,
|
613
|
+
:next_id,:next_secret,:next_title,:next_url
|
614
|
+
|
615
|
+
def initialize(prev_id,prev_secret,prev_title,prev_url,
|
616
|
+
next_id,next_secret,next_title,next_url)
|
617
|
+
@prev_id = prev_id
|
618
|
+
@prev_secret = prev_secret
|
619
|
+
@prev_title = prev_title
|
620
|
+
@prev_url = prev_url
|
621
|
+
@next_id = next_id
|
622
|
+
@next_secret = next_secret
|
623
|
+
@next_title = next_title
|
624
|
+
@next_url = next_url
|
625
|
+
end
|
626
|
+
|
627
|
+
def self.from_xml(xml)
|
628
|
+
a0 = xml.elements['prevphoto'].attributes
|
629
|
+
a1 = xml.elements['nextphoto'].attributes
|
630
|
+
return Flickr::Context.new(
|
631
|
+
a0['id'],a0['secret'],a0['title'],a0['url'],
|
632
|
+
a1['id'],a1['secret'],a1['title'],a1['url'])
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
class Flickr::License
|
637
|
+
attr_reader :id, :name, :url
|
638
|
+
def initialize(id,name,url)
|
639
|
+
@id = id
|
640
|
+
@name = name
|
641
|
+
@url = url
|
642
|
+
end
|
643
|
+
|
644
|
+
def self.from_xml(xml)
|
645
|
+
att = xml.attributes
|
646
|
+
return Flickr::License.new(att['id'],att['name'],
|
647
|
+
att['url'])
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
class Flickr::Note
|
652
|
+
attr_accessor :photo, :x, :y, :w, :h, :text, :id, :author_id
|
653
|
+
def initialize(x, y, w, h, text, flickr = nil)
|
654
|
+
@x = x
|
655
|
+
@y = y
|
656
|
+
@w = w
|
657
|
+
@h = h
|
658
|
+
@text = text
|
659
|
+
@flickr = flickr
|
660
|
+
end
|
661
|
+
|
662
|
+
def author() @author_id && @flickr.people.getInfo(@author_id) end
|
663
|
+
|
664
|
+
def self.from_xml(xml,photo=nil)
|
665
|
+
att = xml.attributes
|
666
|
+
note = Flickr::Note.new(att['x'].to_i,att['y'].to_i,
|
667
|
+
att['w'].to_i,att['h'].to_i,xml.text,
|
668
|
+
photo && photo.flickr)
|
669
|
+
note.photo = photo
|
670
|
+
note.id = att['id']
|
671
|
+
note.author_id = att['author'] if att['author']
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
class Flickr::Count
|
676
|
+
attr_reader :fromdate, :todate, :count
|
677
|
+
def initialize(count,fromdate,todate)
|
678
|
+
@count = count
|
679
|
+
@fromdate = fromdate
|
680
|
+
@todate = todate
|
681
|
+
end
|
682
|
+
|
683
|
+
def self.from_xml(xml)
|
684
|
+
att = xml.attributes
|
685
|
+
return Flickr::Count.new(att['count'].to_i,
|
686
|
+
Time.at(att['fromdate'].to_i),
|
687
|
+
Time.at(att['todate'].to_i))
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
class Flickr::Tag
|
692
|
+
attr_reader :id, :author_id, :raw, :clean
|
693
|
+
|
694
|
+
def initialize(flickr, id,author_id,raw,clean)
|
695
|
+
@flickr = flickr
|
696
|
+
@id = id
|
697
|
+
@author_id = author_id
|
698
|
+
@raw = raw
|
699
|
+
@clean = clean
|
700
|
+
end
|
701
|
+
|
702
|
+
def author() @flickr.people.getInfo(@author_id) end
|
703
|
+
|
704
|
+
def self.from_xml(xml,flickr=nil)
|
705
|
+
att = xml.attributes
|
706
|
+
clean = xml.text
|
707
|
+
return Flickr::Tag.new(flickr,att['id'],att['author'],
|
708
|
+
att['raw'], clean)
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
class Flickr::PhotoSet < Array
|
713
|
+
attr_accessor :id, :title, :url, :server, :primary_id,
|
714
|
+
:photo_count, :description, :secret, :owner
|
715
|
+
|
716
|
+
def initialize(id,flickr)
|
717
|
+
@id = id
|
718
|
+
@flickr = flickr
|
719
|
+
end
|
720
|
+
|
721
|
+
def <<(photo,raw=false)
|
722
|
+
raw ? super(photo) : @flickr.photosets.addPhoto(self,photo)
|
723
|
+
return self
|
724
|
+
end
|
725
|
+
|
726
|
+
def fetch(extras=nil)
|
727
|
+
return self if @fetched
|
728
|
+
set = @flickr.photosets.getPhotos(self,extras)
|
729
|
+
@fetched = true
|
730
|
+
return set
|
731
|
+
end
|
732
|
+
|
733
|
+
alias photos fetch
|
734
|
+
|
735
|
+
def self.from_xml(xml,flickr=nil)
|
736
|
+
att = xml.attributes
|
737
|
+
psid = att['id']
|
738
|
+
|
739
|
+
set = flickr.photoset_cache_lookup(psid) if flickr
|
740
|
+
set ||= Flickr::PhotoSet.new(psid,flickr)
|
741
|
+
|
742
|
+
set.secret = att['secret']
|
743
|
+
set.owner = att['owner']
|
744
|
+
set.url = att['url']
|
745
|
+
set.server = att['server'].to_i
|
746
|
+
set.primary_id = att['primary'].to_i
|
747
|
+
set.photo_count = att['photos'].to_i
|
748
|
+
set.title = xml.elements['title'].text if xml.elements['title']
|
749
|
+
set.description = xml.elements['description'].text if
|
750
|
+
xml.elements['description']
|
751
|
+
if xml.elements['photo']
|
752
|
+
set.clear
|
753
|
+
xml.elements.each('photo') do |el|
|
754
|
+
set.<<(Flickr::Photo.from_xml(el,flickr),true)
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
flickr.photoset_cache_store(set) if flickr
|
759
|
+
return set
|
760
|
+
end
|
761
|
+
|
762
|
+
def url
|
763
|
+
owner = @owner || @flickr.photosets.getInfo(self).owner
|
764
|
+
return "http://www.flickr.com/photos/#{owner}/sets/#{@id}"
|
765
|
+
end
|
766
|
+
|
767
|
+
def primary() @primary ||= @flickr.photos.getInfo(@primary_id) end
|
768
|
+
end
|
769
|
+
|
770
|
+
class Flickr::PhotoPool < Array
|
771
|
+
attr_accessor :page, :pages, :perpage, :total, :title, :id
|
772
|
+
|
773
|
+
def initialize(id,flickr)
|
774
|
+
@id = id
|
775
|
+
@flickr = flickr
|
776
|
+
end
|
777
|
+
|
778
|
+
def <<(photo,raw=false)
|
779
|
+
raw ? super(photo) : @flickr.photosets.addPhoto(self,photo)
|
780
|
+
return self
|
781
|
+
end
|
782
|
+
|
783
|
+
def fetch(extras=nil)
|
784
|
+
return self if @fetched
|
785
|
+
pool = @flickr.groups.pools.getPhotos(self,nil,extras,500)
|
786
|
+
@fetched = true
|
787
|
+
return pool
|
788
|
+
end
|
789
|
+
|
790
|
+
def self.from_xml(xml,flickr=nil)
|
791
|
+
att = xml.attributes
|
792
|
+
ppid = att['id']
|
793
|
+
|
794
|
+
pool = flickr.photopool_cache_lookup(ppid)
|
795
|
+
pool ||= Flickr::PhotoPool.new(ppid,flickr)
|
796
|
+
|
797
|
+
pool.page = att['page'].to_i if att['page']
|
798
|
+
pool.pages = att['pages'].to_i if att['pages']
|
799
|
+
pool.perpage = att['perpage'].to_i if att['perpage']
|
800
|
+
pool.total = att['total'].to_i if att['total']
|
801
|
+
if xml.elements['photo']
|
802
|
+
# I'd like to clear the pool, but I can't because I don't know if I'm
|
803
|
+
# parsing the full set or just a single "page".
|
804
|
+
# pool.clear
|
805
|
+
xml.elements.each('photo') do |el|
|
806
|
+
pool.<<(Flickr::Photo.from_xml(el,flickr),true)
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
flickr.photopool_cache_store(pool) if flickr
|
811
|
+
return pool
|
812
|
+
end
|
813
|
+
end
|