iphoto_backup 1.1.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -2
- data/README.md +11 -3
- data/iphoto_backup.gemspec +1 -0
- data/lib/iphoto_backup/cli.rb +60 -37
- data/lib/iphoto_backup/version.rb +1 -1
- data/spec/fixtures/AlbumData.xml.erb +18 -3
- data/spec/lib/iphoto_backup/cli_spec.rb +14 -2
- data/spec/spec_helper.rb +3 -0
- metadata +15 -3
- data/spec/support/meet_expectations_matcher.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90efaf9d3f334df3de1db5ea6bb0c8985cd5ec36
|
4
|
+
data.tar.gz: 27004da2e2ba4e5caeb4fd0513baea2be8482833
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c94711c88fe9750fb0827d9f958365d9f5f8ae8202634f460ab09841d5035b40038bb9fa7607b1dc9baf2e4519e9dd4b48859a1f905480a47dabb37d980547f
|
7
|
+
data.tar.gz: 5f788f673ad402d14c08ebf49f6be4c6f8776b926ea466047808ae0eda34e1feb0f94393e9fc147d0c617bc45a3604b051808460ba50f5e2a3facdb730f22535
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
[](https://travis-ci.org/wireframe/iphoto_backup)
|
2
|
+
[](https://coveralls.io/r/wireframe/iphoto_backup)
|
3
|
+
[](https://codeclimate.com/github/wireframe/iphoto_backup)
|
2
4
|
|
3
5
|
# iphoto_backup
|
4
6
|
|
5
7
|
> Every photo deserves to live in a folder on the filesystem and not
|
6
8
|
> to be locked up in some cryptic and proprietary iPhoto metadata XML file.
|
7
9
|
|
8
|
-
iphoto_backup is a tool
|
9
|
-
|
10
|
-
[Originally implemented as a Python script](https://github.com/wireframe/dotfiles/blob/628b982d9fc4e7b4cc9e6ca806cae81b541f9bbd/home/bin/iphoto_export.py)
|
10
|
+
iphoto_backup is a tool that simplifies backups and archiving of your iPhoto images.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -43,6 +43,12 @@ Processing Roll: Summer Pics...
|
|
43
43
|
Winter Pics does not match the filter: /Summer/
|
44
44
|
```
|
45
45
|
|
46
|
+
#### --albums
|
47
|
+
|
48
|
+
*aliased to -a*
|
49
|
+
|
50
|
+
Export iPhoto albums instead of events.
|
51
|
+
|
46
52
|
#### --output [/path/to/directory]
|
47
53
|
|
48
54
|
*aliased to -o*
|
@@ -57,3 +63,5 @@ Customize the path for archiving photos.
|
|
57
63
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
64
|
4. Push to the branch (`git push origin my-new-feature`)
|
59
65
|
5. Create new Pull Request
|
66
|
+
|
67
|
+
[Originally implemented as a Python script](https://github.com/wireframe/dotfiles/blob/628b982d9fc4e7b4cc9e6ca806cae81b541f9bbd/home/bin/iphoto_export.py)
|
data/iphoto_backup.gemspec
CHANGED
data/lib/iphoto_backup/cli.rb
CHANGED
@@ -13,24 +13,13 @@ module IphotoBackup
|
|
13
13
|
option :output, desc: 'directory to export albums to', aliases: '-o', default: DEFAULT_OUTPUT_DIRECTORY
|
14
14
|
option :config, desc: 'iPhoto AlbumData.xml file to process', aliases: '-c', default: IPHOTO_ALBUM_PATH
|
15
15
|
option :'include-date-prefix', desc: 'automatically include ISO8601 date prefix to exported events', aliases: '-d', default: false, type: :boolean
|
16
|
-
option :albums, desc: '
|
16
|
+
option :albums, desc: 'use albums for the export instead of events', aliases: '-a', default: false, type: :boolean
|
17
17
|
def export
|
18
|
-
|
19
|
-
say "\n\nProcessing
|
18
|
+
each_photoset do |folder_name, album_info|
|
19
|
+
say "\n\nProcessing photos: #{folder_name}..."
|
20
20
|
|
21
21
|
each_image(album_info) do |image_info|
|
22
|
-
|
23
|
-
|
24
|
-
target_path = File.join(File.expand_path(options[:output]), folder_name, File.basename(source_path))
|
25
|
-
target_dir = File.dirname target_path
|
26
|
-
FileUtils.mkdir_p(target_dir) unless Dir.exists?(target_dir)
|
27
|
-
|
28
|
-
if FileUtils.uptodate?(source_path, [ target_path ])
|
29
|
-
say " copying #{source_path} to #{target_path}"
|
30
|
-
FileUtils.copy source_path, target_path, preserve: true
|
31
|
-
else
|
32
|
-
print '.'
|
33
|
-
end
|
22
|
+
export_image(folder_name, image_info)
|
34
23
|
end
|
35
24
|
end
|
36
25
|
end
|
@@ -38,37 +27,71 @@ module IphotoBackup
|
|
38
27
|
|
39
28
|
private
|
40
29
|
|
30
|
+
def export_image(folder_name, image_info)
|
31
|
+
source_path = value_for_dictionary_key('ImagePath', image_info).content
|
32
|
+
|
33
|
+
target_path = File.join(File.expand_path(options[:output]), folder_name, File.basename(source_path))
|
34
|
+
target_dir = File.dirname target_path
|
35
|
+
FileUtils.mkdir_p(target_dir) unless Dir.exists?(target_dir)
|
36
|
+
|
37
|
+
if FileUtils.uptodate?(source_path, [target_path])
|
38
|
+
say " copying #{source_path} to #{target_path}"
|
39
|
+
FileUtils.copy source_path, target_path, preserve: true
|
40
|
+
else
|
41
|
+
print '.'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def each_photoset(&block)
|
46
|
+
if options[:albums]
|
47
|
+
each_album(&block)
|
48
|
+
else
|
49
|
+
each_event(&block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def each_event(&block)
|
54
|
+
events = value_for_dictionary_key('List of Rolls').children.select {|n| n.name == 'dict' }
|
55
|
+
events.each do |album_info|
|
56
|
+
event_name = value_for_dictionary_key('RollName', album_info).content
|
57
|
+
process_folder(event_name, album_info, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
41
61
|
def each_album(&block)
|
42
|
-
albums = value_for_dictionary_key(
|
62
|
+
albums = value_for_dictionary_key('List of Albums').children.select {|n| n.name == 'dict' }
|
43
63
|
albums.each do |album_info|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
yield folder_name, album_info
|
48
|
-
else
|
49
|
-
say "\n\n#{folder_name} does not match the filter: #{album_filter.inspect}"
|
50
|
-
end
|
64
|
+
album_name = value_for_dictionary_key('AlbumName', album_info).content
|
65
|
+
next if album_name == 'Photos'
|
66
|
+
process_folder(album_name, album_info, &block)
|
51
67
|
end
|
52
68
|
end
|
53
69
|
|
54
|
-
def
|
55
|
-
|
70
|
+
def process_folder(folder, album_info, &block)
|
71
|
+
folder_name = add_date_to_folder_name(folder, album_info)
|
72
|
+
|
73
|
+
if folder_name.match(album_filter)
|
74
|
+
yield folder_name, album_info
|
75
|
+
else
|
76
|
+
say "\n\n#{folder_name} does not match the filter: #{album_filter.inspect}"
|
77
|
+
end
|
56
78
|
end
|
57
79
|
|
58
|
-
def
|
59
|
-
folder_name
|
80
|
+
def add_date_to_folder_name(folder_name, album_info)
|
81
|
+
return folder_name unless options[:'include-date-prefix']
|
82
|
+
return folder_name if folder_name =~ /^\d{4}-\d{2}-\d{2} /
|
83
|
+
[album_date(album_info), folder_name].compact.join(' ')
|
84
|
+
end
|
60
85
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
say "Automatically adding #{album_date} prefix to folder: #{folder_name}"
|
69
|
-
folder_name = "#{album_date} #{folder_name}"
|
86
|
+
# infer the date from the first image within the album
|
87
|
+
def album_date(album_info)
|
88
|
+
album_date = nil
|
89
|
+
each_image album_info do |image_info|
|
90
|
+
next if album_date
|
91
|
+
photo_interval = value_for_dictionary_key('DateAsTimerInterval', image_info).content.to_i
|
92
|
+
album_date = (IPHOTO_EPOCH + photo_interval).strftime('%Y-%m-%d')
|
70
93
|
end
|
71
|
-
|
94
|
+
album_date
|
72
95
|
end
|
73
96
|
|
74
97
|
def each_image(album_info, &block)
|
@@ -4,10 +4,25 @@
|
|
4
4
|
<key>List of Albums</key>
|
5
5
|
<array>
|
6
6
|
<dict>
|
7
|
-
<key>
|
7
|
+
<key>AlbumId</key>
|
8
|
+
<integer>4</integer>
|
9
|
+
<key>AlbumName</key>
|
10
|
+
<string>Photos</string>
|
11
|
+
<key>Album Type</key>
|
12
|
+
<string>99</string>
|
13
|
+
<key>GUID</key>
|
14
|
+
<string>allPhotosAlbum</string>
|
15
|
+
<key>Master</key><true/>
|
16
|
+
<key>TransitionSpeed</key>
|
17
|
+
<real>1.000000</real>
|
18
|
+
<key>ShuffleSlides</key><false/>
|
19
|
+
<key>KeyList</key>
|
20
|
+
</dict>
|
21
|
+
<dict>
|
22
|
+
<key>AlbumID</key>
|
8
23
|
<integer>640</integer>
|
9
|
-
<key>
|
10
|
-
<string>
|
24
|
+
<key>AlbumName</key>
|
25
|
+
<string>My Vacation</string>
|
11
26
|
<key>RollDateAsTimerInterval</key>
|
12
27
|
<real>21600.000000</real>
|
13
28
|
<key>KeyPhotoKey</key>
|
@@ -60,7 +60,7 @@ describe IphotoBackup::CLI do
|
|
60
60
|
it 'creates folder for first event' do
|
61
61
|
expect(File.exists?('tmp/backup/Summer Party')).to eq true
|
62
62
|
end
|
63
|
-
it 'does not
|
63
|
+
it 'does not create folder for second event' do
|
64
64
|
expect(File.exists?('tmp/backup/2013-10-10 Fall Supper')).to eq false
|
65
65
|
end
|
66
66
|
end
|
@@ -74,7 +74,19 @@ describe IphotoBackup::CLI do
|
|
74
74
|
}
|
75
75
|
}
|
76
76
|
it 'creates folder for album' do
|
77
|
-
expect(File.exists?('tmp/backup/
|
77
|
+
expect(File.exists?('tmp/backup/My Vacation')).to eq true
|
78
|
+
end
|
79
|
+
it 'copies images for album' do
|
80
|
+
expect(Dir.glob('tmp/backup/My Vacation/*.jpg').length).to eq 2
|
81
|
+
end
|
82
|
+
it 'does not create folder for Photos album' do
|
83
|
+
expect(File.exists?('tmp/backup/Photos')).to eq false
|
84
|
+
end
|
85
|
+
it 'does not create folder for first event' do
|
86
|
+
expect(File.exists?('tmp/backup/Summer Party')).to eq false
|
87
|
+
end
|
88
|
+
it 'does not create folder for second event' do
|
89
|
+
expect(File.exists?('tmp/backup/2013-10-10 Fall Supper')).to eq false
|
78
90
|
end
|
79
91
|
end
|
80
92
|
context 'with --include-date-prefix option' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iphoto_backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Sonnek
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: tool for backing up iPhoto files
|
98
112
|
email:
|
99
113
|
- ryan@codecrate.com
|
@@ -120,7 +134,6 @@ files:
|
|
120
134
|
- spec/fixtures/image2.jpg
|
121
135
|
- spec/lib/iphoto_backup/cli_spec.rb
|
122
136
|
- spec/spec_helper.rb
|
123
|
-
- spec/support/meet_expectations_matcher.rb
|
124
137
|
homepage: ''
|
125
138
|
licenses:
|
126
139
|
- MIT
|
@@ -151,4 +164,3 @@ test_files:
|
|
151
164
|
- spec/fixtures/image2.jpg
|
152
165
|
- spec/lib/iphoto_backup/cli_spec.rb
|
153
166
|
- spec/spec_helper.rb
|
154
|
-
- spec/support/meet_expectations_matcher.rb
|