photein 0.0.2 β†’ 0.0.7

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
  SHA256:
3
- metadata.gz: 8358c084cdf56bab539d9f429706a606cb346b33e7832e056054b2a4ead67e08
4
- data.tar.gz: c9e77f835d2ba2d39a3edb08e5fc6973a1871b82c52a823e3bf15a446c263f80
3
+ metadata.gz: d67c47b483c2b3525960fa4aa80221a2143b7b4bf92d67e74622ffe3ecbcf40f
4
+ data.tar.gz: 215db5531f34667eab5bd8524a51c9497697e6bb56acf8ccac51acca94aa19ce
5
5
  SHA512:
6
- metadata.gz: '08be8726129d9b2c2ffcfe4f9ba206db82619c6b6cd1f8f50339d7390988a3c224c9ecb69da7dedc6edfee5e0c479f6ea4119551af75716039e55eda4635cdc7'
7
- data.tar.gz: a91a5a3be62081f8a25333f323a1ed98f2e34421134878679d5af8f3fce674c8e7ef8e1a299d4caa3afd9364af38fbe6ef8136045bfe15ac614afd9cf4d2f9ba
6
+ metadata.gz: 356351ee9bdb21340d47b57c12bc11857e774f8a81741f4aa2567b60555d8adc290daad8ddaea9a3ee9d48197b0a3e923fd6e2f6eaea67ef00ca51112e5895b1
7
+ data.tar.gz: f24b64e2a9faafd6ba95e85b7c3e198bc526e4f8dc216fc71e1dfab9ea3e1affacac3b8ce472b880202b13899abb98eb758504c06045380aa4e0c0ceeaa0b293
data/README.md CHANGED
@@ -1,16 +1,12 @@
1
1
  PhπŸ“Έtein
2
2
  ========
3
3
 
4
- All your photos under one roof.
4
+ A no-nonsense way to organize your personal photo library.
5
5
 
6
6
  What does it do?
7
7
  ----------------
8
8
 
9
- This repo provides two related programs: `photein` and `xferase`.
10
-
11
- ### Photein
12
-
13
- Photein is a CLI utility for managing your photos **at the filesystem level**.
9
+ Photein manages your photos **at the filesystem level**.
14
10
  It won’t let you browse or edit your photos,
15
11
  but it will give them a uniform folder structure and filenames,
16
12
  no matter where they come from:
@@ -18,73 +14,53 @@ no matter where they come from:
18
14
  ```sh
19
15
  # Before # After
20
16
 
21
- ~/Pictures ~/Pictures
22
- └── _inbox β”œβ”€β”€ _inbox
23
- β”œβ”€β”€ 1619593208911.jpeg β”œβ”€β”€ 2020
24
- β”œβ”€β”€ DCIM β”‚ β”œβ”€β”€ 2020-08-01_113129.heic
25
- β”‚ └── 2021_03_26 β”‚ └── 2020-05-20_160209.png
26
- β”‚ β”œβ”€β”€ R0014285.MOV └── 2021
27
- β”‚ β”œβ”€β”€ R0014286.DNG β”œβ”€β”€ 2021-02-12_081933a.jpg
28
- β”‚ β”œβ”€β”€ R0014286.JPG β”œβ”€β”€ 2021-02-12_081933b.jpg
29
- β”‚ β”œβ”€β”€ R0014287.DNG β”œβ”€β”€ 2021-02-12_081939.mp4
30
- β”‚ └── R0014287.JPG β”œβ”€β”€ 2021-03-26_161245.mp4
31
- β”œβ”€β”€ IMG_20210212_081933_001.jpg β”œβ”€β”€ 2021-03-26_161518.dng
32
- β”œβ”€β”€ IMG_20210212_081933_002.jpg β”œβ”€β”€ 2021-03-26_161518.jpg
33
- β”œβ”€β”€ IMG_8953.HEIC β”œβ”€β”€ 2021-03-26_170304.dng
34
- β”œβ”€β”€ Screenshot_20200520_160209.png β”œβ”€β”€ 2021-03-26_170304.jpg
35
- └── VID_20210212_081939.mp4 └── 2021-04-28_000008.jpg
17
+ ~ ~
18
+ β”œβ”€β”€ Downloads β”œβ”€β”€ Downloads
19
+ β”‚ β”œβ”€β”€ 1619593208911.jpeg └── Pictures
20
+ β”‚ β”œβ”€β”€ DCIM β”œβ”€β”€ 2020
21
+ β”‚ β”‚ └── 2021_03_26 β”‚ β”œβ”€β”€ 2020-08-01_113129.heic
22
+ β”‚ β”‚ β”œβ”€β”€ R0014285.MOV β”‚ └── 2020-05-20_160209.png
23
+ β”‚ β”‚ β”œβ”€β”€ R0014286.DNG └── 2021
24
+ β”‚ β”‚ β”œβ”€β”€ R0014286.JPG β”œβ”€β”€ 2021-02-12_081933a.jpg
25
+ β”‚ β”‚ β”œβ”€β”€ R0014287.DNG β”œβ”€β”€ 2021-02-12_081933b.jpg
26
+ β”‚ β”‚ └── R0014287.JPG β”œβ”€β”€ 2021-02-12_081939.mp4
27
+ β”‚ β”œβ”€β”€ IMG_20210212_081933_001.jpg β”œβ”€β”€ 2021-03-26_161245.mp4
28
+ β”‚ β”œβ”€β”€ IMG_20210212_081933_002.jpg β”œβ”€β”€ 2021-03-26_161518.dng
29
+ β”‚ β”œβ”€β”€ IMG_8953.HEIC β”œβ”€β”€ 2021-03-26_161518.jpg
30
+ β”‚ β”œβ”€β”€ Screenshot_20200520_160209.png β”œβ”€β”€ 2021-03-26_170304.dng
31
+ β”‚ └── VID_20210212_081939.mp4 β”œβ”€β”€ 2021-03-26_170304.jpg
32
+ └── Pictures └── 2021-04-28_000008.jpg
36
33
  ```
37
34
 
35
+ Photein generates these folders & filenames
36
+ based on metadata timestamps, filename timestamps, or file creation times.
37
+
38
38
  > ⚠️ **Note**
39
39
  >
40
40
  > If you use a photo management app that decides
41
- > where and how your photos should be stored on your system (like Apple Photos),
42
- > Photein is not for you.
43
-
44
- ### Xferase
45
-
46
- Xferase is a background service built on top of photein.
47
- It watches a directory of your choosing,
48
- and whenever any files are placed there,
49
- it automatically imports them into your photo library.
50
-
51
- It creates and manages two parallel copies of your library
52
- (one original/hi-res, one optimized for web)
53
- and ensures that when you delete a photo from one,
54
- it is automatically removed from the other.
55
-
56
- When combined with other software,
57
- Xferase can be used as a self-hosted / DIY alternative
58
- to cloud photo services like Google Photos or iCloud.
59
-
60
- Why?
61
- ----
62
-
63
- I could not find any existing software product that:
41
+ > where and how your photos should be stored on disk
42
+ > (looking at you, Apple Photos πŸ‘€), Photein is not for you.
64
43
 
65
- 1. imports photos from many sources\* with **no mouse or keyboard interaction**
44
+ It can also optimize photos and videos for reduced file size.
66
45
 
67
- \*_e.g.,_ πŸ“± cell phone / πŸ“· digital camera / πŸ’¬ chat app download
46
+ What _doesn’t_ it do?
47
+ ---------------------
68
48
 
69
- 2. enforces a clean, consistent, **user-visible directory & filename scheme**
49
+ On its own, Photein is **not** an alternative
50
+ to cloud photo services like Google Photos or iCloudβ€”but
51
+ in combination with other software, it can be.
70
52
 
71
- (I want to be able to access my photos from the file manager,
72
- find them in an β€œOpen...” dialog,
73
- or sync them to other devices with tools like Dropbox or Syncthing.)
53
+ If you want to:
74
54
 
75
- 3. comes with **no recurring subscription fee**β€”or better yet, is FOSS
55
+ * import photos from your phone as soon as you take them
56
+ * import photos from a digital camera / SD card as soon as you plug it in
57
+ * mirror a low-res copy of your entire photo library to your Android phone
76
58
 
77
- (My digital photo/video library belongs to me,
78
- but if I don’t control the pipeline for viewing/managing it,
79
- then does it really?)
59
+ check out Photein’s sister utility [Xferase][],
60
+ or try the [automation guides][] below.
80
61
 
81
- 4. works with Linux
82
-
83
- > ⚠️ **Note**
84
- >
85
- > Strictly speaking, Photein does not handle requirement #1;
86
- > for that, use Xferase in combination with other software,
87
- > such as Syncthing or systemd.
62
+ [Xferase]: https://github.com/rlue/xferase
63
+ [automation guides]: #automation-guides
88
64
 
89
65
  Installation
90
66
  ------------
@@ -95,7 +71,7 @@ $ gem install photein
95
71
 
96
72
  ### Dependencies
97
73
 
98
- * Ruby 2.7+
74
+ * Ruby 2.6+
99
75
  * [ExifTool][]
100
76
  * [MediaInfo][]
101
77
  * ImageMagick (for `--optimize-for=web` option)
@@ -108,8 +84,6 @@ $ gem install photein
108
84
  Usage
109
85
  -----
110
86
 
111
- ### Simple import
112
-
113
87
  ```sh
114
88
  $ photein \
115
89
  --source /media/ricoh_gr/DCIM \ # batch-import photos from here
@@ -130,9 +104,17 @@ Use `photein --help` for a summary of all options.
130
104
 
131
105
  ### Automation guides
132
106
 
133
- * [πŸ“·βž‘οΈπŸ–₯️ Set up auto-import from a digital camera](doc/auto-import-digital-camera.md)
134
- * [πŸ“±βž‘οΈπŸ–₯️ Set up auto-import from an Android phone](doc/auto-import-android-phone.md)
135
- * [πŸ“±πŸ”„πŸ–₯️ Mirror your library across multiple devices](doc/mirroring-a-library-on-multiple-devices.md)
107
+ Using Photein + systemd, you can:
108
+
109
+ * [πŸ“·βž‘οΈπŸ–₯️ Set up auto-import from a digital camera](guides/auto-import-digital-camera.md)
110
+
111
+ But for more complex tasks, like:
112
+
113
+ * πŸ“±βž‘οΈπŸ–₯️ Setting up auto-import from an Android phone
114
+ * πŸ“±πŸ”„πŸ–₯️ Mirroring your library across multiple devices
115
+
116
+ check out the documentation for [Xferase][],
117
+ an always-on background service based on Photein.
136
118
 
137
119
  Development
138
120
  -----------
@@ -146,8 +128,7 @@ Contributions welcome.
146
128
  > it defines expectations against the effects of `system('photein <args>')`.
147
129
  >
148
130
  > Because `Kernel#system` runs the given command in a subprocess,
149
- > it prints to a different stdout than
150
- > native Ruby code in a normal RSpec example.
131
+ > it prints to a different stdout than `rspec` itself.
151
132
  > This makes test failures cumbersome to debug,
152
133
  > because `puts` statements never appear in the test output,
153
134
  > and `binding.pry` will cause the test to appear to hang
@@ -25,10 +25,14 @@ module Photein
25
25
  .map { |option| option[/\w[a-z\-]+/] }
26
26
  .map(&:to_sym)
27
27
 
28
+ @params = {}
29
+
28
30
  class << self
29
- def parse_opts!
30
- @params = {}
31
+ def set(**params)
32
+ @params.replace(params).freeze
33
+ end
31
34
 
35
+ def parse_opts!
32
36
  parser = OptionParser.new do |opts|
33
37
  opts.version = Photein::VERSION
34
38
  opts.banner = <<~BANNER
data/lib/photein/image.rb CHANGED
@@ -43,7 +43,12 @@ module Photein
43
43
  when '.png'
44
44
  FileUtils.cp(path, tempfile, noop: Photein::Config.dry_run)
45
45
  Photein::Logger.info "optimizing #{path}"
46
- Optipng.optimize(tempfile, level: 4) unless Photein::Config.dry_run
46
+ begin
47
+ Optipng.optimize(tempfile, level: 4) unless Photein::Config.dry_run
48
+ rescue Errno::ENOENT
49
+ Photein::Logger.error('optipng is required to compress PNG images')
50
+ raise
51
+ end
47
52
  end
48
53
  end
49
54
 
@@ -51,10 +56,20 @@ module Photein
51
56
 
52
57
  def image
53
58
  @image ||= MiniMagick::Image.open(path)
59
+ rescue MiniMagick::Invalid => e
60
+ Photein::Logger.error(<<~MSG) if e.message.match?(/You must have ImageMagick/)
61
+ ImageMagick is required to manipulate image files
62
+ MSG
63
+ raise
54
64
  end
55
65
 
56
66
  def metadata_stamp
57
67
  MiniExiftool.new(path.to_s).date_time_original
68
+ rescue MiniExiftool::Error => e
69
+ Photein::Logger.error(<<~MSG) if e.message.match?(/exiftool: not found/)
70
+ exiftool is required to read timestamp metadata
71
+ MSG
72
+ raise
58
73
  end
59
74
 
60
75
  # NOTE: This may be largely unnecessary:
@@ -20,6 +20,7 @@ module Photein
20
20
  end
21
21
 
22
22
  def import
23
+ return if corrupted?
23
24
  return if Photein::Config.interactive && denied_by_user?
24
25
  return if Photein::Config.safe && in_use?
25
26
  return if Photein::Config.optimize_for && non_optimizable_format?
@@ -44,6 +45,12 @@ module Photein
44
45
 
45
46
  private
46
47
 
48
+ def corrupted?(result = false)
49
+ return result.tap do |r|
50
+ Photein::Logger.error("#{path.basename}: cannot import corrupted file") if r
51
+ end
52
+ end
53
+
47
54
  def denied_by_user?
48
55
  $stdout.printf "Import #{path}? [y/N]"
49
56
  (STDIN.getch.downcase != 'y').tap { $stdout.puts }
@@ -113,7 +120,7 @@ module Photein
113
120
  when 1 # if one file found, WITH OR WITHOUT COUNTER, reset counter to a
114
121
  if Dir[collision_glob].first != collision_glob.sub('*', 'a') # don't try if it's already a lone, correctly-countered file
115
122
  Photein::Logger.info('conflicting timestamp found; adding counter to existing file')
116
- FileUtils.mv(Dir[collision_glob].first, collision_glob.sub('*', 'a'))
123
+ FileUtils.mv(Dir[collision_glob].first, collision_glob.sub('*', 'a'), noop: Photein::Config.dry_run)
117
124
  end
118
125
  else # TODO: if multiple files found, rectify them?
119
126
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Photein
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.7'
5
5
  end
data/lib/photein/video.rb CHANGED
@@ -53,13 +53,23 @@ module Photein
53
53
 
54
54
  private
55
55
 
56
+ def corrupted?
57
+ super(video.bitrate.nil?)
58
+ end
59
+
56
60
  def video
57
61
  @video ||= FFMPEG::Movie.new(path.to_s)
62
+ rescue Errno::ENOENT
63
+ Photein::Logger.error('ffmpeg is required to manipulate video files')
64
+ raise
58
65
  end
59
66
 
60
67
  def metadata_stamp
61
68
  # video timestamps are typically UTC
62
69
  MediaInfo.from(path.to_s).general.encoded_date&.getlocal
70
+ rescue MediaInfo::EnvironmentError
71
+ Photein::Logger.error('mediainfo is required to read timestamp metadata')
72
+ raise
63
73
  end
64
74
 
65
75
  # NOTE: This may be largely unnecessary:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: photein
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Lue
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-25 00:00:00.000000000 Z
11
+ date: 2021-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mediainfo
@@ -53,33 +53,33 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '4.11'
55
55
  - !ruby/object:Gem::Dependency
56
- name: optipng
56
+ name: nokogiri
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.2'
61
+ version: '1.11'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.2'
68
+ version: '1.11'
69
69
  - !ruby/object:Gem::Dependency
70
- name: nokogiri
70
+ name: optipng
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.11'
75
+ version: '0.2'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.11'
82
+ version: '0.2'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: streamio-ffmpeg
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -95,19 +95,19 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: pry
98
+ name: pry-remote
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.14'
103
+ version: '0.1'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.14'
110
+ version: '0.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement