flickr-wrapper 0.1.3
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 +7 -0
- data/flickr-wrapper.gemspec +16 -0
- data/lib/flickr-wrapper.rb +21 -0
- data/lib/flickr-wrapper/base.rb +30 -0
- data/lib/flickr-wrapper/machine_tag.rb +24 -0
- data/lib/flickr-wrapper/photo.rb +52 -0
- data/lib/flickr-wrapper/photoset.rb +78 -0
- data/lib/flickr-wrapper/tag.rb +13 -0
- data/lib/flickr-wrapper/user.rb +67 -0
- data/lib/vendor/parallel.rb +121 -0
- metadata +81 -0
data/README
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
A grassroots readonly flickr wrapper - Strictly for convenience
|
2
|
+
Not a full API, no groups, no community, who needs friends anyway?
|
3
|
+
|
4
|
+
If you want a full API, use the `flickr` gem hosted on Rubyforge, its
|
5
|
+
old but it was nailed with its 1.0 release.
|
6
|
+
|
7
|
+
Check the specs for usage
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "flickr-wrapper"
|
3
|
+
s.version = "0.1.3"
|
4
|
+
s.date = "2008-06-07"
|
5
|
+
s.summary = "A grassroots wrapper for flickr"
|
6
|
+
s.email = "ben@germanforblack.com"
|
7
|
+
s.homepage = "http://github.com/benschwarz/flickr-wrapper"
|
8
|
+
s.description = "A grassroots wrapper around flickr - Strictly for convenience"
|
9
|
+
s.authors = ["Ben Schwarz"]
|
10
|
+
s.files = ["README", "flickr-wrapper.gemspec", "lib/flickr-wrapper.rb", "lib/flickr-wrapper/base.rb", "lib/flickr-wrapper/machine_tag.rb", "lib/flickr-wrapper/photo.rb", "lib/flickr-wrapper/photoset.rb", "lib/flickr-wrapper/tag.rb", "lib/flickr-wrapper/user.rb", "lib/vendor/parallel.rb"]
|
11
|
+
s.require_path = "lib"
|
12
|
+
|
13
|
+
# Deps
|
14
|
+
s.add_dependency("flickr-rest", ">= 0.0.1")
|
15
|
+
s.add_dependency("validatable", ">= 1.6.7")
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Dependencies
|
2
|
+
begin
|
3
|
+
require 'minigems'
|
4
|
+
rescue LoadError
|
5
|
+
require 'rubygems'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'flickr-rest'
|
9
|
+
require 'time'
|
10
|
+
require 'validatable'
|
11
|
+
|
12
|
+
# Concurrency
|
13
|
+
require File.join(File.dirname(__FILE__), 'vendor', 'parallel.rb')
|
14
|
+
|
15
|
+
# Namespace junkie
|
16
|
+
module Flickr
|
17
|
+
MAX_THREADS = 50
|
18
|
+
end
|
19
|
+
|
20
|
+
# Classes
|
21
|
+
%w(base photoset photo tag machine_tag user).each {|r| require File.join(File.dirname(__FILE__), 'flickr-wrapper', r)}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Flickr::Base
|
2
|
+
attr_reader :user_id
|
3
|
+
|
4
|
+
def initialize(user_id)
|
5
|
+
@user_id = user_id
|
6
|
+
end
|
7
|
+
|
8
|
+
# Add some convenience class methods, lovley and general; sets, photos, tags and machine tags
|
9
|
+
|
10
|
+
# List all photo sets
|
11
|
+
def sets
|
12
|
+
Flickr::PhotoSet.list(self.user_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
# This is a cheat, we're just going to search with no params.
|
16
|
+
# Maximum amount of photos flickr will let me return is 500
|
17
|
+
def photos
|
18
|
+
Flickr::Photo.list(self.user_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
# List all tags
|
22
|
+
def tags
|
23
|
+
Flickr::Tag.list(self.user_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
# List all machine tags
|
27
|
+
def machine_tags
|
28
|
+
Flickr::MachineTag.list(self.user_id)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Flickr::MachineTag < Flickr::Base #:nodoc
|
2
|
+
class Invalid < StandardError; end;
|
3
|
+
|
4
|
+
include Validatable
|
5
|
+
validates_presence_of :namespace, :predicate, :value
|
6
|
+
|
7
|
+
attr_accessor :namespace, :predicate, :value
|
8
|
+
|
9
|
+
def initialize(namespace, predicate, value)
|
10
|
+
@namespace, @predicate, @value = namespace, predicate, value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.list(user_id)
|
14
|
+
(Flickr::Query.new(user_id).execute('flickr.tags.getListUser')/:tag).parallel_map(Flickr::MAX_THREADS) {|tag| self.from_s tag.inner_text.to_s }.compact
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_s(string)
|
18
|
+
new($1, $2, $3) if string =~ /(.*)\:(.*)\=(.*)/
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"#{namespace}:#{predicate}=#{value}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Flickr::Photo < Flickr::Base
|
2
|
+
class Size < Struct.new :width, :height, :source, :url; end
|
3
|
+
|
4
|
+
attr_accessor :id, :title, :description, :tags, :machine_tags, :taken, :posted, :caller
|
5
|
+
|
6
|
+
def initialize(id, title, description)
|
7
|
+
@id, @title, @description = id, title, description
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.list(user_id)
|
11
|
+
search(user_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Find will grab all sizes of images, process the tags and standard attributes of a photo
|
15
|
+
def self.find(user_id, id)
|
16
|
+
photo_call = Flickr::Query.new(user_id).execute('flickr.photos.getInfo', :photo_id => id)
|
17
|
+
|
18
|
+
# Set basic attributes
|
19
|
+
photo = self.new(id, *%w(title description).map {|a| (photo_call/a).inner_text })
|
20
|
+
photo.taken = Time.parse(photo_call.at(:dates)['taken'])
|
21
|
+
photo.posted = Time.at(photo_call.at(:dates)['posted'].to_i)
|
22
|
+
|
23
|
+
# Set tags for photo
|
24
|
+
photo.tags = (photo_call/"tag[@machine_tag=0]").map{|tag| Flickr::Tag.new tag['raw'] }
|
25
|
+
photo.machine_tags = (photo_call/"tag[@machine_tag=1]").map{|tag| Flickr::MachineTag.from_s tag['raw'] }
|
26
|
+
|
27
|
+
return photo
|
28
|
+
end
|
29
|
+
|
30
|
+
# Find a collection of photos by text
|
31
|
+
# Search options should be hashes with string values or arrays for multiple values
|
32
|
+
def self.search(user_id, search_options={})
|
33
|
+
parse(user_id, Flickr::Query.new(user_id).execute('flickr.photos.search', search_options))
|
34
|
+
end
|
35
|
+
|
36
|
+
def sizes
|
37
|
+
hash = {}
|
38
|
+
(Flickr::Query.new(user_id).execute('flickr.photos.getSizes', :photo_id => id)/:size).parallel_each(Flickr::MAX_THREADS) do |size|
|
39
|
+
hash[size['label'].downcase.to_sym] = Size.new(*%w(width height source url).map{|a| size[a]})
|
40
|
+
end
|
41
|
+
return hash
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
# Parse applies Hpricot to the photos and maps them to a Photo class instance
|
46
|
+
def self.parse(user_id, collection)
|
47
|
+
photos = (collection/:photo)
|
48
|
+
photos.empty? ? [] : photos.parallel_map(Flickr::MAX_THREADS) do |photo|
|
49
|
+
self.find(user_id, photo[:id])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Flickr::PhotoSet < Flickr::Base
|
2
|
+
attr_accessor :id, :title, :description, :photo_count
|
3
|
+
|
4
|
+
def initialize id, title, description, photo_count
|
5
|
+
@id, @title, @description, @photo_count = id, title, description, photo_count
|
6
|
+
end
|
7
|
+
|
8
|
+
# Class methods require scope of the user, it is to be sent into the method
|
9
|
+
def self.list user_id
|
10
|
+
query = Flickr::Query.new user_id
|
11
|
+
parse(query.execute('flickr.photosets.getList'))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get information regarding set by searching with the set's ID
|
15
|
+
def self.find user_id, id
|
16
|
+
# Keep scope of the user that made the call
|
17
|
+
@user_id = user_id
|
18
|
+
|
19
|
+
query = Flickr::Query.new(@user_id).execute('flickr.photosets.getInfo', :photoset_id => id)
|
20
|
+
# Find should return a single entity
|
21
|
+
parse(query).first
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the photos within a set
|
25
|
+
# Queries:
|
26
|
+
# > photosets.getPhotos (1 call)
|
27
|
+
# > Photo.find ID (n calls)
|
28
|
+
def photos
|
29
|
+
return (Flickr::Query.new(user_id).execute('flickr.photosets.getPhotos', :photoset_id => id)/:photo).parallel_map(Flickr::MAX_THREADS) do |photo|
|
30
|
+
Flickr::Photo.find user_id, photo[:id]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return a list of tags for the set
|
35
|
+
# Queries:
|
36
|
+
# > photosets.getPhotos (1 call)
|
37
|
+
# > Photo.find ID (n calls)
|
38
|
+
def tags
|
39
|
+
tags = []
|
40
|
+
photos.each do |photo|
|
41
|
+
photo.tags.each do |tag|
|
42
|
+
tags << tag.value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
tags.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return a list of machine_tags for the set
|
49
|
+
# Queries:
|
50
|
+
# > photosets.getPhotos (1 call)
|
51
|
+
# > Photo.find ID (n calls)
|
52
|
+
def machine_tags
|
53
|
+
tags = []
|
54
|
+
photos.each do |photo|
|
55
|
+
photo.machine_tags.each do |tag|
|
56
|
+
tags << tag
|
57
|
+
end
|
58
|
+
end
|
59
|
+
tags.uniq
|
60
|
+
end
|
61
|
+
|
62
|
+
# Search for photosets that have similar tags
|
63
|
+
def similar
|
64
|
+
sets = []
|
65
|
+
Flickr::PhotoSet.list(user_id).each do |set|
|
66
|
+
sets << set unless (self.tags & set.tags).empty? or set == self
|
67
|
+
end
|
68
|
+
sets.uniq
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def self.parse collection
|
73
|
+
photosets = (collection/:photoset)
|
74
|
+
photosets.empty? ? [] : photosets.map do |set|
|
75
|
+
new(set[:id], (set/:title).inner_text, (set/:description).inner_text, set[:photos])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Flickr::Tag < Flickr::Base
|
2
|
+
attr_accessor :value
|
3
|
+
|
4
|
+
def initialize(tag)
|
5
|
+
@value = tag
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.list(user_id)
|
9
|
+
(Flickr::Query.new(user_id).execute('flickr.tags.getListUser')/:tag).map { |tag|
|
10
|
+
self.new(tag.inner_text.to_s) if tag.inner_text.to_s =~ /^([a-zA-Z0-9_-]+)$/
|
11
|
+
}.compact
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Flickr::User < Flickr::Base
|
2
|
+
attr_accessor :username, :nsid
|
3
|
+
|
4
|
+
def initialize username, nsid
|
5
|
+
@username, @nsid = username, nsid
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
# ==== Class methods
|
10
|
+
#
|
11
|
+
|
12
|
+
def self.find_by_email email
|
13
|
+
result = Flickr::Query.new('').execute('flickr.people.findByEmail', :find_email => email)
|
14
|
+
return Flickr::User.new(result.at(:username).inner_text, result.at(:user)['nsid'])
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_by_username username
|
18
|
+
result = Flickr::Query.new('').execute('flickr.people.findByUsername', :username => username)
|
19
|
+
return Flickr::User.new(result.at(:username).inner_text, result.at(:user)['nsid'])
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# ==== Instance methods
|
24
|
+
#
|
25
|
+
|
26
|
+
def realname
|
27
|
+
get_info.at(:realname).inner_text
|
28
|
+
end
|
29
|
+
|
30
|
+
# The location string as entered in flickr
|
31
|
+
def location
|
32
|
+
get_info.at(:location).inner_text || "Unknown"
|
33
|
+
end
|
34
|
+
|
35
|
+
# has the user shelled out for flickr-pro account?
|
36
|
+
def pro?
|
37
|
+
(get_info.at(:person)["ispro"] == "1") ? true : false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Do they have a buddy icon or not?
|
41
|
+
def buddy_icon?
|
42
|
+
(icon_server > 0) ? true : false
|
43
|
+
end
|
44
|
+
|
45
|
+
def buddy_icon_url
|
46
|
+
buddy_icon? ? "http://farm#{icon_farm}.static.flickr.com/#{icon_server}/buddyicons/#{nsid}.jpg" : "http://www.flickr.com/images/buddyicon.jpg"
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# ==== Private methods
|
51
|
+
#
|
52
|
+
|
53
|
+
private
|
54
|
+
# Get info for the user, once and once only
|
55
|
+
def get_info
|
56
|
+
@info = Flickr::Query.new(@nsid).execute('flickr.people.getInfo', :user_id => nsid) unless @info
|
57
|
+
@info
|
58
|
+
end
|
59
|
+
|
60
|
+
def icon_server
|
61
|
+
get_info.at(:person)["iconserver"].to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
def icon_farm
|
65
|
+
get_info.at(:person)["iconfarm"].to_i
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# Stolen from http://rubyforge.org/projects/concurrent
|
2
|
+
|
3
|
+
#
|
4
|
+
# concurrent/parallel - data-parallel programming for Ruby
|
5
|
+
#
|
6
|
+
# Copyright (C) 2007 MenTaLguY <mental@rydia.net>
|
7
|
+
#
|
8
|
+
# This file is made available under the same terms as Ruby.
|
9
|
+
#
|
10
|
+
|
11
|
+
module Concurrent #:nodoc:
|
12
|
+
module Parallel #:nodoc:
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Enumerable #:nodoc:
|
17
|
+
def parallel_each( n, &block )
|
18
|
+
parallel_subsets( n ).map do |slice|
|
19
|
+
Thread.new { slice.each &block }
|
20
|
+
end.each do |thread|
|
21
|
+
thread.join
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def parallel_map( n, &block )
|
27
|
+
parallel_subsets( n ).map do |slice|
|
28
|
+
Thread.new { slice.map &block }
|
29
|
+
end.inject( [] ) do |a, thread|
|
30
|
+
a.push *thread.value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parallel_select( n, &block )
|
35
|
+
parallel_subsets( n ).map do |slice|
|
36
|
+
Thread.new { slice.select &block }
|
37
|
+
end.inject( [] ) do |a, results|
|
38
|
+
a.push *thread.value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parallel_reject( n, &block )
|
43
|
+
parallel_subsets( n ).map do |slice|
|
44
|
+
Thread.new { slice.reject &block }
|
45
|
+
end.inject( [] ) do |a, thread|
|
46
|
+
a.push *thread.value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parallel_max( n )
|
51
|
+
parallel_subsets( n ).map do |slice|
|
52
|
+
Thread.new { slice.max }
|
53
|
+
end.map { |t| t.value }.max
|
54
|
+
end
|
55
|
+
|
56
|
+
def parallel_min( n )
|
57
|
+
parallel_subsets( n ).map do |slice|
|
58
|
+
Thread.new { slice.min }
|
59
|
+
end.map { |t| t.value }.min
|
60
|
+
end
|
61
|
+
|
62
|
+
def parallel_partition( n, &block )
|
63
|
+
parallel_subsets( n ).map do |slice|
|
64
|
+
Thread.new { slice.partition &block }
|
65
|
+
end.inject( [ [], [] ] ) do |acc, thread|
|
66
|
+
pair = thread.value
|
67
|
+
acc[0].push *pair[0]
|
68
|
+
acc[1].push *pair[1]
|
69
|
+
acc
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def parallel_grep( re, n, &block )
|
74
|
+
parallel_subsets( n ).map do |slice|
|
75
|
+
Thread.new { slice.grep( re, &block ) }
|
76
|
+
end.inject( [] ) do |acc, thread|
|
77
|
+
acc.push *thread.value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def parallel_all?( n, &block )
|
82
|
+
parallel_subsets( n ).map do |slice|
|
83
|
+
Thread.new { slice.all? &block }
|
84
|
+
end.inject( true ) do |acc, thread|
|
85
|
+
acc && thread.value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def parallel_any?( n, &block )
|
90
|
+
parallel_subsets( n ).map do |slice|
|
91
|
+
Thread.new { slice.any? &block }
|
92
|
+
end.inject( false ) do |acc, thread|
|
93
|
+
acc || thread.value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def parallel_include?( n, obj )
|
98
|
+
parallel_subsets( n ).map do |slice|
|
99
|
+
Thread.new { slice.include? obj }
|
100
|
+
end.inject( false ) do |acc, thread|
|
101
|
+
acc || thread.value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def parallel_subsets( n )
|
106
|
+
to_a.parallel_subsets( n )
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Array #:nodoc:
|
111
|
+
def parallel_subsets( n )
|
112
|
+
if n > 1
|
113
|
+
slice_size = (size.to_f / n.to_f).ceil
|
114
|
+
(0...(size.to_f / slice_size)).map do |i|
|
115
|
+
self[i*slice_size, slice_size.ceil]
|
116
|
+
end
|
117
|
+
else
|
118
|
+
[ self ]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flickr-wrapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Schwarz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-06-07 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: flickr-rest
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: validatable
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.7
|
34
|
+
version:
|
35
|
+
description: A grassroots wrapper around flickr - Strictly for convenience
|
36
|
+
email: ben@germanforblack.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- README
|
45
|
+
- flickr-wrapper.gemspec
|
46
|
+
- lib/flickr-wrapper.rb
|
47
|
+
- lib/flickr-wrapper/base.rb
|
48
|
+
- lib/flickr-wrapper/machine_tag.rb
|
49
|
+
- lib/flickr-wrapper/photo.rb
|
50
|
+
- lib/flickr-wrapper/photoset.rb
|
51
|
+
- lib/flickr-wrapper/tag.rb
|
52
|
+
- lib/flickr-wrapper/user.rb
|
53
|
+
- lib/vendor/parallel.rb
|
54
|
+
has_rdoc: false
|
55
|
+
homepage: http://github.com/benschwarz/flickr-wrapper
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.3.1
|
77
|
+
signing_key:
|
78
|
+
specification_version: 2
|
79
|
+
summary: A grassroots wrapper for flickr
|
80
|
+
test_files: []
|
81
|
+
|