Passed
Push — master ( d267c3...11df2b )
by Matias
05:32
created

ExternalModel::getId()   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
		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
	public function install() {
120
		return;
121
	}
122
123
	public function open() {
124
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
125
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
126
127
		$ch = curl_init();
128
		if ($ch === false) {
129
			throw new \Exception('Curl error: unable to initialize curl');
130
		}
131
132
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/open');
133
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: '  . $this->modelApiKey]);
134
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
135
136
		$response = curl_exec($ch);
137
		if ($response === false) {
138
			throw new \Exception('Cannot connect to external model: ' . curl_error($ch));
139
		}
140
141
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
142
		if ($httpCode !== 200) {
143
			throw new \Exception('Can\'t connect with external model. HTTP status code: ' . $httpCode);
144
		}
145
146
		$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

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

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

209
		$jsonResponse = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
210
211
		return $jsonResponse['face'];
212
	}
213
214
}
215