dustin-r2flickr 0.1.1.6 → 0.1.1.7

Sign up to get free protection for your applications and to get access to all the features.
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 the last release of rflickr, which is no longer maintained.
3
+ It is a fork of rflickr, which is no longer maintained.
4
4
 
5
- There are a few example scripts in the examples directory.
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
- [1] http://rubyforge.org/projects/rflickr/
44
+ http://github.com/markoa/r2flickr/tree/master
45
+
46
+ Developers:
47
+
48
+ Trevor Schroeder (rflickr author)
49
+ Marko Anastasov
50
+ Dustin Sallings
@@ -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
- str =<<EOF
18
+ str =<<EOF
19
19
  <a href="#{photo.url}"><img src="#{photo.url('s')}" alt="#{photo.title}"></a>
20
20
  EOF
21
- print str.strip+' '
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
- arr = filename.split(File::SEPARATOR).last.split('.')
7
- arr.pop
8
- my_title = arr.join('.')
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
- nis += flickr.photos.getNotInSet(nil,500,pg)
28
- pg += 1
29
- tot -= 500
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
- my_title = filename_to_title(filename)
36
- photo = eligible.find{|photo| photo.title==my_title}
37
- photo ? uploaded << photo : to_upload << filename
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
- $stderr.puts "Uploading #{fname}..."
47
- flickr.photos.upload.upload_file_async(fname)
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
- 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)
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
- flickr.photos.getInfo(ticket.photoid)
63
+ flickr.photos.getInfo(ticket.photoid)
64
64
  end
65
65
  uploaded.each do |photo|
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
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
- 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
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}"
@@ -3,63 +3,63 @@
3
3
  require 'flickr'
4
4
 
5
5
  class Flickr::Relatedness
6
- attr_reader :photos
6
+ attr_reader :photos
7
7
 
8
- def initialize(photo)
9
- @photo = photo
10
- @threads = []
11
- end
8
+ def initialize(photo)
9
+ @photo = photo
10
+ @threads = []
11
+ end
12
12
 
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
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
- def [](i,j=nil)
25
- return j ? @photos[i,j] : @photos[i]
26
- end
24
+ def [](i,j=nil)
25
+ return j ? @photos[i,j] : @photos[i]
26
+ end
27
27
 
28
- private
28
+ private
29
29
 
30
- def thread_dead_collect
31
- threads = @threads.dup
32
- threads.each { |th| @threads.delete(th) unless th.alive? }
33
- end
30
+ def thread_dead_collect
31
+ threads = @threads.dup
32
+ threads.each { |th| @threads.delete(th) unless th.alive? }
33
+ end
34
34
 
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
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
- 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
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
- 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
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
- 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
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
- 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',
41
- 'frob'=>frob)
42
- @token = Flickr::Token.from_xml(res)
43
- end
44
-
45
- def cache_token
46
- @token_cache.cache_token(@token) if @token_cache
47
- end
48
-
49
- def getFullToken(mini_token)
50
- res = flickr.call_unauth_method('flickr.auth.getFullToken',
51
- 'mini_token' => mini_token)
52
- @token = Flickr::Token.from_xml(res)
53
- end
54
-
55
- def getFrob
56
- doc = @flickr.call_unauth_method('flickr.auth.getFrob')
57
- @frob = doc.elements['/frob'].text
58
- return @frob
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
- def initialize(token_cache=nil,api_key=API_KEY,
84
- shared_secret=SHARED_SECRET,
85
- endpoint='http://www.flickr.com/services/xmlrpc/')
86
- @async = false
87
- @caching = true
88
- @auth_mode = true
89
- @api_key=api_key
90
- @shared_secret=shared_secret
91
- @token_cache = token_cache
92
- @endpoint=endpoint
93
- proto,host,port,path,user,pass=parse_url(@endpoint)
94
- raise ProtoUnknownError.new("Unhandled protocol '#{proto}'") if
95
- proto.downcase != 'http'
96
- @client=XMLRPC::Client.new(host,path,port)
97
- clear_cache
98
- end
99
-
100
- def clear_cache()
101
- @auth = nil
102
- @blogs = nil
103
- @contacts = nil
104
- @favorites = nil
105
- @groups = nil
106
- @interestingness = nil
107
- @reflection = nil
108
- @people = nil
109
- @photos = nil
110
- @photosets = nil
111
- @test = nil
112
- @urls = nil
113
- @comments = nil
114
-
115
- @ticket_by_id = {}
116
- @person_by_nsid = {}
117
- @photo_by_id = {}
118
- @photoset_by_id = {}
119
- @photopool_by_id = {}
120
- @group_by_id = {}
121
- @license_cache = nil
122
- @blog_cache = nil
123
- end
124
-
125
- def auth() @auth ||= Auth.new(self,@token_cache) end
126
- def blogs() @blogs ||= Blogs.new(self) end
127
- def contacts() @contacts ||= Contacts.new(self) end
128
- def favorites() @favorites ||= Favorites.new(self) end
129
- def groups() @groups ||= Groups.new(self) end
130
- def people() @people ||= People.new(self) end
131
- def photos() @photos ||= Photos.new(self) end
132
- def photosets() @photosets ||= PhotoSets.new(self) end
133
- def reflection() @reflection ||= Reflection.new(self) end
134
- def test() @test ||= Test.new(self) end
135
- def urls() @urls ||= Urls.new(self) end
136
- def tags() @tags ||= Tags.new(self) end
137
- def comments() @comments ||= Comments.new(self) end
138
- def interestingness() @interestingness ||= Interestingness.new(self) end
139
-
140
- def call_method(method,args={})
141
- @auth_mode ? call_auth_method(method,args) :
142
- call_unauth_method(method,args)
143
- end
144
-
145
- def call_unauth_method(method,args={})
146
- debug('%s(%s)', method, args.inspect)
147
- tries = 3
148
- args = args.dup
149
- args['api_key'] = @api_key
150
- api_sig=sign(args)
151
- args['api_sig']=api_sig
152
- begin
153
- tries -= 1;
154
- str = @async ? @client.call_async(method,args) :
155
- @client.call(method,args)
156
- debug('RETURN: %s',str)
157
- return REXML::Document.new(str)
158
- rescue Timeout::Error => te
159
- $stderr.puts "Timed out, will try #{tries} more times."
160
- if tries > 0
161
- retry
162
- else
163
- raise te
164
- end
165
- rescue REXML::ParseException => pe
166
- return REXML::Document.new('<rsp>'+str+'</rsp>').
167
- elements['/rsp']
168
- rescue XMLRPC::FaultException => fe
169
- $stderr.puts "ERR: #{fe.faultString} (#{fe.faultCode})"
170
- raise fe
171
- end
172
- end
173
-
174
- def call_auth_method(method,args={})
175
- at = args['auth_token']
176
- args['auth_token'] ||= auth.token.token
177
- res = call_unauth_method(method,args)
178
- args.delete('auth_token') unless at
179
- return res
180
- end
181
-
182
- def sign(args)
183
- return MD5.md5(@shared_secret+args.sort.flatten.join).to_s
184
- end
185
-
186
- def parse_url(url)
187
- url =~ /([^:]+):\/\/([^\/]*)(.*)/
188
- proto = $1.to_s
189
- hostplus = $2.to_s
190
- path = $3.to_s
191
-
192
- hostplus =~ /(?:(.*)@)?(.*)/
193
- userpass = $1
194
- hostport = $2
195
- user,pass = userpass.to_s.split(':',2)
196
- host,port = hostport.to_s.split(':',2)
197
- port = port ? port.to_i : 80
198
-
199
- return proto,host,port,path,user,pass
200
- end
201
-
202
- def mysql_datetime(time) time.strftime('%Y-%m-%d %H:%M:%S') end
203
- def mysql_date(time) time.strftime('%Y-%m-%d') end
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
- attr_reader :flickr
208
+ attr_reader :flickr
208
209
 
209
- def initialize(flickr) @flickr = flickr end
210
+ def initialize(flickr) @flickr = flickr end
210
211
  end
211
212
 
212
213
  class Flickr::Token
213
- attr_reader :token, :perms, :user
214
-
215
- def initialize(token, perms, user)
216
- @token = token
217
- @perms = perms
218
- @user = user
219
- end
220
-
221
- def self.from_xml(xml, flickr=nil)
222
- token = xml.elements['/auth/token'].text
223
- perms = xml.elements['/auth/perms'].text.intern
224
- user = xml.elements['/auth/user']
225
- nsid = user.attributes['nsid']
226
- username = user.attributes['username']
227
- fullname = user.attributes['fullname']
228
-
229
- p = flickr.person_cache_lookup(nsid) if flickr
230
- p ||= Flickr::Person.new(flickr,nsid,username)
231
- p.realname=fullname
232
- flickr.person_cache_store(p) if flickr
233
-
234
- return Flickr::Token.new(token,perms,p)
235
- end
236
-
237
- def to_xml
238
- return "<auth><token>#{self.token}</token>"+
239
- "<perms>#{self.perms}</perms>"+
240
- "<user nsid=\"#{self.user.nsid}\" "+
241
- "username=\"#{self.user.username}\" "+
242
- "fullname=\"#{self.user.realname}\" /></auth>"
243
- end
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
- attr_reader :id, :name, :needspassword, :url
248
-
249
- def initialize(id,name,needspassword,url)
250
- @id = id
251
- @name = name
252
- @needspassword = needspassword
253
- @url = url
254
- end
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
- attr_accessor :nsid, :username, :realname, :mbox_sha1sum, :location,
259
- :photosurl, :profileurl, :photos_firstdate, :photos_firstdatetaken,
260
- :photos_count, :info_fetched, :isadmin, :ispro, :iconserver,
261
- :bandwidth_max, :bandwidth_used, :filesize_max, :upload_fetched,
262
- :friend, :family, :ignored
263
-
264
- def initialize(flickr, nsid, username)
265
- @flickr = flickr
266
- @nsid = nsid
267
- @username = username
268
- @info_fetched = false
269
- @upload_fetched = false
270
- end
271
-
272
- def full_info()
273
- self.info_fetched ? self : @flickr.people.getInfo(self)
274
- end
275
- def upload_status() self.upload_fetched ? self :
276
- @flickr.people.getUploadStatus(self) end
277
-
278
-
279
- # I think this will define a class method. You can't use
280
- # Flickr::Person.from_xml and if you just say Person.from_xml, it
281
- # can't resolve Flickr::Person::Person
282
- def self.from_xml(xml,flickr=nil)
283
- els = xml.elements
284
- att = xml.root.attributes
285
-
286
- nsid = cond_attr(att,'nsid')
287
- username = cond_text(els,'/person/username')
288
-
289
- p = flickr.person_cache_lookup(nsid) if flickr
290
- p ||= Flickr::Person.new(flickr,nsid,username)
291
-
292
- p.username = username
293
- p.isadmin = cond_attr(att,'isadmin') &&
294
- cond_attr(att,'isadmin') == '1'
295
- p.ispro = cond_attr(att,'ispro') &&
296
- cond_attr(att,'ispro') == '1'
297
- p.iconserver = cond_attr(att,'iconserver') &&
298
- cond_attr(att,'iconserver').to_i
299
- p.realname = cond_text(els,'/person/realname')
300
- p.mbox_sha1sum = cond_text(els,'/person/mbox_sha1sum')
301
- p.location = cond_text(els,'/person/location')
302
- p.photosurl = cond_text(els,'/person/photosurl')
303
- p.profileurl = cond_text(els,'/person/profileurl')
304
- tstr = cond_text(els,'/person/photos/firstdate')
305
- p.photos_firstdate = Time.at(tstr.to_i) if tstr
306
- tstr = cond_text(els, '/person/photos/firstdatetaken')
307
- p.photos_firstdatetaken = Time.gm(*ParseDate.parsedate(tstr)) if
308
- tstr
309
- p.photos_count = cond_text(els,'/person/photos/count')
310
- p.photos_count = p.photos_count if p.photos_count
311
-
312
- p.info_fetched = true if p.photos_count
313
-
314
- if els['/user/bandwidth']
315
- att = els['/user/bandwidth'].attributes
316
- p.bandwidth_max = cond_attr(att,'max') &&
317
- cond_attr(att,'max').to_i
318
- p.bandwidth_used = cond_attr(att,'used') &&
319
- cond_attr(att,'used').to_i
320
- end
321
- if els['/user/filesize']
322
- att = els['/user/filesize'].attributes
323
- p.filesize_max = cond_attr(att,'max') &&
324
- cond_attr(att,'max').to_i
325
- end
326
-
327
- p.upload_fetched = true if p.bandwidth_max
328
-
329
- flickr.person_cache_store(p) if flickr
330
- return p
331
- end
332
-
333
- private
334
- def self.cond_text(elements,index)
335
- elements[index] ? elements[index].text : nil
336
- end
337
-
338
- def self.cond_attr(attributes,name) attributes[name] end
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
- attr_reader :label,:width,:height,:source,:url
343
-
344
- def initialize(label,width,height,source,url)
345
- @label = label
346
- @width = width
347
- @height = height
348
- @source = source
349
- @url = url
350
- end
351
-
352
- def self.from_xml(xml)
353
- att = xml.attributes
354
- return Flickr::Size.new(att['label'],att['width'].to_i,
355
- att['height'].to_i,att['source'],att['url'])
356
- end
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
- attr_accessor :id, :owner_id, :secret, :server, :title, :ispublic,
361
- :isfriend, :isfamily, :ownername, :dateadded,
362
- :license_id, :description, :dates, :taken,
363
- :lastupdate, :takengranularity, :cancomment, :canaddmeta,
364
- :comments, :rotation, :notes, :urls, :permaddmeta,
365
- :permcomment, :originalformat
366
-
367
- attr_reader :flickr
368
-
369
- def owner() @owner ||= @flickr.people.getInfo(owner_id) end
370
- def sizes() @sizes || @flickr.photos.getSizes(self).sizes end
371
- def sizes=(sizes) @sizes = sizes end
372
-
373
- def max_size
374
- sizes[:Original] || sizes[:Large] || sizes[:Medium] ||
375
- sizes[:Small]
376
- end
377
-
378
- def initialize(flickr,id)
379
- @flickr = flickr
380
- @id = id
381
- end
382
-
383
- def exif() @exif ||= @flickr.photos.getExif(self) end
384
- def exif=(set) @exif = set end
385
-
386
- def tags() @tags ||= @flickr.tags.getListPhoto(self) end
387
- def tags=(set) @tags = set end
388
-
389
- def license() @flickr.photos.licenses.getInfo[@license_id] end
390
-
391
- def contexts() @contexts ||= @flickr.photos.getAllContexts(self) end
392
-
393
- def url(size=nil)
394
- base = 'http://static.flickr.com'
395
- ext = (size == 'o') ? self.originalformat : 'jpg'
396
- return size ?
397
- "#{base}/#@server/#{@id}_#{@secret}_#{size}.#{ext}" :
398
- "#{base}/#@server/#{@id}_#{@secret}.jpg"
399
- end
400
-
401
- def delete() @flickr.photos.delete(self) end
402
-
403
- def self.from_xml(xml,flickr=nil)
404
- att = xml.attributes
405
- phid = att['id']
406
-
407
- photo = flickr.photo_cache_lookup(phid) if flickr
408
- photo ||= Flickr::Photo.new(flickr,phid)
409
-
410
- photo.owner_id ||= att['owner'] || (xml.elements['owner'] &&
411
- xml.elements['owner'].attributes['nsid'])
412
- photo.secret = att['secret'] if att['secret']
413
- photo.originalformat = att['originalformat'] if
414
- att['originalformat']
415
- photo.server = att['server'].to_i if att['server']
416
- photo.title = att['title'] || cond_text(xml.elements,'title')
417
- photo.license_id = att['license']
418
- photo.rotation = att['rotation'].to_i if att['rotation']
419
-
420
- photo.ispublic = (att['ispublic'].to_i == 1) if att['ispublic']
421
- photo.isfriend = (att['isfriend'].to_i == 1) if att['isfriend']
422
- photo.isfamily = (att['isfamily'].to_i == 1) if att['isfamily']
423
- photo.ownername = att['ownername'] || (xml.elements['owner'] &&
424
- xml.elements['owner'].attributes['username'])
425
- photo.description = cond_text(xml.elements,'description')
426
- photo.dateadded = Time.at(att['dateadded'].to_i) if
427
- att['dateadded']
428
- if xml.elements['exif']
429
- list = []
430
- xml.elements.each('exif') do |el|
431
- exif = Flickr::Exif.from_xml(el)
432
- list << exif
433
- end
434
- photo.exif = list
435
- end
436
- if xml.elements['visibility']
437
- att = xml.elements['visibility'].attributes
438
- photo.ispublic = (att['ispublic'].to_i == 1)
439
- photo.isfriend = (att['isfriend'].to_i == 1)
440
- photo.isfamily = (att['isfamily'].to_i == 1)
441
- end
442
- if xml.elements['dates']
443
- att = xml.elements['dates'].attributes
444
- dates = {}
445
- dates[:posted] = Time.at(att['posted'].to_i)
446
- dates[:taken] = Time.gm(*ParseDate.parsedate(att['taken']))
447
- dates[:lastupdate] = Time.at(att['lastupdate'].to_i)
448
- dates[:takengranularity] = att['takengranularity'].to_i
449
- photo.dates = dates
450
- end
451
- if xml.elements['editability']
452
- att = xml.elements['editability'].attributes
453
- photo.cancomment = (att['cancomment'].to_i == 1)
454
- photo.canaddmeta = (att['canaddmeta'].to_i == 1)
455
- end
456
- photo.comments = cond_text(xml.elements,'comments')
457
- photo.comments &&= photo.comments.to_i
458
- if xml.elements['notes']
459
- notes = []
460
- xml.elements['notes'].each_element do |el|
461
- notes << Flickr::Note.from_xml(el,photo)
462
- end
463
- photo.notes = notes
464
- end
465
- if xml.elements['tags']
466
- tags = []
467
- xml.elements['tags'].each_element do |el|
468
- tags << Flickr::Tag.from_xml(el,photo)
469
- end
470
- photo.tags = tags
471
- end
472
- if xml.elements['urls']
473
- urls = {}
474
- xml.elements['urls'].each_element do |el|
475
- att = el.attributes
476
- urls[att['type'].intern] = el.text
477
- end
478
- photo.urls = urls
479
- end
480
-
481
- flickr.photo_cache_store(photo) if flickr
482
- return photo
483
- end
484
-
485
- private
486
- def self.cond_text(elements,index)
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
- attr_reader :tagspace,:tagspaceid,:tag,:label
493
- attr_accessor :raw,:clean
494
- def initialize(tagspace,tagspaceid,tag,label)
495
- @tagspace = tagspace
496
- @tagspaceid = tagspaceid
497
- @tag = tag
498
- @label = label
499
- end
500
-
501
- def self.from_xml(element)
502
- att = element.attributes
503
- exif = Flickr::Exif.new(att['tagspace'],att['tagspaceid'].to_i,
504
- att['tag'],att['label'])
505
- exif.raw=element.elements['raw'].text if element.elements['raw']
506
- exif.clean=element.elements['clean'].text if
507
- element.elements['clean']
508
- return exif
509
- end
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
- attr_reader :page,:pages,:perpage,:total
514
-
515
- def initialize(page,pages,perpage,total)
516
- @page = page
517
- @pages = pages
518
- @perpage = perpage
519
- @total = total
520
- end
521
-
522
- def self.from_xml(xml,flickr=self)
523
- att = xml.root.attributes
524
- list = Flickr::PhotoList.new(att['page'].to_i,att['pages'].to_i,
525
- att['perpage'].to_i,att['total'].to_i)
526
- xml.elements['/photos'].each_element do |e|
527
- list << Flickr::Photo.from_xml(e,flickr)
528
- end
529
- return list
530
- end
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
- attr_reader :name, :path, :pathids, :groups, :subcats, :id
535
-
536
- def initialize(name,path,pathids)
537
- @name = name
538
- @path = path
539
- @pathids = pathids
540
- @groups = []
541
- @subcats = []
542
- @id = pathids.split('/').last
543
- end
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
- attr_reader :name, :id, :count
546
+ attr_reader :name, :id, :count
548
547
 
549
- def initialize(name,id,count)
550
- @name = name
551
- @id = id
552
- @count = count
553
- end
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
- PRIVACY = [nil,:private,:invite,:public]
560
-
561
- attr_accessor :nsid, :name, :members, :online, :chatnsid, :inchat,
562
- :description, :privacy, :eighteenplus, :fully_fetched, :admin,
563
- :photo_count, :iconserver
564
-
565
- def initialize(flickr,nsid, name=nil, members=nil, online=nil,
566
- chatnsid=nil, inchat=nil)
567
- @flickr = flickr
568
- @nsid = nsid
569
- @name = name
570
- @members = members
571
- @online = online
572
- @chatnsid = chatnsid
573
- @inchat = inchat
574
- @fully_fetched = false
575
- end
576
-
577
- def full_info
578
- self.fully_fetched ? self : @flickr.groups.getInfo(self)
579
- end
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
- attr_reader :page,:pages,:perpage,:total
584
-
585
- def initialize(page,pages,perpage,total)
586
- @page = page
587
- @pages = pages
588
- @perpage = perpage
589
- @total = total
590
- end
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
- attr_reader :prev_id,:prev_secret,:prev_title,:prev_url,
595
- :next_id,:next_secret,:next_title,:next_url
596
-
597
- def initialize(prev_id,prev_secret,prev_title,prev_url,
598
- next_id,next_secret,next_title,next_url)
599
- @prev_id = prev_id
600
- @prev_secret = prev_secret
601
- @prev_title = prev_title
602
- @prev_url = prev_url
603
- @next_id = next_id
604
- @next_secret = next_secret
605
- @next_title = next_title
606
- @next_url = next_url
607
- end
608
-
609
- def self.from_xml(xml)
610
- a0 = xml.elements['prevphoto'].attributes
611
- a1 = xml.elements['nextphoto'].attributes
612
- return Flickr::Context.new(
613
- a0['id'],a0['secret'],a0['title'],a0['url'],
614
- a1['id'],a1['secret'],a1['title'],a1['url'])
615
- end
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
- attr_reader :id, :name, :url
620
- def initialize(id,name,url)
621
- @id = id
622
- @name = name
623
- @url = url
624
- end
625
-
626
- def self.from_xml(xml)
627
- att = xml.attributes
628
- return Flickr::License.new(att['id'],att['name'],
629
- att['url'])
630
- end
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
- attr_accessor :photo, :x, :y, :w, :h, :text, :id, :author_id
635
- def initialize(x, y, w, h, text, flickr = nil)
636
- @x = x
637
- @y = y
638
- @w = w
639
- @h = h
640
- @text = text
641
- @flickr = flickr
642
- end
643
-
644
- def author() @author_id && @flickr.people.getInfo(@author_id) end
645
-
646
- def from_xml(xml,photo=nil)
647
- att = xml.attributes
648
- note = Flickr::Note.new(att['x'].to_i,att['y'].to_i,
649
- att['w'].to_i,att['h'].to_i,xml.text,
650
- photo && photo.flickr)
651
- note.photo = photo
652
- note.id = att['id']
653
- note.author_id = att['author'] if att['author']
654
- end
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
- attr_reader :fromdate, :todate, :count
659
- def initialize(count,fromdate,todate)
660
- @count = count
661
- @fromdate = fromdate
662
- @todate = todate
663
- end
664
-
665
- def self.from_xml(xml)
666
- att = xml.attributes
667
- return Flickr::Count.new(att['count'].to_i,
668
- Time.at(att['fromdate'].to_i),
669
- Time.at(att['todate'].to_i))
670
- end
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
- attr_reader :id, :author_id, :raw, :clean
675
-
676
- def initialize(flickr, id,author_id,raw,clean)
677
- @flickr = flickr
678
- @id = id
679
- @author_id = author_id
680
- @raw = raw
681
- @clean = clean
682
- end
683
-
684
- def author() @flickr.people.getInfo(@author_id) end
685
-
686
- def self.from_xml(xml,flickr=nil)
687
- att = xml.attributes
688
- clean = xml.text
689
- return Flickr::Tag.new(flickr,att['id'],att['author'],
690
- att['raw'], clean)
691
- end
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
- attr_accessor :id, :title, :url, :server, :primary_id,
696
- :photo_count, :description, :secret, :owner
697
-
698
- def initialize(id,flickr)
699
- @id = id
700
- @flickr = flickr
701
- end
702
-
703
- def <<(photo,raw=false)
704
- raw ? super(photo) : @flickr.photosets.addPhoto(self,photo)
705
- return self
706
- end
707
-
708
- def fetch(extras=nil)
709
- return self if @fetched
710
- set = @flickr.photosets.getPhotos(self,extras)
711
- @fetched = true
712
- return set
713
- end
714
-
715
- alias photos fetch
716
-
717
- def self.from_xml(xml,flickr=nil)
718
- att = xml.attributes
719
- psid = att['id']
720
-
721
- set = flickr.photoset_cache_lookup(psid) if flickr
722
- set ||= Flickr::PhotoSet.new(psid,flickr)
723
-
724
- set.secret = att['secret']
725
- set.owner = att['owner']
726
- set.url = att['url']
727
- set.server = att['server'].to_i
728
- set.primary_id = att['primary'].to_i
729
- set.photo_count = att['photos'].to_i
730
- set.title = xml.elements['title'].text if xml.elements['title']
731
- set.description = xml.elements['description'].text if
732
- xml.elements['description']
733
- if xml.elements['photo']
734
- set.clear
735
- xml.elements.each('photo') do |el|
736
- set.<<(Flickr::Photo.from_xml(el,flickr),true)
737
- end
738
- end
739
-
740
- flickr.photoset_cache_store(set) if flickr
741
- return set
742
- end
743
-
744
- def url
745
- owner = @owner || @flickr.photosets.getInfo(self).owner
746
- return "http://www.flickr.com/photos/#{owner}/sets/#{@id}"
747
- end
748
-
749
- def primary() @primary ||= @flickr.photos.getInfo(@primary_id) end
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
- attr_accessor :page, :pages, :perpage, :total, :title, :id
754
-
755
- def initialize(id,flickr)
756
- @id = id
757
- @flickr = flickr
758
- end
759
-
760
- def <<(photo,raw=false)
761
- raw ? super(photo) : @flickr.photosets.addPhoto(self,photo)
762
- return self
763
- end
764
-
765
- def fetch(extras=nil)
766
- return self if @fetched
767
- pool = @flickr.groups.pools.getPhotos(self,nil,extras,500)
768
- @fetched = true
769
- return pool
770
- end
771
-
772
- def self.from_xml(xml,flickr=nil)
773
- att = xml.attributes
774
- ppid = att['id']
775
-
776
- pool = flickr.photopool_cache_lookup(ppid)
777
- pool ||= Flickr::PhotoPool.new(ppid,flickr)
778
-
779
- pool.page = att['page'].to_i if att['page']
780
- pool.pages = att['pages'].to_i if att['pages']
781
- pool.perpage = att['perpage'].to_i if att['perpage']
782
- pool.total = att['total'].to_i if att['total']
783
- if xml.elements['photo']
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
- xml.elements.each('photo') do |el|
788
- pool.<<(Flickr::Photo.from_xml(el,flickr),true)
789
- end
790
- end
791
-
792
- flickr.photopool_cache_store(pool) if flickr
793
- return pool
794
- end
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
- attr_accessor :id, :authorid, :authorname, :datecreated, :permalink, :text
799
-
800
- def self.from_xml(xml, flickr=nil)
801
- att = xml.attributes
802
- c = new
803
- c.id = att['id']
804
- c.authorid = att['author']
805
- c.authorname = att['authorname']
806
- c.datecreated = Time.at(att['datecreate'].to_i)
807
- c.permalink = att['permalink']
808
- c.text = xml.text
809
- c
810
- end
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
-