shutterstock-v2 0.0.1
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 +7 -0
- data/.gitignore +27 -0
- data/.rspec +3 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +6 -0
- data/Dockerfile +17 -0
- data/Gemfile +3 -0
- data/Guardfile +9 -0
- data/README.md +377 -0
- data/Rakefile +19 -0
- data/lib/client/auth.rb +48 -0
- data/lib/client/categories.rb +11 -0
- data/lib/client/category.rb +16 -0
- data/lib/client/client.rb +72 -0
- data/lib/client/collection.rb +151 -0
- data/lib/client/collections.rb +16 -0
- data/lib/client/configuration.rb +20 -0
- data/lib/client/contributor.rb +15 -0
- data/lib/client/driver.rb +38 -0
- data/lib/client/error.rb +20 -0
- data/lib/client/image.rb +139 -0
- data/lib/client/image_assets.rb +56 -0
- data/lib/client/image_size_details.rb +26 -0
- data/lib/client/images.rb +27 -0
- data/lib/client/license.rb +67 -0
- data/lib/client/licenses.rb +23 -0
- data/lib/client/model.rb +15 -0
- data/lib/client/models.rb +11 -0
- data/lib/client/request.rb +41 -0
- data/lib/client/subscription.rb +35 -0
- data/lib/client/subscription_allotment.rb +19 -0
- data/lib/client/subscription_license_format.rb +16 -0
- data/lib/client/subscription_license_formats.rb +12 -0
- data/lib/client/subscription_price.rb +13 -0
- data/lib/client/subscriptions.rb +37 -0
- data/lib/client/thumbnail.rb +17 -0
- data/lib/client/user.rb +68 -0
- data/lib/client/version.rb +3 -0
- data/lib/shutterstock-v2.rb +31 -0
- data/shutterstock-v2.gemspec +30 -0
- data/spec/cassettes/Shutterstock_Auth_get_access_token/returns_hash_with_access_token.yml +86 -0
- data/spec/cassettes/Shutterstock_Auth_get_authorize_url/returns_URL_to_redirect_user_to_passing_redirect_url_an.yml +48 -0
- data/spec/cassettes/Shutterstock_Categories/parses_params_correctly.yml +53 -0
- data/spec/cassettes/Shutterstock_Categories/returns_all_cats_for_an_image.yml +53 -0
- data/spec/cassettes/Shutterstock_Collection/add_image_adds_image_using_Image_new.yml +48 -0
- data/spec/cassettes/Shutterstock_Collection/add_image_adds_image_using_id.yml +48 -0
- data/spec/cassettes/Shutterstock_Collection/add_image_error_raises_error_if_collection_does_not_exist.yml +52 -0
- data/spec/cassettes/Shutterstock_Collection/create_creates_a_collection.yml +52 -0
- data/spec/cassettes/Shutterstock_Collection/destroy_destroys_a_collection.yml +93 -0
- data/spec/cassettes/Shutterstock_Collection/find_adds_an_image.yml +97 -0
- data/spec/cassettes/Shutterstock_Collection/find_converts_list_to_images.yml +101 -0
- data/spec/cassettes/Shutterstock_Collection/find_finds_a_collection_given_an_id.yml +52 -0
- data/spec/cassettes/Shutterstock_Collection/find_raises_error_for_invalid_collection_id.yml +50 -0
- data/spec/cassettes/Shutterstock_Collection/find_removes_an_image.yml +95 -0
- data/spec/cassettes/Shutterstock_Collection/list_returns_all_collections_for_the_current_user.yml +53 -0
- data/spec/cassettes/Shutterstock_Collection/remove_image_removes_image_using_Image_new.yml +46 -0
- data/spec/cassettes/Shutterstock_Collection/remove_image_removes_image_using_id.yml +46 -0
- data/spec/cassettes/Shutterstock_Collection/update_changes_the_name_of_a_collection.yml +97 -0
- data/spec/cassettes/Shutterstock_Collections/returns_all_collections_for_current_user.yml +53 -0
- data/spec/cassettes/Shutterstock_Image/fill_fills_all_details_of_current_image.yml +109 -0
- data/spec/cassettes/Shutterstock_Image/fills_all_details_of_current_image.yml +109 -0
- data/spec/cassettes/Shutterstock_Image/find_places_image_data_into_correct_fields.yml +56 -0
- data/spec/cassettes/Shutterstock_Image/find_places_more_image_data_into_correct_fields.yml +53 -0
- data/spec/cassettes/Shutterstock_Image/find_returns_an_Image_object.yml +56 -0
- data/spec/cassettes/Shutterstock_Image/finds_similar_images_given_an_image.yml +125 -0
- data/spec/cassettes/Shutterstock_Image/returns_an_Image_object.yml +56 -0
- data/spec/cassettes/Shutterstock_Image/returns_similar_images.yml +72 -0
- data/spec/cassettes/Shutterstock_Image/search_searches_for_images_based_on_searchterm.yml +73 -0
- data/spec/cassettes/Shutterstock_Image/search_searches_using_more_than_one_parameter.yml +77 -0
- data/spec/cassettes/Shutterstock_Image/searches_for_images_based_on_searchterm.yml +73 -0
- data/spec/cassettes/Shutterstock_Image/searches_using_more_than_one_parameter.yml +77 -0
- data/spec/cassettes/Shutterstock_Image/similar_finds_similar_images_given_an_image.yml +125 -0
- data/spec/cassettes/Shutterstock_Image/similar_returns_similar_images.yml +72 -0
- data/spec/cassettes/Shutterstock_ImageAssets/find_calculates_fields_correctly.yml +53 -0
- data/spec/cassettes/Shutterstock_ImageAssets/find_places_image_data_into_correct_fields.yml +53 -0
- data/spec/cassettes/Shutterstock_Images/fill_fills_details_of_all_images_in_list_all_images_now_have_keywords.yml +1144 -0
- data/spec/cassettes/Shutterstock_Images/returns_similar_images.yml +126 -0
- data/spec/cassettes/Shutterstock_License/license_editorial_acknowledgement_returns_error_if_requesting_an_image.yml +53 -0
- data/spec/cassettes/Shutterstock_License/license_editorial_acknowledgement_returns_licenced_image_if_ed_ack_is_.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_license_from_image_object_sends_editorial_acknowledgement_when.yml +160 -0
- data/spec/cassettes/Shutterstock_License/license_license_from_image_object_using_found_subscription.yml +108 -0
- data/spec/cassettes/Shutterstock_License/license_license_from_image_object_vector_image.yml +108 -0
- data/spec/cassettes/Shutterstock_License/license_multiple_licenses_multiple_images.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_multiple_licenses_multiple_images_from_Image_new.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_multiple_returns_error_in_Licenses_when_incorrect_image_used.yml +53 -0
- data/spec/cassettes/Shutterstock_License/license_single_licencing_same_image_twice_does_not_give_error.yml +101 -0
- data/spec/cassettes/Shutterstock_License/license_single_license_fails_when_subscription_doesn_t_allow.yml +49 -0
- data/spec/cassettes/Shutterstock_License/license_single_licenses_single_image.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_single_licenses_single_image_from_Image_new.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_single_licenses_single_image_from_hash.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_single_licenses_single_image_with_smaller_size.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_single_licenses_vector_image.yml +52 -0
- data/spec/cassettes/Shutterstock_License/license_single_licensing_same_image_twice_does_not_give_error.yml +101 -0
- data/spec/cassettes/Shutterstock_License/license_single_returns_error_when_incorrect_image_used.yml +103 -0
- data/spec/cassettes/Shutterstock_License/license_single_returns_error_when_incorrect_subscription_used.yml +53 -0
- data/spec/cassettes/Shutterstock_Models/parses_params_correctly.yml +53 -0
- data/spec/cassettes/Shutterstock_Models/returns_all_models_for_an_image.yml +53 -0
- data/spec/cassettes/Shutterstock_Subscription/allows_image_size_download_correctly_finds_if_image_size_is_possi.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscription/new_parses_info_on_user_correctly.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscription/new_returns_a_subscription_for_current_user.yml +59 -0
- data/spec/cassettes/Shutterstock_SubscriptionLicenseFormat/new_parses_info_correctly.yml +59 -0
- data/spec/cassettes/Shutterstock_SubscriptionLicenseFormat/new_returns_a_subscription_license_format_for_curren.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_active_expired_total_subscriptions.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_active_returns_only_active_subscriptions.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_downloads_left_only_subscriptions_with_downloads_l.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_expired_returns_only_expired_subscriptions.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_find_subscription_for_image_size_finds_subs_to_use.yml +59 -0
- data/spec/cassettes/Shutterstock_Subscriptions/subscriptions_returns_all_subscriptions_for_current_user.yml +59 -0
- data/spec/cassettes/Shutterstock_User/find_parses_info_on_user_correctly.yml +52 -0
- data/spec/cassettes/Shutterstock_User/find_returns_object_for_current_user.yml +52 -0
- data/spec/client/auth_spec.rb +36 -0
- data/spec/client/categories_spec.rb +26 -0
- data/spec/client/category_spec.rb +16 -0
- data/spec/client/client_spec.rb +32 -0
- data/spec/client/collection_spec.rb +159 -0
- data/spec/client/collections_spec.rb +18 -0
- data/spec/client/configuration_spec.rb +10 -0
- data/spec/client/contributor_spec.rb +14 -0
- data/spec/client/driver_spec.rb +20 -0
- data/spec/client/image_assets_spec.rb +51 -0
- data/spec/client/image_size_details_spec.rb +33 -0
- data/spec/client/image_spec.rb +102 -0
- data/spec/client/images_spec.rb +31 -0
- data/spec/client/license_spec.rb +234 -0
- data/spec/client/model_spec.rb +14 -0
- data/spec/client/models_spec.rb +23 -0
- data/spec/client/request_spec.rb +79 -0
- data/spec/client/subscription_allotment_spec.rb +25 -0
- data/spec/client/subscription_license_format_spec.rb +29 -0
- data/spec/client/subscription_price_spec.rb +17 -0
- data/spec/client/subscription_spec.rb +60 -0
- data/spec/client/subscriptions_spec.rb +54 -0
- data/spec/client/thumbnail_spec.rb +21 -0
- data/spec/client/user_spec.rb +37 -0
- data/spec/spec_helper.rb +88 -0
- metadata +467 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'rspec/core/rake_task'
|
|
2
|
+
require 'rubocop/rake_task'
|
|
3
|
+
|
|
4
|
+
task :default => :spec
|
|
5
|
+
task :test => :spec
|
|
6
|
+
|
|
7
|
+
desc "Run spec tests"
|
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
9
|
+
t.rspec_opts = "--color"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'Run RuboCop on the lib directory'
|
|
13
|
+
Rubocop::RakeTask.new(:rubocop) do |task|
|
|
14
|
+
task.patterns = ['lib/**/*.rb']
|
|
15
|
+
# only show the files with failures
|
|
16
|
+
task.formatters = ['progress']
|
|
17
|
+
# don't abort rake on failure
|
|
18
|
+
task.fail_on_error = false
|
|
19
|
+
end
|
data/lib/client/auth.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Auth < Driver
|
|
3
|
+
|
|
4
|
+
def self.get_authorize_url(redirect_uri:, scope:, state: 'state_%d' % [Time.now.to_i])
|
|
5
|
+
resp = client.request do |r|
|
|
6
|
+
r.path "/v2/oauth/authorize"
|
|
7
|
+
r.method :get
|
|
8
|
+
r.send_authorization false
|
|
9
|
+
r.success_status 302
|
|
10
|
+
r.params ({ scope: scope,
|
|
11
|
+
client_id: client.config.client_id,
|
|
12
|
+
response_type: 'code',
|
|
13
|
+
state: state,
|
|
14
|
+
redirect_uri: redirect_uri })
|
|
15
|
+
end
|
|
16
|
+
redirect_location = resp.headers[:location]
|
|
17
|
+
returned_state = ensure_next_url_contains_sent_state(redirect_location, state)
|
|
18
|
+
|
|
19
|
+
redirect_location
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.get_access_token(code:, grant_type: 'authorization_code', contributor: 'customer')
|
|
23
|
+
resp = client.request do |r|
|
|
24
|
+
r.path "/v2/oauth/access_token"
|
|
25
|
+
r.method :post
|
|
26
|
+
r.send_authorization false
|
|
27
|
+
r.content_type 'application/x-www-form-urlencoded'
|
|
28
|
+
r.body ({ grant_type: grant_type,
|
|
29
|
+
client_id: client.config.client_id,
|
|
30
|
+
client_secret: client.config.client_secret,
|
|
31
|
+
code: code,
|
|
32
|
+
contributor: contributor })
|
|
33
|
+
end
|
|
34
|
+
resp.body
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def self.ensure_next_url_contains_sent_state(url, state)
|
|
40
|
+
# https://accounts.shutterstock.com/login?next=%2Foauth%2Fauthorize%3Fclient_id%3D<SSTK_CLIENT_ID...
|
|
41
|
+
next_query = CGI.parse(URI.parse(url).query)["next"].first
|
|
42
|
+
# /oauth/authorize?client_id=<SSTK_CLIENT_ID>&redirect_uri=...
|
|
43
|
+
returned_state = CGI.parse(URI.parse(next_query).query)["state"].first
|
|
44
|
+
raise(RuntimeError, "State in redirected url (%s) not identical to sent state (%s)" % [returned_state,state]) unless (returned_state==state)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'faraday_middleware'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'singleton'
|
|
5
|
+
|
|
6
|
+
module Shutterstock
|
|
7
|
+
|
|
8
|
+
class Client
|
|
9
|
+
include Singleton
|
|
10
|
+
|
|
11
|
+
# @return [Configuration] Config instance
|
|
12
|
+
attr_reader :config
|
|
13
|
+
|
|
14
|
+
# Request options
|
|
15
|
+
attr_accessor :options
|
|
16
|
+
|
|
17
|
+
# Creates a new {Client} instance and yields {#config}.
|
|
18
|
+
# Requires a block to be given.
|
|
19
|
+
def configure
|
|
20
|
+
raise AppNotConfigured, "block not given" unless block_given?
|
|
21
|
+
|
|
22
|
+
@config = Configuration.new
|
|
23
|
+
yield config
|
|
24
|
+
|
|
25
|
+
@config.api_url ||= "https://api.shutterstock.com/v2"
|
|
26
|
+
raise AppNotConfigured, "Client ID not provided" if config.client_id.nil?
|
|
27
|
+
raise AppNotConfigured, "Client Secret not provided" if config.client_secret.nil?
|
|
28
|
+
# raise AppNotConfigured, "Access Token not provided" if config.access_token.nil? # Not needed for auth requests
|
|
29
|
+
|
|
30
|
+
@options = {
|
|
31
|
+
base_uri: config.api_url,
|
|
32
|
+
client_id: config.client_id,
|
|
33
|
+
client_secret: config.client_secret,
|
|
34
|
+
access_token: config.access_token
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def configured?
|
|
40
|
+
!config.nil?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def connection
|
|
44
|
+
@connection ||= Faraday.new(config.api_url) do |conn|
|
|
45
|
+
conn.response :json, content_type: /\bjson$/
|
|
46
|
+
conn.adapter Faraday.default_adapter
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def request(&block)
|
|
51
|
+
req = Request.new(&block)
|
|
52
|
+
api(req)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def api(request)
|
|
56
|
+
|
|
57
|
+
raise AppNotConfigured, "Access Token not provided" if options[:access_token].nil? && request.send_authorization
|
|
58
|
+
|
|
59
|
+
content_type = request.content_type
|
|
60
|
+
response = connection.run_request(request.method, nil, nil, nil) do |req|
|
|
61
|
+
req.url request.path
|
|
62
|
+
req.headers['Authorization'] = 'Bearer '+options[:access_token] if request.send_authorization
|
|
63
|
+
req.headers['Content-Type'] = request.content_type
|
|
64
|
+
req.params = request.params if request.params
|
|
65
|
+
req.body = (content_type =~ /json/ ? request.body.to_json : URI.encode_www_form(request.body)) if request.body
|
|
66
|
+
end
|
|
67
|
+
raise FailedResponse.new("Something went wrong: #{response.status}: #{response.body}", response.status) unless (response.status == request.success_status)
|
|
68
|
+
response
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Collection < Driver
|
|
3
|
+
attr_reader :id, :name, :share_url, :share_code, :total_count, :created, :updated, :items_updated, :cover_item, :hash
|
|
4
|
+
|
|
5
|
+
include Equalizer.new(:id)
|
|
6
|
+
|
|
7
|
+
def initialize(params={})
|
|
8
|
+
@hash = params
|
|
9
|
+
@id = params["id"].to_i
|
|
10
|
+
@name = params["name"]
|
|
11
|
+
@share_code = params["share_code"]
|
|
12
|
+
@share_url = params["share_url"]
|
|
13
|
+
@total_count = params["total_item_count"].to_i
|
|
14
|
+
@created = to_date(params["created_time"])
|
|
15
|
+
@updated = to_date(params["updated_time"])
|
|
16
|
+
@items_updated = to_date(params["items_updated_time"])
|
|
17
|
+
@cover_item = Image.new(params["cover_item"]) if params["cover_item"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.find(id)
|
|
21
|
+
resp = client.request do
|
|
22
|
+
path "/v2/images/collections/#{id}"
|
|
23
|
+
method :get
|
|
24
|
+
end
|
|
25
|
+
self.new(resp.body)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def find
|
|
29
|
+
self.class.find(self.id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.create(name)
|
|
33
|
+
raise IncorrectArguments, "collection name not provided" unless name
|
|
34
|
+
|
|
35
|
+
resp = client.request do |r|
|
|
36
|
+
r.path "/v2/images/collections"
|
|
37
|
+
r.method :post
|
|
38
|
+
r.body ({ name: name })
|
|
39
|
+
r.success_status 201
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
resp_hash = resp.body
|
|
43
|
+
resp_hash.merge!("name" => name)
|
|
44
|
+
self.new(resp_hash)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.list
|
|
48
|
+
resp = client.request do
|
|
49
|
+
path "/v2/images/collections"
|
|
50
|
+
method :get
|
|
51
|
+
end
|
|
52
|
+
Collections.new(resp.body)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.update(id:, name:)
|
|
56
|
+
raise IncorrectArguments, "collection name not provided" unless name
|
|
57
|
+
|
|
58
|
+
resp = client.request do |r|
|
|
59
|
+
r.path "/v2/images/collections/#{id}"
|
|
60
|
+
r.method :post
|
|
61
|
+
r.body ({ name: name })
|
|
62
|
+
r.success_status 204
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.destroy(id)
|
|
67
|
+
resp = client.request do |r|
|
|
68
|
+
r.path "/v2/images/collections/#{id}"
|
|
69
|
+
r.method :delete
|
|
70
|
+
r.success_status 204
|
|
71
|
+
end
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def destroy
|
|
76
|
+
self.class.destroy(self.id)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# collection.add_image({collection_id: 1234556, image_id: 98765432})
|
|
80
|
+
def self.add_image(id:, image_id:)
|
|
81
|
+
|
|
82
|
+
raise IncorrectArguments, "collection id not provided" unless id
|
|
83
|
+
raise IncorrectArguments, "image id not provided" unless image_id
|
|
84
|
+
|
|
85
|
+
resp = client.request do |r|
|
|
86
|
+
r.path "/v2/images/collections/#{id}/items"
|
|
87
|
+
r.method :post
|
|
88
|
+
r.body ({ items: image_ids_to_array_of_hash(image_id)})
|
|
89
|
+
r.success_status 204
|
|
90
|
+
end
|
|
91
|
+
self
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# image_id can be a single or array of either image ids or Image objects
|
|
95
|
+
def add_image(image_id)
|
|
96
|
+
self.class.add_image({id: self.id, image_id: image_id})
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.remove_image(id:, image_id:)
|
|
100
|
+
|
|
101
|
+
resp = client.request do |r|
|
|
102
|
+
r.path "/v2/images/collections/#{id}/items"
|
|
103
|
+
r.method :delete
|
|
104
|
+
r.params image_ids_to_hash_of_array(image_id)
|
|
105
|
+
r.success_status 204
|
|
106
|
+
end
|
|
107
|
+
self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def remove_image(image_id)
|
|
111
|
+
self.class.remove_image(id: self.id, image_id: image_id)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.items(id)
|
|
115
|
+
raise IncorrectArguments, "collection id not provided" unless id
|
|
116
|
+
|
|
117
|
+
resp = client.request do |r|
|
|
118
|
+
r.path "/v2/images/collections/#{id}/items"
|
|
119
|
+
r.method :get
|
|
120
|
+
end
|
|
121
|
+
Images.new(resp.body["data"])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def items
|
|
125
|
+
self.class.items(self.id)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# for post request
|
|
133
|
+
# image_id from [1,2,Image.new("id" => 3)] => [{id: "1"},{id: "2"},{id: "3"}]
|
|
134
|
+
# from 1 => [{id: "1"}]
|
|
135
|
+
# from Image.new("id" => 1) => [{id: "1"}]
|
|
136
|
+
def self.image_ids_to_array_of_hash(image_ids)
|
|
137
|
+
Array(image_ids).map{|iid| { id: (iid.is_a?(Image) ? iid.id : iid).to_s} }
|
|
138
|
+
end
|
|
139
|
+
# for delete request
|
|
140
|
+
# image_id from [1,2,Image.new("id" => 3)] => {id: [1,2,3]}
|
|
141
|
+
# from 1 => {id: 1}
|
|
142
|
+
# from Image.new("id" => 1) => {id: 1}
|
|
143
|
+
def self.image_ids_to_hash_of_array(image_ids)
|
|
144
|
+
if image_ids.is_a? Array
|
|
145
|
+
{id: image_ids.map{|iid| iid.is_a?(Image) ? iid.id : iid}}
|
|
146
|
+
else
|
|
147
|
+
{id: image_ids.is_a?(Image) ? image_ids.id : image_ids}
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Collections < Array
|
|
3
|
+
attr_reader :raw_data
|
|
4
|
+
def initialize(raw_data)
|
|
5
|
+
@raw_data = raw_data
|
|
6
|
+
|
|
7
|
+
if raw_data.kind_of? Hash
|
|
8
|
+
super(@raw_data["data"].map{ |collection| Collection.new(collection) })
|
|
9
|
+
elsif raw_data.kind_of? Array
|
|
10
|
+
super( @raw_data.map{ |collection| Collection.new(collection) } )
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Configuration
|
|
3
|
+
# @return [String] The client id.
|
|
4
|
+
attr_accessor :client_id
|
|
5
|
+
|
|
6
|
+
# @return [String] The client secret.
|
|
7
|
+
attr_accessor :client_secret
|
|
8
|
+
|
|
9
|
+
# @return [String] The access token.
|
|
10
|
+
attr_accessor :access_token
|
|
11
|
+
|
|
12
|
+
# @return [String] The API url.
|
|
13
|
+
attr_accessor :api_url
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@client_options = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module Shutterstock
|
|
4
|
+
class Driver
|
|
5
|
+
|
|
6
|
+
def respond_to(method)
|
|
7
|
+
return true if @hash[method.to_s]
|
|
8
|
+
super()
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def methods
|
|
12
|
+
methods = super()
|
|
13
|
+
[methods, @hash.keys.map{ |k| k.to_sym} ].flatten
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
TRUTHY_JSON_VALUES = [ "1", 1, true, "true"]
|
|
17
|
+
def json_true?(thing)
|
|
18
|
+
TRUTHY_JSON_VALUES.include? thing
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_date(text_date)
|
|
22
|
+
return nil if text_date.nil?
|
|
23
|
+
DateTime.parse(text_date)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def client
|
|
27
|
+
Client.instance
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.client
|
|
31
|
+
Client.instance
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.api(*params)
|
|
35
|
+
client.api(*params)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/client/error.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Error < StandardError
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
class IncorrectArguments < Error
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class AppNotConfigured < Error
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class FailedResponse < Error
|
|
12
|
+
attr_reader :code
|
|
13
|
+
alias :msg :message
|
|
14
|
+
def initialize(msg, code = 0)
|
|
15
|
+
@code = code
|
|
16
|
+
super(msg)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
data/lib/client/image.rb
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module Shutterstock
|
|
2
|
+
class Image < Driver
|
|
3
|
+
attr_reader :hash, :id, :description, :added, :media_type, :contributor, :aspect, :image_type, :is_editorial,
|
|
4
|
+
:is_adult, :is_illustration, :has_model_release, :has_property_release, :model_releases, :categories,
|
|
5
|
+
:keywords, :assets, :models
|
|
6
|
+
|
|
7
|
+
include Equalizer.new(:id)
|
|
8
|
+
|
|
9
|
+
# {"id":"118139110",
|
|
10
|
+
# "added_date":"2012-11-09",
|
|
11
|
+
# "aspect":1.5,
|
|
12
|
+
# "assets":{"small_jpg":{"display_name":"Small","dpi":72,"file_size":88064,"format":"jpg","height":333,"is_licensable":true,"width":500},
|
|
13
|
+
# "medium_jpg":{"display_name":"Med","dpi":300,"file_size":301056,"format":"jpg","height":667,"is_licensable":true,"width":1000},
|
|
14
|
+
# "huge_jpg":{"display_name":"Huge","dpi":300,"file_size":2790400,"format":"jpg","height":3090,"is_licensable":true,"width":4635},
|
|
15
|
+
# "supersize_jpg":{"display_name":"Super","dpi":300,"file_size":10112609,"format":"jpg","height":6180,"is_licensable":false,"width":9270},
|
|
16
|
+
# "huge_tiff":{"display_name":"Huge","dpi":300,"file_size":42966450,"format":"tiff","height":3090,"is_licensable":false,"width":4635},
|
|
17
|
+
# "supersize_tiff":{"display_name":"Super","dpi":300,"file_size":171865800,"format":"tiff","height":6180,"is_licensable":false,"width":9270},
|
|
18
|
+
# "preview":{"height":300,"url":"https://image.shutterstock.com/display_pic_with_logo/1306729/118139110/stock-photo-adorable-labrador-puppy-playing-with-a-chew-toy-on-white-backdrop-118139110.jpg","width":450},
|
|
19
|
+
# "small_thumb":{"height":67,"url":"https://thumb1.shutterstock.com/thumb_small/1306729/118139110/stock-photo-adorable-labrador-puppy-playing-with-a-chew-toy-on-white-backdrop-118139110.jpg","width":100},
|
|
20
|
+
# "large_thumb":{"height":100,"url":"https://thumb1.shutterstock.com/thumb_large/1306729/118139110/stock-photo-adorable-labrador-puppy-playing-with-a-chew-toy-on-white-backdrop-118139110.jpg","width":150}
|
|
21
|
+
# },
|
|
22
|
+
# "categories":[{"id":"1","name":"Animals/Wildlife"},{"id":"20","name":"NOT-CATEGORIZED"}],
|
|
23
|
+
# "contributor":{"id":"1306729"},
|
|
24
|
+
# "description":"Adorable Labrador Puppy Playing with a Chew Toy on White Backdrop",
|
|
25
|
+
# "image_type":"photo",
|
|
26
|
+
# "is_adult":false,
|
|
27
|
+
# "keywords":["animal","animal themes","backgrounds","cute","dog","domestic animals","friendship","full length","isolated"],
|
|
28
|
+
# "media_type":"image"}
|
|
29
|
+
|
|
30
|
+
def initialize(params = {})
|
|
31
|
+
load_ivars(params)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def load_ivars(params)
|
|
35
|
+
@hash = params
|
|
36
|
+
@id = params["id"].to_i
|
|
37
|
+
@description = params["description"]
|
|
38
|
+
@added = to_date(params["added_date"])
|
|
39
|
+
@media_type = params["media_type"]
|
|
40
|
+
@contributor = Contributor.new(params["contributor"]) if params["contributor"]
|
|
41
|
+
@aspect = params["aspect"]
|
|
42
|
+
@image_type = params["image_type"]
|
|
43
|
+
@is_editorial = json_true? params["is_editorial"]
|
|
44
|
+
@is_adult = json_true? params["is_adult"]
|
|
45
|
+
@is_illustration = json_true? params["is_illustration"]
|
|
46
|
+
@has_model_release = json_true? params["has_model_release"]
|
|
47
|
+
@has_property_release = json_true? params["has_property_release"]
|
|
48
|
+
@model_releases = params["model_releases"]
|
|
49
|
+
@categories = Categories.new(params["categories"]) if params["categories"]
|
|
50
|
+
@keywords = params["keywords"]
|
|
51
|
+
@assets = ImageAssets.new(params["assets"]) if params["assets"]
|
|
52
|
+
@models = Models.new(params["models"]) if params["models"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# boolean readers
|
|
56
|
+
def editorial?
|
|
57
|
+
@is_editorial
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def adult?
|
|
61
|
+
@is_adult
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def illustration?
|
|
65
|
+
@is_illustration
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def model_release?
|
|
69
|
+
@has_model_release
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def property_release?
|
|
73
|
+
@has_property_release
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def self.find(id)
|
|
79
|
+
resp = client.request do |r|
|
|
80
|
+
r.path "/v2/images/#{id}"
|
|
81
|
+
r.method :get
|
|
82
|
+
end
|
|
83
|
+
self.new(resp.body)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def find
|
|
87
|
+
self.class.find(self.id)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.similar(id)
|
|
91
|
+
resp = client.request do |r|
|
|
92
|
+
r.path "/v2/images/#{id}/similar"
|
|
93
|
+
r.method :get
|
|
94
|
+
end
|
|
95
|
+
Images.new(resp.body)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def similar
|
|
99
|
+
@images = self.class.similar(self.id)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.search(search)
|
|
103
|
+
search_params = {}
|
|
104
|
+
if search.kind_of? String
|
|
105
|
+
search_params[:query] = search
|
|
106
|
+
else
|
|
107
|
+
search_params = search
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
resp = client.request do |r|
|
|
111
|
+
r.path "/v2/images/search"
|
|
112
|
+
r.method :get
|
|
113
|
+
r.params search_params
|
|
114
|
+
end
|
|
115
|
+
Images.new(resp.body)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Fetch all details of this image
|
|
119
|
+
def fill
|
|
120
|
+
load_ivars(find.hash)
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# License current image with available subscription
|
|
125
|
+
def license(format: nil, size: nil)
|
|
126
|
+
sub = find_subscription_for_this_size(size)
|
|
127
|
+
License.license(subscription_id: sub.id, image_id: self, format: format, size: size, editorial_acknowledgement: (editorial? ? true : nil))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def find_subscription_for_this_size(size = nil)
|
|
133
|
+
User.subscriptions.find_subscription_for_image_size
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
end
|