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