phtools 0.1.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/phls +36 -0
- data/lib/pharrange.rb +13 -0
- data/lib/phbackup.rb +13 -0
- data/lib/phclname.rb +13 -0
- data/lib/phevent.rb +13 -0
- data/lib/phfixdate.rb +13 -0
- data/lib/phfixfmd.rb +13 -0
- data/lib/phls.rb +45 -0
- data/lib/phmtags.rb +13 -0
- data/lib/phrename.rb +13 -0
- data/lib/phtagset.rb +13 -0
- data/lib/phtools/error.rb +33 -0
- data/lib/phtools/ph_file.rb +226 -0
- data/lib/phtools/runner.rb +83 -0
- data/lib/phtools/utils/os.rb +25 -0
- data/lib/phtools/utils/os_unix.rb +15 -0
- data/lib/phtools/utils/os_win.rb +17 -0
- data/lib/phtools/utils/ruby_version.rb +13 -0
- data/lib/phtools/version.rb +1 -1
- data/lib/phtools.rb +27 -2
- data/phtools.gemspec +1 -1
- metadata +22 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2b4a9e0f8e817daa66178993aa0d7338cc4640f
|
4
|
+
data.tar.gz: d5ebce5d0aa61183a4e018ae04c2d59b20673980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb219b0a0dca4df74ce3ac11af40ce59893076a6c8c0c353d14e4951c97233aad4da2f50fb0cde74c26042aca13dc14fccc4ce60fad66c1d03ea2f1cf39960c6
|
7
|
+
data.tar.gz: 702e6dd55560a82cc537589a830c610f17c90be5f03ff4c2c6347b364f8f0ca31af867e9722bf63332da4b90fdb959d13031c657586857625bcf8b3bc5659684
|
data/exe/phls
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
module PhTools
|
6
|
+
tool_name = File.basename(__FILE__)
|
7
|
+
require "#{tool_name}"
|
8
|
+
|
9
|
+
file_type = FILE_TYPE_IMAGE + FILE_TYPE_VIDEO + FILE_TYPE_AUDIO
|
10
|
+
usage = <<DOCOPT
|
11
|
+
***************************************************
|
12
|
+
phtools - *Keep Your Photos In Order* (c) ANB
|
13
|
+
***************************************************
|
14
|
+
#{tool_name} scans given directories and
|
15
|
+
generates list of files to standard output.
|
16
|
+
In short it acts like a smart 'ls' command (or 'dir' in Windows).
|
17
|
+
It is a good starting point for all other phtools to be used with pipes.
|
18
|
+
phtools friendly files: #{file_type * ','}
|
19
|
+
|
20
|
+
Example: #{tool_name} -r abc |ftclname => recursively scans 'abc' dir and
|
21
|
+
sends all found phtools friendly files to ftclname command.
|
22
|
+
|
23
|
+
Usage:
|
24
|
+
#{tool_name} [-D] [-r] [DIR_OR_FILE...]
|
25
|
+
#{tool_name} -h | --help
|
26
|
+
#{tool_name} -v | --version
|
27
|
+
|
28
|
+
Options:
|
29
|
+
-D --debug Turn on debugging (verbose) mode
|
30
|
+
-r --recursive Recursively scan directories
|
31
|
+
-h --help Show this screen.
|
32
|
+
-v --version Show version.
|
33
|
+
DOCOPT
|
34
|
+
|
35
|
+
PhTools.const_get(tool_name.capitalize).new(usage, file_type).run!
|
36
|
+
end
|
data/lib/pharrange.rb
ADDED
data/lib/phbackup.rb
ADDED
data/lib/phclname.rb
ADDED
data/lib/phevent.rb
ADDED
data/lib/phfixdate.rb
ADDED
data/lib/phfixfmd.rb
ADDED
data/lib/phls.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'phtools/runner'
|
6
|
+
|
7
|
+
module PhTools
|
8
|
+
# list generation
|
9
|
+
class Phls < Runner
|
10
|
+
def self.about
|
11
|
+
%Q{generates list of phtools friendly files}
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!
|
15
|
+
@options_cli['DIR_OR_FILE'] = ['.'] if @options_cli['DIR_OR_FILE'].empty?
|
16
|
+
@options_cli['DIR_OR_FILE'].each do |item|
|
17
|
+
if File.exist?(item)
|
18
|
+
File.directory?(item) ? output_dir(item) : output_file(item)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
rescue SignalException
|
23
|
+
PhTools.puts_error 'EXIT on user interrupt Ctrl-C'
|
24
|
+
exit 1
|
25
|
+
rescue => e
|
26
|
+
PhTools.puts_error "FATAL: #{e.message}", e
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def output_dir(item)
|
33
|
+
fmask = File.join(item, @options_cli['--recursive'] ? '**' : '',
|
34
|
+
"{#{@file_type.map { |i| "*.#{i}" } * ','}}")
|
35
|
+
files_to_process = Dir.glob(fmask, File::FNM_CASEFOLD |
|
36
|
+
File::FNM_DOTMATCH)
|
37
|
+
files_to_process.each { |f| output_file(f) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def output_file(file)
|
41
|
+
@os.output(File.join(File.dirname(file), File.basename(file))) if
|
42
|
+
@file_type.include?(File.extname(file).slice(1..-1).downcase)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/phmtags.rb
ADDED
data/lib/phrename.rb
ADDED
data/lib/phtagset.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'nesty'
|
6
|
+
|
7
|
+
# Foto tools
|
8
|
+
module PhTools
|
9
|
+
@debug = false
|
10
|
+
def self.debug=(val)
|
11
|
+
@debug = val
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.debug
|
15
|
+
@debug
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.puts_error(msg, e = nil)
|
19
|
+
prefix = File.basename($PROGRAM_NAME, '.rb')
|
20
|
+
STDERR.puts "#{prefix}: #{msg}"
|
21
|
+
if @debug && !e.nil?
|
22
|
+
if e.respond_to?(:cause) && !e.cause.nil?
|
23
|
+
STDERR.puts "#{prefix}: CAUSE: #{e.cause} - #{e.cause.message}"
|
24
|
+
end
|
25
|
+
e.backtrace.each do |b|
|
26
|
+
STDERR.puts "#{prefix}: BACKTRACE: #{b}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Error < Nesty::NestedStandardError; end
|
32
|
+
class ExiftoolTagger < Error; end
|
33
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'phtools/error'
|
6
|
+
require 'date'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
# Foto tools
|
10
|
+
module PhTools
|
11
|
+
# media type constants
|
12
|
+
FILE_TYPE_IMAGE_NORMAL = %w{jpg jpeg tif tiff png}
|
13
|
+
FILE_TYPE_IMAGE_RAW = %w{orf arw dng}
|
14
|
+
FILE_TYPE_IMAGE = FILE_TYPE_IMAGE_NORMAL + FILE_TYPE_IMAGE_RAW
|
15
|
+
FILE_TYPE_VIDEO = %w{avi mp4 mpg mts dv mov mkv m2t m2ts}
|
16
|
+
FILE_TYPE_AUDIO = %w{wav}
|
17
|
+
|
18
|
+
# phtools file name operations
|
19
|
+
class FTFile
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
# filename constants
|
23
|
+
NICKNAME_MIN_SIZE = 3
|
24
|
+
NICKNAME_MAX_SIZE = 6
|
25
|
+
NICKNAME_SIZE = 3 # should be in range of MIN and MAX
|
26
|
+
ZERO_DATE = DateTime.new(0)
|
27
|
+
|
28
|
+
def self.validate_file!(filename, file_type)
|
29
|
+
fail PhTools::Error, 'does not exist' unless
|
30
|
+
filename && File.exist?(filename)
|
31
|
+
fail PhTools::Error, 'not a file' if File.directory?(filename)
|
32
|
+
fail PhTools::Error, 'no permission to write' unless
|
33
|
+
File.writable_real?(filename)
|
34
|
+
fail PhTools::Error, 'unsupported type' unless
|
35
|
+
file_type.include?(File.extname(filename).slice(1..-1).downcase)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.validate_author(author)
|
39
|
+
case
|
40
|
+
when author.size != NICKNAME_SIZE
|
41
|
+
return [false, "'#{author}' wrong author size, should be #{NICKNAME_SIZE} chars long"]
|
42
|
+
when /[-_\s]/.match(author)
|
43
|
+
return [false, "'#{author}' author should not contain spaces [_- ]"]
|
44
|
+
when /[\d]/.match(author)
|
45
|
+
return [false, "'#{author}' author should not contain digits"]
|
46
|
+
when /[\W]/.match(author)
|
47
|
+
return [false, "'#{author}' author should contain only ASCII chars"]
|
48
|
+
end
|
49
|
+
[true, '']
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :filename, :dirname, :extname, :basename, :basename_part,
|
53
|
+
:basename_clean, :date_time, :author
|
54
|
+
|
55
|
+
def initialize(filename)
|
56
|
+
set_state(filename)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"#{@filename}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def <=>(other)
|
64
|
+
@filename <=> other.filename
|
65
|
+
end
|
66
|
+
|
67
|
+
def basename_standard(basename_clean: @basename_clean,
|
68
|
+
date_time: @date_time,
|
69
|
+
author: @author)
|
70
|
+
%Q{#{date_time.strftime('%Y%m%d-%H%M%S')}_#{(author.upcase + "XXXXXX")[0, NICKNAME_SIZE]} #{basename_clean}}
|
71
|
+
end
|
72
|
+
|
73
|
+
def basename_is_standard?
|
74
|
+
@basename == basename_standard
|
75
|
+
end
|
76
|
+
|
77
|
+
def standardize(dirname: @dirname, basename_clean: @basename_clean,
|
78
|
+
extname: @extname, date_time: @date_time,
|
79
|
+
author: @author)
|
80
|
+
File.join(dirname,
|
81
|
+
basename_standard(basename_clean: basename_clean,
|
82
|
+
date_time: date_time,
|
83
|
+
author: author) + extname)
|
84
|
+
end
|
85
|
+
|
86
|
+
def standardize!(dirname: @dirname, basename_clean: @basename_clean,
|
87
|
+
extname: @extname, date_time: @date_time,
|
88
|
+
author: @author)
|
89
|
+
|
90
|
+
filename = standardize(dirname: dirname, basename_clean: basename_clean,
|
91
|
+
extname: extname, date_time: date_time,
|
92
|
+
author: author)
|
93
|
+
set_state(filename)
|
94
|
+
filename
|
95
|
+
end
|
96
|
+
|
97
|
+
def cleanse(dirname: @dirname, basename_clean: @basename_clean,
|
98
|
+
extname: @extname)
|
99
|
+
File.join(dirname, basename_clean + extname)
|
100
|
+
end
|
101
|
+
|
102
|
+
def cleanse!(dirname: @dirname, basename_clean: @basename_clean,
|
103
|
+
extname: @extname)
|
104
|
+
filename = cleanse(dirname: dirname, basename_clean: basename_clean,
|
105
|
+
extname: extname)
|
106
|
+
set_state(filename)
|
107
|
+
filename
|
108
|
+
end
|
109
|
+
|
110
|
+
def dirname=(dirname = @dirname)
|
111
|
+
@dirname = dirname
|
112
|
+
@filename = File.join(@dirname, @basename + @extname)
|
113
|
+
end
|
114
|
+
|
115
|
+
def date_time_ok?
|
116
|
+
@date_time != ZERO_DATE
|
117
|
+
end
|
118
|
+
|
119
|
+
def date_time_to_time
|
120
|
+
Time.new(@date_time.year, @date_time.month, @date_time.day,
|
121
|
+
@date_time.hour, @date_time.min, @date_time.sec)
|
122
|
+
# no use of @date_time.zone - assuming file's timezone always
|
123
|
+
# equals to photografer's computer timezone
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def set_state(filename)
|
129
|
+
@dirname = File.dirname(filename)
|
130
|
+
@extname = File.extname(filename)
|
131
|
+
@basename = File.basename(filename, @extname)
|
132
|
+
@filename = File.join(@dirname, @basename + @extname)
|
133
|
+
parse_basename
|
134
|
+
@basename_clean = @basename_part[:clean]
|
135
|
+
@date_time = reveal_date_time
|
136
|
+
@author = @basename_part[:author] || ''
|
137
|
+
end
|
138
|
+
|
139
|
+
def reveal_date_time
|
140
|
+
date = @basename_part[:date]
|
141
|
+
time = @basename_part[:time]
|
142
|
+
strptime_template = ''
|
143
|
+
strptime_string = ''
|
144
|
+
case date.size
|
145
|
+
when 4 # expecting Year e.g.2001
|
146
|
+
strptime_template += '%Y'
|
147
|
+
strptime_string += date
|
148
|
+
when 8 # expecting YYmmdd e.g.20010101
|
149
|
+
strptime_template += '%Y%m%d'
|
150
|
+
strptime_string += date
|
151
|
+
end
|
152
|
+
case time.size
|
153
|
+
when 4 # expecting HHMM e.g. 1025
|
154
|
+
strptime_template += '%H%M'
|
155
|
+
strptime_string += time
|
156
|
+
when 6 # expecting YHHMMSS e.g.102559
|
157
|
+
strptime_template += '%H%M%S'
|
158
|
+
strptime_string += time
|
159
|
+
end
|
160
|
+
|
161
|
+
return ZERO_DATE if strptime_string.empty?
|
162
|
+
DateTime.strptime(strptime_string, strptime_template)
|
163
|
+
|
164
|
+
rescue ArgumentError
|
165
|
+
return ZERO_DATE
|
166
|
+
end
|
167
|
+
|
168
|
+
def parse_basename
|
169
|
+
default = { prefix: '', clean: '', date: '',
|
170
|
+
time: '', author: '', id: '', flags: '' }
|
171
|
+
case @basename
|
172
|
+
# check YYYYmmdd-HHMMSS_AUT[ID]{FLAGS}cleanname
|
173
|
+
when /^(?<prefix>(?<date>\d{8})-(?<time>\d{6})_(?<author>\w{#{NICKNAME_MIN_SIZE},#{NICKNAME_MAX_SIZE}})\[(?<id>.*)\]\{(?<flags>.*)\})(?<clean>.*)/
|
174
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
175
|
+
clean: Regexp.last_match(:clean),
|
176
|
+
date: Regexp.last_match(:date),
|
177
|
+
time: Regexp.last_match(:time),
|
178
|
+
author: Regexp.last_match(:author),
|
179
|
+
id: Regexp.last_match(:id),
|
180
|
+
flags: Regexp.last_match(:flags))
|
181
|
+
|
182
|
+
# check YYYYmmdd-HHMMSS_AUT[ID]cleanname
|
183
|
+
when /^(?<prefix>(?<date>\d{8})-(?<time>\d{6})_(?<author>\w{#{NICKNAME_MIN_SIZE},#{NICKNAME_MAX_SIZE}})\[(?<id>.*)\])(?<clean>.*)/
|
184
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
185
|
+
clean: Regexp.last_match(:clean),
|
186
|
+
date: Regexp.last_match(:date),
|
187
|
+
time: Regexp.last_match(:time),
|
188
|
+
author: Regexp.last_match(:author),
|
189
|
+
id: Regexp.last_match(:id))
|
190
|
+
# STANDARD template
|
191
|
+
# check YYYYmmdd-HHMMSS_AUT cleanname
|
192
|
+
when /^(?<prefix>(?<date>\d{8})-(?<time>\d{6})_(?<author>\w{#{NICKNAME_MIN_SIZE},#{NICKNAME_MAX_SIZE}})[-\s_])(?<clean>.*)/
|
193
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
194
|
+
clean: Regexp.last_match(:clean),
|
195
|
+
date: Regexp.last_match(:date),
|
196
|
+
time: Regexp.last_match(:time),
|
197
|
+
author: Regexp.last_match(:author))
|
198
|
+
# check if name = YYYYmmdd-HHMM_AAA_name
|
199
|
+
when /^(?<prefix>(?<date>\d{8})-(?<time>\d{4})[-\s_](?<author>\w{#{NICKNAME_MIN_SIZE},#{NICKNAME_MAX_SIZE}})[-\s_])(?<clean>.*)/
|
200
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
201
|
+
clean: Regexp.last_match(:clean),
|
202
|
+
date: Regexp.last_match(:date),
|
203
|
+
time: Regexp.last_match(:time),
|
204
|
+
author: Regexp.last_match(:author))
|
205
|
+
# check if name = YYYYmmdd-HHMM_name
|
206
|
+
when /^(?<prefix>(?<date>\d{8})-(?<time>\d{4})[-\s_])(?<clean>.*)/
|
207
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
208
|
+
clean: Regexp.last_match(:clean),
|
209
|
+
date: Regexp.last_match(:date),
|
210
|
+
time: Regexp.last_match(:time))
|
211
|
+
# check if name = YYYYmmdd_name
|
212
|
+
when /^(?<prefix>(?<date>\d{8})[-\s_])(?<clean>.*)/
|
213
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
214
|
+
clean: Regexp.last_match(:clean),
|
215
|
+
date: Regexp.last_match(:date))
|
216
|
+
# check if name = YYYY_name
|
217
|
+
when /^(?<prefix>(?<date>\d{4})[-\s_])(?<clean>.*)/
|
218
|
+
@basename_part = default.merge(prefix: Regexp.last_match(:prefix),
|
219
|
+
clean: Regexp.last_match(:clean),
|
220
|
+
date: Regexp.last_match(:date))
|
221
|
+
else
|
222
|
+
@basename_part = default.merge(clean: @basename)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'phtools/version'
|
6
|
+
require 'phtools/utils/ruby_version.rb'
|
7
|
+
require 'phtools/utils/os_win.rb'
|
8
|
+
require 'phtools/utils/os_unix.rb'
|
9
|
+
require 'phtools/error.rb'
|
10
|
+
require 'phtools/ph_file.rb'
|
11
|
+
require 'docopt'
|
12
|
+
|
13
|
+
# Foto Tools
|
14
|
+
module PhTools
|
15
|
+
# Main class processing input stream
|
16
|
+
class Runner
|
17
|
+
def initialize(usage, file_type = [])
|
18
|
+
case Utils.os
|
19
|
+
when :windows
|
20
|
+
# workaround for win32
|
21
|
+
ARGV.map! { |a| a.encode('utf-8', 'filesystem') }
|
22
|
+
@os = Utils::OSWin.new
|
23
|
+
else
|
24
|
+
@os = Utils::OSUnix.new
|
25
|
+
end
|
26
|
+
@tool_name = File.basename($PROGRAM_NAME)
|
27
|
+
@options_cli = Docopt.docopt(usage, version: "v#{PhTools::VERSION}")
|
28
|
+
@file_type = file_type
|
29
|
+
PhTools.debug = true if @options_cli['--debug']
|
30
|
+
PhTools.puts_error "OPTIONS = #{@options_cli}" if PhTools.debug
|
31
|
+
|
32
|
+
validate_options
|
33
|
+
|
34
|
+
rescue Docopt::Exit => e
|
35
|
+
STDERR.puts e.message
|
36
|
+
exit 0
|
37
|
+
rescue => e
|
38
|
+
PhTools.puts_error "FATAL: #{e.message}", e
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def run!
|
43
|
+
return if STDIN.tty?
|
44
|
+
ARGV.clear
|
45
|
+
process_before
|
46
|
+
|
47
|
+
ARGF.each_line do |line|
|
48
|
+
filename = line.chomp
|
49
|
+
begin
|
50
|
+
FTFile.validate_file!(filename, @file_type)
|
51
|
+
ftfile = FTFile.new(filename)
|
52
|
+
@os.output process_file(ftfile)
|
53
|
+
rescue PhTools::Error => e
|
54
|
+
PhTools.puts_error "ERROR: '#{filename}' - #{e.message}", e
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
process_after
|
59
|
+
|
60
|
+
rescue SignalException
|
61
|
+
PhTools.puts_error 'EXIT on user interrupt Ctrl-C'
|
62
|
+
exit 1
|
63
|
+
rescue => e
|
64
|
+
PhTools.puts_error "FATAL: #{e.message}", e
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def validate_options
|
71
|
+
end
|
72
|
+
|
73
|
+
def process_before
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_file(file)
|
77
|
+
file
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_after
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'rbconfig'
|
6
|
+
|
7
|
+
# foto tools
|
8
|
+
module Utils
|
9
|
+
# determine OS
|
10
|
+
def self.os(os_string = RbConfig::CONFIG['host_os'])
|
11
|
+
case os_string
|
12
|
+
when /darwin/i then :macosx
|
13
|
+
when /linux/i then :linux
|
14
|
+
when /mswin|mingw|w32/i then :windows
|
15
|
+
else :unknown
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# OS specific output implementation
|
20
|
+
class OS
|
21
|
+
def output(message)
|
22
|
+
STDOUT.puts prepare(message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
require 'phtools/utils/os'
|
6
|
+
|
7
|
+
module Utils
|
8
|
+
# OS platfor related logic
|
9
|
+
class OSWin < OS
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def prepare(message)
|
14
|
+
message.to_s.gsub(/#{"/"}/, '\\')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
# (c) ANB Andrew Bizyaev
|
4
|
+
|
5
|
+
RUBY_VERSION_WANTED = '2.0.0'
|
6
|
+
|
7
|
+
begin
|
8
|
+
fail "Ruby version must be >= #{RUBY_VERSION_WANTED}" if
|
9
|
+
RUBY_VERSION < RUBY_VERSION_WANTED
|
10
|
+
rescue => e
|
11
|
+
STDERR.puts e.message
|
12
|
+
exit 1
|
13
|
+
end
|
data/lib/phtools/version.rb
CHANGED
data/lib/phtools.rb
CHANGED
@@ -1,10 +1,35 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: UTF-8
|
3
3
|
# (c) ANB Andrew Bizyaev
|
4
|
-
require
|
4
|
+
require 'phtools/version'
|
5
|
+
require 'pharrange'
|
6
|
+
require 'phbackup'
|
7
|
+
require 'phclname'
|
8
|
+
require 'phevent'
|
9
|
+
require 'phfixdate'
|
10
|
+
require 'phfixfmd'
|
11
|
+
require 'phls'
|
12
|
+
require 'phmtags'
|
13
|
+
require 'phrename'
|
14
|
+
require 'phtagset'
|
5
15
|
|
6
16
|
module PhTools
|
7
17
|
def self.about
|
8
|
-
|
18
|
+
about = <<TEXT
|
19
|
+
phtools v#{VERSION} is a bundle of small CLI tools for arranging, renaming, tagging
|
20
|
+
of the photo and video files. Helps to keep your photo-video assets in order.
|
21
|
+
Please run phtools in a terminal via CLI commands:
|
22
|
+
pharrange\t(#{Phrename::about}),
|
23
|
+
phbackup\t(#{Phbackup::about}),
|
24
|
+
phclname\t(#{Phclname::about}),
|
25
|
+
phevent\t(#{Phevent::about}),
|
26
|
+
phfixdate\t(#{Phfixdate::about}),
|
27
|
+
phfixmd\t(#{Phfixfmd::about}),
|
28
|
+
phls\t(#{Phls::about}),
|
29
|
+
phmtags\t(#{Phmtags::about}),
|
30
|
+
phrename \t(#{Phrename::about}),
|
31
|
+
phtagset\t(#{Phtagset::about}).
|
32
|
+
For more information run these commands with -h option.
|
33
|
+
TEXT
|
9
34
|
end
|
10
35
|
end
|
data/phtools.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency 'rspec', '~> 2.9'
|
32
32
|
spec.add_development_dependency 'rspec-its', '~> 1.0'
|
33
33
|
spec.add_development_dependency 'cucumber', '~> 1.3'
|
34
|
-
spec.add_development_dependency 'aruba', '~> 0.
|
34
|
+
spec.add_development_dependency 'aruba', '~> 0.14'
|
35
35
|
spec.add_development_dependency 'fuubar'
|
36
36
|
spec.add_development_dependency 'rubocop'
|
37
37
|
spec.add_development_dependency 'pry'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phtools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Bizyaev
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: '0.14'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: '0.14'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: fuubar
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -211,6 +211,7 @@ description: A bundle of small CLI tools for arranging, renaming, tagging of the
|
|
211
211
|
email:
|
212
212
|
- andrew.bizyaev@gmail.com
|
213
213
|
executables:
|
214
|
+
- phls
|
214
215
|
- phtools
|
215
216
|
extensions: []
|
216
217
|
extra_rdoc_files: []
|
@@ -226,8 +227,26 @@ files:
|
|
226
227
|
- bin/console
|
227
228
|
- bin/setup
|
228
229
|
- bin/stmux
|
230
|
+
- exe/phls
|
229
231
|
- exe/phtools
|
232
|
+
- lib/pharrange.rb
|
233
|
+
- lib/phbackup.rb
|
234
|
+
- lib/phclname.rb
|
235
|
+
- lib/phevent.rb
|
236
|
+
- lib/phfixdate.rb
|
237
|
+
- lib/phfixfmd.rb
|
238
|
+
- lib/phls.rb
|
239
|
+
- lib/phmtags.rb
|
240
|
+
- lib/phrename.rb
|
241
|
+
- lib/phtagset.rb
|
230
242
|
- lib/phtools.rb
|
243
|
+
- lib/phtools/error.rb
|
244
|
+
- lib/phtools/ph_file.rb
|
245
|
+
- lib/phtools/runner.rb
|
246
|
+
- lib/phtools/utils/os.rb
|
247
|
+
- lib/phtools/utils/os_unix.rb
|
248
|
+
- lib/phtools/utils/os_win.rb
|
249
|
+
- lib/phtools/utils/ruby_version.rb
|
231
250
|
- lib/phtools/version.rb
|
232
251
|
- phtools.gemspec
|
233
252
|
homepage: https://github.com/AndrewBiz/phtools.git
|