photo-helper 0.1.6 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile.lock +10 -3
- data/bin/console +6 -1
- data/bin/smugmug +15 -0
- data/lib/helpers/file_helper.rb +11 -11
- data/lib/helpers/image_helper.rb +11 -0
- data/lib/helpers/secrets.rb +83 -0
- data/lib/helpers/smugmug_api.rb +268 -0
- data/lib/photo-helper/delete.rb +12 -13
- data/lib/photo-helper/generate.rb +10 -13
- data/lib/photo-helper/smugmug.rb +48 -0
- data/lib/photo-helper/version.rb +1 -1
- data/lib/photo_helper.rb +2 -0
- data/photo-helper.gemspec +4 -0
- metadata +63 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74fe4a8df21965e6b330ce5f80b52602a26b447a
|
4
|
+
data.tar.gz: f77f8d97e6e3019e948de0edb00bd61f58b8d92c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3aec807f26f222bf5764fc2213c5efa75f88168fe24fa67c59d4bd0bd6cc8663580bb3bb0d8ff1deb08842c3a604e3bac02c77c2943426682dcf758a72d0765c
|
7
|
+
data.tar.gz: 0d8a2ec77bc0b2e5825cafa386bcc0a7b9ded76a538f3d21ef1065585270174001474cdf1bd6a918f9cf292ad3a33e4fe2cd574376c6791e8563a8367c2a12f5
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
photo-helper (0.
|
4
|
+
photo-helper (0.2.0)
|
5
|
+
ejson
|
6
|
+
mimemagic
|
5
7
|
mini_magick
|
8
|
+
oauth
|
9
|
+
parallel
|
6
10
|
thor
|
7
11
|
|
8
12
|
GEM
|
@@ -13,7 +17,9 @@ GEM
|
|
13
17
|
builder (3.2.3)
|
14
18
|
byebug (9.0.6)
|
15
19
|
coderay (1.1.1)
|
20
|
+
ejson (1.1.0)
|
16
21
|
method_source (0.8.2)
|
22
|
+
mimemagic (0.3.2)
|
17
23
|
mini_magick (4.8.0)
|
18
24
|
minitest (5.10.3)
|
19
25
|
minitest-reporters (1.1.14)
|
@@ -21,6 +27,7 @@ GEM
|
|
21
27
|
builder
|
22
28
|
minitest (>= 5.0)
|
23
29
|
ruby-progressbar
|
30
|
+
oauth (0.5.3)
|
24
31
|
parallel (1.12.0)
|
25
32
|
parser (2.4.0.0)
|
26
33
|
ast (~> 2.2)
|
@@ -41,7 +48,7 @@ GEM
|
|
41
48
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
42
49
|
ruby-progressbar (1.8.1)
|
43
50
|
slop (3.6.0)
|
44
|
-
thor (0.
|
51
|
+
thor (0.20.0)
|
45
52
|
unicode-display_width (1.3.0)
|
46
53
|
|
47
54
|
PLATFORMS
|
@@ -58,4 +65,4 @@ DEPENDENCIES
|
|
58
65
|
rubocop
|
59
66
|
|
60
67
|
BUNDLED WITH
|
61
|
-
1.
|
68
|
+
1.16.0
|
data/bin/console
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "bundler/setup"
|
4
|
-
|
4
|
+
|
5
|
+
|
6
|
+
lib = File.expand_path('../lib', __FILE__)
|
7
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
8
|
+
|
9
|
+
require 'photo_helper'
|
5
10
|
|
6
11
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
12
|
# with your gem easier. You can also use a different console, if you like.
|
data/bin/smugmug
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'byebug'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
lib = File.expand_path('../lib', __FILE__)
|
8
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
9
|
+
|
10
|
+
require 'photo_helper'
|
11
|
+
|
12
|
+
require 'helpers/smugmug_api'
|
13
|
+
|
14
|
+
s = SmugmugAPI.new
|
15
|
+
puts s.image_list('xDT29x').sort
|
data/lib/helpers/file_helper.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
class FileHelper
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
def self.directory(path)
|
3
|
+
File.dirname(path).split('/').last
|
4
|
+
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def self.ingore_file?(path)
|
7
|
+
IGNORE_FOLDERS.include? directory(path).downcase
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
10
|
+
def self.is_jpeg?(path)
|
11
|
+
extension = File.extname(path)
|
12
|
+
['.jpeg', '.jpg'].include? extension.downcase
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ImageHelper
|
2
|
+
IMAGE_CLASS_REGEX = %r{xmp:Label="(.+)"}
|
3
|
+
|
4
|
+
def self.color_class(image)
|
5
|
+
xmp = File.join(File.dirname(image), File.basename(image, ".*") + ".XMP")
|
6
|
+
return unless File.exists?(xmp)
|
7
|
+
contents = File.read(xmp)
|
8
|
+
matches = contents.match(IMAGE_CLASS_REGEX)
|
9
|
+
matches[1] if matches
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# require "ejson"
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'pathname'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
class Secrets
|
8
|
+
def initialize(config_file = nil, required = [])
|
9
|
+
@secrets = {}
|
10
|
+
unless config_file.nil?
|
11
|
+
load_from_ejson(config_file)
|
12
|
+
load_from_env(@secrets.keys)
|
13
|
+
end
|
14
|
+
unless required.empty?
|
15
|
+
load_from_env(required)
|
16
|
+
check_required(required)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_from_ejson(ejson_path)
|
21
|
+
ejson_path = File.absolute_path(ejson_path) unless Pathname.new(ejson_path).absolute?
|
22
|
+
raise "config file: #{ejson_path} not found" unless File.exist?(ejson_path)
|
23
|
+
|
24
|
+
encrypted_json = JSON.parse(File.read(ejson_path))
|
25
|
+
public_key = encrypted_json['_public_key']
|
26
|
+
private_key_path = "/opt/ejson/keys/#{public_key}"
|
27
|
+
raise "Private key is not listed in #{private_key_path}." unless File.exist?(private_key_path)
|
28
|
+
|
29
|
+
output, status = Open3.capture2e("ejson", "decrypt", ejson_path.to_s)
|
30
|
+
raise "ejson: #{output}" unless status.success?
|
31
|
+
|
32
|
+
secrets = JSON.parse(output)
|
33
|
+
secrets = hash_symblize_keys(secrets)
|
34
|
+
|
35
|
+
@secrets.merge!(secrets)
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_from_env(keys)
|
39
|
+
secrets = {}
|
40
|
+
keys.each do |key|
|
41
|
+
key = key.to_s
|
42
|
+
next if key.start_with?("_")
|
43
|
+
value = ENV[key.upcase]
|
44
|
+
secrets[key] = value unless value.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
secrets = hash_symblize_keys(secrets)
|
48
|
+
@secrets.merge!(secrets)
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_required(required = [])
|
52
|
+
required.each { |key| raise "required secrets not set: #{key}" if @secrets[key].nil? }
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(key, *args)
|
56
|
+
value = @secrets[key]
|
57
|
+
return value unless value.nil?
|
58
|
+
puts "no secret for key: #{key}"
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
def respond_to_missing?(*args)
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
def hash_symblize_keys(hash)
|
67
|
+
hash.keys.each do |key|
|
68
|
+
hash[(begin
|
69
|
+
key.to_sym
|
70
|
+
rescue
|
71
|
+
key
|
72
|
+
end) || key] = hash.delete(key)
|
73
|
+
end
|
74
|
+
hash
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# a = Secrets.new("config/secrets.ejson", [:hello])
|
79
|
+
|
80
|
+
# a.check_required([:cloud_at_cost_email, :cloud_at_cost_api_key])
|
81
|
+
|
82
|
+
# puts a.cloud_at_cost_email
|
83
|
+
# puts a.hello
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'helpers/secrets'
|
2
|
+
require 'oauth'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'mimemagic'
|
6
|
+
require 'parallel'
|
7
|
+
|
8
|
+
class SmugmugAPI
|
9
|
+
attr_accessor :http, :uploader
|
10
|
+
OAUTH_ORIGIN = 'https://secure.smugmug.com'.freeze
|
11
|
+
REQUEST_TOKEN_URL = '/services/oauth/1.0a/getRequestToken'.freeze
|
12
|
+
ACCESS_TOKEN_URL = '/services/oauth/1.0a/getAccessToken'.freeze
|
13
|
+
AUTHORIZE_URL = '/services/oauth/1.0a/authorize'.freeze
|
14
|
+
API_ENDPOINT = 'https://api.smugmug.com'.freeze
|
15
|
+
UPLOAD_ENDPOINT = 'http://upload.smugmug.com/'.freeze
|
16
|
+
|
17
|
+
def initialize(ejson_file = '~/.photo_helper.ejson')
|
18
|
+
ejson_file = File.expand_path(ejson_file)
|
19
|
+
@secrets = Secrets.new(ejson_file, %i[api_key api_secret])
|
20
|
+
get_access_token if !@secrets.access_token || !@secrets.access_secret
|
21
|
+
|
22
|
+
@http = get_access_token
|
23
|
+
@uploader = get_access_token(UPLOAD_ENDPOINT)
|
24
|
+
user_resp = user
|
25
|
+
@user = user_resp['NickName']
|
26
|
+
@root_node = File.basename(user_resp['Uris']['Node']['Uri'])
|
27
|
+
end
|
28
|
+
|
29
|
+
def albums
|
30
|
+
albums_list = []
|
31
|
+
start = 1
|
32
|
+
count = 100
|
33
|
+
loop do
|
34
|
+
resp = get("/api/v2/user/#{@user}!albums", start: start, count: count)
|
35
|
+
|
36
|
+
resp['Album'].each do |album|
|
37
|
+
albums_list.push(album_parser(album))
|
38
|
+
end
|
39
|
+
break if (start + count) > resp['Pages']['Total'].to_i
|
40
|
+
start += count
|
41
|
+
end
|
42
|
+
albums_list
|
43
|
+
end
|
44
|
+
|
45
|
+
def albums_long(path = '', node_id = @root_node)
|
46
|
+
album_list = []
|
47
|
+
node_children(node_id)['Node'].each do |node|
|
48
|
+
node_path = path.empty? ? node['Name'] : File.join(path, node['Name'])
|
49
|
+
case node['Type']
|
50
|
+
when 'Folder'
|
51
|
+
album_list.concat(albums_long(node_path, node['NodeID']))
|
52
|
+
when 'Album'
|
53
|
+
album_list.push(path: node_path,
|
54
|
+
name: node['Name'],
|
55
|
+
web_uri: node['WebUri'],
|
56
|
+
node_uri: node['Uri'],
|
57
|
+
id: File.basename(node['Uris']['Album']['Uri']))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
album_list
|
61
|
+
end
|
62
|
+
|
63
|
+
def node_children(id)
|
64
|
+
get("/api/v2/node/#{id}!children")
|
65
|
+
end
|
66
|
+
|
67
|
+
def user
|
68
|
+
get('/api/v2!authuser')['User']
|
69
|
+
end
|
70
|
+
|
71
|
+
def images; end
|
72
|
+
|
73
|
+
def folders
|
74
|
+
folder_list = []
|
75
|
+
resp = get('/api/v2/folder/user/bcaldwell!folderlist')
|
76
|
+
resp['FolderList'].each do |folder|
|
77
|
+
folder_list.push(folder_parser(folder))
|
78
|
+
end
|
79
|
+
folder_list
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_or_create_album(path, album_url: nil)
|
83
|
+
folder_path = File.dirname(path).split('/').map(&:capitalize).join('/')
|
84
|
+
album_name = File.basename(path).split(' ').map(&:capitalize).join(' ')
|
85
|
+
puts album_name
|
86
|
+
album = nil
|
87
|
+
|
88
|
+
folder = get_or_create_folder(folder_path)
|
89
|
+
resp = get(folder[:albums_url])
|
90
|
+
albums = get(folder[:albums_url])['Album'] if resp.key? 'Album'
|
91
|
+
albums ||= []
|
92
|
+
albums.each do |album_raw|
|
93
|
+
next unless album_raw['Name'] == album_name
|
94
|
+
album = album_parser(album_raw)
|
95
|
+
end
|
96
|
+
|
97
|
+
if album.nil?
|
98
|
+
url = "/api/v2/folder/user/#{@user}"
|
99
|
+
url += "/#{folder_path}" unless folder_path.empty?
|
100
|
+
url += '!albums'
|
101
|
+
album_url = album_name if album_url.nil?
|
102
|
+
resp = post(url, Name: album_name,
|
103
|
+
UrlName: album_url.tr(' ', '-').capitalize,
|
104
|
+
Privacy: 'Unlisted',
|
105
|
+
SmugSearchable: "No",
|
106
|
+
SortMethod: "Date Taken",
|
107
|
+
LargestSize: "X4Large",
|
108
|
+
SortDirection: "Ascending",
|
109
|
+
WorldSearchable: false,
|
110
|
+
EXIF: false,
|
111
|
+
Printable: false,
|
112
|
+
Filenames: true)
|
113
|
+
album_raw = resp['Album']
|
114
|
+
album = album_parser(album_raw)
|
115
|
+
end
|
116
|
+
album
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_or_create_folder(path)
|
120
|
+
parts = path.split('/')
|
121
|
+
current_path = ''
|
122
|
+
folder = nil
|
123
|
+
|
124
|
+
parts.each do |part|
|
125
|
+
part = part.capitalize
|
126
|
+
new_path = current_path.empty? ? part : File.join(current_path, part)
|
127
|
+
resp = http_raw(:get, "/api/v2/folder/user/#{@user}/#{new_path}")
|
128
|
+
if resp.is_a? Net::HTTPSuccess
|
129
|
+
folder_raw = JSON.parse(resp.body)['Response']['Folder']
|
130
|
+
folder = folder_parser(folder_raw)
|
131
|
+
else
|
132
|
+
url = "/api/v2/folder/user/#{@user}"
|
133
|
+
url += "/#{current_path}" unless current_path.empty?
|
134
|
+
url += '!folders'
|
135
|
+
resp = post(url, Name: part.capitalize,
|
136
|
+
UrlName: part.tr(' ', '-').capitalize,
|
137
|
+
Privacy: 'Unlisted')
|
138
|
+
folder = folder_parser(resp['Folder'])
|
139
|
+
end
|
140
|
+
current_path = new_path
|
141
|
+
end
|
142
|
+
|
143
|
+
folder
|
144
|
+
end
|
145
|
+
|
146
|
+
def images(album_id)
|
147
|
+
images = []
|
148
|
+
start = 1
|
149
|
+
count = 100
|
150
|
+
loop do
|
151
|
+
images_raw = get("/api/v2/album/#{album_id}!images", start: start, count: count)
|
152
|
+
return [] unless images_raw.key? 'AlbumImage'
|
153
|
+
images_raw['AlbumImage'].each do |image|
|
154
|
+
images.push(imager_parser(image))
|
155
|
+
end
|
156
|
+
break if (start + count) > images_raw['Pages']['Total'].to_i
|
157
|
+
start += count
|
158
|
+
end
|
159
|
+
images
|
160
|
+
end
|
161
|
+
|
162
|
+
def image_list(album_id)
|
163
|
+
@images = images(album_id)
|
164
|
+
@images.map { |i| i[:filename] }
|
165
|
+
end
|
166
|
+
|
167
|
+
def http(method, url, headers = {}, _body = nil)
|
168
|
+
response = http_raw(method, url, headers, _body)
|
169
|
+
raise 'Request failed' unless response.is_a? Net::HTTPSuccess
|
170
|
+
JSON.parse(response.body)['Response']
|
171
|
+
end
|
172
|
+
|
173
|
+
def get(url, params = nil, headers = {})
|
174
|
+
url.tr!(' ', '-')
|
175
|
+
uri = URI.parse(url)
|
176
|
+
uri.query = URI.encode_www_form(params) if params
|
177
|
+
http(:get, uri.to_s, headers)
|
178
|
+
end
|
179
|
+
|
180
|
+
def post(url, body = {}, headers = {})
|
181
|
+
url.tr!(' ', '-')
|
182
|
+
headers['Accept'] = 'application/json'
|
183
|
+
response = @http.post(url, body, headers)
|
184
|
+
raise "Request failed\n#{response.body}" unless response.is_a? Net::HTTPSuccess
|
185
|
+
JSON.parse(response.body)['Response']
|
186
|
+
end
|
187
|
+
|
188
|
+
def upload(image_path, album_id, headers = {})
|
189
|
+
image = File.open(image_path)
|
190
|
+
|
191
|
+
headers.merge!('Content-Type' => MimeMagic.by_path(image_path).type,
|
192
|
+
'X-Smug-AlbumUri' => "/api/v2/album/#{album_id}",
|
193
|
+
'X-Smug-ResponseType' => 'JSON',
|
194
|
+
'X-Smug-Version' => 'v2',
|
195
|
+
'charset' => 'UTF-8',
|
196
|
+
'Accept' => 'JSON',
|
197
|
+
'X-Smug-FileName' => File.basename(image_path),
|
198
|
+
'Content-MD5' => Digest::MD5.file(image_path).hexdigest)
|
199
|
+
|
200
|
+
resp = @uploader.post('/', image, headers)
|
201
|
+
resp.body
|
202
|
+
end
|
203
|
+
|
204
|
+
def upload_images(images, album_id, headers = {}, workers: 4)
|
205
|
+
counter = 0
|
206
|
+
Parallel.each(images, in_processes: workers) do |image|
|
207
|
+
upload(image, album_id, headers)
|
208
|
+
counter += 1
|
209
|
+
puts "#{counter}/#{images.count / workers} Done #{image}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def request_access_token
|
216
|
+
raise 'Not Implemented'
|
217
|
+
end
|
218
|
+
|
219
|
+
def http_raw(method, url, headers = {}, _body = nil)
|
220
|
+
url.tr!(' ', '-')
|
221
|
+
headers['Accept'] = 'application/json'
|
222
|
+
|
223
|
+
@http.request(method, url, headers)
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_access_token(endpoint = API_ENDPOINT)
|
227
|
+
@consumer = OAuth::Consumer.new(
|
228
|
+
@secrets.api_key,
|
229
|
+
@secrets.api_secret,
|
230
|
+
site: endpoint
|
231
|
+
)
|
232
|
+
# # Create the access_token for all traffic
|
233
|
+
OAuth::AccessToken.new(@consumer, @secrets.access_token, @secrets.access_secret)
|
234
|
+
end
|
235
|
+
|
236
|
+
def folder_parser(folder)
|
237
|
+
{
|
238
|
+
name: folder['Name'],
|
239
|
+
url_name: folder['UrlName'],
|
240
|
+
web_uri: folder['UrlPath'],
|
241
|
+
uri: folder['Uri'],
|
242
|
+
albums_url: folder['Uris']['FolderAlbums']['Uri'],
|
243
|
+
type: 'folder'
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def album_parser(album)
|
248
|
+
{ name: album['Name'],
|
249
|
+
id: album['AlbumKey'],
|
250
|
+
web_uri: album['WebUri'],
|
251
|
+
images_uri: album['Uris']['AlbumImages']['Uri'],
|
252
|
+
type: 'album' }
|
253
|
+
end
|
254
|
+
|
255
|
+
def imager_parser(image)
|
256
|
+
{
|
257
|
+
title: image['Title'],
|
258
|
+
filename: image['FileName'],
|
259
|
+
caption: image['Caption'],
|
260
|
+
keywords: image['KeywordArray'],
|
261
|
+
id: image['ImageKey'],
|
262
|
+
md5: image['ArchivedMD5'],
|
263
|
+
uri: image['Uri'],
|
264
|
+
web_uri: image['WebUri'],
|
265
|
+
type: 'image'
|
266
|
+
}
|
267
|
+
end
|
268
|
+
end
|
data/lib/photo-helper/delete.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
require
|
1
|
+
require 'helpers/trash'
|
2
2
|
|
3
3
|
module PhotoHelper
|
4
4
|
class Delete < Thor
|
5
5
|
include Thor::Actions
|
6
6
|
|
7
|
-
map
|
8
|
-
map
|
9
|
-
map
|
10
|
-
map
|
7
|
+
map 'j' => 'jpeg'
|
8
|
+
map 'jpg' => 'jpeg'
|
9
|
+
map 'jpgs' => 'jpeg'
|
10
|
+
map 'jpegs' => 'jpeg'
|
11
11
|
|
12
|
-
desc
|
13
|
-
method_option :folder, aliases:
|
14
|
-
method_option :hard, aliases:
|
15
|
-
method_option :recursive, aliases:
|
12
|
+
desc 'jpeg', 'delete jpegs that have an raw with same name'
|
13
|
+
method_option :folder, aliases: '-f', type: :string, default: '.'
|
14
|
+
method_option :hard, aliases: '-h', type: :boolean, default: false
|
15
|
+
method_option :recursive, aliases: '-r', type: :boolean, default: false
|
16
16
|
def jpeg(folder = nil)
|
17
17
|
folder ||= options[:folder]
|
18
18
|
puts folder
|
19
19
|
|
20
20
|
search_path = File.expand_path(folder)
|
21
|
-
jpeg_path = File.join(search_path,
|
21
|
+
jpeg_path = File.join(search_path, 'jpegs')
|
22
22
|
|
23
23
|
files =
|
24
24
|
if options[:recursive]
|
@@ -40,14 +40,13 @@ module PhotoHelper
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
return unless File.exist?(jpeg_path) && yes?(
|
44
|
-
say
|
43
|
+
return unless File.exist?(jpeg_path) && yes?('Delete jpeg folder?')
|
44
|
+
say 'Deleting jpeg folder', :red
|
45
45
|
if options[:hard]
|
46
46
|
File.delete(jpeg_path)
|
47
47
|
else
|
48
48
|
File.trash(jpeg_path)
|
49
49
|
end
|
50
|
-
|
51
50
|
end
|
52
51
|
end
|
53
52
|
end
|
@@ -4,35 +4,32 @@ module PhotoHelper
|
|
4
4
|
class Generate < Thor
|
5
5
|
include Thor::Actions
|
6
6
|
|
7
|
-
map
|
8
|
-
map
|
9
|
-
map
|
10
|
-
map
|
7
|
+
map 'j' => 'jpeg'
|
8
|
+
map 'jpg' => 'jpeg'
|
9
|
+
map 'jpgs' => 'jpeg'
|
10
|
+
map 'jpegs' => 'jpeg'
|
11
11
|
|
12
|
-
desc
|
13
|
-
method_option :folder, aliases:
|
12
|
+
desc 'jpeg', 'generate jpeg from raw files'
|
13
|
+
method_option :folder, aliases: '-f', type: :string, default: '.'
|
14
14
|
method_option :dpi, type: :numeric, default: 350
|
15
15
|
def jpeg(folder = nil)
|
16
16
|
folder ||= options[:folder]
|
17
17
|
puts folder
|
18
18
|
|
19
19
|
search_path = File.expand_path(folder)
|
20
|
-
jpeg_path = File.join(search_path,
|
20
|
+
jpeg_path = File.join(search_path, 'jpegs')
|
21
21
|
|
22
|
-
Dir.mkdir(jpeg_path) unless File.
|
22
|
+
Dir.mkdir(jpeg_path) unless File.exist?(jpeg_path)
|
23
23
|
|
24
24
|
files = Dir["#{search_path}/*.#{RAW_EXTENSION}"]
|
25
25
|
|
26
26
|
files.each do |file|
|
27
|
-
jpeg_file_name = File.basename(file.to_s, RAW_EXTENSION) +
|
28
|
-
next if File.
|
27
|
+
jpeg_file_name = File.basename(file.to_s, RAW_EXTENSION) + JPEG_EXTENSION
|
28
|
+
next if File.exist? File.join(search_path, jpeg_file_name)
|
29
29
|
puts file
|
30
30
|
|
31
31
|
`sips -s format jpeg #{file} -s dpiHeight #{options[:dpi]} -s dpiWidth #{options[:dpi]} --out "./jpegs/#{jpeg_file_name}.JPG"`
|
32
32
|
end
|
33
|
-
|
34
|
-
|
35
33
|
end
|
36
|
-
|
37
34
|
end
|
38
35
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'helpers/smugmug_api'
|
2
|
+
require 'date'
|
3
|
+
require 'helpers/image_helper'
|
4
|
+
|
5
|
+
module PhotoHelper
|
6
|
+
class Smugmug < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
PATH_REGEX = %r{^.+Pictures\/.+\/(\d{4})\/(\d{2})_.+\/[^_]+_([^\/]+)}
|
9
|
+
|
10
|
+
map 's' => 'sync'
|
11
|
+
|
12
|
+
desc 'sync', "sync folder's non-raw images with smugmug"
|
13
|
+
method_option :folder, aliases: '-f', type: :string, default: '.'
|
14
|
+
method_option :recursive, aliases: '-r', type: :boolean, default: false
|
15
|
+
method_option :dry_run, aliases: '-d', type: :boolean, default: false
|
16
|
+
def sync(folder = nil, album_name = nil)
|
17
|
+
search_path = File.expand_path(folder)
|
18
|
+
puts search_path
|
19
|
+
unless album_name
|
20
|
+
if matches = "#{search_path}/".to_s.match(PATH_REGEX)
|
21
|
+
year = matches[1]
|
22
|
+
month = Date::MONTHNAMES[matches[2].to_i].capitalize
|
23
|
+
location = matches[3].split("_").map(&:capitalize).join(' ')
|
24
|
+
|
25
|
+
folder = "#{month} #{year}"
|
26
|
+
album_name_short = "#{location} #{month} #{year}"
|
27
|
+
album_name = File.join(year, month, album_name_short)
|
28
|
+
else
|
29
|
+
puts 'Unable to determine album from path'
|
30
|
+
return
|
31
|
+
end
|
32
|
+
end
|
33
|
+
puts "Using album: #{album_name}"
|
34
|
+
|
35
|
+
@smugmug = SmugmugAPI.new
|
36
|
+
album = @smugmug.get_or_create_album(album_name, album_url: location.downcase)
|
37
|
+
puts "#{album[:web_uri]}\n"
|
38
|
+
pictures = Dir["#{search_path}/**/*.JPG"]
|
39
|
+
|
40
|
+
# remove uploaded pictures
|
41
|
+
uploaded = @smugmug.image_list(album[:id])
|
42
|
+
pictures = pictures.reject { |p| (ImageHelper.color_class(p) == "Trash" or uploaded.include? File.basename(p)) }
|
43
|
+
puts "Uploading #{pictures.count} jpegs"
|
44
|
+
|
45
|
+
@smugmug.upload_images(pictures, album[:id], workers: 8)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/photo-helper/version.rb
CHANGED
data/lib/photo_helper.rb
CHANGED
@@ -8,6 +8,7 @@ require 'photo-helper/delete'
|
|
8
8
|
require 'photo-helper/move'
|
9
9
|
require 'photo-helper/instagram'
|
10
10
|
require 'photo-helper/compress'
|
11
|
+
require 'photo-helper/smugmug'
|
11
12
|
|
12
13
|
# todo: move to config file
|
13
14
|
# RAW_EXTENSION = "ORF"
|
@@ -32,5 +33,6 @@ module PhotoHelper
|
|
32
33
|
register PhotoHelper::Instagram, :instagram, "instagram", "Do something else"
|
33
34
|
register PhotoHelper::Move, :move, "move", "Do something else"
|
34
35
|
register PhotoHelper::Compress, :compress, "compress", "Do something else"
|
36
|
+
register PhotoHelper::Smugmug, :smugmug, "smugmug", "Interface with Smugmug"
|
35
37
|
end
|
36
38
|
end
|
data/photo-helper.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: photo-helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Caldwell
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -136,6 +136,62 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: ejson
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: oauth
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: mimemagic
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: parallel
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
139
195
|
description:
|
140
196
|
email:
|
141
197
|
- caldwellbenjamin8@gmail.com
|
@@ -153,15 +209,20 @@ files:
|
|
153
209
|
- bin/console
|
154
210
|
- bin/photo-helper
|
155
211
|
- bin/setup
|
212
|
+
- bin/smugmug
|
156
213
|
- exe/photo-helper
|
157
214
|
- lib/helpers/file_helper.rb
|
215
|
+
- lib/helpers/image_helper.rb
|
158
216
|
- lib/helpers/printer.rb
|
217
|
+
- lib/helpers/secrets.rb
|
218
|
+
- lib/helpers/smugmug_api.rb
|
159
219
|
- lib/helpers/trash.rb
|
160
220
|
- lib/photo-helper/compress.rb
|
161
221
|
- lib/photo-helper/delete.rb
|
162
222
|
- lib/photo-helper/generate.rb
|
163
223
|
- lib/photo-helper/instagram.rb
|
164
224
|
- lib/photo-helper/move.rb
|
225
|
+
- lib/photo-helper/smugmug.rb
|
165
226
|
- lib/photo-helper/version.rb
|
166
227
|
- lib/photo_helper.rb
|
167
228
|
- photo-helper.gemspec
|