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 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
-