fisherman 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df9bf0fb1b8cf3b4671971b6a3e8e25f64d845ce
4
- data.tar.gz: 0e160ccdbaa9e82773fc338f80c0d17a332caf16
3
+ metadata.gz: f83b4c77ef74d96dd704d69120dba125edc2646e
4
+ data.tar.gz: f4bdddd99281320cdf6951519dd039ccfa77a8f4
5
5
  SHA512:
6
- metadata.gz: 11cfc89593f018d57c3a989b0095d23aee4ffd7f60ef7cb94a9c71f5721d83c4bddae3644d689bf2edfd1259f12b0dba0e0b5d00e6a8daec1ee43f7f0fe43bb3
7
- data.tar.gz: 6bceba0ac1aa3f0f0f2230a204e34aa07e494a92b0960f27329e13aa2edf37b2b26a320cd05122a0924016e0175a8ddc1d15ccae18bf59b2f5430a6e7f27f575
6
+ metadata.gz: ceba06213641853638846fd3709fa6c5bd290152d716201d43086dc994359fd94354fd86459695dbfcd9d39087308c72b4524c82a008d9edb0a599667935079d
7
+ data.tar.gz: 2c5770bcc4af1caf5fdff67746bf6a5f8ecc15a0013d86d36b03f5ed8d7ce1f99438ec77c997ec805214fcb301de25867b060fc7d5329ddc7379fde87d3faccf
data/README.md CHANGED
@@ -36,11 +36,16 @@ photos. To use it:
36
36
  ```bash
37
37
  > TOKEN=<access_token from site>
38
38
  > # Output a JSON file describing each ablum
39
- > extract_snapfish_albums $TOKEN > albums.json
39
+ > extract_snapfish_albums --token $TOKEN --output json > albums.json
40
40
  > # Output a bash script that downloads all album photos
41
- > BASH_SCRIPT=true extract_snapfish_albums $TOKEN > download.sh
41
+ > extract_snapfish_albums --token $TOKEN --output bash > download.sh
42
42
  ```
43
43
 
44
+ The generated Bash script will in turn use the `download_snapfish_album` script.
45
+ This script takes a token and an album ID and downloads all of the album's
46
+ photos to a directory underneath the current one. The album directory is named
47
+ with the date of the album and first, then the title of the album.
48
+
44
49
  ## Development
45
50
 
46
51
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fisherman'
4
+ require 'fileutils'
5
+ require 'getoptlong'
6
+
7
+ at_exit do
8
+ last_exception = $!
9
+ if last_exception.is_a?(Faraday::ClientError)
10
+ if last_exception.response && last_exception.response[:status] == 401
11
+ puts "** Snapfish authentication failed."
12
+ puts "** Please make sure your token is valid and try again"
13
+ exit! 1
14
+ end
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ opts = GetoptLong.new(
20
+ ['--token', '-t', GetoptLong::REQUIRED_ARGUMENT],
21
+ ['--album-id', '-a', GetoptLong::REQUIRED_ARGUMENT]
22
+ )
23
+
24
+ opts.each do |op, value|
25
+ case op
26
+ when '--token'
27
+ @token = value
28
+ when '--album-id'
29
+ @album_id = value
30
+ end
31
+ end
32
+
33
+ if !@token || !@album_id
34
+ puts <<-HELP
35
+ ** Usage: #{$0} --token <token> --album-id <album-id>
36
+ **
37
+ ** Will download the contents of the album into a directory relative
38
+ ** to the current directory, and named with the album date plus the
39
+ ** album name.
40
+ HELP
41
+ exit 1
42
+ end
43
+
44
+ def mark_as_thumbnail(file_path, asset)
45
+ file_path.gsub(/(\.#{asset.file_extension})$/) { |ext| "_#{ext}" }
46
+ end
47
+
48
+ def download_photo(photo, destination_file)
49
+ downloader = Fisherman::Downloader.new(photo)
50
+ if file_contents = downloader.download
51
+ if downloader.thumbnail?
52
+ destination_file = mark_as_thumbnail(destination_file, photo)
53
+ end
54
+ File.open(destination_file, 'wb') { |f| f.write(file_contents) }
55
+ puts "Wrote #{destination_file} (#{file_contents.size} bytes)"
56
+ else
57
+ puts "Could not download photo #{photo.id}"
58
+ end
59
+ end
60
+
61
+ Snapfish.connect(@token)
62
+
63
+ puts "Gathering information about album #{@album_id}"
64
+
65
+ album = Snapfish::Album.get(@album_id)
66
+ if album.nil?
67
+ puts "** Could not find album with ID #{@album_id}"
68
+ exit 1
69
+ end
70
+
71
+ album_directory = "#{album.created_at.strftime('%Y-%m-%d')} - #{album.name}"
72
+ FileUtils.mkdir_p(album_directory)
73
+
74
+ puts "Downloading album to directory \"#{album_directory}\""
75
+
76
+ album.assets.each_with_index do |photo, index|
77
+ file_name = index.to_s.rjust(4, '0') + '.' + photo.file_extension
78
+ destination_file = File.join(album_directory, file_name)
79
+ download_photo(photo, destination_file)
80
+ end
@@ -1,51 +1,74 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'fisherman'
4
- require 'shellwords'
4
+ require 'getoptlong'
5
5
 
6
- # TODO:
7
- # - Log in using email/password
8
- # - Use GetOpt for args
6
+ at_exit do
7
+ last_exception = $!
8
+ if last_exception.is_a?(Faraday::ClientError)
9
+ if last_exception.response && last_exception.response[:status] == 401
10
+ puts "** Snapfish authentication failed."
11
+ puts "** Please make sure your token is valid and try again"
12
+ exit! 1
13
+ end
14
+ end
15
+ end
16
+
17
+ opts = GetoptLong.new(
18
+ ['--token', '-t', GetoptLong::REQUIRED_ARGUMENT],
19
+ ['--output', '-o', GetoptLong::REQUIRED_ARGUMENT]
20
+ )
21
+
22
+ @output = :json
23
+
24
+ opts.each do |op, value|
25
+ case op
26
+ when '--token'
27
+ @token = value
28
+ when '--output'
29
+ if value.to_sym == :bash
30
+ @output = :bash
31
+ end
32
+ end
33
+ end
9
34
 
10
- token = ARGV[0]
11
- if token.nil?
12
- puts "** Usage: snapfish_albums <TOKEN>"
35
+ if !@token
36
+ puts <<-HELP
37
+ ** Usage: #{$0} --token <token> --output <json|bash>
38
+ **
39
+ ** Will find all album data for the account with the current token.
40
+ ** Output is either a JSON object representing all album and photo
41
+ ** information (the default), or a bash script that makes calls to
42
+ ** download each album.
43
+ HELP
13
44
  exit 1
14
45
  end
15
46
 
16
- Snapfish.connect(token)
47
+ Snapfish.connect(@token)
17
48
 
18
49
  STDERR.puts 'Gathering album information...'
19
50
 
20
- albums_json = Snapfish::AlbumCollection.new.map do |album|
21
- photos_json = album.assets.map(&:as_json)
22
- album.as_json.merge(photos: photos_json)
23
- end
24
-
51
+ albums_json = Snapfish::AlbumCollection.new.map(&:as_json)
25
52
  albums_json.sort_by! { |album| album[:created_at] }
26
53
 
27
- if ENV['BASH_SCRIPT']
54
+ if @output == :bash
28
55
  puts <<-BASH
29
56
  #!/bin/bash
30
57
  #
31
- # Downloads all Snapfish albums and sorts them into folders by album title.
32
- # Folders are created in the current directory.
33
- # Requires `wget`.
58
+ # Downloads Snapfish albums and sorts them into folders by album title.
34
59
  #
60
+ TOKEN='#{@token}'
61
+
62
+ set -e
63
+
35
64
  BASH
36
65
 
37
- albums_json.each_with_index do |album_json, album_index|
38
- directory_name = [
39
- album_index.to_s.rjust(4, '0'),
40
- Shellwords.escape(album_json[:name])
41
- ].join(' - ')
42
- puts "mkdir -p \"#{directory_name}\""
43
- album_json[:photos].each_with_index do |photo_json, photo_index|
44
- file_name = photo_index.to_s.rjust(4, '0') + '.' + photo_json[:file_extension]
45
- destination_path = File.join(directory_name, file_name)
46
- puts "wget \"#{photo_json[:url]}\" -O \"#{destination_path}\""
47
- end
66
+ albums_json.each do |album_json|
67
+ album_id = album_json[:id]
68
+ puts "# #{album_json[:name]}"
69
+ puts "download_snapfish_album --token $TOKEN --album-id #{album_id}"
70
+ puts
48
71
  end
49
72
  else
50
- puts JSON.pretty_generate(albums_json)
73
+ puts JSON.pretty_generate(albums: albums_json)
51
74
  end
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
+ spec.add_dependency "faraday", "~> 0.11.0"
24
+ spec.add_dependency "faraday_middleware", "~> 0.11.0"
25
+
23
26
  spec.add_development_dependency "bundler", "~> 1.13"
24
27
  spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "faraday", "~> 0.11.0"
26
- spec.add_development_dependency "faraday_middleware", "~> 0.11.0"
27
-
28
28
  spec.add_development_dependency "pry-byebug"
29
29
  end
@@ -3,6 +3,7 @@ require 'faraday'
3
3
  require 'faraday_middleware'
4
4
 
5
5
  require 'fisherman/version'
6
+ require 'fisherman/downloader'
6
7
  require 'snapfish'
7
8
 
8
9
  module Fisherman
@@ -0,0 +1,54 @@
1
+ module Fisherman
2
+ class Downloader
3
+ THUMBNAIL_API_URL = 'https://tnl.snapfish.com/assetrenderer/v2/thumbnail/'
4
+
5
+ attr_reader :asset
6
+
7
+ def initialize(asset)
8
+ @asset = asset
9
+ @thumbnail = false
10
+ end
11
+
12
+ def thumbnail?
13
+ @thumbnail
14
+ end
15
+
16
+ def download
17
+ response = Faraday.get(asset.hires_url)
18
+ # The Snapfish main CDN sometimes doesn't have older photos. In that case,
19
+ # the only way to get them is via their thumbnail API. We ask for the
20
+ # largest thumbnail we can get. If you were to download this same asset from
21
+ # the site, the large thumbnail is what it would return.
22
+ if response.status == 404
23
+ @thumbnail = true
24
+ response = download_thumbnail(asset)
25
+ end
26
+ response && response.body
27
+ end
28
+
29
+ private
30
+
31
+ def download_thumbnail(asset)
32
+ retries_remaining = 3
33
+ begin
34
+ thumbnail_url = File.join(THUMBNAIL_API_URL, asset.snapfish_ref)
35
+ response = Faraday.get(thumbnail_url,
36
+ height: asset.height,
37
+ access_token: Snapfish.token
38
+ )
39
+ if response.status == 302
40
+ response = Faraday.get(response.headers['location'])
41
+ end
42
+ rescue Faraday::ResourceNotFound
43
+ response = nil
44
+ rescue Faraday::ConnectionFailed => e
45
+ retries_remaining -= 1
46
+ if retries_remaining == 0
47
+ raise e
48
+ else
49
+ retry
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Fisherman
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -4,11 +4,13 @@ require 'snapfish/album'
4
4
  require 'snapfish/album_collection'
5
5
 
6
6
  module Snapfish
7
- API_BASE_URL = 'https://assets-aus.snapfish.com/pict/v2/collection'
7
+ API_BASE_URL = 'https://assets-aus.snapfish.com/pict/v2/'
8
8
 
9
9
  def self.connect(token)
10
10
  connection = Faraday.new(url: API_BASE_URL) do |conn|
11
+ conn.request :json
11
12
  conn.response :json
13
+ conn.response :raise_error
12
14
  conn.adapter Faraday.default_adapter
13
15
  end
14
16
 
@@ -18,4 +20,8 @@ module Snapfish
18
20
 
19
21
  Snapfish::Base.connection = connection
20
22
  end
23
+
24
+ def self.token
25
+ Snapfish::Base.connection.headers['access_token']
26
+ end
21
27
  end
@@ -1,5 +1,12 @@
1
1
  module Snapfish
2
2
  class Album < Base
3
+ def self.get(album_id)
4
+ response = connection.get("collection/#{album_id}")
5
+ if response.success?
6
+ Snapfish::Album.new(response.body['entities'].first)
7
+ end
8
+ end
9
+
3
10
  def id
4
11
  json['id']
5
12
  end
@@ -15,7 +22,7 @@ module Snapfish
15
22
  def assets
16
23
  album_json = connection.get(album_url,
17
24
  assetType: 'ALL',
18
- limit: 100,
25
+ limit: asset_count,
19
26
  skip: 0,
20
27
  sortCriteria: 'dateTaken',
21
28
  sortOrder: 'ascending'
@@ -28,6 +35,7 @@ module Snapfish
28
35
 
29
36
  def as_json
30
37
  {
38
+ id: id,
31
39
  name: name,
32
40
  created_at: created_at
33
41
  }
@@ -36,7 +44,7 @@ module Snapfish
36
44
  private
37
45
 
38
46
  def album_url
39
- "#{id}/assets"
47
+ "collection/#{id}/assets"
40
48
  end
41
49
  end
42
50
  end
@@ -9,7 +9,7 @@ module Snapfish
9
9
  private
10
10
 
11
11
  def all
12
- albums_json = connection.get('monthIndex',
12
+ albums_json = connection.get('collection/monthIndex',
13
13
  skip: 0,
14
14
  limit: 100,
15
15
  minCollection: 1000,
@@ -1,5 +1,13 @@
1
1
  module Snapfish
2
2
  class Asset < Base
3
+ def id
4
+ json['_id']
5
+ end
6
+
7
+ def snapfish_ref
8
+ find_file('THUMBNAIL')['url']
9
+ end
10
+
3
11
  def filename
4
12
  os_metadata = extract_tag('systemTags', 'OS_METADATA')
5
13
  if os_metadata && os_metadata = os_metadata['value']
@@ -7,6 +15,18 @@ module Snapfish
7
15
  end
8
16
  end
9
17
 
18
+ def size
19
+ json['files'].first['size']
20
+ end
21
+
22
+ def width
23
+ find_file('HIRES')['width']
24
+ end
25
+
26
+ def height
27
+ find_file('HIRES')['height']
28
+ end
29
+
10
30
  def caption
11
31
  extract_tag('userTags', 'caption')
12
32
  end
@@ -16,19 +36,27 @@ module Snapfish
16
36
  end
17
37
 
18
38
  def hires_url
19
- hires_file = json['files'].find { |f| f['fileType'] == 'HIRES' }
20
- if hires_file
21
- hires_file['url']
22
- end
39
+ find_file('HIRES')['url']
23
40
  end
24
41
 
25
42
  def as_json(options = {})
26
43
  {
44
+ id: id,
45
+ ref: snapfish_ref,
27
46
  filename: filename,
28
47
  file_extension: file_extension,
48
+ width: width,
49
+ height: height,
50
+ size: size,
29
51
  caption: caption,
30
52
  url: hires_url
31
53
  }
32
54
  end
55
+
56
+ private
57
+
58
+ def find_file(type)
59
+ json['files'].find { |f| f['fileType'] == type} || {}
60
+ end
33
61
  end
34
62
  end
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fisherman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rusty Geldmacher
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-11 00:00:00.000000000 Z
11
+ date: 2017-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.13'
20
- type: :development
19
+ version: 0.11.0
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.13'
26
+ version: 0.11.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: faraday_middleware
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
34
- type: :development
33
+ version: 0.11.0
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 0.11.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: faraday
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.11.0
47
+ version: '1.13'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.11.0
54
+ version: '1.13'
55
55
  - !ruby/object:Gem::Dependency
56
- name: faraday_middleware
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.11.0
61
+ version: '10.0'
62
62
  type: :development
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.11.0
68
+ version: '10.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry-byebug
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -84,6 +84,7 @@ description: API wrapper for Snapfish's unpublished APIs
84
84
  email:
85
85
  - russell.geldmacher@gmail.com
86
86
  executables:
87
+ - download_snapfish_album
87
88
  - extract_snapfish_albums
88
89
  extensions: []
89
90
  extra_rdoc_files: []
@@ -95,9 +96,11 @@ files:
95
96
  - Rakefile
96
97
  - bin/console
97
98
  - bin/setup
99
+ - exe/download_snapfish_album
98
100
  - exe/extract_snapfish_albums
99
101
  - fisherman.gemspec
100
102
  - lib/fisherman.rb
103
+ - lib/fisherman/downloader.rb
101
104
  - lib/fisherman/version.rb
102
105
  - lib/snapfish.rb
103
106
  - lib/snapfish/album.rb