Class: SmugmugAPI

Inherits:
Object
  • Object
show all
Defined in:
lib/helpers/smugmug_api.rb

Constant Summary collapse

OAUTH_ORIGIN =
'https://secure.smugmug.com'.freeze
REQUEST_TOKEN_URL =
'/services/oauth/1.0a/getRequestToken'.freeze
ACCESS_TOKEN_URL =
'/services/oauth/1.0a/getAccessToken'.freeze
AUTHORIZE_URL =
'/services/oauth/1.0a/authorize'.freeze
API_ENDPOINT =
'https://api.smugmug.com'.freeze
UPLOAD_ENDPOINT =
'https://upload.smugmug.com/'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ejson_file = '~/.photo_helper.ejson') ⇒ SmugmugAPI

Returns a new instance of SmugmugAPI.



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/helpers/smugmug_api.rb', line 19

def initialize(ejson_file = '~/.photo_helper.ejson')
  ejson_file = File.expand_path(ejson_file)
  @secrets = Secrets.new(ejson_file, %i[api_key api_secret])
  request_access_token if !@secrets['access_token'] || !@secrets['access_secret']

  @http = get_access_token
  @uploader = get_access_token(UPLOAD_ENDPOINT)
  user_resp = user
  @user = user_resp['NickName']
  @root_node = File.basename(user_resp['Uris']['Node']['Uri'])
end

Instance Attribute Details

#http(method, url, headers = {}, body = nil) ⇒ Object

Returns the value of attribute http.



11
12
13
# File 'lib/helpers/smugmug_api.rb', line 11

def http
  @http
end

#uploaderObject

Returns the value of attribute uploader.



11
12
13
# File 'lib/helpers/smugmug_api.rb', line 11

def uploader
  @uploader
end

Instance Method Details

#albumsObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/helpers/smugmug_api.rb', line 31

def albums
  albums_list = []
  start = 1
  count = 100
  loop do
    resp = get("/api/v2/user/#{@user}!albums", start: start, count: count)

    resp['Album'].each do |album|
      albums_list.push(album_parser(album))
    end
    break if (start + count) > resp['Pages']['Total'].to_i
    start += count
  end
  albums_list
end

#albums_long(path = '', node_id = @root_node) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/helpers/smugmug_api.rb', line 47

def albums_long(path = '', node_id = @root_node)
  album_list = []
  node_children(node_id)['Node'].each do |node|
    node_path = path.empty? ? node['Name'] : File.join(path, node['Name'])
    case node['Type']
    when 'Folder'
      album_list.concat(albums_long(node_path, node['NodeID']))
    when 'Album'
      album_list.push(path: node_path,
                      name: node['Name'],
                      web_uri: node['WebUri'],
                      node_uri: node['Uri'],
                      id: File.basename(node['Uris']['Album']['Uri']),
                      type: 'node')
    end
  end
  album_list
end

#collect_images(images, album_id) ⇒ Object



232
233
234
235
236
# File 'lib/helpers/smugmug_api.rb', line 232

def collect_images(images, album_id)
  return if images.empty?
  images = images.join(',') if images.is_a? Array
  post("/api/v2/album/#{album_id}!collectimages", 'CollectUris' => images)
end

#delete_images(images) ⇒ Object



163
164
165
166
167
168
# File 'lib/helpers/smugmug_api.rb', line 163

def delete_images(images)
  images = [images] unless images.is_a? Array
  images.each do |image|
    http(:delete, image[:uri])
  end
end

#foldersObject



74
75
76
77
78
79
80
81
# File 'lib/helpers/smugmug_api.rb', line 74

def folders
  folder_list = []
  resp = get('/api/v2/folder/user/bcaldwell!folderlist')
  resp['FolderList'].each do |folder|
    folder_list.push(folder_parser(folder))
  end
  folder_list
end

#get(url, params = nil, headers = {}) ⇒ Object



181
182
183
184
185
186
# File 'lib/helpers/smugmug_api.rb', line 181

def get(url, params = nil, headers = {})
  url = url.tr(' ', '-')
  uri = URI.parse(url)
  uri.query = URI.encode_www_form(params) if params
  http(:get, uri.to_s, headers)
end

#get_or_create_album(path, album_url: nil) ⇒ Object

path is the full path with spaces



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/helpers/smugmug_api.rb', line 84

def get_or_create_album(path, album_url: nil)
  folder_path = File.dirname(path).split('/').map(&:capitalize).join('/')
  album_name = File.basename(path).split(' ').map(&:capitalize).join(' ')
  album = nil

  folder = get_or_create_folder(folder_path)
  resp = get(folder[:albums_url])
  albums = get(folder[:albums_url])['Album'] if resp.key? 'Album'
  albums ||= []
  albums.each do |album_raw|
    next unless album_raw['Name'] == album_name
    album = album_parser(album_raw)
  end

  if album.nil?
    url = "/api/v2/folder/user/#{@user}"
    url += "/#{folder_path}" unless folder_path.empty?
    url += '!albums'
    album_url = album_name if album_url.nil?
    resp = post(url, Name: album_name,
                     UrlName: album_url.tr(' ', '-').capitalize,
                     Privacy: 'Unlisted',
                     SmugSearchable: 'No',
                     SortMethod: 'Date Taken',
                     LargestSize: 'X4Large',
                     SortDirection: 'Ascending',
                     WorldSearchable: false,
                     EXIF: false,
                     Printable: false,
                     Filenames: true)
    album_raw = resp['Album']
    album = album_parser(album_raw)
  end
  album
end

#get_or_create_folder(path) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/helpers/smugmug_api.rb', line 120

def get_or_create_folder(path)
  parts = path.split('/')
  current_path = ''
  folder = nil

  parts.each do |part|
    part = part.capitalize
    new_path = current_path.empty? ? part : File.join(current_path, part)
    resp = http_raw(:get, "/api/v2/folder/user/#{@user}/#{new_path}")
    if resp.is_a? Net::HTTPSuccess
      folder_raw = JSON.parse(resp.body)['Response']['Folder']
      folder = folder_parser(folder_raw)
    else
      url = "/api/v2/folder/user/#{@user}"
      url += "/#{current_path}" unless current_path.empty?
      url += '!folders'
      resp = post(url, Name: part.capitalize,
                       UrlName: part.tr(' ', '-').capitalize,
                       Privacy: 'Unlisted')
      folder = folder_parser(resp['Folder'])
    end
    current_path = new_path
  end

  folder
end

#image_list(album_id) ⇒ Object



170
171
172
173
# File 'lib/helpers/smugmug_api.rb', line 170

def image_list(album_id)
  @images = images(album_id)
  @images.map { |i| i[:filename] }
end

#images(album_id) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/helpers/smugmug_api.rb', line 147

def images(album_id)
  images = []
  start = 1
  count = 100
  loop do
    images_raw = get("/api/v2/album/#{album_id}!images", start: start, count: count)
    return [] unless images_raw.key? 'AlbumImage'
    images_raw['AlbumImage'].each do |image|
      images.push(imager_parser(image))
    end
    break if (start + count) > images_raw['Pages']['Total'].to_i
    start += count
  end
  images
end

#move_images(images, album_id) ⇒ Object



238
239
240
241
242
# File 'lib/helpers/smugmug_api.rb', line 238

def move_images(images, album_id)
  return if images.empty?
  images = images.join(',') if images.is_a? Array
  post("/api/v2/album/#{album_id}!moveimages", 'MoveUris' => images)
end

#node_children(id) ⇒ Object



66
67
68
# File 'lib/helpers/smugmug_api.rb', line 66

def node_children(id)
  get("/api/v2/node/#{id}!children", count: 100)
end

#post(url, body = {}, headers = {}) ⇒ Object



188
189
190
191
192
193
194
195
196
# File 'lib/helpers/smugmug_api.rb', line 188

def post(url, body = {}, headers = {})
  url = url.tr(' ', '-')
  headers['Accept'] = 'application/json'
  headers['Content-Type'] = 'application/json'
  headers['Connection'] = 'keep-alive'
  response = @http.post(url, body, headers)
  raise "Request failed\n#{URI.unescape(response.body)}" unless response.is_a? Net::HTTPSuccess
  JSON.parse(response.body)['Response']
end

#request_access_tokenObject



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/helpers/smugmug_api.rb', line 252

def request_access_token
  @consumer = OAuth::Consumer.new(@secrets.api_key, @secrets.api_secret,
                                  site: OAUTH_ORIGIN,
                                  name: 'photo-helper',
                                  request_token_path: REQUEST_TOKEN_URL,
                                  authorize_path: AUTHORIZE_URL,
                                  access_token_path: ACCESS_TOKEN_URL)

  # Generate request token
  @request_token = @consumer.get_request_token

  # Get authorize URL
  @request_token.authorize_url

  url = add_auth_params(@request_token.authorize_url, 'Access' => 'Full', 'Permissions' => 'Modify')

  puts "Go to #{url} and enter shown 6 digit number:"
  verifier = STDIN.gets.strip

  # Now go to that url and when you're done authorization you can run the
  # following command where you put in the value for oauth_verifier that you got
  # from completely the above URL request:
  access_token = @request_token.get_access_token(oauth_verifier: verifier)

  puts "Add the following to your ejson file #{@secrets.ejson_config_file}:"
  puts "\"access_token\": \"#{access_token.token}\","
  puts "\"access_secret\": \"#{access_token.secret}\""
  exit 0
end

#update_images(images, album_id, headers = {}, workers: 4, filename_as_title: false) ⇒ Object



223
224
225
226
227
228
229
230
# File 'lib/helpers/smugmug_api.rb', line 223

def update_images(images, album_id, headers = {}, workers: 4, filename_as_title: false)
  Parallel.each(images, in_processes: workers, progress: 'Updating images') do |image|
    # replace not working, delete then upload
    http(:delete, image[:uri])
    upload(image[:file], album_id, headers, filename_as_title: filename_as_title)
    puts "Done #{image[:file]}\n"
  end
end

#update_keywords(image, keywords, overwrite = false) ⇒ Object



244
245
246
247
248
249
250
# File 'lib/helpers/smugmug_api.rb', line 244

def update_keywords(image, keywords, overwrite = false)
  return if image.nil?
  keywords = (image[:keywords] + keywords).uniq unless overwrite

  # inspect need outwise it isnt encoded right
  post("#{image[:image_uri]}?_method=PATCH", KeywordArray: keywords.inspect)
end

#upload(image_path, album_id, headers = {}, filename_as_title: false) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/helpers/smugmug_api.rb', line 198

def upload(image_path, album_id, headers = {}, filename_as_title: false)
  image = File.open(image_path)

  headers.merge!('Content-Type' => MimeMagic.by_path(image_path).type,
                 'X-Smug-AlbumUri' => "/api/v2/album/#{album_id}",
                 'X-Smug-ResponseType' => 'JSON',
                 'X-Smug-Version' => 'v2',
                 'charset' => 'UTF-8',
                 'Accept' => 'JSON',
                 'X-Smug-FileName' => File.basename(image_path),
                 'Content-MD5' => Digest::MD5.file(image_path).hexdigest)

  headers['X-Smug-Title'] = File.basename(image_path, '.*') if filename_as_title

  resp = @uploader.post('/', image, headers)
  resp.body
end

#upload_images(images, album_id, headers = {}, workers: 4, filename_as_title: false) ⇒ Object



216
217
218
219
220
221
# File 'lib/helpers/smugmug_api.rb', line 216

def upload_images(images, album_id, headers = {}, workers: 4, filename_as_title: false)
  Parallel.each(images, in_processes: workers, progress: 'Uploading images') do |image|
    upload(image, album_id, headers, filename_as_title: filename_as_title)
    puts "Done #{image}\n"
  end
end

#userObject



70
71
72
# File 'lib/helpers/smugmug_api.rb', line 70

def user
  get('/api/v2!authuser')['User']
end