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 +4 -4
- data/README.md +7 -2
- data/exe/download_snapfish_album +80 -0
- data/exe/extract_snapfish_albums +52 -29
- data/fisherman.gemspec +3 -3
- data/lib/fisherman.rb +1 -0
- data/lib/fisherman/downloader.rb +54 -0
- data/lib/fisherman/version.rb +1 -1
- data/lib/snapfish.rb +7 -1
- data/lib/snapfish/album.rb +10 -2
- data/lib/snapfish/album_collection.rb +1 -1
- data/lib/snapfish/asset.rb +32 -4
- metadata +19 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f83b4c77ef74d96dd704d69120dba125edc2646e
|
4
|
+
data.tar.gz: f4bdddd99281320cdf6951519dd039ccfa77a8f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
>
|
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
|
data/exe/extract_snapfish_albums
CHANGED
@@ -1,51 +1,74 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'fisherman'
|
4
|
-
require '
|
4
|
+
require 'getoptlong'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
11
|
-
|
12
|
-
|
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
|
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
|
54
|
+
if @output == :bash
|
28
55
|
puts <<-BASH
|
29
56
|
#!/bin/bash
|
30
57
|
#
|
31
|
-
# Downloads
|
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.
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
data/fisherman.gemspec
CHANGED
@@ -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
|
data/lib/fisherman.rb
CHANGED
@@ -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
|
data/lib/fisherman/version.rb
CHANGED
data/lib/snapfish.rb
CHANGED
@@ -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/
|
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
|
data/lib/snapfish/album.rb
CHANGED
@@ -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:
|
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
|
-
"
|
47
|
+
"collection/#{id}/assets"
|
40
48
|
end
|
41
49
|
end
|
42
50
|
end
|
data/lib/snapfish/asset.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
+
date: 2017-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: faraday
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
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:
|
26
|
+
version: 0.11.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: faraday_middleware
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
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:
|
40
|
+
version: 0.11.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
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:
|
54
|
+
version: '1.13'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
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:
|
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
|