gallery-remote 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.
@@ -0,0 +1 @@
1
+ [License](http://mwalker.info/license.html)
data/README ADDED
@@ -0,0 +1,3 @@
1
+ I'm expanding the gallery-remote code for use in another project. I don't
2
+ intend to make it feature-complete or package it well, just make it serviceable
3
+ for my other work.
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/gempackagetask'
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '/lib')
6
+ require 'gallery'
7
+
8
+ PKG_NAME = 'gallery-remote'
9
+ PKG_VERSION = Gallery::VERSION
10
+
11
+ LIB_FILES = Dir.glob('lib/**/*')
12
+ RELEASE_FILES = [ 'Rakefile', 'README', 'LICENSE.md' ] + LIB_FILES
13
+
14
+ task :default => [ :package ]
15
+
16
+ spec = Gem::Specification.new do |spec|
17
+ spec.name = PKG_NAME
18
+ spec.version = PKG_VERSION
19
+ spec.summary = 'A Ruby client for the Gallery2 photo gallery system'
20
+ spec.description = 'gallery-remote is an implementation of the Gallery Remote protocol in Ruby.'
21
+ spec.authors = ['Carl Leiby', 'Matt Walker']
22
+ spec.email = 'matt.r.walker@gmail.com'
23
+ spec.homepage = 'http://github.com/mrwalker/gallery-remote'
24
+ spec.files = RELEASE_FILES
25
+ spec.rubyforge_project = PKG_NAME
26
+ end
27
+
28
+ Rake::GemPackageTask.new(spec) do |package|
29
+ package.need_tar = true
30
+ end
@@ -0,0 +1,59 @@
1
+ #
2
+ # cookie_jar.rb
3
+ #
4
+ # Created on Sep 19, 2007, 7:39:33 PM
5
+ require 'date'
6
+
7
+ class CookieJar
8
+ def initialize
9
+ @cookies = []
10
+ end
11
+
12
+ def add(cookie_list)
13
+ return if ! cookie_list
14
+ cookie_list.each do |cookie_string|
15
+ cookie = Cookie.new(cookie_string)
16
+ if ! cookie.expired
17
+ @cookies = @cookies.reject {|c| c.name==cookie.name}
18
+ @cookies << cookie
19
+ end
20
+ end
21
+ end
22
+
23
+ def cookies
24
+ return if @cookies.length == 0
25
+ @cookies.map { |cookie| cookie.to_s }.join("; ")
26
+ end
27
+ end
28
+
29
+ class Cookie
30
+ attr_accessor :name, :value, :expiration
31
+
32
+ def initialize(cookie_str)
33
+ cookie_str.split(/;\s?/).each_with_index do |c,i|
34
+ if i==0
35
+ @name, *values = c.split("=")
36
+ @value = values.join("=")
37
+ else
38
+ attr_name, *attr_values = c.split("=")
39
+ attr_value = attr_values.join("=")
40
+ if attr_name =~ /expires/i
41
+ @expiration = attr_value
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def to_s
48
+ "#{@name}=#{@value}"
49
+ end
50
+
51
+ def expired
52
+ if @expiration
53
+ @expiration.sub!(/^([a-zA-Z]+,)(\d)/) { |s| "#{$1} #{$2}" }
54
+ expiration_date = DateTime.parse( @expiration, true )
55
+ return expiration_date < DateTime.now
56
+ end
57
+ false
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ require 'gallery/remote'
2
+ require 'gallery/gallery'
3
+ require 'gallery/album'
4
+ require 'gallery/image'
5
+
6
+ module Gallery
7
+ VERSION = '0.0.1'
8
+ end
@@ -0,0 +1,64 @@
1
+ module Gallery
2
+ class Album
3
+ attr_accessor :remote, :params
4
+
5
+ def initialize(remote, params)
6
+ @remote, @params = remote, params
7
+ end
8
+
9
+ def properties(params = {})
10
+ @remote.album_properties(name, params)
11
+ end
12
+
13
+ def images(params = {})
14
+ response = @remote.fetch_album_images(name, params)
15
+ image_params = response.keys.inject([]) do |image_params, key|
16
+ next image_params unless key =~ /image\.(.*)\.(\d+)/
17
+ _, param, index = key.match(/image\.(.*)\.(\d+)/).to_a
18
+ index = index.to_i
19
+ image_params[index] ||= {}
20
+ image_params[index][param] = response[key]
21
+ image_params
22
+ end.compact # Keys are 1-based; remove first element
23
+ image_params.map do |params|
24
+ image = Image.new(@remote, params)
25
+ yield image if block_given?
26
+ image
27
+ end
28
+ end
29
+
30
+ def add_item(file_name, params = {})
31
+ @remote.add_item(name, file_name, params)
32
+ end
33
+
34
+ def add_album(title, params = {})
35
+ @remote.new_album(name, { :newAlbumName => title_to_name(title), :newAlbumTitle => title }.merge(params))
36
+ end
37
+
38
+ def name
39
+ @params['name']
40
+ end
41
+
42
+ def title
43
+ @params['title']
44
+ end
45
+
46
+ def parent
47
+ @params['parent']
48
+ end
49
+
50
+ def to_s
51
+ "Album #{name}: #{title}"
52
+ end
53
+
54
+ private
55
+
56
+ def title_to_name(title)
57
+ name = title.dup
58
+ name.downcase!
59
+ name.gsub!(/[,'\-:]/, '') # Remove illegal characters
60
+ name.gsub!(/\s+/, '_') # Join words with underscores
61
+ name
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ module Gallery
2
+ class Gallery
3
+ attr_accessor :remote
4
+
5
+ def initialize(url, &block)
6
+ @remote = Remote.new(url)
7
+ instance_eval(&block) if block_given?
8
+ end
9
+
10
+ def login(user, pass)
11
+ @remote.login(user, pass)
12
+ end
13
+
14
+ def albums(params = {})
15
+ response = @remote.fetch_albums_prune
16
+ album_params = response.keys.inject([]) do |album_params, key|
17
+ next album_params unless key =~ /album\.(.*)\.(\d+)/
18
+ _, param, index = key.match(/album\.(.*)\.(\d+)/).to_a
19
+ index = index.to_i
20
+ album_params[index] ||= {}
21
+ album_params[index][param] = response[key]
22
+ album_params
23
+ end.compact # Keys are 1-based; remove first element
24
+ album_params.map do |params|
25
+ album = Album.new(@remote, params)
26
+ yield album if block_given?
27
+ album
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ module Gallery
2
+ class Image
3
+ attr_accessor :remote, :params
4
+
5
+ def initialize(remote, params = {})
6
+ @remote, @params = remote, params
7
+ end
8
+
9
+ def properties(params = {})
10
+ @remote.image_properites(name, params)
11
+ end
12
+
13
+ def name
14
+ @params['name']
15
+ end
16
+
17
+ def title
18
+ @params['title']
19
+ end
20
+
21
+ def caption
22
+ @params['caption']
23
+ end
24
+
25
+ def to_s
26
+ "Image #{name}: #{title}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,233 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'cookie_jar'
4
+
5
+ module Gallery
6
+ class Remote
7
+ attr_accessor :last_response, :status, :status_text
8
+
9
+ GR_STAT_SUCCESS = 0
10
+ PROTO_MAJ_VER_INVAL = 101
11
+ PROTO_MIN_VER_INVAL = 102
12
+ PROTO_VER_FMT_INVAL = 103
13
+ PROTO_VER_MISSING = 104
14
+ PASSWD_WRONG = 201
15
+ LOGIN_MISSING = 202
16
+ UNKNOWN_CMD = 301
17
+ NO_ADD_PERMISSION = 401
18
+ NO_FILENAME = 402
19
+ UPLOAD_PHOTO_FAIL = 403
20
+ NO_WRITE_PERMISSION = 404
21
+ NO_VIEW_PERMISSIO = 405
22
+ NO_CREATE_ALBUM_PERMISSION = 501
23
+ CREATE_ALBUM_FAILED = 502
24
+ MOVE_ALBUM_FAILED = 503
25
+ ROTATE_IMAGE_FAILED = 504
26
+ STATUS_DESCRIPTIONS = {
27
+ GR_STAT_SUCCESS => 'The command the client sent in the request completed successfully. The data (if any) in the response should be considered valid.',
28
+ PROTO_MAJ_VER_INVAL => 'The protocol major version the client is using is not supported.',
29
+ PROTO_MIN_VER_INVAL => 'The protocol minor version the client is using is not supported.',
30
+ PROTO_VER_FMT_INVAL => 'The format of the protocol version string the client sent in the request is invalid.',
31
+ PROTO_VER_MISSING => 'The request did not contain the required protocol_version key.',
32
+ PASSWD_WRONG => 'The password and/or username the client send in the request is invalid.',
33
+ LOGIN_MISSING => 'The client used the login command in the request but failed to include either the username or password (or both) in the request.',
34
+ UNKNOWN_CMD => 'The value of the cmd key is not valid.',
35
+ NO_ADD_PERMISSION => 'The user does not have permission to add an item to the gallery.',
36
+ NO_FILENAME => 'No filename was specified.',
37
+ UPLOAD_PHOTO_FAIL => 'The file was received, but could not be processed or added to the album.',
38
+ NO_WRITE_PERMISSION => 'No write permission to destination album.',
39
+ NO_VIEW_PERMISSIO => 'No view permission for this image.',
40
+ NO_CREATE_ALBUM_PERMISSION => 'A new album could not be created because the user does not have permission to do so.',
41
+ CREATE_ALBUM_FAILED => 'A new album could not be created, for a different reason (name conflict).',
42
+ MOVE_ALBUM_FAILED => 'The album could not be moved.',
43
+ ROTATE_IMAGE_FAILED => 'The image could not be rotated'
44
+ }
45
+
46
+ @@supported_types = {
47
+ '.avi' => 'video/x-msvideo',
48
+ '.bmp' => 'image/bmp',
49
+ '.gif' => 'image/gif',
50
+ '.jpe' => 'image/jpeg',
51
+ '.jpg' => 'image/jpeg',
52
+ '.jpeg' => 'image/jpeg',
53
+ '.mov' => 'video/quicktime',
54
+ '.qt' => 'video/quicktime',
55
+ '.mp4' => 'video/mp4',
56
+ '.tif' => 'image/tiff',
57
+ '.tiff' => 'image/tiff'
58
+ }
59
+
60
+ def self.supported_type?(extension)
61
+ @@supported_types.has_key?(extension)
62
+ end
63
+
64
+ def initialize(url)
65
+ @uri = URI.parse(url)
66
+ @base_params = {
67
+ 'g2_controller' => 'remote:GalleryRemote',
68
+ 'g2_form[protocol_version]' => '2.9'
69
+ }
70
+ @cookie_jar = CookieJar.new
71
+ @boundary = '7d21f123d00c4'
72
+ end
73
+
74
+ # cmd=login
75
+ # protocol_version=2.0
76
+ # uname=gallery-user-name
77
+ # password=cleartext-password
78
+ def login(uname, password, params = {})
79
+ params = { :cmd => 'login', :uname => uname, :password => password }.merge(params)
80
+ send_request(params)
81
+ end
82
+
83
+ # cmd=fetch-albums-prune
84
+ # protocol_version=2.2
85
+ # no_perms=yes/no [optional, G2 since 2.9]
86
+ def fetch_albums_prune(params = {})
87
+ params = { :cmd => 'fetch-albums-prune', :no_perms => 'y' }.merge(params)
88
+ send_request(params)
89
+ end
90
+
91
+ # cmd=add-item
92
+ # protocol_version=2.0
93
+ # set_albumName=album name
94
+ # userfile=user-file
95
+ # userfile_name=file-name
96
+ # caption=caption [optional]
97
+ # force_filename=force-filename [optional]
98
+ # auto_rotate=yes/no [optional, since 2.5]
99
+ # extrafield.fieldname=fieldvalue [optional, since 2.3]
100
+ def add_item(set_albumName, userfile_name, params = {})
101
+ params = { :cmd => 'add-item', :set_albumName => set_albumName, :userfile_name => userfile_name }.merge(params)
102
+ send_request(params)
103
+ end
104
+
105
+ # cmd=album-properties
106
+ # protocol_version=2.0
107
+ # set_albumName=album-name
108
+ def album_properties(set_albumName, params = {})
109
+ params = { :cmd => 'album-properties', :set_albumName => set_albumName }.merge(params)
110
+ send_request(params)
111
+ end
112
+
113
+ # cmd=new-album
114
+ # protocol_version=2.1
115
+ # set_albumName=parent-album-name
116
+ # newAlbumName=album-name [optional]
117
+ # newAlbumTitle=album-title [optional]
118
+ # newAlbumDesc=album-description [optional]
119
+ def new_album(set_albumName, params = {})
120
+ params = { :cmd => 'new-album', :set_albumName => set_albumName }.merge(params)
121
+ send_request(params)
122
+ end
123
+
124
+ # cmd=fetch-album-images
125
+ # protocol_version=2.4
126
+ # set_albumName=album-name
127
+ # albums_too=yes/no [optional, since 2.13]
128
+ # random=yes/no [optional, G2 since ***]
129
+ # limit=number-of-images [optional, G2 since ***]
130
+ # extrafields=yes/no [optional, G2 since 2.12]
131
+ # all_sizes=yes/no [optional, G2 since 2.14]
132
+ def fetch_album_images(set_albumName, params = {})
133
+ params = { :cmd => 'fetch-album-images', :set_albumName => set_albumName }.merge(params)
134
+ send_request(params)
135
+ end
136
+
137
+ # cmd=image-properties
138
+ # protocol_version=***
139
+ # id=item-id
140
+ def image_properties(id, params = {})
141
+ params = { :cmd => 'image-properties', :id => id }.merge(params)
142
+ send_request(params)
143
+ end
144
+
145
+ def status_msg
146
+ "#{@status} - (#{@status_text})"
147
+ end
148
+
149
+ private
150
+
151
+ def build_multipart_query(params, userfile_name)
152
+ params['g2_userfile_name'] = userfile_name
153
+ request = params.map{ |k, v| "Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n" }
154
+ content = File.open(userfile_name, 'r'){ |f| f.read }
155
+ request << "Content-Disposition: form-data; name=\"g2_userfile\"; filename=\"#{userfile_name}\"\r\n" +
156
+ "Content-Transfer-Encoding: binary\r\n" +
157
+ "Content-Type: #{@@supported_types[File.extname(userfile_name).downcase]}\r\n\r\n" +
158
+ content + "\r\n"
159
+ request.collect { |p| "--#{@boundary}\r\n#{p}" }.join("") + "--#{@boundary}--"
160
+ end
161
+
162
+ def build_query(params)
163
+ params.map{ |k, v| "#{k}=#{v}" }.join('&')
164
+ end
165
+
166
+ def send_request(params)
167
+ userfile_name = params.delete(:userfile_name)
168
+ post_parameters = prep_params(params)
169
+ headers = {}
170
+ headers['Cookie'] = @cookie_jar.cookies if @cookie_jar.cookies
171
+ if userfile_name && File.file?(userfile_name)
172
+ query = build_multipart_query(post_parameters, userfile_name)
173
+ headers['Content-type'] = "multipart/form-data, boundary=#{@boundary}" if @boundary
174
+ else
175
+ query = build_query(post_parameters)
176
+ end
177
+ res = post(query, headers)
178
+ case res
179
+ when Net::HTTPSuccess, Net::HTTPRedirection
180
+ handle_response(res)
181
+ else
182
+ res.error!
183
+ end
184
+ end
185
+
186
+ def post(query, headers = {}, retries = 3)
187
+ begin
188
+ Net::HTTP.start(@uri.host, @uri.port) do |h|
189
+ h.post(@uri.path, query, headers)
190
+ end
191
+ rescue Exception => e
192
+ puts "Error during POST (#{retries} retries remain): #{e}"
193
+ throw e if retries == 0
194
+ post(query, headers, retries - 1)
195
+ end
196
+ end
197
+
198
+ def prep_params(params)
199
+ result = {}
200
+ result = result.merge(@base_params)
201
+ params.each_pair do |name, value|
202
+ result["g2_form[#{name}]"] = value
203
+ end
204
+ result['g2_authToken'] = @auth_token if @auth_token
205
+ result
206
+ end
207
+
208
+ def handle_response(res)
209
+ @cookie_jar.add(res.header.get_fields('set-cookie'))
210
+ @last_response = {}
211
+ begin
212
+ header = false
213
+ res.body.each do |line|
214
+ header = true if line.chomp == '#__GR2PROTO__'
215
+ next unless header # Ignore debug output
216
+ next if line =~ /^#/ # Ignore comments
217
+ name, *values = line.strip.split(/\s*=\s*/)
218
+ @last_response[name.strip] = values.join('=')
219
+ end
220
+ rescue Exception => e
221
+ puts "Error parsing response:\n#{res.body}"
222
+ throw e
223
+ end
224
+ @auth_token ||= @last_response['auth_token']
225
+ puts 'WARN: no auth token in response (using last)' unless @last_response['auth_token']
226
+ @status = @last_response['status'].to_i
227
+ @status_text = @last_response['status_text']
228
+ puts status_msg
229
+ puts "WARN: #{STATUS_DESCRIPTIONS[@status]}" unless @status == GR_STAT_SUCCESS
230
+ @last_response
231
+ end
232
+ end
233
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gallery-remote
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Carl Leiby
8
+ - Matt Walker
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-18 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: gallery-remote is an implementation of the Gallery Remote protocol in Ruby.
18
+ email: matt.r.walker@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - Rakefile
27
+ - README
28
+ - LICENSE.md
29
+ - lib/cookie_jar.rb
30
+ - lib/gallery/album.rb
31
+ - lib/gallery/gallery.rb
32
+ - lib/gallery/image.rb
33
+ - lib/gallery/remote.rb
34
+ - lib/gallery.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/mrwalker/gallery-remote
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: gallery-remote
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: A Ruby client for the Gallery2 photo gallery system
63
+ test_files: []
64
+