fix video uploads
This commit is contained in:
@@ -105,7 +105,7 @@ class MediaController extends ApiController
|
|||||||
|
|
||||||
$mediaItem = $request->user()->media()->create($request->except(['file','transform']));
|
$mediaItem = $request->user()->media()->create($request->except(['file','transform']));
|
||||||
|
|
||||||
$temporaryFilePath = generateTempFilePath();
|
$temporaryFilePath = generateTempFilePath(pathinfo($mediaItem->name, PATHINFO_EXTENSION));
|
||||||
copy($file->path(), $temporaryFilePath);
|
copy($file->path(), $temporaryFilePath);
|
||||||
|
|
||||||
$transformData = ['file' => [
|
$transformData = ['file' => [
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class MediaJob implements ShouldQueue
|
|||||||
$jpgFileName = pathinfo($filePath, PATHINFO_FILENAME) . '.jpg';
|
$jpgFileName = pathinfo($filePath, PATHINFO_FILENAME) . '.jpg';
|
||||||
$jpgFilePath = $uploadedFileDirectory . '/' . $jpgFileName;
|
$jpgFilePath = $uploadedFileDirectory . '/' . $jpgFileName;
|
||||||
if (file_exists($jpgFilePath) === true) {
|
if (file_exists($jpgFilePath) === true) {
|
||||||
$this->media->error("File already exists in storage");
|
$this->media->error("File already exists on server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +110,9 @@ class MediaJob implements ShouldQueue
|
|||||||
// Check if file already exists
|
// Check if file already exists
|
||||||
if (Storage::disk($this->media->storage)->exists($this->media->name) === true) {
|
if (Storage::disk($this->media->storage)->exists($this->media->name) === true) {
|
||||||
if (array_key_exists('replace', $uploadData) === false || isTrue($uploadData['replace']) === false) {
|
if (array_key_exists('replace', $uploadData) === false || isTrue($uploadData['replace']) === false) {
|
||||||
$this->media->error("File already exists in storage");
|
$this->media->error("File already exists on server");
|
||||||
$errorStr = "cannot upload file {$this->media->storage} " . // phpcs:ignore
|
$errorStr = "cannot upload file " . $this->media->storage . " " . // phpcs:ignore
|
||||||
"/ {$this->media->name} as it already exists";
|
"/ " . $this->media->name . " as it already exists";
|
||||||
Log::info($errorStr);
|
Log::info($errorStr);
|
||||||
throw new \Exception($errorStr);
|
throw new \Exception($errorStr);
|
||||||
}
|
}
|
||||||
@@ -282,10 +282,10 @@ class MediaJob implements ShouldQueue
|
|||||||
$this->media->deleteStagingFile();
|
$this->media->deleteStagingFile();
|
||||||
|
|
||||||
if (strpos($this->media->status, 'Error') !== 0) {
|
if (strpos($this->media->status, 'Error') !== 0) {
|
||||||
$this->media->error('Failed to process');
|
$this->media->error('Failed to process the file');
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::error($e->getMessage());
|
Log::error($e->getMessage() . "\n" . $e->getFile() . " - " . $e->getLine() . "\n" . $e->getTraceAsString());
|
||||||
$this->fail($e);
|
$this->fail($e);
|
||||||
}//end try
|
}//end try
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use App\Jobs\MoveMediaJob;
|
|||||||
use App\Jobs\StoreUploadedFileJob;
|
use App\Jobs\StoreUploadedFileJob;
|
||||||
use App\Traits\Uuids;
|
use App\Traits\Uuids;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use FFMpeg\Coordinate\TimeCode;
|
||||||
|
use FFMpeg\FFMpeg;
|
||||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\InvalidCastException;
|
use Illuminate\Database\Eloquent\InvalidCastException;
|
||||||
@@ -694,7 +696,7 @@ class Media extends Model
|
|||||||
public function createStagingFile(): bool
|
public function createStagingFile(): bool
|
||||||
{
|
{
|
||||||
if ($this->stagingFilePath === "") {
|
if ($this->stagingFilePath === "") {
|
||||||
$readStream = Storage::disk($this->storageDisk)->readStream($this->name);
|
$readStream = Storage::disk($this->storage)->readStream($this->name);
|
||||||
$filePath = generateTempFilePath(pathinfo($this->name, PATHINFO_EXTENSION));
|
$filePath = generateTempFilePath(pathinfo($this->name, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
$writeStream = fopen($filePath, 'w');
|
$writeStream = fopen($filePath, 'w');
|
||||||
@@ -788,8 +790,8 @@ class Media extends Model
|
|||||||
// delete existing thumbnail
|
// delete existing thumbnail
|
||||||
if (strlen($this->thumbnail) !== 0) {
|
if (strlen($this->thumbnail) !== 0) {
|
||||||
$path = substr($this->thumbnail, strlen($this->getUrlPath()));
|
$path = substr($this->thumbnail, strlen($this->getUrlPath()));
|
||||||
if (strlen($path) > 0 && Storage::disk($this->storageDisk)->exists($path) === true) {
|
if (strlen($path) > 0 && Storage::disk($this->storage)->exists($path) === true) {
|
||||||
Storage::disk($this->storageDisk)->delete($path);
|
Storage::disk($this->storage)->delete($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,8 +802,6 @@ class Media extends Model
|
|||||||
$newFilename = pathinfo($this->name, PATHINFO_FILENAME) . "-thumb.webp";
|
$newFilename = pathinfo($this->name, PATHINFO_FILENAME) . "-thumb.webp";
|
||||||
$success = false;
|
$success = false;
|
||||||
|
|
||||||
$ffmpegPath = env('FFMPEG_PATH', '/usr/bin/ffmpeg');
|
|
||||||
|
|
||||||
if (strpos($this->mime_type, 'image/') === 0) {
|
if (strpos($this->mime_type, 'image/') === 0) {
|
||||||
$image = Image::make($filePath);
|
$image = Image::make($filePath);
|
||||||
$image->resize($thumbnailWidth, $thumbnailHeight, function ($constraint) {
|
$image->resize($thumbnailWidth, $thumbnailHeight, function ($constraint) {
|
||||||
@@ -852,18 +852,24 @@ class Media extends Model
|
|||||||
$image->encode('webp', 75)->save($tempImagePath);
|
$image->encode('webp', 75)->save($tempImagePath);
|
||||||
|
|
||||||
$success = true;
|
$success = true;
|
||||||
} elseif (file_exists($ffmpegPath) === true && strpos($this->mime_type, 'video/') === 0) {
|
} elseif (strpos($this->mime_type, 'video/') === 0) {
|
||||||
$tempImagePath .= '.webp';
|
$tempImagePath .= '.webp';
|
||||||
$command = "$ffmpegPath -i $filePath -ss 00:00:05 -vframes 1 " . // phpcs:ignore
|
|
||||||
"-s {$thumbnailWidth}x{$thumbnailHeight} -c:v webp {$tempImagePath}";
|
try {
|
||||||
exec($command);
|
$ffmpeg = FFMpeg::create();
|
||||||
|
$video = $ffmpeg->open($filePath);
|
||||||
|
$frame = $video->frame(TimeCode::fromSeconds(5));
|
||||||
|
$frame->save($tempImagePath);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
Log::error($e);
|
||||||
|
}
|
||||||
|
|
||||||
$success = true;
|
$success = true;
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
if ($success === true && file_exists($tempImagePath) === true) {
|
if ($success === true && file_exists($tempImagePath) === true) {
|
||||||
/** @var Illuminate\Filesystem\FilesystemAdapter */
|
/** @var Illuminate\Filesystem\FilesystemAdapter */
|
||||||
$fileSystem = Storage::disk($this->storageDisk);
|
$fileSystem = Storage::disk($this->storage);
|
||||||
$fileSystem->putFileAs('/', new SplFileInfo($tempImagePath), $newFilename);
|
$fileSystem->putFileAs('/', new SplFileInfo($tempImagePath), $newFilename);
|
||||||
unlink($tempImagePath);
|
unlink($tempImagePath);
|
||||||
|
|
||||||
@@ -893,8 +899,8 @@ class Media extends Model
|
|||||||
if (strlen($this->thumbnail) > 0) {
|
if (strlen($this->thumbnail) > 0) {
|
||||||
$path = substr($this->thumbnail, strlen($this->getUrlPath()));
|
$path = substr($this->thumbnail, strlen($this->getUrlPath()));
|
||||||
|
|
||||||
if (strlen($path) > 0 && Storage::disk($this->storageDisk)->exists($path) === true) {
|
if (strlen($path) > 0 && Storage::disk($this->storage)->exists($path) === true) {
|
||||||
Storage::disk($this->storageDisk)->delete($path);
|
Storage::disk($this->storage)->delete($path);
|
||||||
$this->thumbnail = ''; // Clear the thumbnail property
|
$this->thumbnail = ''; // Clear the thumbnail property
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -917,8 +923,8 @@ class Media extends Model
|
|||||||
// delete existing variants
|
// delete existing variants
|
||||||
if (is_array($this->variants) === true) {
|
if (is_array($this->variants) === true) {
|
||||||
foreach ($this->variants as $variantName => $variantFile) {
|
foreach ($this->variants as $variantName => $variantFile) {
|
||||||
if (Storage::disk($this->storageDisk)->exists($variantFile) === true) {
|
if (Storage::disk($this->storage)->exists($variantFile) === true) {
|
||||||
Storage::disk($this->storageDisk)->delete($variantFile);
|
Storage::disk($this->storage)->delete($variantFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -973,7 +979,7 @@ class Media extends Model
|
|||||||
$tempImagePath = tempnam(sys_get_temp_dir(), 'optimize');
|
$tempImagePath = tempnam(sys_get_temp_dir(), 'optimize');
|
||||||
$image->encode('webp', 75)->save($tempImagePath);
|
$image->encode('webp', 75)->save($tempImagePath);
|
||||||
/** @var Illuminate\Filesystem\FilesystemAdapter */
|
/** @var Illuminate\Filesystem\FilesystemAdapter */
|
||||||
$fileSystem = Storage::disk($this->storageDisk);
|
$fileSystem = Storage::disk($this->storage);
|
||||||
$fileSystem->putFileAs('/', new SplFileInfo($tempImagePath), $newFilename);
|
$fileSystem->putFileAs('/', new SplFileInfo($tempImagePath), $newFilename);
|
||||||
unlink($tempImagePath);
|
unlink($tempImagePath);
|
||||||
}//end if
|
}//end if
|
||||||
|
|||||||
@@ -192,7 +192,8 @@
|
|||||||
class="mt-4 text-center">
|
class="mt-4 text-center">
|
||||||
<p class="text-xs text-black mb-4">
|
<p class="text-xs text-black mb-4">
|
||||||
Showing {{ mediaItems.length }} of
|
Showing {{ mediaItems.length }} of
|
||||||
{{ totalItems }} media item{{
|
{{ totalItems }}
|
||||||
|
media item{{
|
||||||
totalItems == 1 ? "" : "s"
|
totalItems == 1 ? "" : "s"
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
@@ -242,17 +243,17 @@
|
|||||||
<div v-if="lastSelected != null">
|
<div v-if="lastSelected != null">
|
||||||
<div
|
<div
|
||||||
class="flex text-xs border-b border-gray-3 pb-4">
|
class="flex text-xs border-b border-gray-3 pb-4">
|
||||||
<img
|
<div
|
||||||
:src="
|
class="w-100 h-100 max-h-20 max-w-20 mr-2 bg-contain bg-no-repeat bg-center"
|
||||||
mediaGetThumbnail(
|
:style="{
|
||||||
|
backgroundImage: `url('${mediaGetThumbnail(
|
||||||
lastSelected,
|
lastSelected,
|
||||||
null,
|
null,
|
||||||
itemRequiresRefresh(
|
itemRequiresRefresh(
|
||||||
lastSelected.id,
|
lastSelected.id,
|
||||||
),
|
),
|
||||||
)
|
)}')`,
|
||||||
"
|
}"></div>
|
||||||
class="max-h-20 max-w-20 mr-2" />
|
|
||||||
<div class="flex flex-col w-100">
|
<div class="flex flex-col w-100">
|
||||||
<p class="m-0 text-bold">
|
<p class="m-0 text-bold">
|
||||||
{{ lastSelected.title }}
|
{{ lastSelected.title }}
|
||||||
@@ -910,6 +911,7 @@ const startFilesUpload = async () => {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
totalItems.value++;
|
||||||
updateFiles();
|
updateFiles();
|
||||||
currentUploadFileNum.value++;
|
currentUploadFileNum.value++;
|
||||||
}
|
}
|
||||||
@@ -963,11 +965,14 @@ const updateFiles = async () => {
|
|||||||
(mediaItem) =>
|
(mediaItem) =>
|
||||||
mediaItem.id !== updateData.medium.id,
|
mediaItem.id !== updateData.medium.id,
|
||||||
);
|
);
|
||||||
|
lastSelected.value = null;
|
||||||
|
totalItems.value--;
|
||||||
|
|
||||||
useToastStore().addToast({
|
useToastStore().addToast({
|
||||||
title: "Upload failed",
|
title: "Upload failed",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
content: `${item.name} failed to be processed by the server.`,
|
content: updateData.medium.status,
|
||||||
|
// content: `${item.name} failed to be processed by the server.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -71,11 +71,21 @@
|
|||||||
<template #item-size="item">
|
<template #item-size="item">
|
||||||
{{ bytesReadable(item.size) }}
|
{{ bytesReadable(item.size) }}
|
||||||
</template>
|
</template>
|
||||||
<template #item-title="item"
|
<template #item-title="item">
|
||||||
>{{ item.title }}<br /><span class="small"
|
<div class="flex gap-2">
|
||||||
>({{ item.name }})</span
|
<div
|
||||||
></template
|
class="w-100 h-100 max-h-15 max-w-20 mr-2 bg-contain bg-no-repeat bg-center"
|
||||||
>
|
:style="{
|
||||||
|
backgroundImage: `url('${mediaGetThumbnail(
|
||||||
|
item,
|
||||||
|
)}')`,
|
||||||
|
}"></div>
|
||||||
|
<div class="flex flex-col flex-justify-center">
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
<span class="small">({{ item.name }})</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #item-actions="item">
|
<template #item-actions="item">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -164,6 +174,7 @@ import { updateRouterParams } from "../../helpers/url";
|
|||||||
import { userHasPermission } from "../../helpers/utils";
|
import { userHasPermission } from "../../helpers/utils";
|
||||||
import SMPageStatus from "../../components/SMPageStatus.vue";
|
import SMPageStatus from "../../components/SMPageStatus.vue";
|
||||||
import SMCheckbox from "../../components/SMCheckbox.vue";
|
import SMCheckbox from "../../components/SMCheckbox.vue";
|
||||||
|
import { mediaGetThumbnail } from "../../helpers/media";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
Reference in New Issue
Block a user