smugmug 0.0.1
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/HISTORY +2 -0
- data/LICENSE +19 -0
- data/MANIFEST +48 -0
- data/README +30 -0
- data/Rakefile +100 -0
- data/bin/smcli +225 -0
- data/bin/smugmug2sql +158 -0
- data/doc/API +310 -0
- data/doc/TODO +32 -0
- data/lib/net/httpz.rb +31 -0
- data/lib/smugmug.rb +179 -0
- data/lib/smugmug/album/info.rb +131 -0
- data/lib/smugmug/album/stats.rb +31 -0
- data/lib/smugmug/albums.rb +39 -0
- data/lib/smugmug/base.rb +104 -0
- data/lib/smugmug/cache.rb +33 -0
- data/lib/smugmug/config.rb +48 -0
- data/lib/smugmug/image/exif.rb +72 -0
- data/lib/smugmug/image/info.rb +88 -0
- data/lib/smugmug/image/stats.rb +32 -0
- data/lib/smugmug/images.rb +52 -0
- data/lib/smugmug/table.rb +133 -0
- data/lib/smugmug/util.rb +12 -0
- data/test/album.rb +359 -0
- data/test/config.rb +39 -0
- data/test/httpz.rb +120 -0
- data/test/image.rb +540 -0
- data/test/login.rb +24 -0
- data/test/runner.rb +83 -0
- data/test/servlet.rb +257 -0
- data/test/table.rb +113 -0
- data/xml/canned +212 -0
- data/xml/fail/empty.set.xml +4 -0
- data/xml/fail/invalid.apikey.xml +4 -0
- data/xml/fail/invalid.login.xml +4 -0
- data/xml/fail/invalid.method.xml +4 -0
- data/xml/fail/invalid.user.xml +4 -0
- data/xml/fail/system.error.xml +4 -0
- data/xml/standard/albums.get.xml +24 -0
- data/xml/standard/albums.getInfo.xml +38 -0
- data/xml/standard/albums.getStats.xml +43 -0
- data/xml/standard/categories.get.xml +213 -0
- data/xml/standard/images.get.xml +9 -0
- data/xml/standard/images.getEXIF.xml +34 -0
- data/xml/standard/images.getInfo.xml +29 -0
- data/xml/standard/images.getStats.xml +15 -0
- data/xml/standard/login.withHash.xml +7 -0
- data/xml/standard/login.withPassword.xml +10 -0
- metadata +103 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- Mode: ruby; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2
|
+
# $Hg: info.rb,v 03ae0d184343 2007/08/11 07:05:36 boumenot $
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'smugmug/base'
|
6
|
+
require 'smugmug/table'
|
7
|
+
require 'smugmug/images'
|
8
|
+
|
9
|
+
module SmugMug
|
10
|
+
module Album
|
11
|
+
class Info < Base
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
extend Forwardable
|
15
|
+
def_delegator :images, :each, :each
|
16
|
+
def_delegator :images, :any?, :any?
|
17
|
+
def_delegator :images, :size, :size
|
18
|
+
|
19
|
+
attr_reader :doc
|
20
|
+
|
21
|
+
def initialize(smugmug, doc)
|
22
|
+
super(smugmug)
|
23
|
+
@doc = doc
|
24
|
+
self.class.table = MethodTable.new(Album.data(), smugmug.account_type, self)
|
25
|
+
end
|
26
|
+
|
27
|
+
## overrides
|
28
|
+
|
29
|
+
def method
|
30
|
+
return 'smugmug.albums.getInfo'
|
31
|
+
end
|
32
|
+
|
33
|
+
## methods
|
34
|
+
|
35
|
+
def <=>(rhs)
|
36
|
+
return self.title <=> rhs.title
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete
|
40
|
+
raise RuntimeError, "method is not yet implemented"
|
41
|
+
end
|
42
|
+
|
43
|
+
def images
|
44
|
+
@images = Images.new(@smugmug, self) if @images.nil?
|
45
|
+
return @images
|
46
|
+
end
|
47
|
+
private :images
|
48
|
+
|
49
|
+
def fetch
|
50
|
+
params = {}
|
51
|
+
params[:AlbumID] = album_id
|
52
|
+
# params[:Password] = XXX
|
53
|
+
|
54
|
+
# NOTE:
|
55
|
+
# The initial document is different from the re-fetched document.
|
56
|
+
# --initial--
|
57
|
+
# <album>...</album>
|
58
|
+
# --re-fetched--
|
59
|
+
# <rsp><albums><album>...</album></albums></rsp>
|
60
|
+
return super(params)
|
61
|
+
end
|
62
|
+
|
63
|
+
def sort_by
|
64
|
+
end
|
65
|
+
|
66
|
+
def stats
|
67
|
+
@stats = Stats.new(@smugmug, self) if @stats.nil?
|
68
|
+
return @stats
|
69
|
+
end
|
70
|
+
|
71
|
+
## accessors
|
72
|
+
|
73
|
+
## FIXME: needed to prevent infinite recursion
|
74
|
+
def album_id() return self.class.table.get(@doc, :album_id) ; end
|
75
|
+
def title() return self.class.table.get(@doc, :title) ; end
|
76
|
+
|
77
|
+
def Album.data
|
78
|
+
return %q{
|
79
|
+
<!--
|
80
|
+
Unless specified the following defaults are assumed.
|
81
|
+
|
82
|
+
- type="string"
|
83
|
+
- xpath="//<field>"
|
84
|
+
- account="standard"
|
85
|
+
-->
|
86
|
+
<smugmug>
|
87
|
+
<AlbumID type="int" xpath="//Album/attribute::id"/>
|
88
|
+
<HighlightID type="int" xpath="//Highlight/attribute::id"/>
|
89
|
+
<CategoryID type="int" xpath="//Category/attribute::id"/>
|
90
|
+
<!--
|
91
|
+
<CategoryTitle xpath="//Category/Title"/>
|
92
|
+
-->
|
93
|
+
<SubCategoryID type="int" xpath="//SubCategory/attribute::id"/>
|
94
|
+
<Position type="int"/>
|
95
|
+
<SortMethod/>
|
96
|
+
<SortDirection type="boolean"/>
|
97
|
+
<ImageCount type="int"/>
|
98
|
+
<Title/>
|
99
|
+
<Keywords/>
|
100
|
+
<Description/>
|
101
|
+
<LastUpdated/>
|
102
|
+
<CommunityID type="int" xpath="//Community/attribute::id"/>
|
103
|
+
<Public type="boolean"/>
|
104
|
+
<Password/>
|
105
|
+
<PasswordHint/>
|
106
|
+
<CanRank type="boolean"/>
|
107
|
+
<Printable type="boolean"/>
|
108
|
+
<Filenames type="boolean"/>
|
109
|
+
<Comments type="boolean"/>
|
110
|
+
<External type="boolean"/>
|
111
|
+
<Originals type="boolean"/>
|
112
|
+
<FamilyEdit type="boolean"/>
|
113
|
+
<FriendEdit type="boolean"/>
|
114
|
+
<HideOwner type="boolean"/>
|
115
|
+
<EXIF type="boolean"/>
|
116
|
+
<Share type="boolean"/>
|
117
|
+
<Header type="boolean" account="power"/>
|
118
|
+
<TemplateID type="int" account="power" xpath="//Template/attribute::id"/>
|
119
|
+
<Larges type="boolean" account="pro"/>
|
120
|
+
<DefaultColor type="boolean" account="pro"/>
|
121
|
+
<Clean type="boolean" account="pro"/>
|
122
|
+
<Protected type="boolean" account="pro"/>
|
123
|
+
<Watermark type="boolean" account="pro"/>
|
124
|
+
<SmugSearchable type="boolean"/>
|
125
|
+
<WorldSearchable type="boolean"/>
|
126
|
+
</smugmug>
|
127
|
+
}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- Mode: ruby; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
require 'smugmug/base'
|
5
|
+
|
6
|
+
module SmugMug
|
7
|
+
module Album
|
8
|
+
class Stats < BaseStats
|
9
|
+
attr_reader :album, :doc
|
10
|
+
|
11
|
+
def initialize(smugmug, _album)
|
12
|
+
@album = _album
|
13
|
+
super(smugmug)
|
14
|
+
@doc = fetch()
|
15
|
+
end
|
16
|
+
|
17
|
+
## overrides
|
18
|
+
|
19
|
+
def method
|
20
|
+
return 'smugmug.albums.getStats'
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch
|
24
|
+
params = {}
|
25
|
+
params[:AlbumID] = album.album_id
|
26
|
+
|
27
|
+
return super(params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'smugmug/base'
|
6
|
+
require 'smugmug/album/info'
|
7
|
+
|
8
|
+
module SmugMug
|
9
|
+
class Albums < Base
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
def_delegator :@albums, :each, :each
|
14
|
+
def_delegator :@albums, :any?, :any?
|
15
|
+
def_delegator :@albums, :size, :size
|
16
|
+
|
17
|
+
def initialize(smugmug)
|
18
|
+
super(smugmug)
|
19
|
+
@albums = []
|
20
|
+
|
21
|
+
@doc = fetch().root
|
22
|
+
@doc.elements.each('Albums/Album') do |x|
|
23
|
+
@albums << Album::Info.new(@smugmug, REXML::Document.new(x.to_s))
|
24
|
+
$log.info('Albums#initialize') { "Added the album #{@albums[-1].title}" }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
## overrides
|
29
|
+
|
30
|
+
def method
|
31
|
+
return 'smugmug.albums.get'
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(sym, *args)
|
35
|
+
raise NoMethodError.new("undefined method `#{method}' for " +
|
36
|
+
"#{self.inspect}:#{self.class.name}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/smugmug/base.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# $Hg: base.rb,v 1a7959f46f5c 2007/08/11 05:56:18 boumenot $
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'logger'
|
6
|
+
require 'net/https'
|
7
|
+
require 'net/httpz'
|
8
|
+
require 'rexml/document'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
module SmugMug
|
12
|
+
class Base
|
13
|
+
extend Forwardable
|
14
|
+
def_delegator :@smugmug, :url, :url
|
15
|
+
|
16
|
+
attr_reader :smugmug
|
17
|
+
|
18
|
+
@table = nil
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_reader :table
|
22
|
+
def table=(val)
|
23
|
+
return unless @table.nil?
|
24
|
+
@table = val
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(smugmug)
|
29
|
+
@smugmug = smugmug
|
30
|
+
@heavy = false
|
31
|
+
end
|
32
|
+
|
33
|
+
## accessors
|
34
|
+
|
35
|
+
def data
|
36
|
+
raise SmugMugError, "data() must be defined in the derived class!"
|
37
|
+
end
|
38
|
+
|
39
|
+
def method
|
40
|
+
raise SmugMugError, "method() must be defined in the derived class!"
|
41
|
+
end
|
42
|
+
|
43
|
+
## methods
|
44
|
+
|
45
|
+
def heavy?
|
46
|
+
return @heavy == true
|
47
|
+
end
|
48
|
+
|
49
|
+
def fetch(params={})
|
50
|
+
params[:method] = method
|
51
|
+
params[:SessionID] = smugmug.session_id
|
52
|
+
|
53
|
+
@heavy = true
|
54
|
+
return smugmug.fetch(params)
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(sym, *args)
|
58
|
+
if sym.to_s[-1] == '='
|
59
|
+
raise NoMethodError.new("undefined method `#{method}' for " +
|
60
|
+
"#{self.inspect}:#{self.class.name}")
|
61
|
+
end
|
62
|
+
@doc = fetch unless heavy?
|
63
|
+
return self.class.table.get(@doc, sym)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BaseStats < Base
|
68
|
+
def initialize(smugmug)
|
69
|
+
super(smugmug)
|
70
|
+
self.class.table = MethodTable.new(BaseStats.data(), smugmug.account_type, self.class)
|
71
|
+
end
|
72
|
+
|
73
|
+
## methods
|
74
|
+
|
75
|
+
def fetch(params)
|
76
|
+
params[:Month] = Time.now.month
|
77
|
+
params[:Year] = Time.now.year
|
78
|
+
|
79
|
+
return super(params)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.data
|
83
|
+
return %q{
|
84
|
+
<!--
|
85
|
+
Unless specified the following defaults are assumed.
|
86
|
+
|
87
|
+
- type="string"
|
88
|
+
- xpath="//<field>"
|
89
|
+
- account="standard"
|
90
|
+
= readonly="true"
|
91
|
+
-->
|
92
|
+
<smugmug>
|
93
|
+
<Bytes type="int"/>
|
94
|
+
<Tiny type="int"/>
|
95
|
+
<Thumb type="int"/>
|
96
|
+
<Small type="int"/>
|
97
|
+
<Medium type="int"/>
|
98
|
+
<Large type="int"/>
|
99
|
+
<Original type="float"/>
|
100
|
+
</smugmug>
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- Mode: ruby; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
module SmugMug
|
5
|
+
class Cache
|
6
|
+
def initialize
|
7
|
+
@cache = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](val)
|
11
|
+
return @cache[val]['data']
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(val, data)
|
15
|
+
@cache[val] ||= {}
|
16
|
+
|
17
|
+
@cache[val]['data'] = data
|
18
|
+
@cache[val]['time'] = Time.now.httpdate
|
19
|
+
end
|
20
|
+
|
21
|
+
def cached?(val)
|
22
|
+
return @cache.has_key?(val)
|
23
|
+
end
|
24
|
+
|
25
|
+
def last_modified(val)
|
26
|
+
return @cache[val]['time']
|
27
|
+
end
|
28
|
+
|
29
|
+
def invalidate(val)
|
30
|
+
@cache.delete(val)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# $Hg$
|
3
|
+
|
4
|
+
require 'smugmug/util.rb'
|
5
|
+
|
6
|
+
module SmugMug
|
7
|
+
class Config
|
8
|
+
attr_accessor :AccountType, :EmailAddress, :PasswordHash, :UserID
|
9
|
+
|
10
|
+
FILENAME = '~/.smugmugrc'
|
11
|
+
VARIABLES = %w{AccountType EmailAddress PasswordHash UserID}
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@filename = ENV.has_key?('SMUGMUG_RC') ? ENV['SMUGMUG_RC'] : FILENAME
|
15
|
+
restore()
|
16
|
+
end
|
17
|
+
|
18
|
+
## methods
|
19
|
+
|
20
|
+
def save
|
21
|
+
doc = REXML::Element.new('smugmug')
|
22
|
+
VARIABLES.each do |var|
|
23
|
+
doc << REXML::Element.new(var).add_text(self.send(var.to_sym))
|
24
|
+
end
|
25
|
+
File.write(filename(), doc.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
## accesors
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def filename
|
33
|
+
return File.expand_path(@filename)
|
34
|
+
end
|
35
|
+
|
36
|
+
def restore
|
37
|
+
begin
|
38
|
+
doc = REXML::Document.new(IO.read(filename()))
|
39
|
+
VARIABLES.each do |var|
|
40
|
+
val = (doc.root.elements[var].nil?) ? nil : doc.root.elements[var].text
|
41
|
+
self.instance_variable_set("@#{var}", val)
|
42
|
+
end
|
43
|
+
rescue
|
44
|
+
$log.info('Config') { "#{filename} does not exist, skipping restore" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# $Hg: exif.rb,v 1f3d53375e7c 2007/08/11 06:35:02 boumenot $
|
3
|
+
|
4
|
+
require 'smugmug/base'
|
5
|
+
require 'smugmug/table'
|
6
|
+
|
7
|
+
module SmugMug
|
8
|
+
module Image
|
9
|
+
class Exif < Base
|
10
|
+
attr_reader :image
|
11
|
+
def initialize(smugmug, image)
|
12
|
+
super(smugmug)
|
13
|
+
@image = image
|
14
|
+
self.class.table = MethodTable.new(Exif.data(), smugmug.account_type, self)
|
15
|
+
end
|
16
|
+
|
17
|
+
## overrides
|
18
|
+
|
19
|
+
def method
|
20
|
+
return 'smugmug.images.getEXIF'
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch
|
24
|
+
params = {}
|
25
|
+
# params[:Password] = image.album.password
|
26
|
+
params[:ImageID] = image.image_id
|
27
|
+
|
28
|
+
return super(params)
|
29
|
+
end
|
30
|
+
|
31
|
+
## methods
|
32
|
+
|
33
|
+
## accessors
|
34
|
+
|
35
|
+
def Exif.data
|
36
|
+
return %q{
|
37
|
+
<smugmug>
|
38
|
+
<ImageID type="int" xpath="//Image/attribute::id"/>
|
39
|
+
<DateTime/>
|
40
|
+
<DateTimeOriginal/>
|
41
|
+
<DateTimeDigitized/>
|
42
|
+
<Make/>
|
43
|
+
<Model/>
|
44
|
+
<ExposureTime/>
|
45
|
+
<Aperture/>
|
46
|
+
<ISO type="int"/>
|
47
|
+
<FocalLength/>
|
48
|
+
<FocalLength35mmFilm/>
|
49
|
+
<CCDWidth/>
|
50
|
+
<CompressedBitsPerPixel/>
|
51
|
+
<Flash type="int"/>
|
52
|
+
<Metering type="int"/>
|
53
|
+
<ExposureProgram type="int"/>
|
54
|
+
<ExposureBiasValue/>
|
55
|
+
<ExposureMode type="int"/>
|
56
|
+
<LightSource type="int"/>
|
57
|
+
<WhiteBalance type="int"/>
|
58
|
+
<DigitalZoomRatio/>
|
59
|
+
<Contrast type="int"/>
|
60
|
+
<Saturation type="int"/>
|
61
|
+
<Sharpness type="int"/>
|
62
|
+
<SubjectDistance/>
|
63
|
+
<SubjectDistanceRange/>
|
64
|
+
<SensingMethod type="int"/>
|
65
|
+
<ColorSpace/>
|
66
|
+
<Brightness/>
|
67
|
+
</smugmug>
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|