initial commit
This commit is contained in:
commit
7bfed5a48b
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
**/*/logs
|
||||
upload.php
|
||||
config.php
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/composer,macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=composer,macos
|
||||
|
||||
### Composer ###
|
||||
composer.phar
|
||||
/vendor/
|
||||
|
||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
# composer.lock
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/composer,macos
|
2
.htaccess
Normal file
2
.htaccess
Normal file
@ -0,0 +1,2 @@
|
||||
php_value upload_max_filesize 100M
|
||||
php_value post_max_size 100M
|
4
.nova/Configuration.json
Normal file
4
.nova/Configuration.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"workspace.art_style" : 0,
|
||||
"workspace.name" : "PhotoPrismUpload"
|
||||
}
|
8
composer.json
Normal file
8
composer.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"require": {
|
||||
"monolog/monolog": "^2.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"PhotoPrismUpload\\": "src"}
|
||||
}
|
||||
}
|
165
composer.lock
generated
Normal file
165
composer.lock
generated
Normal file
@ -0,0 +1,165 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a2eae41321c2d6bf3b57063bb8fe8c3b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "2.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "71312564759a7db5b789296369c1a264efc43aad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/71312564759a7db5b789296369c1a264efc43aad",
|
||||
"reference": "71312564759a7db5b789296369c1a264efc43aad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/log": "^1.0.1"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||
"doctrine/couchdb": "~1.0@dev",
|
||||
"elasticsearch/elasticsearch": "^7",
|
||||
"graylog2/gelf-php": "^1.4.2",
|
||||
"mongodb/mongodb": "^1.8",
|
||||
"php-amqplib/php-amqplib": "~2.4",
|
||||
"php-console/php-console": "^3.1.3",
|
||||
"phpspec/prophecy": "^1.6.1",
|
||||
"phpstan/phpstan": "^0.12.91",
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"predis/predis": "^1.1",
|
||||
"rollbar/rollbar": "^1.3",
|
||||
"ruflin/elastica": ">=0.90 <7.0.1",
|
||||
"swiftmailer/swiftmailer": "^5.3|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
|
||||
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||
"elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
|
||||
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||
"ext-mbstring": "Allow to work properly with unicode symbols",
|
||||
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
|
||||
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
|
||||
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
|
||||
"php-console/php-console": "Allow sending log messages to Google Chrome",
|
||||
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
||||
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Monolog\\": "src/Monolog"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "https://seld.be"
|
||||
}
|
||||
],
|
||||
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
|
||||
"homepage": "https://github.com/Seldaek/monolog",
|
||||
"keywords": [
|
||||
"log",
|
||||
"logging",
|
||||
"psr-3"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/2.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Seldaek",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-23T07:42:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/log/tree/1.1.4"
|
||||
},
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
6
config.php.sample
Normal file
6
config.php.sample
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'username' => '',
|
||||
'password' => ''
|
||||
];
|
71
index.php
Normal file
71
index.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
session_start();
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use PhotoPrismUpload\API\PhotoPrism;
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body></body>
|
||||
<?php
|
||||
|
||||
if (!isset($_POST['submit'])) {
|
||||
?>
|
||||
<script>
|
||||
window.tooLarge = false;
|
||||
window.tooManyFiles = false;
|
||||
function validateForm() {
|
||||
const isInvalid = window.tooLarge || window.tooManyFiles;
|
||||
console.log('Validating', window.tooLarge, window.tooManyFiles, isInvalid);
|
||||
if (isInvalid) {
|
||||
const errorDiv = document.getElementById('error');
|
||||
errorDiv.innerText = '';
|
||||
errorDiv.innerText += window.tooLarge ? 'Zu groß, Upload muss weniger als 200MB sein. ' : '';
|
||||
errorDiv.innerText += window.tooManyFiles ? 'Zu viele Dateien, maximal 200 erlaubt. ' : '';
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
return !isInvalid;
|
||||
}
|
||||
</script>
|
||||
<form method="POST" enctype="multipart/form-data" onsubmit="return validateForm();">
|
||||
<input multiple type="file" name="files[]" id="input" />
|
||||
<input type="submit" name="submit" value="Upload" />
|
||||
</form>
|
||||
<div id="error" style="display:none"></div>
|
||||
<script>
|
||||
const input = document.getElementById('input')
|
||||
|
||||
input.addEventListener('change', (event) => {
|
||||
const errorDiv = document.getElementById('error');
|
||||
errorDiv.innerText = '';
|
||||
errorDiv.style.display = 'none';
|
||||
|
||||
const target = event.target;
|
||||
let totalSize = 0;
|
||||
if (target.files) {
|
||||
for (file of target.files) {
|
||||
totalSize += file.size;
|
||||
}
|
||||
}
|
||||
const sizeInMb = totalSize / 1000 / 1000;
|
||||
window.tooLarge = sizeInMb >= 200;
|
||||
console.log('Total size:', totalSize, 'too large: ', window.tooLarge);
|
||||
window.tooManyFiles = target.files.length > 200;
|
||||
console.log('Total files:', target.files.length, 'too many: ', window.tooManyFiles);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
die();
|
||||
}
|
||||
|
||||
$config = require(__DIR__ . '/config.php');
|
||||
$api = new PhotoPrism($config);
|
||||
try {
|
||||
$api->login();
|
||||
$api->getAlbums();
|
||||
} catch (\Exception $e) {
|
||||
die('Fehler: ' . $e->getMessage());
|
||||
}
|
177
src/API/PhotoPrism.php
Normal file
177
src/API/PhotoPrism.php
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace PhotoPrismUpload\API;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use PhotoPrismUpload\Exceptions\NetworkException;
|
||||
use PhotoPrismUpload\Exceptions\AuthenticationException;
|
||||
use PhotoPrismUpload\Entities\Album;
|
||||
|
||||
class PhotoPrism
|
||||
{
|
||||
protected string $base_url = 'https://photos.phlaym.net';
|
||||
protected string $api_url = '';
|
||||
protected ?string $session_id = null;
|
||||
protected array $config;
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
array $config,
|
||||
?string $log_path = null
|
||||
) {
|
||||
$this->api_url = $this->base_url.'/api/v1';
|
||||
$this->config = $config;
|
||||
$this->logger = new Logger('PhotoPrismUpload');
|
||||
if (empty($log_path)) {
|
||||
$log_path = __DIR__.'/logs/log.log';
|
||||
}
|
||||
$handler = new RotatingFileHandler($log_path, 5, Logger::DEBUG, true);
|
||||
$this->logger->pushHandler($handler);
|
||||
if (isset($_SESSION['pp_sessionid'])) {
|
||||
$this->session_id = $_SESSION['pp_sessionid'];
|
||||
}
|
||||
}
|
||||
|
||||
private function parseHeaders(string $response): array
|
||||
{
|
||||
$response = explode("\r\n\r\n", $response, 2);
|
||||
$headers = $response[0];
|
||||
if ($headers === 'HTTP/1.1 100 Continue') {
|
||||
$response = explode("\r\n\r\n", $response[1], 2);
|
||||
$headers = $response[0];
|
||||
}
|
||||
if (isset($response[1])) {
|
||||
$content = $response[1];
|
||||
} else {
|
||||
$content = '';
|
||||
}
|
||||
// this is not a good way to parse http headers
|
||||
// it will not (for example) take into account multiline headers
|
||||
// but what we're looking for is pretty basic, so we can ignore those shortcomings
|
||||
$response_headers = explode("\r\n", $headers);
|
||||
$header_arr = [];
|
||||
foreach ($response_headers as $header) {
|
||||
$header = explode(': ', $header, 2);
|
||||
if (count($header) < 2) {
|
||||
continue;
|
||||
}
|
||||
list($k,$v) = $header;
|
||||
$header_arr[$k] = $v;
|
||||
}
|
||||
return ['headers' => $header_arr, 'content' => $content];
|
||||
}
|
||||
|
||||
private function makeRequest(
|
||||
string $method,
|
||||
string $path,
|
||||
array $data = [],
|
||||
string $content_type = 'application/json'
|
||||
): string {
|
||||
$url = $this->api_url.$path;
|
||||
$method = strtolower($method);
|
||||
$query_data = [];
|
||||
$headers = [];
|
||||
$result = null;
|
||||
try {
|
||||
if (is_array($data) && $method !== 'post-raw') {
|
||||
$query_data = $content_type === 'application/json' ? json_encode($data) : http_build_query($data);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
if ($method === 'get' && !empty($query_data)) {
|
||||
$url .= '?'.$query_data;
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $query_data);
|
||||
}
|
||||
if ($method !== 'get') {
|
||||
$headers[] = 'Content-Type: '.$content_type;
|
||||
}
|
||||
|
||||
if (!empty($this->session_id)) {
|
||||
$headers[] = 'X-Session-Id: ' . $this->session_id;
|
||||
}
|
||||
$this->logger->info($method .' request to ' . $url);
|
||||
$this->logger->debug('Headers ' . json_encode($headers));
|
||||
$this->logger->debug('postfields data ' . json_encode($data));
|
||||
$this->logger->debug('postfields ' . $query_data);
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, $method !== 'get');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
|
||||
$output = curl_exec($ch);
|
||||
|
||||
$request = curl_getinfo($ch, CURLINFO_HEADER_OUT);
|
||||
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($output === false) {
|
||||
throw new NetworkException("Error sending request to " . $url, 0);
|
||||
}
|
||||
if (empty($output) || $output === false) {
|
||||
$e = new NetworkException("No answer from" . $url, 0);
|
||||
$this->logger->error("Error sending request", ['Exception' => $e]);
|
||||
throw $e;
|
||||
}
|
||||
if ($http_status === 0) {
|
||||
throw new NetworkException('Unable to connect to API ' . $url);
|
||||
}
|
||||
if ($http_status >= 400) {
|
||||
throw new NetworkException('Invalid response ' . $url . ': ' . $http_status);
|
||||
}
|
||||
$result = $this->parseHeaders($output);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error sending request", ['Exception' => $e]);
|
||||
throw new NetworkException("Error sending request to " . $url, 0, $e);
|
||||
}
|
||||
return $result['content'];
|
||||
}
|
||||
|
||||
public function login(bool $force = false)
|
||||
{
|
||||
if (!empty($this->session_id) && !$force) {
|
||||
$this->logger->info('Skipping login, already logged in');
|
||||
return;
|
||||
}
|
||||
$data = [
|
||||
'username' => $this->config['username'],
|
||||
'password' => $this->config['password'],
|
||||
];
|
||||
$res = $this->makeRequest('POST', '/session', $data);
|
||||
$this->logger->info('Login result: ' . $res);
|
||||
$response = json_decode($res, true);
|
||||
if (!empty($response['error'])) {
|
||||
throw new AuthenticationException($response['error']);
|
||||
}
|
||||
$this->session_id = $response['id'];
|
||||
$_SESSION['pp_sessionid'] = $this->session_id;
|
||||
$this->logger->debug('Session ID: ' . $this->session_id);
|
||||
}
|
||||
|
||||
public function getAlbums(int $count = 1000, int $offset = 0): array
|
||||
{
|
||||
$data = [
|
||||
'count' => $count,
|
||||
'offset' => $offset,
|
||||
'type' => 'album'
|
||||
];
|
||||
$res = $this->makeRequest('GET', '/albums', $data, 'text/plain');
|
||||
$response = json_decode($res, true);
|
||||
if (!empty($response['error'])) {
|
||||
throw new NetworkException($response['error']);
|
||||
}
|
||||
$albums = array_map(
|
||||
function ($entry) {
|
||||
return new Album($entry);
|
||||
},
|
||||
$response
|
||||
);
|
||||
$this->logger->debug('Albums' . json_encode($albums));
|
||||
return $albums;
|
||||
}
|
||||
|
||||
#https://photos.phlaym.net/api/v1/albums?q=&count=1000&offset=0&type=album
|
||||
}
|
18
src/Entities/Album.php
Normal file
18
src/Entities/Album.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace PhotoPrismUpload\Entities;
|
||||
|
||||
use Monolog\Logger;
|
||||
|
||||
class Album
|
||||
{
|
||||
public string $uid = '';
|
||||
public string $slug = '';
|
||||
public string $title = '';
|
||||
public function __construct(
|
||||
array $response
|
||||
) {
|
||||
$this->uid = $response['UID'];
|
||||
$this->slug = $response['Slug'];
|
||||
$this->title = $response['Title'];
|
||||
}
|
||||
}
|
7
src/Exceptions/AuthenticationException.php
Normal file
7
src/Exceptions/AuthenticationException.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace PhotoPrismUpload\Exceptions;
|
||||
|
||||
class AuthenticationException extends \Exception
|
||||
{
|
||||
|
||||
}
|
7
src/Exceptions/NetworkException.php
Normal file
7
src/Exceptions/NetworkException.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace PhotoPrismUpload\Exceptions;
|
||||
|
||||
class NetworkException extends \Exception
|
||||
{
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user