imagebackup 0.3.0 → 0.3.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/Gemfile +5 -3
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/imagebackup.gemspec +14 -12
- data/lib/classes/filetypes.rb +11 -8
- data/lib/imagebackup.rb +27 -37
- data/lib/imagebackup/version.rb +1 -1
- data/lib/methods/build_paths.rb +6 -4
- data/lib/methods/copy_pic.rb +11 -12
- data/lib/methods/display_help.rb +54 -54
- data/lib/methods/get_dates.rb +5 -5
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4a1d4dd786b4165fd4812dea1de48ef9ad9ad9040e2b4c572f633117e7147f9
|
4
|
+
data.tar.gz: 67d0e990a7491fee092a0b58386b218a52635ae1f68a79608dca347c791d8814
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b0b396b58101630b0c8390e9b0a6b8e139774b29d775136518becc866157bc66245682be814fc299b79095147f00c53d15a13079017fdc73b2973a8148aa511
|
7
|
+
data.tar.gz: d6c1d1dadc59c952dfca3d99f559fc69538459cde68d6268914a5e25bb4668fe5ec05d8e4f6e4d6914744cc91623331c79556bc3fbfc3b96c33d7322a9b7e38f
|
data/Gemfile
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in imagebackup.gemspec
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem
|
7
|
-
gem
|
8
|
+
gem 'rake', '~> 12.0'
|
9
|
+
gem 'rspec', '~> 3.0'
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'imagebackup'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "imagebackup"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/imagebackup.gemspec
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/imagebackup/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name =
|
6
|
+
spec.name = 'imagebackup'
|
5
7
|
spec.version = Imagebackup::VERSION
|
6
|
-
spec.authors = [
|
7
|
-
spec.email = [
|
8
|
+
spec.authors = ['adrian-sal-kennedy']
|
9
|
+
spec.email = ['adrian.sal.kennedy@gmail.com']
|
8
10
|
|
9
|
-
spec.summary =
|
10
|
-
spec.description =
|
11
|
-
spec.homepage =
|
12
|
-
spec.license =
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new(
|
11
|
+
spec.summary = 'A simple CLI app to backup all pics and vids from current folder to a destination in yyyy-mm-dd folders.'
|
12
|
+
spec.description = 'Uses Exiv2 and ffprobe to find creation dates for media types it finds.'
|
13
|
+
spec.homepage = 'https://github.com/adrian-sal-kennedy/imagebackup'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
14
16
|
|
15
17
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
16
18
|
|
17
|
-
spec.metadata[
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
20
|
# spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
19
21
|
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
20
22
|
|
21
23
|
# Specify which files should be added to the gem when it is released.
|
22
24
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
-
spec.files
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
26
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
27
|
end
|
26
|
-
spec.bindir =
|
28
|
+
spec.bindir = 'exe'
|
27
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
-
spec.require_paths = [
|
30
|
+
spec.require_paths = ['lib']
|
29
31
|
|
30
32
|
spec.add_dependency 'exiv2'
|
31
33
|
spec.add_dependency 'ffprober'
|
data/lib/classes/filetypes.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'csv'
|
2
2
|
|
3
|
+
# FileTypes is a very simple file class to store and retrieve the list of files
|
4
|
+
# we are interested in backing up.
|
3
5
|
class FileTypes
|
4
6
|
def self.list
|
5
7
|
file_list = []
|
@@ -7,11 +9,11 @@ class FileTypes
|
|
7
9
|
file_list << "**/*.#{csv[0]}"
|
8
10
|
file_list << "**/*.#{csv[0].upcase}"
|
9
11
|
end
|
10
|
-
|
12
|
+
file_list
|
11
13
|
end
|
12
14
|
|
13
|
-
def self.add(ext=nil,type=nil)
|
14
|
-
unless ext
|
15
|
+
def self.add(ext = nil, type = nil)
|
16
|
+
unless ext
|
15
17
|
puts 'please enter the file type\'s extension, with or without the dot.'
|
16
18
|
ext = gets.strip
|
17
19
|
end
|
@@ -19,12 +21,13 @@ class FileTypes
|
|
19
21
|
puts 'please enter "movie" or "pic" so we know what we\'re working with.'
|
20
22
|
type = gets.strip
|
21
23
|
end
|
22
|
-
ext = ext.to_s.gsub(/[*."]/,'')
|
23
|
-
|
24
|
-
type = (type.downcase.include?('m')) ? "movie" : "pic"
|
24
|
+
ext = ext.to_s.gsub(/[*."]/, '')
|
25
|
+
type = type.downcase.include?('m') ? 'movie' : 'pic'
|
25
26
|
puts "opening file \"#{File.dirname(__FILE__)}/filetypes.csv\""
|
26
|
-
CSV.open("#{File.dirname(__FILE__)}/filetypes.csv",
|
27
|
-
|
27
|
+
CSV.open("#{File.dirname(__FILE__)}/filetypes.csv", 'a') do |csv|
|
28
|
+
csv << [ext, type]
|
28
29
|
end
|
30
|
+
puts 'New file types registered.'
|
31
|
+
exit
|
29
32
|
end
|
30
33
|
end
|
data/lib/imagebackup.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
2
3
|
require_relative 'imagebackup/version'
|
3
4
|
|
4
5
|
require_relative 'classes/filetypes'
|
@@ -11,56 +12,46 @@ require 'exiv2'
|
|
11
12
|
require 'fileutils'
|
12
13
|
require 'ffprober'
|
13
14
|
|
14
|
-
def main_loop(dest,dryrun=true,file_op=
|
15
|
+
def main_loop(dest, dryrun = true, file_op = 'copy')
|
15
16
|
file_types = FileTypes.list
|
16
17
|
Dir.glob(file_types).reverse_each do |f|
|
17
|
-
|
18
18
|
file = "#{Dir.pwd}/#{f}"
|
19
19
|
|
20
|
-
parms = build_paths(dest,file,get_dates(file))
|
20
|
+
parms = build_paths(dest, file, get_dates(file))
|
21
21
|
outfile = parms[0]
|
22
22
|
destpath = parms[1]
|
23
23
|
|
24
|
-
copy_pic(file,outfile,destpath,dryrun,file_op)
|
25
|
-
|
24
|
+
copy_pic(file, outfile, destpath, dryrun, file_op)
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
|
-
# main_loop(ARGV[0])
|
30
|
-
|
31
28
|
dryrun = true
|
32
|
-
#
|
33
|
-
# move = false
|
34
|
-
file_op = "cp" # or "mv" or "ln_s"
|
29
|
+
file_op = 'cp' # or "mv" or "ln_s"
|
35
30
|
|
36
|
-
if (ARGV & ['-m','--move']).any?
|
37
|
-
|
38
|
-
file_op = "mv"
|
31
|
+
if (ARGV & ['-m', '--move']).any?
|
32
|
+
file_op = 'mv'
|
39
33
|
ARGV.delete('-m')
|
40
34
|
ARGV.delete('--move')
|
41
35
|
end
|
42
|
-
if (ARGV & ['-l','--link']).any?
|
43
|
-
|
44
|
-
file_op = "ln_s"
|
36
|
+
if (ARGV & ['-l', '--link']).any?
|
37
|
+
file_op = 'ln_s'
|
45
38
|
ARGV.delete('-l')
|
46
39
|
ARGV.delete('--link')
|
47
40
|
end
|
48
41
|
if ARGV.include?('-a')
|
49
|
-
ext=ARGV[ARGV.index('-a')+1]
|
50
|
-
type=ARGV[ARGV.index('-a')+2]
|
51
|
-
ARGV.slice!(ARGV.index('-a')..ARGV.index('-a')+2)
|
42
|
+
ext = ARGV[ARGV.index('-a') + 1]
|
43
|
+
type = ARGV[ARGV.index('-a') + 2]
|
44
|
+
ARGV.slice!(ARGV.index('-a')..ARGV.index('-a') + 2)
|
52
45
|
end
|
53
46
|
if ARGV.include?('--add-filetype')
|
54
|
-
ext=ARGV[ARGV.index('--add-filetype')+1]
|
55
|
-
type=ARGV[ARGV.index('--add-filetype')+2]
|
56
|
-
ARGV.slice!(ARGV.index('--add-filetype')..ARGV.index('--add-filetype')+2)
|
57
|
-
end
|
58
|
-
if ext
|
59
|
-
FileTypes.add(ext,type)
|
47
|
+
ext = ARGV[ARGV.index('--add-filetype') + 1]
|
48
|
+
type = ARGV[ARGV.index('--add-filetype') + 2]
|
49
|
+
ARGV.slice!(ARGV.index('--add-filetype')..ARGV.index('--add-filetype') + 2)
|
60
50
|
end
|
61
|
-
|
51
|
+
FileTypes.add(ext, type) if ext
|
52
|
+
if (ARGV & ['-n', '--dry-run']).any?
|
62
53
|
dryrun = true
|
63
|
-
puts
|
54
|
+
puts 'Doing a dry run - no operations will happen.'
|
64
55
|
sleep 1
|
65
56
|
ARGV.delete('-n')
|
66
57
|
ARGV.delete('--dry-run')
|
@@ -68,16 +59,15 @@ else
|
|
68
59
|
dryrun = false
|
69
60
|
end
|
70
61
|
|
71
|
-
if (ARGV & ['-h','--help','-?']).any?
|
72
|
-
display_help()
|
73
|
-
end
|
62
|
+
display_help if (ARGV & ['-h', '--help', '-?']).any?
|
74
63
|
|
75
64
|
if ARGV[0].to_s == ''
|
76
|
-
display_help
|
65
|
+
display_help
|
66
|
+
elsif File.exist?(ARGV[0])
|
67
|
+
main_loop(ARGV[0], dryrun, file_op)
|
68
|
+
elsif ARGV[0][0] == '-'
|
69
|
+
puts "Invalid option!\n-----\n\n"
|
70
|
+
display_help
|
77
71
|
else
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
puts "Specified destination does not exist. Please create this folder first."
|
82
|
-
end
|
83
|
-
end
|
72
|
+
puts 'Specified destination does not exist. Please create this folder first.'
|
73
|
+
end
|
data/lib/imagebackup/version.rb
CHANGED
data/lib/methods/build_paths.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def build_paths(dest, file, date)
|
2
4
|
date = get_dates(file)
|
3
5
|
destpath = "#{dest}/#{date}"
|
4
|
-
destpath = destpath.gsub('//','/')
|
6
|
+
destpath = destpath.gsub('//', '/')
|
5
7
|
outfile = "#{destpath}/#{File.basename(file)}"
|
6
|
-
|
7
|
-
end
|
8
|
+
[outfile, destpath]
|
9
|
+
end
|
data/lib/methods/copy_pic.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
def process_file(file,outfile,dryrun=nil,file_op=
|
1
|
+
def process_file(file, outfile, dryrun = nil, file_op = 'cp')
|
2
2
|
if dryrun
|
3
3
|
puts "pretending to #{file_op} \"#{file} to #{outfile}\""
|
4
4
|
else
|
5
5
|
puts "#{file_op}-ing \"#{file} to #{outfile}\"..."
|
6
|
-
FileUtils.public_send(file_op,file,outfile)
|
7
|
-
FileUtils.public_send(file_op,"#{file}.xmp",outfile
|
8
|
-
if file_op ==
|
9
|
-
FileUtils.public_send(file_op,"#{file}.xmp",outfile)
|
6
|
+
FileUtils.public_send(file_op, file, outfile)
|
7
|
+
FileUtils.public_send(file_op, "#{file}.xmp", outfile, force: true)
|
8
|
+
if file_op == 'cp'
|
9
|
+
FileUtils.public_send(file_op, "#{file}.xmp", outfile)
|
10
10
|
else
|
11
|
-
FileUtils.public_send(file_op,"#{file}.xmp",outfile
|
11
|
+
FileUtils.public_send(file_op, "#{file}.xmp", outfile, force: true)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def copy_pic(file,outfile,destpath,dryrun=nil,file_op=
|
17
|
-
|
16
|
+
def copy_pic(file, outfile, destpath, dryrun = nil, file_op = 'cp')
|
17
|
+
if File.exist?(outfile)
|
18
|
+
puts "\"#{outfile}\" already exists. Skipping..."
|
19
|
+
else
|
18
20
|
unless dryrun
|
19
21
|
unless File.exist?(destpath)
|
20
22
|
puts "creating folder \"#{destpath}\""
|
21
23
|
FileUtils.mkdir_p(destpath)
|
22
|
-
else
|
23
24
|
end
|
24
25
|
end
|
25
|
-
process_file(file,outfile,dryrun,file_op)
|
26
|
-
else
|
27
|
-
puts "\"#{outfile}\" already exists. Skipping..."
|
26
|
+
process_file(file, outfile, dryrun, file_op)
|
28
27
|
end
|
29
28
|
end
|
data/lib/methods/display_help.rb
CHANGED
@@ -1,82 +1,82 @@
|
|
1
1
|
def display_help
|
2
|
-
print
|
3
|
-
===== ImageBackup =====
|
2
|
+
print <<~HELPFILE
|
3
|
+
===== ImageBackup =====
|
4
4
|
|
5
|
-
Keep your photos and videos organized by date.
|
5
|
+
Keep your photos and videos organized by date.
|
6
6
|
|
7
|
-
A simple terminal app to crawl a folder (usually a camera card's DCIM folder) for pictures and videos, and pop them in dated folders to your destination folder of choice. Uses exif data when available, creation_time, or the file's own creation date if nothing better is present.
|
7
|
+
A simple terminal app to crawl a folder (usually a camera card's DCIM folder) for pictures and videos, and pop them in dated folders to your destination folder of choice. Uses exif data when available, creation_time, or the file's own creation date if nothing better is present.
|
8
8
|
|
9
|
-
Will also copy over any xmp sidecar files found, not overwriting.
|
9
|
+
Will also copy over any xmp sidecar files found, not overwriting.
|
10
10
|
|
11
|
-
Options:
|
11
|
+
Options:
|
12
12
|
|
13
|
-
-n, --dry-run Run without actually doing anything. Good for making sure
|
14
|
-
|
15
|
-
|
13
|
+
-n, --dry-run Run without actually doing anything. Good for making sure
|
14
|
+
things are working properly. This will also give you console
|
15
|
+
output which helps identify unreadable files.
|
16
16
|
|
17
|
-
-a, --add-filetype <extension> <type> This will add a custom file type to the list of files it's
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
-a, --add-filetype <extension> <type> This will add a custom file type to the list of files it's
|
18
|
+
looking for. If you have an arcane digital camera (we
|
19
|
+
currently support canon, sony, pentax but new stuff comes out
|
20
|
+
all the time), this will allow you to add your raw files.
|
21
|
+
You can also access *filetypes.csv* and add them manually,
|
22
|
+
but ensure there's a blank line at the end or this program
|
23
|
+
may behave badly.
|
24
24
|
|
25
|
-
-m will move (deleting the original), which is probably not a
|
26
|
-
|
25
|
+
-m will move (deleting the original), which is probably not a
|
26
|
+
good idea in most cases but still useful at times.
|
27
27
|
|
28
|
-
Usage:
|
28
|
+
Usage:
|
29
29
|
|
30
|
-
- To backup a camera card:
|
30
|
+
- To backup a camera card:
|
31
31
|
|
32
|
-
$ cd /media/username/EOS_DIGITAL/DCIM
|
33
|
-
$ imagebackup.rb ~/Photos/raw
|
32
|
+
$ cd /media/username/EOS_DIGITAL/DCIM
|
33
|
+
$ imagebackup.rb ~/Photos/raw
|
34
34
|
|
35
|
-
This will search all files within the DCIM folder, check them with either exiv2 (for stills) or ffprobe (for videos) and retrieve their creation dates.
|
36
|
-
It will then copy them to a folder of the form ~/Photos/raw/<yyyy>-<mm>-<dd>
|
37
|
-
If it's unable to find metadata in a file it will look at the file's creation time attribute, which is less reliable but usually ok.
|
35
|
+
This will search all files within the DCIM folder, check them with either exiv2 (for stills) or ffprobe (for videos) and retrieve their creation dates.
|
36
|
+
It will then copy them to a folder of the form ~/Photos/raw/<yyyy>-<mm>-<dd>
|
37
|
+
If it's unable to find metadata in a file it will look at the file's creation time attribute, which is less reliable but usually ok.
|
38
38
|
|
39
|
-
- To register a new file type:
|
39
|
+
- To register a new file type:
|
40
40
|
|
41
|
-
$ imagebackup.rb --add-filetype orf pic
|
42
|
-
or
|
43
|
-
$ imagebackup.rb -a orf pic
|
41
|
+
$ imagebackup.rb --add-filetype orf pic
|
42
|
+
or
|
43
|
+
$ imagebackup.rb -a orf pic
|
44
44
|
|
45
|
-
This will work with *.ORF, orf, ".orf" as it will strip off any unnecessary characters. It is case-insensitive.
|
45
|
+
This will work with *.ORF, orf, ".orf" as it will strip off any unnecessary characters. It is case-insensitive.
|
46
46
|
|
47
|
-
- To do a dry run, checking each file and destination but not actually copying:
|
47
|
+
- To do a dry run, checking each file and destination but not actually copying:
|
48
48
|
|
49
|
-
$ imagebackup.rb -n ~/Photos/raw
|
50
|
-
or
|
51
|
-
$ imagebackup.rb ~/Photos/raw --dry-run
|
49
|
+
$ imagebackup.rb -n ~/Photos/raw
|
50
|
+
or
|
51
|
+
$ imagebackup.rb ~/Photos/raw --dry-run
|
52
52
|
|
53
|
-
- To move files instead of copying them (careful!):
|
53
|
+
- To move files instead of copying them (careful!):
|
54
54
|
|
55
|
-
$ imagebackup.rb -m ~/Photos/raw
|
56
|
-
or
|
57
|
-
$ imagebackup.rb --move ~/Photos/raw
|
55
|
+
$ imagebackup.rb -m ~/Photos/raw
|
56
|
+
or
|
57
|
+
$ imagebackup.rb --move ~/Photos/raw
|
58
58
|
|
59
|
-
- To make symbolic links instead of copying files:
|
59
|
+
- To make symbolic links instead of copying files:
|
60
60
|
|
61
|
-
$ imagebackup.rb -l ~/Photos/raw
|
62
|
-
or
|
63
|
-
$ imagebackup.rb --link ~/Photos/raw
|
61
|
+
$ imagebackup.rb -l ~/Photos/raw
|
62
|
+
or
|
63
|
+
$ imagebackup.rb --link ~/Photos/raw
|
64
64
|
|
65
|
-
This mode can be useful if you want to operate on the files in one place but keep them on their media. Particularly useful for large movie files.
|
65
|
+
This mode can be useful if you want to operate on the files in one place but keep them on their media. Particularly useful for large movie files.
|
66
66
|
|
67
|
-
Dependencies:
|
67
|
+
Dependencies:
|
68
68
|
|
69
|
-
Ruby v2.3.0 or greater, plus gems:
|
70
|
-
|
71
|
-
|
69
|
+
Ruby v2.3.0 or greater, plus gems:
|
70
|
+
- exiv2
|
71
|
+
- ffprober
|
72
72
|
|
73
|
-
Ruby modules:
|
74
|
-
|
73
|
+
Ruby modules:
|
74
|
+
- fileutils
|
75
75
|
|
76
|
-
Bundler should handle all these dependencies. If for some reason it doesn't you can run this in terminal:
|
77
|
-
$ gem install exiv2 ffprober
|
76
|
+
Bundler should handle all these dependencies. If for some reason it doesn't you can run this in terminal:
|
77
|
+
$ gem install exiv2 ffprober
|
78
78
|
|
79
|
-
|
79
|
+
HELPFILE
|
80
80
|
|
81
|
-
exit(0)
|
82
|
-
end
|
81
|
+
exit(0)
|
82
|
+
end
|
data/lib/methods/get_dates.rb
CHANGED
@@ -3,15 +3,15 @@ def get_dates(file)
|
|
3
3
|
image = Exiv2::ImageFactory.open(file)
|
4
4
|
image.read_metadata
|
5
5
|
date = image.exif_data.find { |v| v[0] == 'Exif.Image.DateTime' }
|
6
|
-
date = date[1].split[0].gsub(':','-')
|
7
|
-
rescue
|
6
|
+
date = date[1].split[0].gsub(':', '-')
|
7
|
+
rescue StandardError
|
8
8
|
begin
|
9
9
|
probe = Ffprober::Parser.from_file(file)
|
10
10
|
date = probe.format.tags[:creation_time].split('T')[0]
|
11
|
-
rescue
|
11
|
+
rescue StandardError
|
12
12
|
fileobj = File.new(file)
|
13
13
|
date = "#{fileobj.stat.ctime.year}-#{fileobj.stat.ctime.month}-#{fileobj.stat.ctime.day}"
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
17
|
-
end
|
16
|
+
date
|
17
|
+
end
|