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 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
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
11
+
12
+ # Temporary images during testing
13
+ /images
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in photo_party_sync.gemspec
4
+ gemspec
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,14 @@
1
+ require 'logger'
2
+
3
+ module PhotoPartySync
4
+ # Global logging module
5
+ module Logging
6
+ def logger
7
+ Logging.logger
8
+ end
9
+
10
+ def self.logger
11
+ @logger ||= ::Logger.new(STDOUT)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ # Provides a global version number
2
+ module PhotoPartySync
3
+ VERSION = '0.1.0'
4
+ 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
@@ -0,0 +1,2 @@
1
+ Metrics/LineLength:
2
+ Max: 120
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: []