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