faceapp 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f6b57f4675e25c6bcd9a57789bd6059f52cd36fe
4
+ data.tar.gz: 5f2249a23cdafc983f95417b01623b9ccb697f8a
5
+ SHA512:
6
+ metadata.gz: 5dc081794102567ce12a3634dd2699c80af525a442f8cf66c1c9aad0917a57a89be29a3d6500b39e9924db532a6ec2125744cbd3375d7d125dd8b3dfe98bd427
7
+ data.tar.gz: 7c617965779af62386f0c3a915a5cf3cb48556b0423656ee60279f0ce8466ac5791abc5fabc936f1cdf9887161e18b1c2988cee9846722268a1d939bd69b562f
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,14 @@
1
+ Metrics/AbcSize:
2
+ Enabled: false
3
+
4
+ Metrics/BlockLength:
5
+ Enabled: false
6
+
7
+ Metrics/LineLength:
8
+ Max: 120
9
+
10
+ Metrics/MethodLength:
11
+ Enabled: false
12
+
13
+ Style/Documentation:
14
+ Enabled: false
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.5
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in faceapp.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Igor Yamolov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ # Faceapp
2
+
3
+ Faceapp is neural-network face manipulation application for smartphones.
4
+
5
+ https://play.google.com/store/apps/details?id=io.faceapp&hl=ru
6
+ https://itunes.apple.com/us/app/faceapp-free-neural-face-transformations/id1180884341
7
+
8
+ This gem provides command-line utility and Ruby library to utilize Faceapp API without using smartphone application.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'faceapp'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install faceapp
25
+
26
+ ## Usage
27
+
28
+ ### Commandline utility
29
+
30
+ ```bash
31
+ faceapp [options] <filter> <input> [ouput]
32
+
33
+ <filter> - Faceapp filter name
34
+ Possible values: smile, smile_2, hot, old, young, female, male
35
+
36
+ <input> - Input file name
37
+ Use '-' for STDIN
38
+
39
+ [output] - Optinal, output file name
40
+ Do not specify or use '-' for STDOUT
41
+
42
+ Options:
43
+ --help - Display this message
44
+ --cropped[=true|false] - Crop output image to face region. Enabled by default.
45
+ --api_host=<api_host> - Faceapp API host
46
+ --device_id=<device_id> - DeviceId for Faceapp
47
+ --user_agent=<user_agent> - User-Agent header for Faceapp API requests
48
+ --silent - Keep quiet, it will override `debug`
49
+ --debug - Print HTTP requests/responses to STDERR.
50
+ ```
51
+
52
+ **Example:**
53
+
54
+ ```bash
55
+ $ faceapp female hitler.jpg adolfina.jpg
56
+ ```
57
+
58
+ ### Ruby library
59
+
60
+ ```ruby
61
+ require 'faceapp'
62
+
63
+ client = Faceapp::Client.new
64
+
65
+ file = File.open('hitler.jpg', 'rb')
66
+ filter = 'female' # smile, smile_2, hot, old, young, male
67
+
68
+ # File could be String or IO
69
+
70
+ code = client.upload_photo(file)
71
+ # => "2017blablabla"
72
+
73
+ # By default it will return StringIO
74
+ result = client.apply_filter(code, filter)
75
+ # => StringIO
76
+
77
+ output = File.open('output.jpg', 'wb')
78
+
79
+ # Third argument might be an IO object
80
+ result = client.apply_filter(code, filter, output)
81
+ # => File
82
+
83
+ # Or you may specify block, it will receive response chunks
84
+ client.apply_filter(code, filter) do |chunk, cursor, total|
85
+ # chunk is String
86
+ puts chunk.bytesize
87
+ # render fancy progress bar
88
+ end
89
+ # => 100500 # Returns total bytes count
90
+
91
+ ```
92
+
93
+ ## License
94
+
95
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
96
+
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'faceapp/cli'
6
+
7
+ Faceapp::CLI.new(ARGV).run!
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'faceapp'
7
+ spec.version = '0.1.0'
8
+ spec.authors = ['Igor Yamolov']
9
+ spec.email = ['clouster@yandex.ru']
10
+
11
+ spec.summary = 'Faceapp API wrapper'
12
+ spec.description = 'Simple Ruby wrapper for Faceapp API'
13
+ spec.homepage = 'http://github.com/t3hk0d3/faceapp'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'bin'
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'multipart-post', '~> 2.0'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.13'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'webmock', '~> 3.0'
29
+ spec.add_development_dependency 'vcr', '~> 3.0'
30
+ end
@@ -0,0 +1,7 @@
1
+ module Faceapp
2
+ require_relative 'faceapp/client'
3
+
4
+ KNOWN_FILTERS = %w(smile smile_2 hot old young female male)
5
+
6
+ class RequestError < StandardError; end
7
+ end
@@ -0,0 +1,143 @@
1
+ require 'logger'
2
+ require 'faceapp'
3
+
4
+ module Faceapp
5
+ class CLI
6
+ attr_reader :params, :options, :debug, :silent
7
+
8
+ OPTION_REGEXP = /\A--(?<name>[^=]+)(?:=(?<value>.+))?\Z/
9
+
10
+ PARAMS = [:filter, :input, :output].freeze
11
+
12
+ def initialize(args)
13
+ @params, @options = parse_arguments(args)
14
+ end
15
+
16
+ def run!
17
+ print_usage if params.empty? || options[:help]
18
+
19
+ options[:logger] = Logger.new(STDERR) if debug?
20
+
21
+ filter = params[:filter]
22
+ input = parse_input(params[:input])
23
+ output = parse_output(params[:output])
24
+
25
+ client = Faceapp::Client.new(options)
26
+
27
+ code = client.upload_photo(input)
28
+
29
+ info "Successfuly uploaded input photo. Result code = #{code}"
30
+ info "Applying filter '#{filter}'"
31
+
32
+ client.apply_filter(code, filter, output)
33
+
34
+ info 'Done.'
35
+ rescue => e
36
+ info "Error: #{e.message}"
37
+ exit(-1)
38
+ ensure
39
+ input.close if input.is_a?(File)
40
+ output.close if output.is_a?(File)
41
+ end
42
+
43
+ def print_usage
44
+ info <<TEXT
45
+ faceapp [options] <filter> <input> [ouput]
46
+
47
+ <filter> - Faceapp filter name
48
+ Possible values: #{Faceapp::KNOWN_FILTERS.join(', ')}
49
+
50
+ <input> - Input file name
51
+ Use '-' for STDIN
52
+
53
+ [output] - Optinal, output file name
54
+ Do not specify or use '-' for STDOUT
55
+
56
+ Options:
57
+ --help - Display this message
58
+ --cropped[=true|false] - Crop output image to face region. Enabled by default.
59
+ --api_host=<api_host> - Faceapp API host
60
+ --device_id=<device_id> - DeviceId for Faceapp
61
+ --user_agent=<user_agent> - User-Agent header for Faceapp API requests
62
+ --silent - Keep quiet, it will override `debug`
63
+ --debug - Print HTTP requests/responses to STDERR.
64
+
65
+ TEXT
66
+ exit(-1)
67
+ end
68
+
69
+ private
70
+
71
+ def parse_input(input)
72
+ case input
73
+ when '-'
74
+ STDIN
75
+ when nil
76
+ print_usage
77
+ else
78
+ File.open(input, 'rb')
79
+ end
80
+ rescue => e
81
+ info "Unable to open input: #{e.message}"
82
+ exit(-1)
83
+ end
84
+
85
+ def parse_output(output)
86
+ case output
87
+ when String
88
+ File.open(output, 'wb')
89
+ else
90
+ STDOUT
91
+ end
92
+ rescue => e
93
+ info "Unable to open output: #{e.message}"
94
+ exit(-1)
95
+ end
96
+
97
+ def parse_arguments(args)
98
+ params = []
99
+ options = {}
100
+
101
+ args.each do |arg|
102
+ match = OPTION_REGEXP.match(arg)
103
+
104
+ if match
105
+ options[match[:name].to_sym] = parse_option(match[:value])
106
+ else
107
+ params << arg
108
+ end
109
+ end
110
+
111
+ params = PARAMS.zip(params).to_h
112
+
113
+ [params, options]
114
+ end
115
+
116
+ def parse_option(value)
117
+ case value
118
+ when String
119
+ value
120
+ when 'false', '0'
121
+ false
122
+ else
123
+ true
124
+ end
125
+ end
126
+
127
+ def debug(text)
128
+ STDERR.puts(text) if debug?
129
+ end
130
+
131
+ def info(text)
132
+ STDERR.puts(text) unless silent?
133
+ end
134
+
135
+ def debug?
136
+ options[:debug] && !silent?
137
+ end
138
+
139
+ def silent?
140
+ options[:silent]
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,135 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'openssl'
4
+ require 'net/http'
5
+ require 'net/http/post/multipart'
6
+
7
+ module Faceapp
8
+ class Client
9
+ DEFAULT_API_HOST = 'https://node-01.faceapp.io'.freeze
10
+ DEFAULT_USER_AGENT = 'FaceApp/1.0.229 (Linux; Android 4.4)'.freeze
11
+
12
+ DEVICE_ID_LENGTH = 8
13
+ DEVICE_ID_LETTERS = ('a'..'z').to_a.freeze
14
+
15
+ KNOWN_FACEAPP_ERRORS = {
16
+ 'bad_filter_id' => 'Unknown filter'
17
+ }.freeze
18
+
19
+ attr_reader :options
20
+
21
+ def initialize(options = {})
22
+ @options = options
23
+
24
+ options[:api_host] ||= DEFAULT_API_HOST
25
+ options[:user_agent] ||= DEFAULT_USER_AGENT
26
+ options[:device_id] ||= generate_device_id
27
+
28
+ options[:headers] = {
29
+ 'User-Agent' => options[:user_agent],
30
+ 'X-FaceApp-DeviceID' => options[:device_id]
31
+ }.merge!(options[:headers] || {})
32
+ end
33
+
34
+ def upload_photo(photo)
35
+ photo_io = UploadIO.new(photo, 'image/jpeg', 'image.jpg')
36
+
37
+ request =
38
+ Net::HTTP::Post::Multipart.new '/api/v2.3/photos',
39
+ { 'file' => photo_io },
40
+ options[:headers]
41
+
42
+ response = http.request(request)
43
+
44
+ body = JSON.parse(response.body)
45
+
46
+ unless response.is_a?(Net::HTTPSuccess)
47
+ if body.is_a?(Hash) && body['err']
48
+ error = body['err']
49
+
50
+ error_message = "(#{error['code']}) #{error['desc']}"
51
+ else
52
+ error_message = response.body.to_s
53
+ end
54
+
55
+ raise Faceapp::RequestError, error_message
56
+ end
57
+
58
+ body['code']
59
+ end
60
+
61
+ def apply_filter(photo_code, filter, io = nil, &block)
62
+ cropped = options.fetch(:cropped, true) ? 1 : 0
63
+
64
+ url = "/api/v2.3/photos/#{photo_code}/filters/#{filter}?cropped=#{cropped}"
65
+
66
+ request = Net::HTTP::Get.new(url, options[:headers])
67
+
68
+ dest = io ? io : StringIO.new
69
+
70
+ http.request(request) do |response|
71
+ unless response.is_a?(Net::HTTPSuccess)
72
+ error_code =
73
+ if response['x-faceapp-errorcode']
74
+ response['x-faceapp-errorcode']
75
+ elsif response.code == '404'
76
+ 'bad_photo_code'
77
+ else
78
+ 'unknown_error'
79
+ end
80
+
81
+ error_message = KNOWN_FACEAPP_ERRORS.fetch(error_code, error_code)
82
+ error_message = format(error_message, photo_code, filter)
83
+
84
+ raise Faceapp::RequestError, error_message
85
+ end
86
+
87
+ dest = handler_filter_response(response, dest, &block)
88
+ end
89
+
90
+ dest.rewind if !io && !block_given?
91
+
92
+ dest
93
+ end
94
+
95
+ private
96
+
97
+ def http
98
+ @http ||= begin
99
+ uri = URI.parse(options[:api_host])
100
+
101
+ http = Net::HTTP.new(uri.hostname, uri.port)
102
+
103
+ http.set_debug_output(options[:logger]) if options[:logger]
104
+
105
+ if uri.scheme == 'https'
106
+ http.use_ssl = { verify_mode: OpenSSL::SSL::VERIFY_PEER }
107
+ end
108
+
109
+ http
110
+ end
111
+ end
112
+
113
+ def handler_filter_response(response, dest)
114
+ if block_given?
115
+ cursor = 0
116
+ total = response.content_length
117
+
118
+ response.read_body do |chunk|
119
+ yield chunk, cursor, total
120
+ cursor += chunk.size
121
+ end
122
+
123
+ total # return total size
124
+ else
125
+ response.read_body(dest)
126
+
127
+ dest
128
+ end
129
+ end
130
+
131
+ def generate_device_id(length = DEVICE_ID_LENGTH)
132
+ Array.new(length) { DEVICE_ID_LETTERS.sample }.join
133
+ end
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faceapp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Igor Yamolov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: multipart-post
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vcr
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Simple Ruby wrapper for Faceapp API
98
+ email:
99
+ - clouster@yandex.ru
100
+ executables:
101
+ - faceapp
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".rubocop.yml"
108
+ - ".travis.yml"
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/faceapp
114
+ - faceapp.gemspec
115
+ - lib/faceapp.rb
116
+ - lib/faceapp/cli.rb
117
+ - lib/faceapp/client.rb
118
+ homepage: http://github.com/t3hk0d3/faceapp
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.2.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Faceapp API wrapper
142
+ test_files: []