photo_party_sync 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +48 -0
- data/Rakefile +1 -0
- data/bin/photo_party_sync +49 -0
- data/lib/photo_party_sync.rb +43 -0
- data/lib/photo_party_sync/card.rb +84 -0
- data/lib/photo_party_sync/cardfile.rb +81 -0
- data/lib/photo_party_sync/logger.rb +14 -0
- data/lib/photo_party_sync/version.rb +4 -0
- data/photo_party_sync.gemspec +27 -0
- data/rubocop.yml +2 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f576e5c044f8b23f5bb137201cad99cbaa28339c
|
4
|
+
data.tar.gz: 5e9be44a14fa5b27909424338c915e7630ee9c9d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 35c65eaa9b09e55fa7b42190358ed24da303c2262d66dd29054633cb4acf794d3af6f63a88aa0667350131db6fffb43a1157cf13283014502f225286cbea7267
|
7
|
+
data.tar.gz: 85cfa14e93948a9c49e720ce7ef8b66ac32a1d6706dd3a5499052ba53b2a6760704c29973fc74f6c98bf2e58331280075d4e400c5586a385428b342f7799a00c
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Gerrit Visscher
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# PhotoPartySync
|
2
|
+
|
3
|
+
PhotoPartySync is a small gem that scans for given sd cards in the network and downloads all available images to
|
4
|
+
your local machine.
|
5
|
+
|
6
|
+
# Why?
|
7
|
+
|
8
|
+
For example at a wedding or a party you give out some wireless sd cards to your guests. They put them in their cameras
|
9
|
+
and all their photos at the event will be collected by one computer which can display them in a slideshow.
|
10
|
+
|
11
|
+
## Requirements
|
12
|
+
|
13
|
+
You need sd card compatible with the Toshiba Flash Air card.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
$ gem install photo_party_sync
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Run
|
22
|
+
|
23
|
+
$ photo_party_sync --help
|
24
|
+
|
25
|
+
to get a list of options. You need to know the IP address of your sd card(s). Run
|
26
|
+
|
27
|
+
$ photo_party_sync --card <ip>
|
28
|
+
|
29
|
+
to let the script check for a card. It can take a while for the card to login into your network. If you have
|
30
|
+
multiple cards, you can add all of them to a text file and supply this file to photo_party_sync.
|
31
|
+
|
32
|
+
$ photo_party_sync --card-file <filename>
|
33
|
+
|
34
|
+
The parameter --watch will let the script keep polling all cards and download all new images once the appear.
|
35
|
+
|
36
|
+
$ photo_party_sync --card <ip> --watch
|
37
|
+
|
38
|
+
**HINT:** You can add all your cards addresses to your host file (/etc/hosts on Linux/Mac) to have nive names for them.
|
39
|
+
These names can be used in the card parameter or the list file. This is especially helpful since photo_party_sync uses
|
40
|
+
these names as folders to separate the images.
|
41
|
+
|
42
|
+
## Contributing
|
43
|
+
|
44
|
+
1. Fork it ( https://github.com/kayssun/photo_party_sync/fork )
|
45
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
46
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
47
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
48
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'photo_party_sync'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
options[:quiet] = false
|
9
|
+
# options[:verbose] = false
|
10
|
+
options[:cards] = []
|
11
|
+
options[:dir] = ''
|
12
|
+
options[:watch] = false
|
13
|
+
|
14
|
+
optparse = OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: upload_files.rb [options]\nDefault host: #{options[:server]}"
|
16
|
+
|
17
|
+
opts.on('-q', '--quiet', 'Only output error messages') do
|
18
|
+
options[:quiet] = true
|
19
|
+
end
|
20
|
+
|
21
|
+
# opts.on("-v", "--verbose", "Show extra output") do
|
22
|
+
# @options[:verbose] = true
|
23
|
+
# end
|
24
|
+
|
25
|
+
opts.on('--watch', 'Run forever an keep watching for available cards') do
|
26
|
+
options[:watch] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-c [CARD]', '--card [CARD]', 'Name of the card to parse') do |card|
|
30
|
+
options[:cards] << PhotoPartySync::Card.new(card)
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-f [CARD]', '--card-file [CARD]', 'Name of the card file to parse (one card per line)') do |card|
|
34
|
+
options[:cards] = open(card).read.split("\n").collect { |name| PhotoPartySync::Card.new(name) }
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-d [DIR]', '--directory [DIR]', 'Directory to where the files are downloaded') do |dir|
|
38
|
+
options[:dir] = File.realdirpath(dir)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
optparse.parse!
|
42
|
+
|
43
|
+
photo_sync = PhotoPartySync::Watcher.new(options)
|
44
|
+
|
45
|
+
if options[:watch]
|
46
|
+
photo_sync.watch
|
47
|
+
else
|
48
|
+
photo_sync.check_all
|
49
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'photo_party_sync/version'
|
2
|
+
require 'photo_party_sync/card'
|
3
|
+
require 'photo_party_sync/logger'
|
4
|
+
|
5
|
+
module PhotoPartySync
|
6
|
+
# Calls all cards
|
7
|
+
class Watcher
|
8
|
+
include PhotoPartySync::Logging
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
if options[:cards].empty?
|
12
|
+
STDERR.puts 'You need to supply a card name.'
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
@options = options
|
17
|
+
|
18
|
+
@options[:cards].each { |card| card.target_base_path = @options[:dir] } unless @options[:dir].empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def watch
|
22
|
+
loop do
|
23
|
+
check_all
|
24
|
+
sleep 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_all
|
29
|
+
@options[:cards].each do |cardname|
|
30
|
+
check_card cardname
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_card(card)
|
35
|
+
if card.ready?
|
36
|
+
logger.info "Found #{card.name}, getting file list..." unless @options[:quiet]
|
37
|
+
card.download_all
|
38
|
+
else
|
39
|
+
logger.warn "Cannot reach #{card.name}. Skipping." unless @options[:quiet]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'photo_party_sync/logger'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'photo_party_sync/cardfile.rb'
|
5
|
+
|
6
|
+
module PhotoPartySync
|
7
|
+
# Represents a remote sd card - sets up connects and gets file list
|
8
|
+
class Card
|
9
|
+
attr_accessor :target_base_path
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
include PhotoPartySync::Logging
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def ready?
|
19
|
+
system("ping -c 1 -t 1 '#{@name}' > /dev/null 2>&1")
|
20
|
+
end
|
21
|
+
|
22
|
+
def folders
|
23
|
+
@folders ||= read_folders
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_folders
|
27
|
+
folder_list = entry_list('/DCIM')
|
28
|
+
folder_list.collect { |row| row.split(',')[1] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def files
|
32
|
+
found_files = []
|
33
|
+
|
34
|
+
folders.each do |folder|
|
35
|
+
file_list = entry_list("/DCIM/#{folder}")
|
36
|
+
file_list.each do |file_row|
|
37
|
+
file_info = file_row.split ','
|
38
|
+
file = CardFile.new
|
39
|
+
file.path = file_info[0]
|
40
|
+
file.name = file_info[1]
|
41
|
+
file.size = file_info[2]
|
42
|
+
file.attributes = file_info[3]
|
43
|
+
file.date = file_info[4].to_i
|
44
|
+
file.time = file_info[5].to_i
|
45
|
+
file.target_base_path = @target_base_path
|
46
|
+
file.card = @name
|
47
|
+
found_files << file
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
found_files
|
52
|
+
end
|
53
|
+
|
54
|
+
def download_all
|
55
|
+
files.each do |file|
|
56
|
+
if file.valid? && !file.exist?
|
57
|
+
logger.info "Downloading #{file.name}..." unless @options[:quiet]
|
58
|
+
file.download
|
59
|
+
else
|
60
|
+
logger.info "Skipping file #{file.name}..."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid?
|
66
|
+
open("http://#{@options[:card]}/command.cgi?op=100&DIR=/DCIM")
|
67
|
+
true
|
68
|
+
rescue SocketError
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def entry_list(folder)
|
75
|
+
doc = Nokogiri::HTML(open("http://#{@name}/command.cgi?op=100&DIR=#{folder}", read_timeout: 2))
|
76
|
+
entries = doc.css('p').first.content.split("\n")
|
77
|
+
entries.shift
|
78
|
+
entries
|
79
|
+
rescue StandardError => e
|
80
|
+
logger.warn e.message
|
81
|
+
[]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module PhotoPartySync
|
4
|
+
# Provides access to a single file on the remote sd card
|
5
|
+
class CardFile
|
6
|
+
YEAR_MASK = 0b1111111000000000
|
7
|
+
MONTH_MASK = 0b0000000111100000
|
8
|
+
DAY_MASK = 0b0000000000011111
|
9
|
+
|
10
|
+
HOUR_MASK = 0b1111100000000000
|
11
|
+
MINUTE_MASK = 0b0000011111100000
|
12
|
+
SECOND_MASK = 0b0000000000011111
|
13
|
+
|
14
|
+
BASE_PATH = File.realdirpath('images')
|
15
|
+
|
16
|
+
attr_accessor :path, :name, :size, :attributes, :card, :target_base_path
|
17
|
+
attr_reader :date, :time
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@path = ''
|
21
|
+
@name = ''
|
22
|
+
@size = ''
|
23
|
+
@attributes = ''
|
24
|
+
@card = ''
|
25
|
+
@target_base_path = BASE_PATH
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{@path}/#{@name} (#{@size} Bytes, #{@date} #{@time})"
|
30
|
+
end
|
31
|
+
|
32
|
+
def local_filename
|
33
|
+
"#{@date}_#{@time}_#{@card}" + File.extname(@name).downcase
|
34
|
+
end
|
35
|
+
|
36
|
+
def temp_path
|
37
|
+
"temp/#{@card}/#{local_filename}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def local_path
|
41
|
+
@target_base_path + "/#{card}/#{local_filename}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def date=(bits)
|
45
|
+
year = 1980 + ((bits & CardFile::YEAR_MASK) >> 9)
|
46
|
+
month = (bits & CardFile::MONTH_MASK) >> 5
|
47
|
+
day = (bits & CardFile::DAY_MASK)
|
48
|
+
@date = "#{year}-#{month.to_s.rjust(2, '0')}-#{day.to_s.rjust(2, '0')}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def time=(bits)
|
52
|
+
hour = (bits & CardFile::HOUR_MASK) >> 11
|
53
|
+
minute = (bits & CardFile::MINUTE_MASK) >> 5
|
54
|
+
second = (bits & CardFile::SECOND_MASK)
|
55
|
+
@time = "#{hour.to_s.rjust(2, '0')}-#{minute.to_s.rjust(2, '0')}-#{second.to_s.rjust(2, '0')}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def exist?
|
59
|
+
File.exist?(local_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
def valid?
|
63
|
+
File.extname(@name).downcase == '.jpg' && @name.upcase != 'FA000001.JPG'
|
64
|
+
end
|
65
|
+
|
66
|
+
def download
|
67
|
+
# logger.info "Downloading: http://#{card}#{path}/#{name}"
|
68
|
+
create_directories
|
69
|
+
continue = File.exist?(temp_path) ? ' --continue' : ''
|
70
|
+
timeout = ' --timeout=5 --tries=1 --dns-timeout=1'
|
71
|
+
success = system("wget 'http://#{card}#{path}/#{name}' -O '#{temp_path}'#{continue}#{timeout}")
|
72
|
+
FileUtils.mv(temp_path, local_path) if success
|
73
|
+
success
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_directories
|
77
|
+
FileUtils.mkdir_p(File.dirname(temp_path)) unless Dir.exist?(File.dirname(temp_path))
|
78
|
+
FileUtils.mkdir_p(File.dirname(local_path)) unless Dir.exist?(File.dirname(local_path))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'photo_party_sync/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'photo_party_sync'
|
8
|
+
spec.version = PhotoPartySync::VERSION
|
9
|
+
spec.authors = ['Gerrit Visscher']
|
10
|
+
spec.email = ['gerrit@visscher.de']
|
11
|
+
|
12
|
+
spec.summary = 'Copies images from wireless sd cards to the local machine'
|
13
|
+
spec.description = 'PhotoPartySync is a small gem that scans for given sd cards in the network and downloads'\
|
14
|
+
'all available images to your local machine.'
|
15
|
+
spec.homepage = 'https://github.com/kayssun/photo_party_sync'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^(test|spec|features)\//) }
|
19
|
+
spec.bindir = 'bin'
|
20
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
|
26
|
+
spec.add_dependency 'nokogiri', '~> 1'
|
27
|
+
end
|
data/rubocop.yml
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: photo_party_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gerrit Visscher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1'
|
55
|
+
description: PhotoPartySync is a small gem that scans for given sd cards in the network
|
56
|
+
and downloadsall available images to your local machine.
|
57
|
+
email:
|
58
|
+
- gerrit@visscher.de
|
59
|
+
executables:
|
60
|
+
- photo_party_sync
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- CODE_OF_CONDUCT.md
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/photo_party_sync
|
71
|
+
- lib/photo_party_sync.rb
|
72
|
+
- lib/photo_party_sync/card.rb
|
73
|
+
- lib/photo_party_sync/cardfile.rb
|
74
|
+
- lib/photo_party_sync/logger.rb
|
75
|
+
- lib/photo_party_sync/version.rb
|
76
|
+
- photo_party_sync.gemspec
|
77
|
+
- rubocop.yml
|
78
|
+
homepage: https://github.com/kayssun/photo_party_sync
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.4.5
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Copies images from wireless sd cards to the local machine
|
102
|
+
test_files: []
|