Skip to content

Commit

Permalink
Store poster thumb/full image paths in the DB to avoid filesystem see…
Browse files Browse the repository at this point in the history
…king in lots of places.
  • Loading branch information
BusterNeece committed Apr 4, 2024
1 parent 17071aa commit b06cc5c
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 73 deletions.
4 changes: 2 additions & 2 deletions backend/bootstrap/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function mediaUrl(string $url): string
function avatarUrl(string|bool|null $userImg): string
{
return (!empty($userImg))
? mediaUrl('/img/profile/' . $userImg)
? mediaUrl(App\Media::avatarPath($userImg))
: '/static/img/avatar.webp';
}

Expand All @@ -57,7 +57,7 @@ function djAvatarUrl(
string|bool|null $userImg
): string {
return (!empty($djImg))
? mediaUrl('/img/djs/' . $djImg)
? mediaUrl(App\Media::djAvatarPath($djImg))
: avatarUrl($userImg);
}

Expand Down
2 changes: 1 addition & 1 deletion backend/src/Controller/Dashboard/Admin/AddWorldAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function __invoke(
? $this->http->get($worldData['image'])->getBody()->getContents()
: $worldData['image'];

$imageRelativePath = '/img/worlds/' . $worldDbTitle . '.png';
$imageRelativePath = Media::worldPath($worldDbTitle . '.png');

$image = $this->imageManager->read($imageData);

Expand Down
11 changes: 7 additions & 4 deletions backend/src/Controller/Dashboard/AvatarAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use Doctrine\DBAL\Connection;
use GuzzleHttp\Psr7\UploadedFile;
use Intervention\Image\ImageManager;
use League\Flysystem\UnableToDeleteFile;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Filesystem\Filesystem;

final readonly class AvatarAction
{
Expand Down Expand Up @@ -37,8 +37,8 @@ public function __invoke(
};

$uploadFolder = match ($type) {
'dj' => '/img/djs',
default => '/img/profile'
'dj' => Media::DJ_AVATARS_DIR,
default => Media::AVATARS_DIR
};

if ($request->isPost()) {
Expand All @@ -54,8 +54,11 @@ public function __invoke(
// Remove existing image if it's set.
if (!empty($currentUser[$avatarField])) {
$currentImage = $uploadFolder . '/' . $currentUser[$avatarField];
if ($fs->has($currentImage)) {

try {
$fs->delete($currentImage);
} catch (UnableToDeleteFile) {
// Noop
}
}

Expand Down
101 changes: 50 additions & 51 deletions backend/src/Controller/Dashboard/PostersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Intervention\Image\ImageManager;
use League\Flysystem\StorageAttributes;
use League\Flysystem\UnableToDeleteFile;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UploadedFileInterface;

Expand Down Expand Up @@ -70,34 +70,12 @@ public function listAction(

$nowDt = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));

// Iterate files in the poster uploads directory once to avoid a bunch of repeat calls.
$fs = Media::getFilesystem();

$posterFiles = [];

/** @var StorageAttributes $posterFile */
foreach ($fs->listContents('img/posters/') as $posterFile) {
if (!$posterFile->isFile()) {
continue;
}

$posterFiles[$posterFile->path()] = $posterFile->path();
}

foreach ($qb->fetchAllAssociative() as $poster) {
// Get poster URL
$tryMediaUrls = [
'img/posters/' . urlencode($poster['file']) . '_thumb.jpg',
'img/posters/' . urlencode($poster['file']) . '_150x200.jpeg',
];

$mediaUrl = '/static/img/no_poster_thumb.jpg';

foreach ($tryMediaUrls as $tryMediaUrl) {
if (isset($posterFiles[$tryMediaUrl])) {
$mediaUrl = mediaUrl($tryMediaUrl);
break;
}
if (!empty($poster['thumb_path'])) {
$mediaUrl = mediaUrl(Media::posterPath($poster['thumb_path']));
} else {
$mediaUrl = '/static/img/no_poster_thumb.jpg';
}
$poster['mediaUrl'] = $mediaUrl;

Expand Down Expand Up @@ -156,10 +134,8 @@ public function createAction(
/** @var UploadedFileInterface $file */
$file = $files['fileToUpload'];

$basename = $this->generateAssets($file);

$row = $this->generateAssets($file);
$row['creator'] = $currentUser['id'];
$row['file'] = $basename;

$postData = $request->getParsedBody();

Expand Down Expand Up @@ -229,8 +205,12 @@ public function editAction(
$file = $files['fileToUpload'];

if ($file->getError() === UPLOAD_ERR_OK) {
$this->deleteAssets($row['file']);
$row['file'] = $this->generateAssets($files['fileToUpload']);
$this->deleteAssets($row);

$row = [
...$row,
...$this->generateAssets($files['fileToUpload']),
];
}
}

Expand Down Expand Up @@ -301,7 +281,7 @@ public function deleteAction(
$id = $params['id'] ?? $request->getParam('pid');
$row = $this->getEditablePoster($request, $id);

$this->deleteAssets($row['file']);
$this->deleteAssets($row);

$this->db->delete(
'web_posters',
Expand Down Expand Up @@ -391,47 +371,66 @@ private function getEditablePoster(
return $row;
}

private function deleteAssets(string $posterFile): void
private function deleteAssets(array $poster): void
{
$sizes = [
'_thumb.jpg',
'_full.jpg',
'_300x500.jpeg',
'_150x200.jpeg',
'_590x1000.jpeg',
];

$fs = Media::getFilesystem();

foreach ($sizes as $size) {
$filePath = '/img/posters/' . $posterFile . $size;
if ($fs->has($filePath)) {
$fs->delete($filePath);
if (!empty($poster['full_path'])) {
$fullPath = Media::posterPath($poster['full_path']);

try {
$fs->delete($fullPath);
} catch (UnableToDeleteFile) {
// Noop
}
}

if (!empty($poster['thumb_path'])) {
$thumbPath = Media::posterPath($poster['thumb_path']);

try {
$fs->delete($thumbPath);
} catch (UnableToDeleteFile) {
// Noop
}
}
}

private function generateAssets(UploadedFileInterface $filePath): string
/**
* @return array{
* file: string,
* full_path: string,
* thumb_path: string
* }
*/
private function generateAssets(UploadedFileInterface $filePath): array
{
$image = $this->imageManager->read($filePath->getStream()->getContents());

// Create a random 12 digit hash for file name
$basename = bin2hex(random_bytes(6)); // 6 bytes = 12 hex characters

$fullPath = $basename . '_full.jpg';
$thumbPath = $basename . '_thumb.jpg';

$sizes = [
[118, 200, $basename . '_thumb.jpg'],
[590, 1000, $basename . '_full.jpg'],
[118, 200, $thumbPath],
[590, 1000, $fullPath],
];

$fs = Media::getFilesystem();
foreach ($sizes as [$width, $height, $filename]) {
$thumbnail = clone $image;
$thumbnail->cover($width, $height);

$destPath = '/img/posters/' . $filename;
$destPath = Media::posterPath($filename);
$fs->write($destPath, $thumbnail->encodeByPath($destPath)->toString());
}

return $basename;
return [
'file' => $basename,
'full_path' => $fullPath,
'thumb_path' => $thumbPath,
];
}
}
19 changes: 5 additions & 14 deletions backend/src/Controller/Posters/GetPosterAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __invoke(
$posterId = $params['id'] ?? $request->getParam('pid', $request->getParam('id'));

$qb = $this->db->createQueryBuilder()
->select('p.id', 'p.file')
->select('p.id', 'p.full_path')
->from('web_posters', 'p')
->join('p', 'web_users', 'u', 'p.creator = u.id')
->where('u.banned != 1')
Expand Down Expand Up @@ -121,19 +121,10 @@ public function __invoke(

if (!empty($post)) {
$fs = Media::getFilesystem();

$tryPaths = [
'/img/posters/' . $post['file'] . '_full.jpg',
'/img/posters/' . $post['file'] . '_590x1000.jpeg',
];

foreach ($tryPaths as $tryPath) {
try {
$image = $fs->read($tryPath);
break;
} catch (FilesystemException) {
// Noop
}
try {
$image = $fs->read(Media::posterPath($post['full_path']));
} catch (FilesystemException) {
// Noop
}

// Update view count
Expand Down
25 changes: 25 additions & 0 deletions backend/src/Media.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

final class Media
{
public const string AVATARS_DIR = 'img/profile';
public const string DJ_AVATARS_DIR = 'img/djs';
public const string POSTERS_DIR = 'img/posters';
public const string WORLDS_DIR = 'img/worlds';

public static function getFilesystem(): Filesystem
{
static $fs;
Expand Down Expand Up @@ -66,4 +71,24 @@ private static function getRemoteAdapter(): FilesystemAdapter

return new AwsS3V3Adapter($s3Client, ltrim($dsnParsed->getPath(), '/'));
}

public static function avatarPath(string $path): string
{
return self::AVATARS_DIR . '/' . ltrim($path, '/');
}

public static function djAvatarPath(string $path): string
{
return self::DJ_AVATARS_DIR . '/' . ltrim($path, '/');
}

public static function posterPath(string $path): string
{
return self::POSTERS_DIR . '/' . ltrim($path, '/');
}

public static function worldPath(string $path): string
{
return self::WORLDS_DIR . '/' . ltrim($path, '/');
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "project",
"description": "The new WaterWolf web site.",
"require": {
"php": "^8.2",
"php": "^8.3",
"ext-curl": "*",
"ext-fileinfo": "*",
"ext-gd": "*",
Expand Down
Loading

0 comments on commit b06cc5c

Please sign in to comment.