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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9dc6333b0ff52d5877e21a81cc783da8c1425a1a
4
- data.tar.gz: d40cfb52061a847c085d444e7bd5aec1a6db2323
3
+ metadata.gz: 90efaf9d3f334df3de1db5ea6bb0c8985cd5ec36
4
+ data.tar.gz: 27004da2e2ba4e5caeb4fd0513baea2be8482833
5
5
  SHA512:
6
- metadata.gz: a2a91715481008c95ae63674149c22503ba73464fe1ecec5da343128a9b5673ba47681c2ac4bab5e7503dfb0fb743f761f807a3e87eb7a2489ebae61bf0482ac
7
- data.tar.gz: 33649f5162aeec4b51ad59e191e5aba8938c403d07f432d1b3337eb8cabd82febc76b3b962ed095d28197747f5c55d56f9b6e5f6d4121f6b894dae9f6d4e789c
6
+ metadata.gz: 8c94711c88fe9750fb0827d9f958365d9f5f8ae8202634f460ab09841d5035b40038bb9fa7607b1dc9baf2e4519e9dd4b48859a1f905480a47dabb37d980547f
7
+ data.tar.gz: 5f788f673ad402d14c08ebf49f6be4c6f8776b926ea466047808ae0eda34e1feb0f94393e9fc147d0c617bc45a3604b051808460ba50f5e2a3facdb730f22535
@@ -1,3 +1 @@
1
1
  language: ruby
2
- rvm:
3
- - '2.0.0'
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  [![Build Status](https://travis-ci.org/wireframe/iphoto_backup.png?branch=master)](https://travis-ci.org/wireframe/iphoto_backup)
2
+ [![Coverage Status](https://coveralls.io/repos/wireframe/iphoto_backup/badge.png)](https://coveralls.io/r/wireframe/iphoto_backup)
3
+ [![Code Climate](https://codeclimate.com/github/wireframe/iphoto_backup/badges/gpa.svg)](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 to simplify backups and archiving of your iPhoto images.
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)
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rake"
26
26
  spec.add_development_dependency 'rspec'
27
27
  spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'coveralls'
28
29
  end
@@ -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: 'export albums instead of events', aliases: '-a', default: false, type: :boolean
16
+ option :albums, desc: 'use albums for the export instead of events', aliases: '-a', default: false, type: :boolean
17
17
  def export
18
- each_album do |folder_name, album_info|
19
- say "\n\nProcessing Roll: #{folder_name}..."
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
- source_path = value_for_dictionary_key('ImagePath', image_info).content
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(album_type).children.select {|n| n.name == 'dict' }
62
+ albums = value_for_dictionary_key('List of Albums').children.select {|n| n.name == 'dict' }
43
63
  albums.each do |album_info|
44
- folder_name = album_name album_info
45
-
46
- if folder_name.match(album_filter)
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 album_type
55
- options[:album] ? 'List of Albums' : 'List of Rolls'
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 album_name(album_info)
59
- folder_name = value_for_dictionary_key('RollName', album_info).content
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
- if options[:'include-date-prefix'] && folder_name !~ /^\d{4}-\d{2}-\d{2} /
62
- album_date = nil
63
- each_image album_info do |image_info|
64
- next if album_date
65
- photo_interval = value_for_dictionary_key('DateAsTimerInterval', image_info).content.to_i
66
- album_date = (IPHOTO_EPOCH + photo_interval).strftime('%Y-%m-%d')
67
- end
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
- folder_name
94
+ album_date
72
95
  end
73
96
 
74
97
  def each_image(album_info, &block)
@@ -1,3 +1,3 @@
1
1
  module IphotoBackup
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.1"
3
3
  end
@@ -4,10 +4,25 @@
4
4
  <key>List of Albums</key>
5
5
  <array>
6
6
  <dict>
7
- <key>RollID</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>RollName</key>
10
- <string>Summer Party</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 createfolder for second event' do
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/Summer Party')).to eq true
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
@@ -1,3 +1,6 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require 'rubygems'
2
5
  require 'bundler/setup'
3
6
  require 'pry'
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.0
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
@@ -1,7 +0,0 @@
1
- # empty matcher to allow for mock expectations to fire
2
- RSpec::Matchers.define :meet_expectations do |expected|
3
- match do |actual|
4
- # do nothing
5
- expect(true).to be_true
6
- end
7
- end