fisherman 0.1.0 → 0.3.0

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 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