Skip to content

Commit 4e94dc5

Browse files
authored
Merge pull request #26 from andrey18106/dev
MediaDC v0.1.7
2 parents 665b717 + 64c1ceb commit 4e94dc5

25 files changed

+360
-204
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ A clear and concise description of what you expected to happen.
2020
If applicable, add screenshots to help explain your problem.
2121

2222
**Desktop (please complete the following information):**
23-
- OS: [e.g. iOS]
23+
- OS [e.g. iOS]
24+
- CPU (architecture)
2425
- Browser [e.g. chrome, safari]
2526
- Nextcloud version [e.g. 22]
2627
- Database configuration (without sensitive information)
@@ -29,7 +30,7 @@ If applicable, add screenshots to help explain your problem.
2930

3031
**Smartphone (please complete the following information):**
3132
- Device: [e.g. iPhone6]
32-
- OS: [e.g. iOS8.1]
33+
- OS: [e.g. iOS 8.1]
3334
- Browser [e.g. stock browser, safari]
3435
- Nextcloud version [e.g. 22]
3536
- MediaDC version [e.g. 0.1.0]

.github/workflows/static-analysis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Static analysis
22

33
on:
44
pull_request:
5-
branches: [main, test, dev]
5+
branches: [main, dev]
66
types: [opened, reopened, edited]
77
push:
88
branches: [dev]
@@ -37,7 +37,7 @@ jobs:
3737
runs-on: ubuntu-latest
3838
strategy:
3939
matrix:
40-
php-versions: [7.4, 8.0]
40+
php-versions: [7.3, 7.4, 8.0]
4141
steps:
4242
- uses: actions/checkout@v2
4343

CHANGELOG.md

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.1.7 - 2021-10-30]
6+
7+
### Added
8+
9+
- Added php `exec` function availability check
10+
11+
### Changed
12+
13+
- Changed missed PHP version requirement from 7.4 to 7.3
14+
- Moved MediaDC admin settings from Groupware tab to separate Administrator menu item
15+
- Changed server errors messages and moved them to the Congiguration page
16+
17+
### Fixed
18+
19+
- Fixed Python version validation fails
20+
521
## [0.1.6 - 2021-10-22]
622

723
### Added

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
![build](https://github.com/andrey18106/mediadc/actions/workflows/create-release-draft.yml/badge.svg)
44
![static-analysis](https://github.com/andrey18106/mediadc/actions/workflows/static-analysis.yml/badge.svg)
55

6+
[![Github All Releases](https://img.shields.io/github/downloads/andrey18106/mediadc/total.svg)](https://github.com/andrey18106/mediadc/releases)
7+
8+
69
**📸📹 Collect photo and video duplicates to save your cloud storage space**
710

811
![Home page](/screenshots/task-managment.png)

appinfo/info.xml

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<summary>Nextcloud Media Duplicate Collector application</summary>
77
<description>
88
<![CDATA[
9+
10+
**Python 3.6.8 or higher required**
11+
912
This app allows to find duplicate or similar 📸📹 photos and videos
1013
* **Many image formats supported** Jpeg, png, tiff, bmp, gif, heic/hif, cr2 and others.
1114
* **Large amount of supported video formats** All that is supported by ffmpeg.
@@ -17,7 +20,7 @@ This app allows to find duplicate or similar 📸📹 photos and videos
1720
Quick start guide and further information in our [Wiki](https://github.com/andrey18106/mediadc/wiki).
1821
]]>
1922
</description>
20-
<version>0.1.6</version>
23+
<version>0.1.7</version>
2124
<licence>agpl</licence>
2225
<author mail="andrey18106x@gmail.com" homepage="https://github.com/andrey18106">Andrey Borysenko</author>
2326
<author mail="bigcat88@icloud.com" homepage="https://github.com/bigcat88">Alexander Piskun</author>
@@ -36,11 +39,11 @@ Quick start guide and further information in our [Wiki](https://github.com/andre
3639
<screenshot>https://raw.githubusercontent.com/andrey18106/mediadc/main/screenshots/admin-config.png</screenshot>
3740
<dependencies>
3841
<nextcloud min-version="21" max-version="22" />
39-
<php min-version="7.4" />
40-
<python min-version="3.6.8" />
42+
<php min-version="7.3" />
4143
</dependencies>
4244
<settings>
4345
<admin>OCA\MediaDC\Settings\AdminSettings</admin>
46+
<admin-section>OCA\MediaDC\Settings\AdminSection</admin-section>
4447
</settings>
4548
<repair-steps>
4649
<install>

appinfo/routes.php

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
/**
33
* @copyright 2021 Andrey Borysenko <andrey18106x@gmail.com>
4+
*
45
* @copyright 2021 Alexander Piskun <bigcat88@icloud.com>
56
*
67
* @author Andrey Borysenko <andrey18106x@gmail.com>

img/settings.svg

+2
Loading

js/mediadc-dashboard.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-dashboard.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-main.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-main.js.LICENSE.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
*/
4242

4343
/*!
44-
* vue-router v3.5.2
44+
* vue-router v3.5.3
4545
* (c) 2021 Evan You
4646
* @license MIT
4747
*/

js/mediadc-main.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-settings.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-settings.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-src_views_Configuration_vue.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/mediadc-src_views_Configuration_vue.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/Service/PythonService.php

+105-44
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
use OCA\MediaDC\AppInfo\Application;
3232
use OCA\MediaDC\Db\Setting;
3333
use OCA\MediaDC\Db\SettingMapper;
34-
use OCA\MediaDC\Exception\PythonNotValidException;
34+
3535
use OCP\IConfig;
36+
use Psr\Log\LoggerInterface;
37+
use bantu\IniGetWrapper\IniGetWrapper;
3638

3739

3840
class PythonService {
@@ -46,16 +48,17 @@ class PythonService {
4648
/** @var IConfig */
4749
private $config;
4850

49-
public function __construct(SettingMapper $settingMapper, IConfig $config)
51+
/** @var LoggerInterface */
52+
private $logger;
53+
54+
public function __construct(SettingMapper $settingMapper, IConfig $config, LoggerInterface $logger)
5055
{
5156
$this->config = $config;
57+
$this->logger = $logger;
5258
/** @var Setting */
5359
$pythonCommand = $settingMapper->findByName('python_command');
5460
$this->pythonCommand = $pythonCommand->getValue();
5561
$this->cwd = $this->getCustomAppsDirectory() . Application::APP_ID . '/lib/Service/python';
56-
if (!$this->isPythonCompatible()) {
57-
throw new PythonNotValidException("Python version is lower then 3.6.8 or not available");
58-
}
5962
}
6063

6164
/**
@@ -119,38 +122,62 @@ static function scriptEnvsCallback($key, $value) {
119122
return "$key=\"$value\" ";
120123
}
121124

125+
/**
126+
* Check server requirements
127+
*
128+
* @return array check results with errors list
129+
*/
130+
private function checkDepsRequirements() {
131+
$errors = [];
132+
if (!$this->isFunctionEnabled('exec')) {
133+
array_push($errors, '`exec` PHP function is not available.');
134+
}
135+
if (!$this->isPythonCompatible()) {
136+
array_push($errors, 'Python version is lower then 3.6.8 or not available');
137+
}
138+
return ['success' => count($errors) === 0, 'errors' => $errors];
139+
}
140+
122141
/**
123142
* @param string @listName
124143
*
125144
* @return array installation results list
126145
*/
127146
public function installDependencies($listName = '') {
128-
try {
129-
$pythonResult = $this->run('/install.py', [
130-
'--install' => $listName === '' ? 'required optional' : $listName,
131-
], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
132-
return $this->parsePythonOutput($pythonResult);
133-
} catch (\Exception $e) {
134-
return [
135-
'success' => false,
136-
'message' => 'Some error while running the Python script',
137-
];
147+
$depsCheck = $this->checkDepsRequirements();
148+
if ($depsCheck['success']) {
149+
try {
150+
$pythonResult = $this->run('/install.py', [
151+
'--install' => $listName === '' ? 'required optional' : $listName,
152+
], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
153+
return $this->parsePythonOutput($pythonResult);
154+
} catch (\Exception $e) {
155+
return [
156+
'success' => false,
157+
'message' => 'Some error while running the Python script',
158+
];
159+
}
138160
}
161+
return $depsCheck;
139162
}
140163

141164
/**
142165
* @return array list of uninstalled Python packages
143166
*/
144167
public function checkInstallation() {
145-
try {
146-
$pythonResult = $this->run('/install.py', ['--check' => ''], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
147-
return $this->parsePythonOutput($pythonResult);
148-
} catch (\Exception $e) {
149-
return [
150-
'success' => false,
151-
'message' => 'Some error while running the Python script',
152-
];
168+
$depsCheck = $this->checkDepsRequirements();
169+
if ($depsCheck['success']) {
170+
try {
171+
$pythonResult = $this->run('/install.py', ['--check' => ''], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
172+
return $this->parsePythonOutput($pythonResult);
173+
} catch (\Exception $e) {
174+
return [
175+
'success' => false,
176+
'message' => 'Some error while running the Python script',
177+
];
178+
}
153179
}
180+
return $depsCheck;
154181
}
155182

156183
/**
@@ -159,15 +186,19 @@ public function checkInstallation() {
159186
* @return array installed packages list after deleting
160187
*/
161188
public function deleteDependencies($packagesList = []) {
162-
try {
163-
$pythonResult = $this->run('/install.py', ['--delete' => join(" ", $packagesList)], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
164-
return $this->parsePythonOutput($pythonResult);
165-
} catch (\Exception $e) {
166-
return [
167-
'success' => false,
168-
'message' => 'Some error while running the Python script',
169-
];
189+
$depsCheck = $this->checkDepsRequirements();
190+
if ($depsCheck['success']) {
191+
try {
192+
$pythonResult = $this->run('/install.py', ['--delete' => join(" ", $packagesList)], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
193+
return $this->parsePythonOutput($pythonResult);
194+
} catch (\Exception $e) {
195+
return [
196+
'success' => false,
197+
'message' => 'Some error while running the Python script',
198+
];
199+
}
170200
}
201+
return $depsCheck;
171202
}
172203

173204
/**
@@ -176,27 +207,31 @@ public function deleteDependencies($packagesList = []) {
176207
* @return array installed packages list after deleting
177208
*/
178209
public function updateDependencies($packagesList = []) {
179-
try {
180-
$pythonResult = $this->run('/install.py', ['--update' => join(" ", $packagesList)], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
181-
return $this->parsePythonOutput($pythonResult);
182-
} catch (\Exception $e) {
183-
return [
184-
'success' => false,
185-
'message' => 'Some error while running the Python script',
186-
];
210+
$depsCheck = $this->checkDepsRequirements();
211+
if ($depsCheck['success']) {
212+
try {
213+
$pythonResult = $this->run('/install.py', ['--update' => join(" ", $packagesList)], false, ['PHP_PATH' => $this->getPhpInterpreter()]);
214+
return $this->parsePythonOutput($pythonResult);
215+
} catch (\Exception $e) {
216+
return [
217+
'success' => false,
218+
'message' => 'Some error while running the Python script',
219+
];
220+
}
187221
}
222+
return $depsCheck;
188223
}
189224

190225
/**
191226
* @return string|null
192227
*/
193228
public function getPythonVersion() {
194-
exec($this->pythonCommand . ' --version', $output);
195-
if (preg_match_all("/\d{1}\.\d{1,2}(\.\d{1,2}){0,1}/s", $output[0], $matches)) {
196-
return $matches[0][0];
197-
} else {
198-
return null;
229+
exec($this->pythonCommand . ' --version', $output, $result_code);
230+
if ($result_code === 0 && isset($output[0]) && preg_match_all("/\d{1}\.\d{1,2}(\.\d{1,2}){0,1}/s", $output[0], $matches)) {
231+
return isset($matches[0][0]) ? $matches[0][0] : null;
199232
}
233+
$this->logger->error('[' . self::class . '] Command executed with error result_code: ' . $result_code);
234+
return null;
200235
}
201236

202237
/**
@@ -345,4 +380,30 @@ private function getCustomAppsDirectory() {
345380
return getcwd() . '/apps/';
346381
}
347382

383+
/**
384+
* Check if a php function available
385+
*
386+
* @param string $function_name
387+
*
388+
* @return bool
389+
*/
390+
private function isFunctionEnabled($function_name) {
391+
if (!function_exists($function_name)) {
392+
return false;
393+
}
394+
/** @var IniGetWrapper $ini */
395+
$ini = \OC::$server->get(IniGetWrapper::class);
396+
$disabled = explode(',', $ini->get('disable_functions') ?: '');
397+
$disabled = array_map('trim', $disabled);
398+
if (in_array($function_name, $disabled)) {
399+
return false;
400+
}
401+
$disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
402+
$disabled = array_map('trim', $disabled);
403+
if (in_array($function_name, $disabled)) {
404+
return false;
405+
}
406+
return true;
407+
}
408+
348409
}

lib/Exception/PythonNotValidException.php renamed to lib/Settings/AdminSection.php

+36-3
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,43 @@
2626
*
2727
*/
2828

29-
namespace OCA\MediaDC\Exception;
29+
namespace OCA\MediaDC\Settings;
3030

31-
use Exception;
31+
use OC\URLGenerator;
32+
use OCA\MediaDC\AppInfo\Application;
33+
use OCP\IL10N;
34+
use OCP\Settings\IIconSection;
3235

3336

34-
class PythonNotValidException extends Exception {
37+
class AdminSection implements IIconSection {
38+
39+
/** @var IL10N */
40+
private $l;
41+
42+
/** @var UrlGenerator */
43+
private $urlGenerator;
44+
45+
public function __construct(IL10N $l, URLGenerator $urlGenerator)
46+
{
47+
$this->l = $l;
48+
$this->urlGenerator = $urlGenerator;
49+
}
50+
51+
public function getId() {
52+
return Application::APP_ID;
53+
}
54+
55+
public function getName() {
56+
return $this->l->t('MediaDC');
57+
}
58+
59+
public function getPriority()
60+
{
61+
return 50;
62+
}
63+
64+
public function getIcon() {
65+
return $this->urlGenerator->imagePath(Application::APP_ID, 'settings.svg');
66+
}
67+
3568
}

0 commit comments

Comments
 (0)