Passed
Push — master ( 92d53a...d267c3 )
by Matias
60:52 queued 10s
created

ExternalModel::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2020, Matias De lellis <[email protected]>
4
 *
5
 * @author Matias De lellis <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\FaceRecognition\Model\ExternalModel;
25
26
use OCA\FaceRecognition\Service\SettingsService;
27
28
use OCA\FaceRecognition\Model\IModel;
29
30
class ExternalModel implements IModel {
31
	/*
32
	 * Model description
33
	 */
34
	const FACE_MODEL_ID = 5;
35
	const FACE_MODEL_NAME = 'ExternalModel';
36
	const FACE_MODEL_DESC = 'External Model (EXPERIMENTAL)';
37
	const FACE_MODEL_DOC = 'https://github.com/matiasdelellis/facerecognition-external-model#run-service';
38
39
	/** This model practically does not consume memory. Directly set the limits. */
40
	const MINIMUM_MEMORY_REQUIREMENTS = 128 * 1024 * 1024;
41
42
	/** @var String model api endpoint */
43
	private $modelUrl = null;
44
45
	/** @var String model api key */
46
	private $modelApiKey = null;
47
48
	/** @var String preferred mimetype */
49
	private $preferredMimetype = null;
50
51
	/** @var int maximun image area */
52
	private $maximumImageArea = -1;
53
54
	/** @var SettingsService */
55
	private $settingsService;
56
57
	/**
58
	 * ExternalModel __construct.
59
	 *
60
	 * @param SettingsService $settingsService
61
	 */
62 1
	public function __construct(SettingsService $settingsService)
63
	{
64 1
		$this->settingsService = $settingsService;
65 1
	}
66
67
	public function getId(): int {
68
		return static::FACE_MODEL_ID;
69
	}
70
71
	public function getName(): string {
72
		return static::FACE_MODEL_NAME;
73
	}
74
75
	public function getDescription(): string {
76
		return static::FACE_MODEL_DESC;
77
	}
78
79
	public function getDocumentation(): string {
80
		return static::FACE_MODEL_DOC;
81
	}
82
83
	public function isInstalled(): bool {
84
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
85
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
86
		return !is_null($this->modelUrl) && !is_null($this->modelApiKey);
87
	}
88
89
	public function meetDependencies(string &$error_message): bool {
90
		return true;
91
	}
92
93
	public function getMaximumArea(): int {
94
		if ($this->maximumImageArea < 0) {
95
			$this->open();
96
		}
97
		return $this->maximumImageArea;
98
	}
99
100
	public function getPreferredMimeType(): string {
101
		if (is_null($this->preferredMimetype)) {
0 ignored issues
show
introduced by
The condition is_null($this->preferredMimetype) is always false.
Loading history...
102
			$this->open();
103
		}
104
		return $this->preferredMimetype;
105
	}
106
107
	public function install() {
108
		return;
109
	}
110
111
	public function open() {
112
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
113
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
114
115
		$ch = curl_init();
116
		if ($ch === false) {
117
			throw new \Exception('Curl error: unable to initialize curl');
118
		}
119
120
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/open');
121
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: '  . $this->modelApiKey]);
122
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
123
124
		$response = curl_exec($ch);
125
		if ($response === false) {
126
			throw new \Exception('Cannot connect to external model: ' . curl_error($ch));
127
		}
128
129
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
130
		if ($httpCode !== 200) {
131
			throw new \Exception('Can\'t connect with external model. HTTP status code: ' . $httpCode);
132
		}
133
134
		$jsonResponse = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

134
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
135
136
		$this->maximumImageArea = intval($jsonResponse['maximum_area']);
137
		$this->preferredMimetype = $jsonResponse['preferred_mimetype'];
138
139
		curl_close($ch);
140
	}
141
142
	public function detectFaces(string $imagePath, bool $compute = true): array {
143
		$ch = curl_init();
144
		if ($ch === false) {
145
			throw new \Exception('Curl error: unable to initialize curl');
146
		}
147
148
		$cFile = curl_file_create($imagePath);
149
		$post = array('file'=> $cFile);
150
151
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/detect');
152
		curl_setopt($ch, CURLOPT_POST, true);
153
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
154
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
155
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
156
157
		$response = curl_exec($ch);
158
		if ($response === false) {
159
			throw new \Exception('External model dont response: ' . curl_error($ch));
160
		}
161
162
		curl_close($ch);
163
164
		$jsonResponse = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

164
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
165
166
		if ($jsonResponse['faces-count'] == 0)
167
			return [];
168
169
		return $jsonResponse['faces'];
170
	}
171
172
	public function compute(string $imagePath, array $face): array {
173
		$ch = curl_init();
174
		if ($ch === false) {
175
			throw new \Exception('Curl error: unable to initialize curl');
176
		}
177
178
		$cFile = curl_file_create($imagePath, $this->preferredMimetype, basename($imagePath));
179
		$post = [
180
			'file' => $cFile,
181
			'face' => json_encode($face),
182
		];
183
184
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/compute');
185
		curl_setopt($ch, CURLOPT_POST, true);
186
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
187
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key:' . $this->modelApiKey]);
188
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
189
190
		$response = curl_exec($ch);
191
		if ($response === false) {
192
			throw new \Exception('External model dont response: ' . curl_error($ch));
193
		}
194
195
		curl_close($ch);
196
197
		$jsonResponse = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

197
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
198
199
		return $jsonResponse['face'];
200
	}
201
202
}
203