iphoto_backup 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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