smugsync 0.2
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/.gitignore +3 -0
- data/AUTHORS +2 -0
- data/COPYING +340 -0
- data/README.md +35 -0
- data/Rakefile +13 -0
- data/bin/smug +5 -0
- data/lib/smugsync/albums.rb +26 -0
- data/lib/smugsync/command.rb +67 -0
- data/lib/smugsync/config.rb +66 -0
- data/lib/smugsync/console.rb +45 -0
- data/lib/smugsync/download.rb +68 -0
- data/lib/smugsync/fetch.rb +95 -0
- data/lib/smugsync/init.rb +40 -0
- data/lib/smugsync/status.rb +123 -0
- data/lib/smugsync/upload.rb +117 -0
- data/lib/smugsync/utils.rb +22 -0
- data/lib/smugsync.rb +11 -0
- data/smugsync.gemspec +27 -0
- data/smugsync.kdev4 +3 -0
- data/test/local/.gitignore +1 -0
- data/test/mock/command.rb +15 -0
- data/test/mock/oauth/tokens/request_token.rb +11 -0
- data/test/mock/smug_server.rb +196 -0
- data/test/server/.gitignore +1 -0
- data/test/smug_test_case.rb +88 -0
- data/test/status_command_test.rb +55 -0
- metadata +190 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
module Smug
|
3
|
+
|
4
|
+
# TODO: maybe rename
|
5
|
+
class FetchCommand < Command
|
6
|
+
|
7
|
+
def exec
|
8
|
+
optparser = Trollop::Parser.new do
|
9
|
+
banner <<-END
|
10
|
+
Usage: smug fetch [<options>]
|
11
|
+
Show the status of local SmugMug folder.
|
12
|
+
|
13
|
+
Options:
|
14
|
+
END
|
15
|
+
opt :force,
|
16
|
+
"Force full refresh of albums and images list",
|
17
|
+
:short => :f
|
18
|
+
end
|
19
|
+
|
20
|
+
opts = Trollop::with_standard_exception_handling(optparser) do
|
21
|
+
optparser.parse(ARGV)
|
22
|
+
end
|
23
|
+
|
24
|
+
authenticate
|
25
|
+
|
26
|
+
status_message "Refreshing albums cache"
|
27
|
+
refresh_cache(:all_albums, opts) { |album, cache_status| status_message "." }
|
28
|
+
status_message " done\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
# block is executed for each album with two argumens: album and cache_status
|
32
|
+
# cache_status is:
|
33
|
+
# - :refreshed album cache was refreshed
|
34
|
+
# - :fresh album in cache is the same as in server
|
35
|
+
# options can be:
|
36
|
+
# - :force => true list of album's images is always refreshed
|
37
|
+
# this is necessary for refreshing cache after uploads and here's why:
|
38
|
+
# - smugmug stores LastUpdated data for albums with 1 second precision
|
39
|
+
# - upload can be fast enough to create album and add image to the album
|
40
|
+
# in one second
|
41
|
+
def refresh_cache(albums_to_refresh, options = {}, &block)
|
42
|
+
authenticate
|
43
|
+
|
44
|
+
old_cache = if File.exist?(Config::config_file_name("cache"))
|
45
|
+
JSON::parse(Config::config_file("cache", "r").read)
|
46
|
+
else
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
# - to refresh all albums we need to reconstruct the cache from scratch
|
51
|
+
# to make sure that deleted albums are not left in the cache
|
52
|
+
# - to refresh selected albums we start from existing cache
|
53
|
+
# and replace only refreshed albums in that cache
|
54
|
+
albums_on_server = smugmug_albums_get(:Heavy => true)["Albums"]
|
55
|
+
if albums_to_refresh == :all_albums
|
56
|
+
albums_to_refresh = albums_on_server
|
57
|
+
new_cache = []
|
58
|
+
else
|
59
|
+
albums_to_refresh_ids = albums_to_refresh.map { |a| a["id"] }
|
60
|
+
# refetch albums metadata
|
61
|
+
albums_to_refresh = albums_on_server.select { |a| albums_to_refresh_ids.include?(a["id"]) }
|
62
|
+
# leave albums that should not be refreshed in the cache
|
63
|
+
new_cache = old_cache.reject { |a| albums_to_refresh_ids.include?(a["id"]) }
|
64
|
+
end
|
65
|
+
|
66
|
+
albums_to_refresh.each do |album|
|
67
|
+
cached_album = old_cache.find { |a| a["id"] == album["id"] }
|
68
|
+
|
69
|
+
if !options[:force] and cached_album and cached_album["LastUpdated"] == album["LastUpdated"]
|
70
|
+
# album is in cache and is not changed: copy images from old cache
|
71
|
+
# because it can take a long time to get the list of images
|
72
|
+
# from server for large albums
|
73
|
+
album["Images"] = cached_album["Images"]
|
74
|
+
yield(album, :skipped) if block_given?
|
75
|
+
else
|
76
|
+
# album was changed, get the list of images
|
77
|
+
album["Images"] = smugmug_images_get(
|
78
|
+
:AlbumID => album["id"],
|
79
|
+
:AlbumKey => album["Key"],
|
80
|
+
:Heavy => true
|
81
|
+
)["Album"]["Images"]
|
82
|
+
yield(album, :refreshed) if block_given?
|
83
|
+
end
|
84
|
+
|
85
|
+
new_cache << album
|
86
|
+
end
|
87
|
+
|
88
|
+
cache_file = Config::config_file("cache", "w+")
|
89
|
+
cache_file.puts JSON::pretty_generate(new_cache)
|
90
|
+
cache_file.close
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
|
3
|
+
module Smug
|
4
|
+
|
5
|
+
class InitCommand < Command
|
6
|
+
|
7
|
+
def exec
|
8
|
+
# need to request access token from the user
|
9
|
+
request_token = oauth_consumer.get_request_token
|
10
|
+
# TODO: explain what happens better
|
11
|
+
puts <<-EOS
|
12
|
+
Authorize app at:
|
13
|
+
#{request_token.authorize_url}&Access=Full&Permissions=Modify
|
14
|
+
Press Enter when finished
|
15
|
+
EOS
|
16
|
+
gets
|
17
|
+
access_token = nil
|
18
|
+
begin
|
19
|
+
access_token = request_token.get_access_token
|
20
|
+
rescue OAuth::Unauthorized
|
21
|
+
$stderr.puts "Fatal: Could not authorize with SmugMug. Run 'smug init' again."
|
22
|
+
exit(-1)
|
23
|
+
end
|
24
|
+
|
25
|
+
config = {
|
26
|
+
:access_token => {
|
27
|
+
:secret => access_token.secret,
|
28
|
+
:token => access_token.token
|
29
|
+
}
|
30
|
+
}
|
31
|
+
Smug::Config::create_config_dir
|
32
|
+
File.open(Smug::Config::config_file_name(Smug::Config::ACCESS_TOKEN_CONFIG_FILE), "w+") do |f|
|
33
|
+
f.puts JSON.pretty_generate(config)
|
34
|
+
end
|
35
|
+
puts "Initialized SmugMug folder and authorized with SmugMug"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
module Smug
|
3
|
+
|
4
|
+
class StatusCommand < Command
|
5
|
+
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
ALBUM_STATUS_FORMAT = "%20s\t%s/(%s files)"
|
9
|
+
IMAGE_STATUS_FORMAT = "%20s\t%s/%s"
|
10
|
+
|
11
|
+
def exec
|
12
|
+
authenticate
|
13
|
+
|
14
|
+
# TODO: support categories and nested categories
|
15
|
+
# for now comparison supports only flat list of albums
|
16
|
+
status = current_dir_status
|
17
|
+
status.each do |album_status|
|
18
|
+
if album_status[:status] != :not_modified
|
19
|
+
puts sprintf(ALBUM_STATUS_FORMAT, album_status[:status], album_status[:album], album_status[:images].length)
|
20
|
+
else
|
21
|
+
album_status[:images].each do |image_status|
|
22
|
+
puts sprintf(IMAGE_STATUS_FORMAT, image_status[:status], album_status[:album], image_status[:image])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_dir_status
|
29
|
+
current_dir = Config::relative_to_root(Dir.pwd).to_s
|
30
|
+
if current_dir == '.'
|
31
|
+
albums_status
|
32
|
+
else
|
33
|
+
Dir::chdir('..')
|
34
|
+
status = [album_status(
|
35
|
+
:local => current_dir,
|
36
|
+
:remote => albums.find { |a| a["Title"] == current_dir }
|
37
|
+
)]
|
38
|
+
Dir::chdir(current_dir)
|
39
|
+
status
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def albums_status
|
44
|
+
status = []
|
45
|
+
|
46
|
+
local_albums = Pathname::pwd.children(false).find_all { |a| a.directory? and a.basename.to_s != ".smug" }.map { |a| a.basename.to_s }
|
47
|
+
remote_albums = albums.map { |a| a["Title"] }
|
48
|
+
|
49
|
+
status += albums.map do |album|
|
50
|
+
album_status(
|
51
|
+
:remote => album,
|
52
|
+
:local => local_albums.find { |a| a == album["Title"] }
|
53
|
+
)
|
54
|
+
end
|
55
|
+
status += (local_albums - remote_albums).map do |local_album|
|
56
|
+
album_status(:remote => nil, :local => local_album)
|
57
|
+
end
|
58
|
+
|
59
|
+
status
|
60
|
+
end
|
61
|
+
|
62
|
+
def album_status(options = { :local => nil, :remote => nil })
|
63
|
+
local = options[:local]
|
64
|
+
remote = options[:remote]
|
65
|
+
|
66
|
+
if !local and !remote
|
67
|
+
raise "album_status: requires either local or remote album"
|
68
|
+
elsif local and !remote
|
69
|
+
{
|
70
|
+
:album => local,
|
71
|
+
:status => :not_uploaded,
|
72
|
+
:images => local_images(local).map { |img| { :image => img, :status => :not_uploaded} }
|
73
|
+
}
|
74
|
+
elsif !local and remote
|
75
|
+
{
|
76
|
+
:album => remote["Title"],
|
77
|
+
:status => :not_downloaded,
|
78
|
+
:images => remote_images(remote).map { |img| { :image => img, :status => :not_downloaded} }
|
79
|
+
}
|
80
|
+
else
|
81
|
+
# TODO: assure that local and remote titles are the same
|
82
|
+
# TODO: document case insensitivity
|
83
|
+
not_uploaded = local_images(local) - remote_images(remote)
|
84
|
+
not_downloaded = remote_images(remote) - local_images(local)
|
85
|
+
|
86
|
+
{
|
87
|
+
:album => remote["Title"],
|
88
|
+
:status => :not_modified,
|
89
|
+
:images => not_uploaded.map { |img| { :image => img, :status => :not_uploaded} } + not_downloaded.map { |img| { :image => img, :status => :not_downloaded} }
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def albums
|
97
|
+
# TODO: status doesn't work without cache
|
98
|
+
@albums ||= JSON::parse Config::config_file("cache", "r").read
|
99
|
+
end
|
100
|
+
|
101
|
+
def local_images(album_path)
|
102
|
+
ignore_list = []
|
103
|
+
|
104
|
+
ignore_file_name = Pathname.new(album_path).join(".smugignore")
|
105
|
+
if File.exists?(ignore_file_name)
|
106
|
+
ignore_list = File.read(ignore_file_name).split($/)
|
107
|
+
end
|
108
|
+
|
109
|
+
Pathname.new(album_path).children(false).reject do |p|
|
110
|
+
filename = p.basename.to_s
|
111
|
+
filename == '.smugignore' or ignore_list.include? filename
|
112
|
+
end.map do |p|
|
113
|
+
p.basename.to_s.gsub(/^\d\d\d\./, '')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def remote_images(album)
|
118
|
+
album["Images"].map {|p| p["FileName"] }
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'system_timer'
|
2
|
+
require 'md5'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Smug
|
6
|
+
|
7
|
+
class UploadCommand < Command
|
8
|
+
|
9
|
+
# current upload algorithm:
|
10
|
+
# 1. get the list not uploaded stuff starting from current dir and below
|
11
|
+
# 2. chdir to root (for simplicity)
|
12
|
+
# 3. for each album
|
13
|
+
# 3.1. create it if it doesn't exist
|
14
|
+
# 3.2. upload files
|
15
|
+
def exec
|
16
|
+
optparser = Trollop::Parser.new do
|
17
|
+
banner <<-END
|
18
|
+
Usage: smug upload [<options>]
|
19
|
+
Upload files to SmugMug.
|
20
|
+
|
21
|
+
Options:
|
22
|
+
END
|
23
|
+
opt :timeout,
|
24
|
+
"Timeout to upload one file in seconds",
|
25
|
+
:short => :t,
|
26
|
+
:default => 24*3600
|
27
|
+
end
|
28
|
+
|
29
|
+
opts = Trollop::with_standard_exception_handling(optparser) do
|
30
|
+
optparser.parse(ARGV)
|
31
|
+
end
|
32
|
+
|
33
|
+
authenticate
|
34
|
+
|
35
|
+
status = StatusCommand.new.current_dir_status
|
36
|
+
Dir::chdir(Config::root_dir)
|
37
|
+
|
38
|
+
modified_albums = Set.new
|
39
|
+
|
40
|
+
status.each_with_index do |album_status, i|
|
41
|
+
if album_status[:status] == :not_uploaded
|
42
|
+
# create album
|
43
|
+
puts "Creating album #{album_status[:album]}"
|
44
|
+
result = smugmug_albums_create(
|
45
|
+
"Title" => album_status[:album]
|
46
|
+
)
|
47
|
+
raise "Cannot create album" unless result["stat"] == "ok"
|
48
|
+
|
49
|
+
# refresh cache for the newly created album
|
50
|
+
album = smugmug_albums_getInfo(
|
51
|
+
:AlbumID => result["Album"]["id"],
|
52
|
+
:AlbumKey => result["Album"]["Key"]
|
53
|
+
)["Album"]
|
54
|
+
|
55
|
+
FetchCommand.new.refresh_cache([album])
|
56
|
+
@albums = nil # TODO: hack to force reparsing of cache file
|
57
|
+
elsif album_status[:status] == :not_downloaded
|
58
|
+
next
|
59
|
+
end
|
60
|
+
|
61
|
+
album = albums.find { |a| a["Title"] == album_status[:album] }
|
62
|
+
|
63
|
+
files_to_upload = album_status[:images].find_all { |i| i[:status] == :not_uploaded }.map { |i| "#{album["Title"]}/#{i[:image]}" }
|
64
|
+
|
65
|
+
File.open("upload.log", "w+") { |f| f.puts "start" }
|
66
|
+
num_files = files_to_upload.length
|
67
|
+
files_to_upload.each_with_index do |filename, i|
|
68
|
+
modified_albums << album
|
69
|
+
|
70
|
+
begin
|
71
|
+
SystemTimer.timeout_after(opts[:timeout]) do
|
72
|
+
puts "Uploading #{filename} (#{Time.now.to_s}) (#{i}/#{num_files})"
|
73
|
+
|
74
|
+
data = File.open(filename, "rb") { |f| f.read }
|
75
|
+
|
76
|
+
req = {}
|
77
|
+
req['Content-Length'] = File.size(filename).to_s
|
78
|
+
req['Content-MD5'] = MD5.hexdigest(data)
|
79
|
+
req['X-Smug-AlbumID'] = album["id"].to_s
|
80
|
+
req['X-Smug-Version'] = '1.3.0'
|
81
|
+
req['X-Smug-FileName'] = filename
|
82
|
+
req['X-Smug-ResponseType'] = "JSON"
|
83
|
+
|
84
|
+
results = put("http://upload.smugmug.com/#{filename}", data, req)
|
85
|
+
puts " => #{results["stat"]} (#{Time.now.to_s})"
|
86
|
+
end
|
87
|
+
rescue Timeout::Error
|
88
|
+
puts " => timed out"
|
89
|
+
File.open("upload.log", "a") { |f| f.puts filename }
|
90
|
+
|
91
|
+
# TODO: delete image from server
|
92
|
+
rescue Exception => e
|
93
|
+
puts " => error #{e.message}"
|
94
|
+
puts e.backtrace.join("\n")
|
95
|
+
File.open("upload.log", "a") { |f| f.puts filename }
|
96
|
+
|
97
|
+
# TODO: delete image from server
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# refresh cache for modified albums only
|
103
|
+
puts "Refreshing albums cache"
|
104
|
+
FetchCommand.new.refresh_cache(modified_albums, :force => true)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def albums
|
110
|
+
# TODO: upload doesn't work without cache
|
111
|
+
@albums ||= JSON::parse Config::config_file("cache", "r").read
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Smug
|
3
|
+
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
def status_message(message)
|
7
|
+
$stdout.print message
|
8
|
+
$stdout.flush
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
# if method looks like smugmug API call, call get()
|
13
|
+
if method.to_s =~ /^smugmug_/
|
14
|
+
get(method.to_s.gsub('_', '.'), *args, &block)
|
15
|
+
else
|
16
|
+
super(method, *args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/smugsync.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'trollop'
|
3
|
+
require 'smugsync/config'
|
4
|
+
require 'smugsync/utils'
|
5
|
+
require 'smugsync/command'
|
6
|
+
require 'smugsync/albums'
|
7
|
+
require 'smugsync/upload'
|
8
|
+
require 'smugsync/init'
|
9
|
+
require 'smugsync/status'
|
10
|
+
require 'smugsync/fetch'
|
11
|
+
require 'smugsync/download'
|
data/smugsync.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
SPEC = Gem::Specification.new do |s|
|
4
|
+
s.name = "smugsync"
|
5
|
+
s.version = "0.2"
|
6
|
+
s.author = "Alexander Dymo"
|
7
|
+
s.email = "adymo@kdevelop.org"
|
8
|
+
s.homepage = "http://github.com/adymo/smugsync"
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = "."
|
11
|
+
|
12
|
+
s.add_development_dependency('bundler', '>= 1.0.0')
|
13
|
+
s.add_development_dependency('assert_same', '>= 0.1.0')
|
14
|
+
|
15
|
+
s.add_dependency('trollop', '>= 1.16.0')
|
16
|
+
s.add_dependency('json', '>= 1.6.0')
|
17
|
+
s.add_dependency('oauth', '>= 0.4.0')
|
18
|
+
s.add_dependency('system_timer', '>= 1.2.0')
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
|
24
|
+
s.require_path = "lib"
|
25
|
+
s.autorequire = "smugsync"
|
26
|
+
s.has_rdoc = false
|
27
|
+
end
|
data/smugsync.kdev4
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
class Smug::Command
|
3
|
+
|
4
|
+
alias_method :get_without_mock, :get
|
5
|
+
def get(method, params = {})
|
6
|
+
mocked_method = method.gsub(".", "_")
|
7
|
+
server = SmugServer.new
|
8
|
+
if server.respond_to?(mocked_method)
|
9
|
+
JSON.parse(server.send(mocked_method, params))
|
10
|
+
else
|
11
|
+
get_without_mock(method, params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
|
2
|
+
class SmugServer
|
3
|
+
|
4
|
+
attr_reader :server_albums
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@server_albums = get_server_albums_info
|
8
|
+
end
|
9
|
+
|
10
|
+
def smugmug_albums_get(params = { :Heavy => false })
|
11
|
+
result = {
|
12
|
+
"stat" => "ok",
|
13
|
+
"method" => "smugmug.albums.get",
|
14
|
+
"Albums" => []
|
15
|
+
}
|
16
|
+
|
17
|
+
server_albums.each do |album|
|
18
|
+
if params[:Heavy]
|
19
|
+
result["Albums"] << album.delete_if { |key, value| key == 'Images' }
|
20
|
+
else
|
21
|
+
result["Albums"] << album.delete_if do |key, value|
|
22
|
+
not ["id", "Key", "Category", "SubCategory", "Title"].include?(key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
result.to_json
|
28
|
+
end
|
29
|
+
|
30
|
+
def smugmug_images_get(params = { :AlbumID => nil, :AlbumKey => nil, :Heavy => false })
|
31
|
+
result = {
|
32
|
+
"stat" => "ok",
|
33
|
+
"method" => "smugmug.images.get",
|
34
|
+
}
|
35
|
+
|
36
|
+
album = server_albums.find { |album| album["id"] == params[:AlbumID] and album["Key"] == params[:AlbumKey] }
|
37
|
+
return result unless album
|
38
|
+
|
39
|
+
result["Album"] = {
|
40
|
+
"id" => params[:AlbumID],
|
41
|
+
"Key" => params[:AlbumKey],
|
42
|
+
"ImageCount" => 0,
|
43
|
+
"Images" => []
|
44
|
+
}
|
45
|
+
|
46
|
+
album["Images"].each do |image|
|
47
|
+
result["Album"]["ImageCount"] += 1
|
48
|
+
if params[:Heavy]
|
49
|
+
result["Album"]["Images"] << image
|
50
|
+
else
|
51
|
+
result["Album"]["Images"] << image.delete_if do |key, value|
|
52
|
+
not ["id", "Key"].include?(key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
result.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def get_server_albums_info
|
63
|
+
albums = []
|
64
|
+
|
65
|
+
server_dir = Dir.new(SmugTestCase.server_dir)
|
66
|
+
album_id = 0
|
67
|
+
image_id = 0
|
68
|
+
server_dir.each do |album_name|
|
69
|
+
next if ['.', '..', '.gitignore'].include?(album_name)
|
70
|
+
|
71
|
+
album_full_path = File.join(SmugTestCase.server_dir, album_name)
|
72
|
+
raise "Server directory contains file #{album_name} outside of album" unless File.directory?(album_full_path)
|
73
|
+
|
74
|
+
# nice name in smugmug is the name users see in url
|
75
|
+
# currently we do nothing with that but let's keep it
|
76
|
+
# different from album name
|
77
|
+
album_nice_name = album_name.gsub(" ", "_")
|
78
|
+
album_id += 1
|
79
|
+
album_key = "key#{album_id.to_s(27).tr("0-9a-q", "A-Z")}"
|
80
|
+
|
81
|
+
album = {
|
82
|
+
# default settings for smugmug albums that we do not care about
|
83
|
+
"WordSearchable" => true,
|
84
|
+
"Theme" => {
|
85
|
+
"Name" => "default",
|
86
|
+
"id" => 1
|
87
|
+
},
|
88
|
+
"SortMethod" => "Position",
|
89
|
+
"ImageCount" => 1,
|
90
|
+
"Highlight" => {
|
91
|
+
"id" => 1725930094,
|
92
|
+
"Key" => "b9fk6m6",
|
93
|
+
"Type" => "Random"
|
94
|
+
},
|
95
|
+
"External" => true,
|
96
|
+
"Comments" => true,
|
97
|
+
"Clean" => false,
|
98
|
+
"X3Larges" => true,
|
99
|
+
"Keywords" => "",
|
100
|
+
"UnsharpAmount" => 0.2,
|
101
|
+
"Public" => true,
|
102
|
+
"Position" => 1,
|
103
|
+
"UnsharpRadius" => 1,
|
104
|
+
"SortDirection" => false,
|
105
|
+
"Filenames" => true,
|
106
|
+
"ColorCorrection" => 2,
|
107
|
+
"SquareThumbs" => true,
|
108
|
+
"Originals" => true,
|
109
|
+
"SmugSearchable" => true,
|
110
|
+
"Header" => false,
|
111
|
+
"X2Larges" => true,
|
112
|
+
"FamilyEdit" => false,
|
113
|
+
"EXIF" => true,
|
114
|
+
"UnsharpThreshold" => 0.05,
|
115
|
+
"Template" => {
|
116
|
+
"id" => 0
|
117
|
+
},
|
118
|
+
"HideOwner" => false,
|
119
|
+
"CanRank" => true,
|
120
|
+
"UnsharpSigma" => 1,
|
121
|
+
"Share" => true,
|
122
|
+
"Protected" => false,
|
123
|
+
"Printable" => true,
|
124
|
+
"UploadKey" => "",
|
125
|
+
"Geography" => true,
|
126
|
+
"FriendEdit" => false,
|
127
|
+
"Category" => {
|
128
|
+
"Name" => "TestCategory",
|
129
|
+
"id" => 1
|
130
|
+
},
|
131
|
+
"Passworded" => false,
|
132
|
+
"PasswordHint" => "",
|
133
|
+
"Password" => "",
|
134
|
+
"Description" => "",
|
135
|
+
|
136
|
+
# albums settings that we currently care about
|
137
|
+
"id" => album_id,
|
138
|
+
"Key" => album_key,
|
139
|
+
"NiceName" => album_nice_name,
|
140
|
+
"URL" => "http://user.smugmug.com/TestCategory/#{album_nice_name}/#{album_id}_#{album_key}",
|
141
|
+
"Title" => album_name,
|
142
|
+
"LastUpdated" => "2012-02-26 02:40:44",
|
143
|
+
"Images" => []
|
144
|
+
}
|
145
|
+
|
146
|
+
album_dir = Dir.new(album_full_path)
|
147
|
+
album_dir.each do |image_name|
|
148
|
+
next if ['.', '..'].include?(image_name)
|
149
|
+
|
150
|
+
image_id += 1
|
151
|
+
image_key = "key#{image_id.to_s(27).tr("0-9a-q", "A-Z")}"
|
152
|
+
|
153
|
+
album["Images"] << {
|
154
|
+
# image urls, for now not used/tested
|
155
|
+
"URL" => "http://url",
|
156
|
+
"OriginalURL" => "http://original_url",
|
157
|
+
"X3LargeURL" => "http://xl3_url",
|
158
|
+
"X2LargeURL" => "http://xl2_url",
|
159
|
+
"XLargeURL" => "http://xl_url",
|
160
|
+
"LargeURL" => "http://large_url",
|
161
|
+
"MediumURL" => "http://medium_url",
|
162
|
+
"SmallURL" => "http://small_url",
|
163
|
+
"TinyURL" => "http://tiny_url",
|
164
|
+
"ThumbURL" => "http://thumb_url",
|
165
|
+
"LightboxURL" => "http://lightbox_url",
|
166
|
+
|
167
|
+
# settings that we don't care about
|
168
|
+
"Status" => "Open",
|
169
|
+
"Keywords" => "",
|
170
|
+
"Hidden" => false,
|
171
|
+
"Serial" => 1,
|
172
|
+
"Position" => 1,
|
173
|
+
"Size" => 91144,
|
174
|
+
"Width" => 638,
|
175
|
+
"Height" => 638,
|
176
|
+
"Format" => "JPG",
|
177
|
+
"Date" => "2012-02-26 02:40:44",
|
178
|
+
"Watermark" => false,
|
179
|
+
"MD5Sum" => "d63af258e5ec78db084af9164a8f3581",
|
180
|
+
"Caption" => "",
|
181
|
+
"LastUpdated" => "2012-02-26 02:42:01",
|
182
|
+
|
183
|
+
# albums settings that we currently care about
|
184
|
+
"FileName" => image_name,
|
185
|
+
"id" => image_id,
|
186
|
+
"Key" => image_key,
|
187
|
+
"Type" => "Album",
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
albums << album
|
192
|
+
end
|
193
|
+
albums
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
*
|