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.
Files changed (49) hide show
  1. data/HISTORY +2 -0
  2. data/LICENSE +19 -0
  3. data/MANIFEST +48 -0
  4. data/README +30 -0
  5. data/Rakefile +100 -0
  6. data/bin/smcli +225 -0
  7. data/bin/smugmug2sql +158 -0
  8. data/doc/API +310 -0
  9. data/doc/TODO +32 -0
  10. data/lib/net/httpz.rb +31 -0
  11. data/lib/smugmug.rb +179 -0
  12. data/lib/smugmug/album/info.rb +131 -0
  13. data/lib/smugmug/album/stats.rb +31 -0
  14. data/lib/smugmug/albums.rb +39 -0
  15. data/lib/smugmug/base.rb +104 -0
  16. data/lib/smugmug/cache.rb +33 -0
  17. data/lib/smugmug/config.rb +48 -0
  18. data/lib/smugmug/image/exif.rb +72 -0
  19. data/lib/smugmug/image/info.rb +88 -0
  20. data/lib/smugmug/image/stats.rb +32 -0
  21. data/lib/smugmug/images.rb +52 -0
  22. data/lib/smugmug/table.rb +133 -0
  23. data/lib/smugmug/util.rb +12 -0
  24. data/test/album.rb +359 -0
  25. data/test/config.rb +39 -0
  26. data/test/httpz.rb +120 -0
  27. data/test/image.rb +540 -0
  28. data/test/login.rb +24 -0
  29. data/test/runner.rb +83 -0
  30. data/test/servlet.rb +257 -0
  31. data/test/table.rb +113 -0
  32. data/xml/canned +212 -0
  33. data/xml/fail/empty.set.xml +4 -0
  34. data/xml/fail/invalid.apikey.xml +4 -0
  35. data/xml/fail/invalid.login.xml +4 -0
  36. data/xml/fail/invalid.method.xml +4 -0
  37. data/xml/fail/invalid.user.xml +4 -0
  38. data/xml/fail/system.error.xml +4 -0
  39. data/xml/standard/albums.get.xml +24 -0
  40. data/xml/standard/albums.getInfo.xml +38 -0
  41. data/xml/standard/albums.getStats.xml +43 -0
  42. data/xml/standard/categories.get.xml +213 -0
  43. data/xml/standard/images.get.xml +9 -0
  44. data/xml/standard/images.getEXIF.xml +34 -0
  45. data/xml/standard/images.getInfo.xml +29 -0
  46. data/xml/standard/images.getStats.xml +15 -0
  47. data/xml/standard/login.withHash.xml +7 -0
  48. data/xml/standard/login.withPassword.xml +10 -0
  49. 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
@@ -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