Passed
Pull Request — master (#534)
by Matias
07:42 queued 05:25
created

ExternalModel::open()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 4
eloc 18
c 3
b 1
f 0
nc 4
nop 0
dl 0
loc 29
ccs 0
cts 19
cp 0
crap 20
rs 9.6666
1
<?php
2
/**
3
 * @copyright Copyright (c) 2021, 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 to separate image processing from the web server';
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
		if (!extension_loaded('pdlib')) {
91
			$error_message = "The PDlib PHP extension is not loaded.";
92
			return false;
93
		}
94
		if (is_null($this->settingsService->getExternalModelUrl())) {
95
			$error_message = "You still need to configure the URL of the service running the model.";
96
			return false;
97
		}
98
		if (is_null($this->settingsService->getExternalModelApiKey())) {
99
			$error_message = "You still need to configure the API KEY of the service running the model.";
100
			return false;
101
		}
102
		return true;
103
	}
104
105
	public function getMaximumArea(): int {
106
		if ($this->maximumImageArea < 0) {
107
			$this->open();
108
		}
109
		return $this->maximumImageArea;
110
	}
111
112
	public function getPreferredMimeType(): string {
113
		if (is_null($this->preferredMimetype)) {
0 ignored issues
show
introduced by
The condition is_null($this->preferredMimetype) is always false.
Loading history...
114
			$this->open();
115
		}
116
		return $this->preferredMimetype;
117
	}
118
119
	/**
120
	 * @return void
121
	 */
122
	public function install() {
123
		$this->open();
124
		return;
125
	}
126
127
	/**
128
	 * @return void
129
	 */
130
	public function open() {
131
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
132
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
133
134
		$ch = curl_init();
135
		if ($ch === false) {
136
			throw new \Exception('Curl error: unable to initialize curl');
137
		}
138
139
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/open');
140
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: '  . $this->modelApiKey]);
141
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
142
143
		$response = curl_exec($ch);
144
		if ($response === false) {
145
			throw new \Exception('Cannot connect to external model: ' . curl_error($ch));
146
		}
147
148
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
149
		if ($httpCode !== 200) {
150
			throw new \Exception('Can\'t connect with external model. HTTP status code: ' . $httpCode);
151
		}
152
153
		$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

153
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
154
155
		$this->maximumImageArea = intval($jsonResponse['maximum_area']);
156
		$this->preferredMimetype = $jsonResponse['preferred_mimetype'];
157
158
		curl_close($ch);
159
	}
160
161
	public function detectFaces(string $imagePath, bool $compute = true): array {
162
		$ch = curl_init();
163
		if ($ch === false) {
164
			throw new \Exception('Curl error: unable to initialize curl');
165
		}
166
167
		$cFile = curl_file_create($imagePath);
168
		$post = array('file'=> $cFile);
169
170
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/detect');
171
		curl_setopt($ch, CURLOPT_POST, true);
172
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
173
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
174
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
175
176
		$response = curl_exec($ch);
177
		if ($response === false) {
178
			throw new \Exception('External model dont response: ' . curl_error($ch));
179
		}
180
181
		curl_close($ch);
182
183
		$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

183
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
184
185
		if ($jsonResponse['faces-count'] == 0)
186
			return [];
187
188
		return $jsonResponse['faces'];
189
	}
190
191
	public function compute(string $imagePath, array $face): array {
192
		$ch = curl_init();
193
		if ($ch === false) {
194
			throw new \Exception('Curl error: unable to initialize curl');
195
		}
196
197
		$cFile = curl_file_create($imagePath, $this->preferredMimetype, basename($imagePath));
198
		$post = [
199
			'file' => $cFile,
200
			'face' => json_encode($face),
201
		];
202
203
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/compute');
204
		curl_setopt($ch, CURLOPT_POST, true);
205
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
206
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key:' . $this->modelApiKey]);
207
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
208
209
		$response = curl_exec($ch);
210
		if ($response === false) {
211
			throw new \Exception('External model dont response: ' . curl_error($ch));
212
		}
213
214
		curl_close($ch);
215
216
		$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

216
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
217
218
		return $jsonResponse['face'];
219
	}
220
221
}
222