smugmugr 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Rob Sterner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,60 @@
1
+ h1. smugmugr
2
+
3
+ A (he's the DJ, I'm the) wrapper for v1.2.2 of the SmugMug "JSON API":http://wiki.smugmug.net/display/API/API+1.2.2.
4
+
5
+ Originally inspired by Scott White's simple_smugmug Rails plugin:
6
+ http://github.com/scottwhite/simple_smugmug/tree/master
7
+
8
+ Current version: 0.2.0
9
+
10
+ h2. Roadmap
11
+
12
+ h3. v1.0
13
+
14
+ * read-only (with and without login (password-based and OAuth)) bits
15
+
16
+ h3. v2.0
17
+
18
+ * read-write
19
+
20
+ h2. Usage
21
+
22
+ The current highly opinionated way to use this gem is:
23
+
24
+ user = Smugmugr::User.new(api_key, :email => 'foo@example.com', :password => 'secret')
25
+ albums = user.albums
26
+ images = albums.first.images
27
+
28
+ As of v1.2.2 of the API, Smugmug allows the passing of an Extras parameter to allow customization of a call to your liking:
29
+
30
+ "http://www.dgrin.com/showthread.php?t=90636":http://www.dgrin.com/showthread.php?t=90636
31
+
32
+ Taking advantage of this is a cinch with smugmugr:
33
+
34
+ albums = user.albums(:SmugSearchable,:LastUpdated,:ImageCount)
35
+
36
+ will yield:
37
+
38
+ albums.first.extras => {:ImageCount=>29, :LastUpdated=>"2009-05-16 15:23:11", :SmugSearchable=>true}
39
+
40
+ Also:
41
+
42
+ albums.first.last_updated => "2009-05-16 15:23:11"
43
+ albums.first.LastUpdated => "2009-05-16 15:23:11"
44
+
45
+ albums.first.image_count => 29
46
+ albums.first.ImageCount => 29
47
+
48
+ h3. TODO
49
+ * TESTS: added rspec requires and shell spec classes.
50
+ * TBD
51
+
52
+ h3. Author(s)
53
+
54
+ Written by "Rob Sterner":http://github.com/fermion = "robsterner.com":http://robsterner.com
55
+
56
+ h3. Copyright
57
+
58
+ Released under the MIT license.
59
+
60
+ Copyright (c) 2009 Rob Sterner. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+ require 'rake/rdoctask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "smugmugr"
10
+ gem.summary = %Q{smugmugr, the smugmug API wrapper}
11
+ gem.email = "rob.sterner@gmail.com"
12
+ gem.homepage = "http://github.com/fermion/smugmugr"
13
+ gem.authors = ["Rob Sterner"]
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ desc 'Default: run unit specs.'
22
+ task :default => :spec
23
+
24
+ desc 'Test the smugmugr gem.'
25
+ Spec::Rake::SpecTask.new('spec') do |t|
26
+ t.spec_files = FileList['spec/**/*_spec.rb']
27
+ t.spec_opts = ["-c"]
28
+ end
29
+
30
+ Rake::RDocTask.new do |rdoc|
31
+ if File.exist?('VERSION.yml')
32
+ config = YAML.load(File.read('VERSION.yml'))
33
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
34
+ else
35
+ version = ""
36
+ end
37
+
38
+ rdoc.rdoc_dir = 'rdoc'
39
+ rdoc.title = "smugmugr #{version}"
40
+ rdoc.rdoc_files.include('README*')
41
+ rdoc.rdoc_files.include('lib/**/*.rb')
42
+ end
43
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,134 @@
1
+ module Smugmugr
2
+ class Album < Smugmugr::Base
3
+ attr_accessor :user, :id, :title, :key, :pub_key, :session_id
4
+
5
+ #############################################
6
+ # all attributes available on an Album
7
+ #############################################
8
+ class_inheritable_reader :attributes
9
+ write_inheritable_attribute :attributes, [:id,:Key,:Backprinting,:CanRank,:Category,:Clean,:ColorCorrection,:Comments,:Community,
10
+ :Description,:EXIF,:External,:FamilyEdit,:Filenames,:FriendEdit,:Geography,:Header,:HideOwner,:Highlight,:ImageCount,:Larges,:LastUpdated,
11
+ :Originals,:Password,:PasswordHint,:Position,:Printable,:ProofDays,:Protected,:Public,:Share,:SmugSearchable,:SortDirection,
12
+ :SortMethod,:SquareThumbs,:SubCategory,:Template,:Theme,:Title,:UnsharpAmount,:UnsharpRadius,:UnsharpSigma,:UnsharpThreshold,
13
+ :Watermark,:Watermarking,:WorldSearchable,:X2Larges,:X3Larges,:XLarges]
14
+
15
+ class_inheritable_reader :complex_attributes
16
+ write_inheritable_attribute :complex_attributes, [:Album, :Category, :Community, :Highlight, :SubCategory, :Template, :Theme, :Watermark]
17
+
18
+ def initialize(user)
19
+ @extras = {}
20
+ @user = user
21
+ end
22
+
23
+ #############################################
24
+ # class
25
+ #############################################
26
+ class << self
27
+ def get(user, args={})
28
+ args[:heavy] ||= false
29
+ args[:extras] ||= []
30
+ params = {
31
+ :method => smug_method + '.get'
32
+ }
33
+ params.merge!(:Heavy => args[:heavy].to_s) if args[:heavy]
34
+ params.merge!(:Extras => args[:extras].collect(&:to_s).join(','))
35
+
36
+ json = user.call(params)
37
+ return albums_from_json(json, user, args[:extras])
38
+ end
39
+
40
+ def albums_from_json(json, user, extra_params=[])
41
+ json["Albums"].map do |album|
42
+ album_from_json(Smugmugr::Album.new(user), album, user, extra_params)
43
+ end
44
+ end
45
+
46
+ def album_from_json(album, json, user, extras=[])
47
+ album.id = json['id']
48
+ album.title = json['Title']
49
+ album.key = json['Key']
50
+ album.extras = {}
51
+ self.attributes.each{ |key|
52
+ next unless json.has_key?(key.to_s)
53
+
54
+ album.extras[key] = if self.complex_attributes.include?(key)
55
+ Object.class_eval("Smugmugr::Album::#{key}").new(json[key.to_s])
56
+ else
57
+ json[key.to_s]
58
+ end
59
+ }
60
+ album
61
+ end
62
+
63
+ def smug_method
64
+ "smugmug.albums"
65
+ end
66
+ end
67
+
68
+ #############################################
69
+ # instance
70
+ #############################################
71
+ def images(*extras)
72
+ extras = [extras] unless extras.is_a?(Array)
73
+ Smugmugr::Image.get(self, :extras => extras)
74
+ end
75
+
76
+ def get_info
77
+ params = {
78
+ :method => Smugmugr::Album.smug_method + '.getInfo',
79
+ :AlbumID => self.id,
80
+ :AlbumKey => self.key
81
+ }
82
+ json = call(params)
83
+ return Smugmugr::Album.album_from_json(self, json["Album"], user, Smugmugr::Album.attributes)
84
+ end
85
+
86
+ def call(params)
87
+ @user.call(params)
88
+ end
89
+
90
+ class Simple
91
+ attr_accessor :id, :name
92
+ def initialize(json)
93
+ self.id = json["id"]
94
+ self.name = json["Name"]
95
+ end
96
+ end
97
+
98
+ class Category < Simple
99
+ end
100
+
101
+ class Community < Simple
102
+ end
103
+
104
+ class Highlight
105
+ attr_accessor :id, :key
106
+ def initialize(json)
107
+ self.id = json["id"]
108
+ self.key = json["Key"]
109
+ end
110
+ end
111
+
112
+ class SubCategory < Simple
113
+ end
114
+
115
+ class Template
116
+ attr_accessor :id
117
+ def initialize(json)
118
+ self.id = json["id"]
119
+ end
120
+ end
121
+
122
+ class Theme
123
+ attr_accessor :id, :name, :type
124
+ def initialize(json)
125
+ self.id = json["id"]
126
+ self.name = json["Name"]
127
+ self.type = json["Type"]
128
+ end
129
+ end
130
+
131
+ class Watermark < Simple
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,25 @@
1
+ module Smugmugr
2
+ class Base
3
+ class_inheritable_reader :attributes
4
+ write_inheritable_attribute :attributes, []
5
+
6
+ class_inheritable_reader :complex_attributes
7
+ write_inheritable_attribute :complex_attributes, []
8
+
9
+ attr_accessor :extras
10
+
11
+ def method_missing(method)
12
+ # check extras for method_name, MethodName
13
+ methods = [method.to_s.classify, method.to_s, method.to_s.downcase, method.to_s.upcase]
14
+ methods.each do |extras_attribute|
15
+ return self.extras[extras_attribute.to_sym] if self.extras.has_key?(extras_attribute.to_sym)
16
+ end
17
+
18
+ # if it in @@attributes but not populated, return nil
19
+ return nil unless (methods.collect(&:to_sym) & self.class.attributes).empty?
20
+
21
+ # otherwise, move along
22
+ super(method)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Category < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Community < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Family < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Friend < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,89 @@
1
+ module Smugmugr
2
+ class Image < Smugmugr::Base
3
+ attr_accessor :album, :id, :key
4
+
5
+ class_inheritable_reader :attributes
6
+ write_inheritable_attribute :attributes, [:id,:Key,:Album,:Altitude,:Caption,:Date,:Duration,:FileName,:Format,:Height,:Hidden,
7
+ :Keywords,:LargeURL,:LastUpdated,:Latitude,:Longitude,:MD5Sum,:MediumURL,:OriginalURL,:Position,:Serial,
8
+ :Size,:SmallURL,:ThumbURL,:TinyURL,:Video320URL,:Video640URL,:Video960URL,:Video1280URL,:Video1920URL,:Width,
9
+ :X2LargeURL,:X3LargeURL,:XLargeURL]
10
+
11
+ class_inheritable_reader :complex_attributes
12
+ write_inheritable_attribute :complex_attributes, [:Album]
13
+
14
+ def initialize(album)
15
+ @extras = {}
16
+ @album = album
17
+ end
18
+
19
+ def get(*extras)
20
+ extras = [extras] unless extras.is_a?(Array)
21
+ Smugmugr::Image.get(self.album, :extras => extras)
22
+ end
23
+
24
+ class << self
25
+ def get(album, args={})
26
+ args[:heavy] ||= false
27
+ args[:extras] ||= []
28
+ params = {
29
+ :method => smug_method + 'get',
30
+ :AlbumID => album.id,
31
+ :AlbumKey => album.key
32
+ }
33
+ params.merge!(:Heavy => args[:heavy]) if args[:heavy]
34
+ params.merge!(:Extras => args[:extras].collect(&:to_s).join(',')) if args[:extras].any?
35
+
36
+ json = album.call(params)
37
+ images_from_json(json, album, params[:Extras])
38
+ end
39
+
40
+ def smug_method
41
+ return "smugmug.images."
42
+ end
43
+
44
+ def images_from_json(json, album, extra_params=[])
45
+ json['Album']['Images'].map{ |image_json|
46
+ image_from_json(Smugmugr::Image.new(album), image_json, extra_params)
47
+ }
48
+ end
49
+
50
+ def image_from_json(image, json, extras=[])
51
+ image.id = json['id']
52
+ image.key = json['Key']
53
+ image.extras = {}
54
+ self.attributes.each{ |key|
55
+ next unless json.has_key?(key.to_s)
56
+
57
+ image.extras[key] = if self.complex_attributes.include?(key)
58
+ Object.class_eval("Smugmugr::Image::#{key}").new(json[key.to_s])
59
+ else
60
+ json[key.to_s]
61
+ end
62
+ }
63
+ image
64
+ end
65
+ end
66
+
67
+ #############################################
68
+ # instance
69
+ #############################################
70
+ def get_info
71
+ params = {
72
+ :method => Smugmugr::Image.smug_method + '.getInfo',
73
+ :ImageID => self.id,
74
+ :ImageKey => self.key
75
+ }
76
+ json = @album.call(params)
77
+ return Smugmugr::Image.image_from_json(self, json["Image"], Smugmugr::Image.attributes)
78
+ end
79
+
80
+ class Album
81
+ attr_accessor :id, :key, :url
82
+ def initialize(json)
83
+ self.id = json["id"]
84
+ self.key = json["Key"]
85
+ self.url = json["URL"]
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,126 @@
1
+ require 'curl'
2
+ require 'json'
3
+ require 'cgi'
4
+
5
+ module Smugmugr
6
+ class Session
7
+ attr_accessor :params, :host, :port, :timeout, :retry, :use_ssl, :session_id
8
+ attr_accessor :api_path, :session, :api_key, :user, :attributes, :headers
9
+
10
+ def initialize(user, args={})
11
+ @user = user
12
+ @api_key = user.api_key
13
+ # TODO: externalize, make overrideable from ~/smugmugr.yml or RAILS_ROOT/config/smugmugr.yml
14
+ attributes = {
15
+ :host => 'api.smugmug.com',
16
+ :port => 443,
17
+ :timeout => 10,
18
+ :retry => 1, # no retry for now
19
+ :use_ssl => true,
20
+ :api_path => '/services/api/json/1.2.2/',
21
+ :headers => {
22
+ "User-Agent" => 'smugmugr v0.0.1'
23
+ }
24
+ }
25
+ attributes.merge!(args)
26
+ attributes.each{|k,v|
27
+ self.send("#{k}=", v) if self.respond_to?(k)
28
+ }
29
+ end
30
+
31
+ def establish_session
32
+ if user.email && user.password
33
+ establish_session_with_login
34
+ else
35
+ establish_anonymous_session
36
+ end
37
+ return self
38
+ end
39
+
40
+ def establish_anonymous_session
41
+ establish_new_session({:method => 'smugmug.login.anonymously'})
42
+ end
43
+
44
+ def establish_session_with_login
45
+ establish_new_session({:method => 'smugmug.login.withPassword',
46
+ :EmailAddress => "#{user.email}",
47
+ :Password => "#{user.password}"})
48
+ end
49
+
50
+ def establish_session_with_open_auth
51
+ raise "unimplemented"
52
+ end
53
+
54
+ def get_session_id
55
+ @session ||= establish_session
56
+ return @session.session_id
57
+ end
58
+
59
+ def call(params={}, with_session_id=true)
60
+ params.merge!(:SessionID => "#{get_session_id}") if with_session_id
61
+ data = nil
62
+ begin
63
+ url = protocol + @host + build_url_request(params)
64
+ # puts "\nsending: #{url}\n"
65
+ response = Curl::Easy.perform(url) do |easy|
66
+ easy.headers.merge!(headers)
67
+ easy.timeout = @timeout
68
+ end
69
+
70
+ data = response.body_str unless response.nil?
71
+ rescue Timeout::Error => e
72
+ # TODO: custom exception
73
+ raise e
74
+ end
75
+ # check status of response
76
+ json = JSON.parse(data)
77
+
78
+ unless json['stat'] == 'ok'
79
+ # TODO: custom exception
80
+ raise "error from smugmug: #{json.inspect}"
81
+ end
82
+
83
+ return json
84
+ end
85
+
86
+ def establish_new_session(params)
87
+ begin
88
+ json = call(params, false)
89
+
90
+ set_user_data(json["Login"])
91
+ self.session_id = json["Login"]["Session"]["id"]
92
+ rescue => e
93
+ # TODO: custom exception
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def set_user_data(json)
100
+ @user.user_id = json["User"]["id"]
101
+ @user.nickname = json["User"]["NickName"]
102
+ @user.password_hash = json['PasswordHash']
103
+ @user.filesize_limit = json['FileSizeLimit']
104
+ end
105
+
106
+ def protocol
107
+ return "http#{'s' if @use_ssl}://"
108
+ end
109
+
110
+ # {:foo => 'bar', :baz => "blingy things"} => ['foo=bar','baz=blingy+things']
111
+ def encode_params(raw_params)
112
+ return raw_params.map{ |k,v|
113
+ [k.to_s,CGI::escape(v.to_s)].join('=')
114
+ }
115
+ end
116
+
117
+ # returns everything except protocol/host
118
+ def build_url_request(params)
119
+ params.merge!(:APIKey => @api_key)
120
+ param_string = encode_params(params)
121
+ return @api_path + '?' + param_string.join('&')
122
+ end
123
+
124
+ end
125
+ end
126
+
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class ShareGroup < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class SubCategory < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Theme < Smugmugr::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,34 @@
1
+ module Smugmugr
2
+ class User < Smugmugr::Base
3
+ attr_accessor :credentials, :session_id, :session, :email, :password, :api_key
4
+ # set by smugmug response data
5
+ attr_accessor :user_id, :nickname, :password_hash, :filesize_limit
6
+
7
+ def initialize(api_key, args={})
8
+ @credentials = {}
9
+ self.api_key = api_key
10
+ args.each{ |k,v|
11
+ self.send("#{k}=", v) if self.respond_to?(k)
12
+ }
13
+ @session = Smugmugr::Session.new(self, args[:session_args] || {})
14
+ return
15
+ end
16
+
17
+ def send_request_with_session(params)
18
+ @session.send_request_with_session(params)
19
+ end
20
+
21
+ def albums(*extras)
22
+ extras = [extras] unless extras.is_a?(Array)
23
+ Album.get(self, :extras => extras)
24
+ end
25
+
26
+ def session_id
27
+ @session.session_id
28
+ end
29
+
30
+ def call(params={})
31
+ @session.call(params)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Smugmugr
2
+ class Watermark < Smugmugr::Base
3
+
4
+ end
5
+ end
data/lib/smugmugr.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'curb'
3
+ # TODO: look for json_pure, then json
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ require 'smugmugr/base'
8
+ require 'smugmugr/album'
9
+ require 'smugmugr/category'
10
+ require 'smugmugr/community'
11
+ require 'smugmugr/family'
12
+ require 'smugmugr/friend'
13
+ require 'smugmugr/image'
14
+ require 'smugmugr/session'
15
+ require 'smugmugr/share_group'
16
+ require 'smugmugr/sub_category'
17
+ require 'smugmugr/theme'
18
+ require 'smugmugr/user'
19
+ require 'smugmugr/watermark'
20
+
21
+
22
+ module Smugmugr
23
+ # TODO: attempt to load from a configuration file
24
+ end
25
+
26
+ def logger
27
+ @logger ||= if Module.constants.include?('RAILS_DEFAULT_LOGGER')
28
+ RAILS_DEFAULT_LOGGER
29
+ else
30
+ Logger.new('smugmugr.log')
31
+ end
32
+ end