mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
feat(admin): migrate db dumps from github to platform (#465)
* feat: migrate encoding scripts from github to platform * style: fix StyleCI finding * fix: don't concatenate updated videos when attempting to attach entry on video upload * fix(api): fix field dependencies & feat(admin): upload script from video detail screen
This commit is contained in:
@@ -151,12 +151,22 @@ DUMP_STREAM_READS=
|
||||
DUMP_DISABLE_ASSERTS=
|
||||
DUMP_VISIBILITY=
|
||||
|
||||
SCRIPT_ACCESS_KEY_ID=
|
||||
SCRIPT_SECRET_ACCESS_KEY=
|
||||
SCRIPT_DEFAULT_REGION=
|
||||
SCRIPT_ENDPOINT=
|
||||
SCRIPT_BUCKET=
|
||||
SCRIPT_STREAM_READS=
|
||||
SCRIPT_DISABLE_ASSERTS=
|
||||
SCRIPT_VISIBILITY=
|
||||
|
||||
# flags
|
||||
ALLOW_VIDEO_STREAMS=false
|
||||
ALLOW_AUDIO_STREAMS=false
|
||||
ALLOW_DISCORD_NOTIFICATIONS=false
|
||||
ALLOW_VIEW_RECORDING=false
|
||||
ALLOW_DUMP_DOWNLOADING=false
|
||||
ALLOW_SCRIPT_DOWNLOADING=false
|
||||
|
||||
# fortify
|
||||
FORTIFY_PATH=
|
||||
@@ -257,6 +267,11 @@ VIDEO_NGINX_REDIRECT=
|
||||
VIDEO_ENCODER_VERSION=
|
||||
VIDEO_UPLOAD_DISKS=
|
||||
|
||||
SCRIPT_DISK=scripts
|
||||
SCRIPT_DISK_ROOT=
|
||||
SCRIPT_URL=http://localhost
|
||||
SCRIPT_PATH=
|
||||
|
||||
# web
|
||||
WEB_URL=http://localhost
|
||||
WEB_PATH=
|
||||
|
||||
@@ -139,6 +139,6 @@ abstract class ReconcileResults extends ActionResult
|
||||
*/
|
||||
protected function label(int|array|Countable $models = 1): string
|
||||
{
|
||||
return Str::plural(class_basename($this->model()), $models);
|
||||
return Str::plural(Str::headline(class_basename($this->model())), $models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptRepositoriesAction.
|
||||
*
|
||||
* @extends ReconcileRepositoriesAction<VideoScript>
|
||||
*/
|
||||
class ReconcileScriptRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForCreateDelete(): array
|
||||
{
|
||||
return [
|
||||
VideoScript::ATTRIBUTE_ID,
|
||||
VideoScript::ATTRIBUTE_PATH,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for create and delete set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForCreateDelete(): Closure
|
||||
{
|
||||
return fn (VideoScript $first, VideoScript $second) => [$first->path] <=> [$second->path];
|
||||
}
|
||||
|
||||
/**
|
||||
* The columns used for update set operation.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForUpdate(): array
|
||||
{
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for update set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForUpdate(): Closure
|
||||
{
|
||||
return fn () => 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source model that has been updated for destination model.
|
||||
*
|
||||
* @param Collection $sourceModels
|
||||
* @param Model $destinationModel
|
||||
* @return Model|null
|
||||
*/
|
||||
protected function resolveUpdatedModel(Collection $sourceModels, Model $destinationModel): ?Model
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reconciliation results.
|
||||
*
|
||||
* @param Collection $created
|
||||
* @param Collection $deleted
|
||||
* @param Collection $updated
|
||||
* @return ReconcileResults
|
||||
*/
|
||||
protected function getResults(Collection $created, Collection $deleted, Collection $updated): ReconcileResults
|
||||
{
|
||||
return new ReconcileScriptResults($created, $deleted, $updated);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptResults.
|
||||
*
|
||||
* @extends ReconcileResults<VideoScript>
|
||||
*/
|
||||
class ReconcileScriptResults extends ReconcileResults
|
||||
{
|
||||
/**
|
||||
* Get the model of the reconciliation results.
|
||||
*
|
||||
* @return class-string<VideoScript>
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return VideoScript::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Base\DeleteAction;
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptAction.
|
||||
*
|
||||
* @extends DeleteAction<VideoScript>
|
||||
*/
|
||||
class DeleteScriptAction extends DeleteAction
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to delete.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function path(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Base\MoveAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveVideoAction.
|
||||
*
|
||||
* @extends MoveAction<VideoScript>
|
||||
*/
|
||||
class MoveScriptAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param string $to
|
||||
*/
|
||||
public function __construct(VideoScript $script, string $to)
|
||||
{
|
||||
parent::__construct($script, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to move from.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function from(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update underlying model.
|
||||
* We want to apply these updates through Eloquent to preserve relations when renaming.
|
||||
* Otherwise, reconciliation would destroy the old model and create a new model for the new name.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update(): void
|
||||
{
|
||||
$this->model->update([
|
||||
VideoScript::ATTRIBUTE_PATH => $this->to,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Actions\Storage\Base\UploadAction;
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UploadScriptAction.
|
||||
*/
|
||||
class UploadScriptAction extends UploadAction
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param UploadedFile $file
|
||||
* @param string $path
|
||||
* @param Video|null $video
|
||||
*/
|
||||
public function __construct(UploadedFile $file, string $path, protected ?Video $video = null)
|
||||
{
|
||||
parent::__construct($file, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
|
||||
// The script was successfully uploaded and reconciled into the database, so we can attempt further actions
|
||||
if ($reconcileResults instanceof ReconcileResults) {
|
||||
$this->attachVideo($reconcileResults);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach video if uploaded from Upload Video Action.
|
||||
*
|
||||
* @param ReconcileResults $reconcileResults
|
||||
* @return void
|
||||
*/
|
||||
protected function attachVideo(ReconcileResults $reconcileResults): void
|
||||
{
|
||||
$path = Str::of($this->path)
|
||||
->finish('/')
|
||||
->append($this->file->getClientOriginalName())
|
||||
->__toString();
|
||||
|
||||
$script = $reconcileResults->getCreated()->firstWhere(VideoScript::ATTRIBUTE_PATH, $path);
|
||||
|
||||
if ($script instanceof VideoScript && $this->video !== null) {
|
||||
$script->video()->associate($this->video)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace App\Actions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Actions\Storage\Base\UploadAction;
|
||||
use App\Actions\Storage\Wiki\Video\Script\UploadScriptAction;
|
||||
use App\Concerns\Repositories\Wiki\ReconcilesVideoRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
@@ -27,9 +28,14 @@ class UploadVideoAction extends UploadAction
|
||||
* @param UploadedFile $file
|
||||
* @param string $path
|
||||
* @param AnimeThemeEntry|null $entry
|
||||
* @param UploadedFile|null $script
|
||||
*/
|
||||
public function __construct(UploadedFile $file, string $path, protected ?AnimeThemeEntry $entry = null)
|
||||
{
|
||||
public function __construct(
|
||||
UploadedFile $file,
|
||||
string $path,
|
||||
protected ?AnimeThemeEntry $entry = null,
|
||||
protected readonly ?UploadedFile $script = null
|
||||
) {
|
||||
parent::__construct($file, $path);
|
||||
}
|
||||
|
||||
@@ -47,11 +53,48 @@ class UploadVideoAction extends UploadAction
|
||||
|
||||
$reconcileResults->toLog();
|
||||
|
||||
if ($reconcileResults instanceof ReconcileResults && $this->entry !== null) {
|
||||
$video = $reconcileResults->getCreated()->firstWhere(Video::ATTRIBUTE_BASENAME, $this->file->getClientOriginalName());
|
||||
if ($video instanceof Video) {
|
||||
$video->animethemeentries()->attach($this->entry);
|
||||
}
|
||||
// The video was successfully uploaded and reconciled into the database, so we can attempt further actions
|
||||
if ($reconcileResults instanceof ReconcileResults) {
|
||||
$this->attachEntry($reconcileResults);
|
||||
$this->uploadScript($reconcileResults);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach entry to created video if uploaded from entry detail screen.
|
||||
*
|
||||
* @param ReconcileResults $reconcileResults
|
||||
* @return void
|
||||
*/
|
||||
protected function attachEntry(ReconcileResults $reconcileResults): void
|
||||
{
|
||||
$video = $reconcileResults->getCreated()->firstWhere(Video::ATTRIBUTE_BASENAME, $this->file->getClientOriginalName());
|
||||
|
||||
if ($video instanceof Video && $this->entry !== null) {
|
||||
$video->animethemeentries()->attach($this->entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload & Associate Script if video upload was successful.
|
||||
*
|
||||
* @param ReconcileResults $reconcileResults
|
||||
* @return void
|
||||
*/
|
||||
protected function uploadScript(ReconcileResults $reconcileResults): void
|
||||
{
|
||||
$video = $reconcileResults->getCreated()->firstWhere(Video::ATTRIBUTE_BASENAME, $this->file->getClientOriginalName());
|
||||
|
||||
if ($video === null) {
|
||||
$video = $reconcileResults->getUpdated()->firstWhere(Video::ATTRIBUTE_BASENAME, $this->file->getClientOriginalName());
|
||||
}
|
||||
|
||||
if ($video instanceof Video && $this->script !== null) {
|
||||
$uploadScript = new UploadScriptAction($this->script, $this->path, $video);
|
||||
|
||||
$scriptResult = $uploadScript->handle();
|
||||
|
||||
$uploadScript->then($scriptResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\Wiki\Video\Script\ReconcileScriptRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\Video\ScriptRepository as ScriptDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\Video\ScriptRepository as ScriptSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesScriptRepositories.
|
||||
*/
|
||||
trait ReconcilesScriptRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(ScriptSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(ScriptDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileScriptRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Storage\Wiki\Video;
|
||||
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Console\Commands\Repositories\Storage\StorageReconcileCommand;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ScriptReconcileCommand.
|
||||
*/
|
||||
class ScriptReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:script
|
||||
{--path= : The directory of scripts to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and script database';
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,8 @@ abstract class StorageCommand extends Command
|
||||
$storageResults->toLog();
|
||||
$storageResults->toConsole($this);
|
||||
|
||||
$action->then($storageResults);
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
return $result->hasFailed() ? 1 : 0;
|
||||
|
||||
@@ -28,4 +28,8 @@ class FlagConstants
|
||||
final public const ALLOW_DUMP_DOWNLOADING_FLAG = 'allow_dump_downloading';
|
||||
|
||||
final public const ALLOW_DUMP_DOWNLOADING_FLAG_QUALIFIED = 'flags.allow_dump_downloading';
|
||||
|
||||
final public const ALLOW_SCRIPT_DOWNLOADING_FLAG = 'allow_script_downloading';
|
||||
|
||||
final public const ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED = 'flags.allow_script_downloading';
|
||||
}
|
||||
|
||||
@@ -24,4 +24,10 @@ class VideoConstants
|
||||
final public const STREAMING_METHOD_QUALIFIED = 'video.streaming_method';
|
||||
|
||||
final public const URL_QUALIFIED = 'video.url';
|
||||
|
||||
final public const SCRIPT_DISK_QUALIFIED = 'video.script.disk';
|
||||
|
||||
final public const SCRIPT_PATH_QUALIFIED = 'video.script.path';
|
||||
|
||||
final public const SCRIPT_URL_QUALIFIED = 'video.script.url';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Wiki\Video\Script;
|
||||
|
||||
use App\Events\Base\Wiki\WikiCreatedEvent;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class VideoScriptCreated.
|
||||
*
|
||||
* @extends WikiCreatedEvent<VideoScript>
|
||||
*/
|
||||
class VideoScriptCreated extends WikiCreatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return VideoScript
|
||||
*/
|
||||
public function getModel(): VideoScript
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Script '**{$this->getModel()->getName()}**' has been created.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Wiki\Video\Script;
|
||||
|
||||
use App\Events\Base\Wiki\WikiDeletedEvent;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Nova\Resources\Wiki\Video\Script;
|
||||
|
||||
/**
|
||||
* Class VideoScriptDeleted.
|
||||
*
|
||||
* @extends WikiDeletedEvent<VideoScript>
|
||||
*/
|
||||
class VideoScriptDeleted extends WikiDeletedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return VideoScript
|
||||
*/
|
||||
public function getModel(): VideoScript
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Script '**{$this->getModel()->getName()}**' has been deleted.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message for the nova notification.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getNotificationMessage(): string
|
||||
{
|
||||
return "Script '**{$this->getModel()->getName()}**' has been deleted. It will be automatically pruned in one week. Please review.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for the nova notification.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getNotificationUrl(): string
|
||||
{
|
||||
$uriKey = Script::uriKey();
|
||||
|
||||
return "/resources/$uriKey/{$this->getModel()->getKey()}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Wiki\Video\Script;
|
||||
|
||||
use App\Events\Base\Wiki\WikiRestoredEvent;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class VideoScriptRestored.
|
||||
*
|
||||
* @extends WikiRestoredEvent<VideoScript>
|
||||
*/
|
||||
class VideoScriptRestored extends WikiRestoredEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return VideoScript
|
||||
*/
|
||||
public function getModel(): VideoScript
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Script '**{$this->getModel()->getName()}**' has been restored.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Wiki\Video\Script;
|
||||
|
||||
use App\Events\Base\Wiki\WikiUpdatedEvent;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class VideoScriptUpdated.
|
||||
*
|
||||
* @extends WikiUpdatedEvent<VideoScript>
|
||||
*/
|
||||
class VideoScriptUpdated extends WikiUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
$this->initializeEmbedFields($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return VideoScript
|
||||
*/
|
||||
public function getModel(): VideoScript
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Script '**{$this->getModel()->getName()}**' has been updated.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Admin\Dump;
|
||||
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Resources\Admin\Resource\DumpResource;
|
||||
|
||||
/**
|
||||
* Class DumpLinkField.
|
||||
*/
|
||||
class DumpLinkField extends Field
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(DumpResource::ATTRIBUTE_LINK);
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,7 @@ namespace App\Http\Api\Field\Admin\Dump;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Criteria\Field\Criteria;
|
||||
use App\Http\Api\Field\StringField;
|
||||
use App\Http\Resources\Admin\Resource\DumpResource;
|
||||
use App\Models\Admin\Dump;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -25,18 +23,6 @@ class DumpPathField extends StringField implements CreatableField, UpdatableFiel
|
||||
parent::__construct(Dump::ATTRIBUTE_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Criteria|null $criteria
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(?Criteria $criteria): bool
|
||||
{
|
||||
// The link field is dependent on this field to build the url.
|
||||
return parent::shouldSelect($criteria) || $criteria->isAllowedField(DumpResource::ATTRIBUTE_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Config\Flags;
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Http\Api\Field\Field;
|
||||
|
||||
/**
|
||||
* Class FlagsAllowScriptDownloadingField.
|
||||
*/
|
||||
class FlagsAllowScriptDownloadingField extends Field
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
|
||||
/**
|
||||
* Class ScriptLinkField.
|
||||
*/
|
||||
class ScriptLinkField extends Field
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(ScriptResource::ATTRIBUTE_LINK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Wiki\Video\Script;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Field\StringField;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ScriptPathField.
|
||||
*/
|
||||
class ScriptPathField extends StringField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(VideoScript::ATTRIBUTE_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
'string',
|
||||
'max:192',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
'string',
|
||||
'max:192',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Wiki\Video\Script;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Criteria\Field\Criteria;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class ScriptVideoIdField.
|
||||
*/
|
||||
class ScriptVideoIdField extends Field implements CreatableField, SelectableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(VideoScript::ATTRIBUTE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists(Video::TABLE, Video::ATTRIBUTE_ID),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Criteria|null $criteria
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(?Criteria $criteria): bool
|
||||
{
|
||||
// Needed to match video relation.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists(Video::TABLE, Video::ATTRIBUTE_ID),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Query\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentReadQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Resources\BaseCollection;
|
||||
use App\Http\Resources\BaseResource;
|
||||
use App\Http\Resources\Wiki\Video\Collection\ScriptCollection;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Class ScriptReadQuery.
|
||||
*/
|
||||
class ScriptReadQuery extends EloquentReadQuery
|
||||
{
|
||||
/**
|
||||
* Get the resource schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
public function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder of the resource.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function builder(): Builder
|
||||
{
|
||||
return VideoScript::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the json resource.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @return BaseResource
|
||||
*/
|
||||
public function resource(mixed $resource): BaseResource
|
||||
{
|
||||
return new ScriptResource($resource, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource collection.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @return BaseCollection
|
||||
*/
|
||||
public function collection(mixed $resource): BaseCollection
|
||||
{
|
||||
return new ScriptCollection($resource, $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Query\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Resources\BaseResource;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Class ScriptWriteQuery.
|
||||
*/
|
||||
class ScriptWriteQuery extends EloquentWriteQuery
|
||||
{
|
||||
/**
|
||||
* Get the resource schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
public function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder of the resource.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function builder(): Builder
|
||||
{
|
||||
return VideoScript::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the json resource.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @return BaseResource
|
||||
*/
|
||||
public function resource(mixed $resource): BaseResource
|
||||
{
|
||||
return new ScriptResource($resource, new ScriptReadQuery());
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Schema\Admin;
|
||||
|
||||
use App\Http\Api\Field\Admin\Dump\DumpLinkField;
|
||||
use App\Http\Api\Field\Admin\Dump\DumpPathField;
|
||||
use App\Http\Api\Field\Base\IdField;
|
||||
use App\Http\Api\Field\Field;
|
||||
@@ -59,6 +60,7 @@ class DumpSchema extends EloquentSchema
|
||||
[
|
||||
new IdField(Dump::ATTRIBUTE_ID),
|
||||
new DumpPathField(),
|
||||
new DumpLinkField(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Http\Api\Schema\Config;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowAudioStreamsField;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowDiscordNotificationsField;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowDumpDownloadingField;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowScriptDownloadingField;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowVideoStreamsField;
|
||||
use App\Http\Api\Field\Config\Flags\FlagsAllowViewRecordingField;
|
||||
use App\Http\Api\Field\Field;
|
||||
@@ -54,6 +55,7 @@ class FlagsSchema extends Schema
|
||||
new FlagsAllowDiscordNotificationsField(),
|
||||
new FlagsAllowViewRecordingField(),
|
||||
new FlagsAllowDumpDownloadingField(),
|
||||
new FlagsAllowScriptDownloadingField(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Schema\Wiki\Video;
|
||||
|
||||
use App\Http\Api\Field\Base\IdField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Field\Wiki\Video\Script\ScriptLinkField;
|
||||
use App\Http\Api\Field\Wiki\Video\Script\ScriptPathField;
|
||||
use App\Http\Api\Field\Wiki\Video\Script\ScriptVideoIdField;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\VideoSchema;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class ScriptSchema.
|
||||
*/
|
||||
class ScriptSchema extends EloquentSchema
|
||||
{
|
||||
/**
|
||||
* The model this schema represents.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function model(): string
|
||||
{
|
||||
return VideoScript::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return ScriptResource::$wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the allowed includes.
|
||||
*
|
||||
* @return AllowedInclude[]
|
||||
*/
|
||||
public function allowedIncludes(): array
|
||||
{
|
||||
return [
|
||||
new AllowedInclude(new VideoSchema(), VideoScript::RELATION_VIDEO),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct fields of the resource.
|
||||
*
|
||||
* @return Field[]
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::fields(),
|
||||
[
|
||||
new IdField(VideoScript::ATTRIBUTE_ID),
|
||||
new ScriptPathField(),
|
||||
new ScriptLinkField(),
|
||||
new ScriptVideoIdField(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Anime\Theme\EntrySchema;
|
||||
use App\Http\Api\Schema\Wiki\Anime\ThemeSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Resources\Wiki\Resource\VideoResource;
|
||||
use App\Models\Wiki\Video;
|
||||
|
||||
@@ -64,6 +65,7 @@ class VideoSchema extends EloquentSchema
|
||||
new AllowedInclude(new AnimeSchema(), Video::RELATION_ANIME),
|
||||
new AllowedInclude(new AudioSchema(), Video::RELATION_AUDIO),
|
||||
new AllowedInclude(new EntrySchema(), Video::RELATION_ANIMETHEMEENTRIES),
|
||||
new AllowedInclude(new ScriptSchema(), Video::RELATION_SCRIPT),
|
||||
new AllowedInclude(new ThemeSchema(), Video::RELATION_ANIMETHEME),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\Wiki\Video;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptDestroyRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptForceDeleteRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptIndexRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptRestoreRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptShowRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptStoreRequest;
|
||||
use App\Http\Requests\Api\Wiki\Video\Script\ScriptUpdateRequest;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Spatie\RouteDiscovery\Attributes\Route;
|
||||
|
||||
/**
|
||||
* Class ScriptController.
|
||||
*/
|
||||
class ScriptController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param ScriptIndexRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(fullUri: 'videoscript', name: 'videoscript.index')]
|
||||
public function index(ScriptIndexRequest $request): JsonResponse
|
||||
{
|
||||
$query = $request->getQuery();
|
||||
|
||||
return $query->index()->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param ScriptStoreRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(fullUri: 'videoscript', name: 'videoscript.store', middleware: 'auth:sanctum')]
|
||||
public function store(ScriptStoreRequest $request): JsonResponse
|
||||
{
|
||||
$resource = $request->getQuery()->store();
|
||||
|
||||
return $resource->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ScriptShowRequest $request
|
||||
* @param VideoScript $script
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(fullUri: 'videoscript/{videoscript}', name: 'videoscript.show')]
|
||||
public function show(ScriptShowRequest $request, VideoScript $script): JsonResponse
|
||||
{
|
||||
$resource = $request->getQuery()->show($script);
|
||||
|
||||
return $resource->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param ScriptUpdateRequest $request
|
||||
* @param VideoScript $script
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(fullUri: 'videoscript/{videoscript}', name: 'videoscript.update', middleware: 'auth:sanctum')]
|
||||
public function update(ScriptUpdateRequest $request, VideoScript $script): JsonResponse
|
||||
{
|
||||
$resource = $request->getQuery()->update($script);
|
||||
|
||||
return $resource->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource.
|
||||
*
|
||||
* @param ScriptDestroyRequest $request
|
||||
* @param VideoScript $script
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(fullUri: 'videoscript/{videoscript}', name: 'videoscript.destroy', middleware: 'auth:sanctum')]
|
||||
public function destroy(ScriptDestroyRequest $request, VideoScript $script): JsonResponse
|
||||
{
|
||||
$resource = $request->getQuery()->destroy($script);
|
||||
|
||||
return $resource->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource.
|
||||
*
|
||||
* @param ScriptRestoreRequest $request
|
||||
* @param VideoScript $script
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(method: 'patch', fullUri: 'restore/videoscript/{videoscript}', name: 'videoscript.restore', middleware: 'auth:sanctum')]
|
||||
public function restore(ScriptRestoreRequest $request, VideoScript $script): JsonResponse
|
||||
{
|
||||
$resource = $request->getQuery()->restore($script);
|
||||
|
||||
return $resource->toResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete the specified resource.
|
||||
*
|
||||
* @param ScriptForceDeleteRequest $request
|
||||
* @param VideoScript $script
|
||||
* @return JsonResponse
|
||||
*/
|
||||
#[Route(method: 'delete', fullUri: 'forceDelete/videoscript/{videoscript}', name: 'videoscript.forceDelete', middleware: 'auth:sanctum')]
|
||||
public function forceDelete(ScriptForceDeleteRequest $request, VideoScript $script): JsonResponse
|
||||
{
|
||||
return $request->getQuery()->forceDelete($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Wiki\Video\Script;
|
||||
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* Class ScriptController.
|
||||
*/
|
||||
class ScriptController extends Controller
|
||||
{
|
||||
/**
|
||||
* Download dump.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function show(VideoScript $script): StreamedResponse
|
||||
{
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::disk(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
return $fs->download($script->path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentDestroyRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptDestroyRequest.
|
||||
*/
|
||||
class ScriptDestroyRequest extends EloquentDestroyRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentWriteQuery
|
||||
*/
|
||||
public function getQuery(): EloquentWriteQuery
|
||||
{
|
||||
return new ScriptWriteQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentForceDeleteRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptForceDeleteRequest.
|
||||
*/
|
||||
class ScriptForceDeleteRequest extends EloquentForceDeleteRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentWriteQuery
|
||||
*/
|
||||
public function getQuery(): EloquentWriteQuery
|
||||
{
|
||||
return new ScriptWriteQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentReadQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptReadQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentIndexRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptIndexRequest.
|
||||
*/
|
||||
class ScriptIndexRequest extends EloquentIndexRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentReadQuery
|
||||
*/
|
||||
public function getQuery(): EloquentReadQuery
|
||||
{
|
||||
return new ScriptReadQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentRestoreRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptRestoreRequest.
|
||||
*/
|
||||
class ScriptRestoreRequest extends EloquentRestoreRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentWriteQuery
|
||||
*/
|
||||
public function getQuery(): EloquentWriteQuery
|
||||
{
|
||||
return new ScriptWriteQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentReadQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptReadQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentShowRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptShowRequest.
|
||||
*/
|
||||
class ScriptShowRequest extends EloquentShowRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentReadQuery
|
||||
*/
|
||||
public function getQuery(): EloquentReadQuery
|
||||
{
|
||||
return new ScriptReadQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentStoreRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptStoreRequest.
|
||||
*/
|
||||
class ScriptStoreRequest extends EloquentStoreRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentWriteQuery
|
||||
*/
|
||||
public function getQuery(): EloquentWriteQuery
|
||||
{
|
||||
return new ScriptWriteQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Http\Api\Query\Base\EloquentWriteQuery;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptWriteQuery;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Requests\Api\Base\EloquentUpdateRequest;
|
||||
|
||||
/**
|
||||
* Class ScriptUpdateRequest.
|
||||
*/
|
||||
class ScriptUpdateRequest extends EloquentUpdateRequest
|
||||
{
|
||||
/**
|
||||
* Get the schema.
|
||||
*
|
||||
* @return EloquentSchema
|
||||
*/
|
||||
protected function schema(): EloquentSchema
|
||||
{
|
||||
return new ScriptSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation API Query.
|
||||
*
|
||||
* @return EloquentWriteQuery
|
||||
*/
|
||||
public function getQuery(): EloquentWriteQuery
|
||||
{
|
||||
return new ScriptWriteQuery($this->validated());
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,10 @@ class FlagsResource extends BaseResource
|
||||
$result[FlagConstants::ALLOW_DUMP_DOWNLOADING_FLAG] = Config::bool(FlagConstants::ALLOW_DUMP_DOWNLOADING_FLAG_QUALIFIED);
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG)) {
|
||||
$result[FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG] = Config::bool(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Http\Resources\Wiki\Resource;
|
||||
use App\Http\Api\Query\ReadQuery;
|
||||
use App\Http\Resources\BaseResource;
|
||||
use App\Http\Resources\Wiki\Anime\Theme\Collection\EntryCollection;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -126,6 +127,7 @@ class VideoResource extends BaseResource
|
||||
|
||||
$result[Video::RELATION_ANIMETHEMEENTRIES] = new EntryCollection($this->whenLoaded(Video::RELATION_ANIMETHEMEENTRIES), $this->query);
|
||||
$result[Video::RELATION_AUDIO] = new AudioResource($this->whenLoaded(Video::RELATION_AUDIO), $this->query);
|
||||
$result[Video::RELATION_SCRIPT] = new ScriptResource($this->whenLoaded(Video::RELATION_SCRIPT), $this->query);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Wiki\Video\Collection;
|
||||
|
||||
use App\Http\Resources\BaseCollection;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ScriptCollection.
|
||||
*/
|
||||
class ScriptCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'videoscripts';
|
||||
|
||||
/**
|
||||
* Transform the resource into a JSON array.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return $this->collection->map(fn (VideoScript $script) => new ScriptResource($script, $this->query))->all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Wiki\Video\Resource;
|
||||
|
||||
use App\Http\Api\Query\ReadQuery;
|
||||
use App\Http\Resources\BaseResource;
|
||||
use App\Http\Resources\Wiki\Resource\VideoResource;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\MissingValue;
|
||||
|
||||
/**
|
||||
* Class ScriptResource.
|
||||
*
|
||||
* @mixin VideoScript
|
||||
*/
|
||||
class ScriptResource extends BaseResource
|
||||
{
|
||||
final public const ATTRIBUTE_LINK = 'link';
|
||||
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'videoscript';
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param VideoScript | MissingValue | null $script
|
||||
* @param ReadQuery $query
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(VideoScript|MissingValue|null $script, ReadQuery $query)
|
||||
{
|
||||
parent::__construct($script, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if ($this->isAllowedField(BaseResource::ATTRIBUTE_ID)) {
|
||||
$result[BaseResource::ATTRIBUTE_ID] = $this->getKey();
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(VideoScript::ATTRIBUTE_PATH)) {
|
||||
$result[VideoScript::ATTRIBUTE_PATH] = $this->path;
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(BaseModel::ATTRIBUTE_CREATED_AT)) {
|
||||
$result[BaseModel::ATTRIBUTE_CREATED_AT] = $this->created_at;
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(BaseModel::ATTRIBUTE_UPDATED_AT)) {
|
||||
$result[BaseModel::ATTRIBUTE_UPDATED_AT] = $this->updated_at;
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(BaseModel::ATTRIBUTE_DELETED_AT)) {
|
||||
$result[BaseModel::ATTRIBUTE_DELETED_AT] = $this->deleted_at;
|
||||
}
|
||||
|
||||
if ($this->isAllowedField(ScriptResource::ATTRIBUTE_LINK)) {
|
||||
$result[ScriptResource::ATTRIBUTE_LINK] = route('videoscript.show', $this);
|
||||
}
|
||||
|
||||
$result[VideoScript::RELATION_VIDEO] = new VideoResource($this->whenLoaded(VideoScript::RELATION_VIDEO), $this->query);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use App\Events\Wiki\Video\VideoRestored;
|
||||
use App\Events\Wiki\Video\VideoUpdated;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Pivots\AnimeThemeEntryVideo;
|
||||
use BenSampo\Enum\Enum;
|
||||
use CyrildeWit\EloquentViewable\Contracts\Viewable;
|
||||
@@ -23,6 +24,7 @@ use Elastic\ScoutDriverPlus\Searchable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Nova\Actions\Actionable;
|
||||
|
||||
@@ -40,6 +42,7 @@ use Laravel\Nova\Actions\Actionable;
|
||||
* @property Enum $overlap
|
||||
* @property string $path
|
||||
* @property int|null $resolution
|
||||
* @property VideoScript|null $script
|
||||
* @property int $size
|
||||
* @property Enum|null $source
|
||||
* @property bool $subbed
|
||||
@@ -78,6 +81,7 @@ class Video extends BaseModel implements Streamable, Viewable
|
||||
final public const RELATION_ANIMETHEME = 'animethemeentries.animetheme';
|
||||
final public const RELATION_ANIMETHEMEENTRIES = 'animethemeentries';
|
||||
final public const RELATION_AUDIO = 'audio';
|
||||
final public const RELATION_SCRIPT = 'videoscript';
|
||||
final public const RELATION_SONG = 'animethemeentries.animetheme.song';
|
||||
|
||||
/**
|
||||
@@ -323,4 +327,14 @@ class Video extends BaseModel implements Streamable, Viewable
|
||||
{
|
||||
return $this->belongsTo(Audio::class, Video::ATTRIBUTE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script that the video owns.
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function videoscript(): HasOne
|
||||
{
|
||||
return $this->hasOne(VideoScript::class, VideoScript::ATTRIBUTE_VIDEO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Wiki\Video;
|
||||
|
||||
use App\Events\Wiki\Video\Script\VideoScriptCreated;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptDeleted;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptRestored;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptUpdated;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use Database\Factories\Wiki\Video\VideoScriptFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Laravel\Nova\Actions\Actionable;
|
||||
|
||||
/**
|
||||
* Class VideoScript.
|
||||
*
|
||||
* @property int $script_id
|
||||
* @property string $path
|
||||
* @property Video $video
|
||||
* @property int $video_id
|
||||
*
|
||||
* @method static VideoScriptFactory factory(...$parameters)
|
||||
*/
|
||||
class VideoScript extends BaseModel
|
||||
{
|
||||
use Actionable;
|
||||
|
||||
final public const TABLE = 'video_scripts';
|
||||
|
||||
final public const ATTRIBUTE_ID = 'script_id';
|
||||
final public const ATTRIBUTE_PATH = 'path';
|
||||
final public const ATTRIBUTE_VIDEO = 'video_id';
|
||||
|
||||
final public const RELATION_VIDEO = 'video';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
VideoScript::ATTRIBUTE_PATH,
|
||||
];
|
||||
|
||||
/**
|
||||
* The event map for the model.
|
||||
*
|
||||
* Allows for object-based events for native Eloquent events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dispatchesEvents = [
|
||||
'created' => VideoScriptCreated::class,
|
||||
'deleted' => VideoScriptDeleted::class,
|
||||
'restored' => VideoScriptRestored::class,
|
||||
'updated' => VideoScriptUpdated::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = VideoScript::TABLE;
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = VideoScript::ATTRIBUTE_ID;
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the video that owns the script.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function video(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Video::class, VideoScript::ATTRIBUTE_VIDEO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Actions\Repositories\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Nova\Actions\Repositories\Storage\ReconcileStorageAction;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptAction.
|
||||
*/
|
||||
class ReconcileScriptAction extends ReconcileStorageAction
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* Get the displayable name of the action.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return __('nova.actions.repositories.name', ['label' => __('nova.resources.label.video_scripts')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ use App\Contracts\Storage\InteractsWithDisk;
|
||||
use App\Nova\Actions\Storage\StorageAction;
|
||||
use App\Rules\Storage\StorageFileDirectoryExistsRule;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Nova\Fields\ActionFields;
|
||||
use Laravel\Nova\Fields\Text;
|
||||
@@ -32,7 +31,7 @@ abstract class MoveAction extends StorageAction implements InteractsWithDisk
|
||||
{
|
||||
$defaultPath = $this->defaultPath($request);
|
||||
|
||||
$fs = Storage::disk(Config::get($this->disk()));
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
return [
|
||||
Text::make(__('nova.actions.storage.move.fields.path.name'), 'path')
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\DeleteScriptAction as DeleteScript;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Nova\Actions\Storage\Base\DeleteAction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Nova\Fields\ActionFields;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptAction.
|
||||
*/
|
||||
class DeleteScriptAction extends DeleteAction
|
||||
{
|
||||
/**
|
||||
* Get the displayable name of the action.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return __('nova.actions.video_script.delete.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param ActionFields $fields
|
||||
* @param Collection<int, VideoScript> $models
|
||||
* @return DeleteScript
|
||||
*/
|
||||
protected function action(ActionFields $fields, Collection $models): DeleteScript
|
||||
{
|
||||
$script = $models->first();
|
||||
|
||||
return new DeleteScript($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\MoveScriptAction as MoveScript;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Nova\Actions\Storage\Base\MoveAction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Laravel\Nova\Fields\ActionFields;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
|
||||
/**
|
||||
* Class MoveScriptAction.
|
||||
*/
|
||||
class MoveScriptAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Get the displayable name of the action.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return __('nova.actions.video_script.move.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param ActionFields $fields
|
||||
* @param Collection<int, VideoScript> $models
|
||||
* @return MoveScript
|
||||
*/
|
||||
protected function action(ActionFields $fields, Collection $models): MoveScript
|
||||
{
|
||||
$path = $fields->get('path');
|
||||
|
||||
$script = $models->first();
|
||||
|
||||
return new MoveScript($script, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(NovaRequest $request): ?string
|
||||
{
|
||||
$script = $request->findModelQuery()->first();
|
||||
|
||||
return $script instanceof VideoScript
|
||||
? $script->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.txt';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\UploadScriptAction as UploadScript;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Nova\Actions\Storage\Base\UploadAction;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Validation\Rules\File as FileRule;
|
||||
use Laravel\Nova\Fields\ActionFields;
|
||||
use Laravel\Nova\Fields\Hidden;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
|
||||
/**
|
||||
* Class UploadScriptAction.
|
||||
*/
|
||||
class UploadScriptAction extends UploadAction
|
||||
{
|
||||
/**
|
||||
* Get the displayable name of the action.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return __('nova.actions.video_script.upload.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function fields(NovaRequest $request): array
|
||||
{
|
||||
$model = $request->findModelQuery()->first();
|
||||
|
||||
return array_merge(
|
||||
parent::fields($request),
|
||||
[
|
||||
Hidden::make(__('nova.resources.singularLabel.video'), Video::ATTRIBUTE_ID)
|
||||
->default(fn () => $model instanceof Video ? $model->getKey() : null),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param ActionFields $fields
|
||||
* @param Collection $models
|
||||
* @return UploadScript
|
||||
*/
|
||||
protected function action(ActionFields $fields, Collection $models): UploadScript
|
||||
{
|
||||
/** @var UploadedFile $file */
|
||||
$file = $fields->get('file');
|
||||
$path = $fields->get('path');
|
||||
|
||||
/** @var Video|null $video */
|
||||
$video = Video::query()->find($fields->get(Video::ATTRIBUTE_ID));
|
||||
|
||||
return new UploadScript($file, $path, $video);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fileRules(): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
FileRule::types('txt')->max(2 * 1024),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Validation\Rules\File as FileRule;
|
||||
use Laravel\Nova\Fields\ActionFields;
|
||||
use Laravel\Nova\Fields\File;
|
||||
use Laravel\Nova\Fields\Heading;
|
||||
use Laravel\Nova\Fields\Hidden;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
|
||||
@@ -64,10 +66,20 @@ class UploadVideoAction extends UploadAction
|
||||
$parent = $request->findParentModel();
|
||||
|
||||
return array_merge(
|
||||
[
|
||||
Heading::make(__('nova.resources.singularLabel.video')),
|
||||
],
|
||||
parent::fields($request),
|
||||
[
|
||||
Hidden::make(__('nova.resources.singularLabel.anime_theme_entry'), AnimeThemeEntry::ATTRIBUTE_ID)
|
||||
->default(fn () => $parent instanceof AnimeThemeEntry ? $parent->getKey() : null),
|
||||
|
||||
Heading::make(__('nova.resources.singularLabel.video_script')),
|
||||
|
||||
File::make(__('nova.resources.singularLabel.video_script'), 'script')
|
||||
->nullable()
|
||||
->rules(['nullable', FileRule::types('txt')->max(2 * 1024)])
|
||||
->help(__('nova.actions.storage.upload.fields.file.help')),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -81,14 +93,18 @@ class UploadVideoAction extends UploadAction
|
||||
*/
|
||||
protected function action(ActionFields $fields, Collection $models): UploadVideo
|
||||
{
|
||||
$path = $fields->get('path');
|
||||
|
||||
/** @var UploadedFile $file */
|
||||
$file = $fields->get('file');
|
||||
$path = $fields->get('path');
|
||||
|
||||
/** @var AnimeThemeEntry|null $entry */
|
||||
$entry = AnimeThemeEntry::query()->find($fields->get(AnimeThemeEntry::ATTRIBUTE_ID));
|
||||
|
||||
return new UploadVideo($file, $path, $entry);
|
||||
/** @var UploadedFile|null $script */
|
||||
$script = $fields->get('script');
|
||||
|
||||
return new UploadVideo($file, $path, $entry, $script);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Lenses\Video;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Enums\Models\Wiki\VideoOverlap;
|
||||
use App\Enums\Models\Wiki\VideoSource;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Nova\Lenses\BaseLens;
|
||||
use BenSampo\Enum\Enum;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Laravel\Nova\Fields\Boolean;
|
||||
use Laravel\Nova\Fields\DateTime;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Number;
|
||||
use Laravel\Nova\Fields\Select;
|
||||
use Laravel\Nova\Fields\Text;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
|
||||
/**
|
||||
* Class VideoAudioLens.
|
||||
*/
|
||||
class VideoScriptLens extends BaseLens
|
||||
{
|
||||
/**
|
||||
* Get the displayable name of the lens.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return __('nova.lenses.video.script.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* The criteria used to refine the models for the lens.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public static function criteria(Builder $query): Builder
|
||||
{
|
||||
return $query->whereDoesntHave(Video::RELATION_SCRIPT)
|
||||
->where(Video::ATTRIBUTE_PATH, ComparisonOperator::NOTLIKE, 'misc%');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available to the lens.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fields(NovaRequest $request): array
|
||||
{
|
||||
return [
|
||||
ID::make(__('nova.fields.base.id'), Video::ATTRIBUTE_ID)
|
||||
->sortable()
|
||||
->showOnPreview(),
|
||||
|
||||
Text::make(__('nova.fields.video.filename.name'), Video::ATTRIBUTE_FILENAME)
|
||||
->sortable()
|
||||
->copyable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Number::make(__('nova.fields.video.resolution.name'), Video::ATTRIBUTE_RESOLUTION)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Boolean::make(__('nova.fields.video.nc.name'), Video::ATTRIBUTE_NC)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Boolean::make(__('nova.fields.video.subbed.name'), Video::ATTRIBUTE_SUBBED)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Boolean::make(__('nova.fields.video.lyrics.name'), Video::ATTRIBUTE_LYRICS)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Boolean::make(__('nova.fields.video.uncen.name'), Video::ATTRIBUTE_UNCEN)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Select::make(__('nova.fields.video.overlap.name'), Video::ATTRIBUTE_OVERLAP)
|
||||
->options(VideoOverlap::asSelectArray())
|
||||
->displayUsing(fn (?Enum $enum) => $enum?->description)
|
||||
->onlyOnPreview(),
|
||||
|
||||
Select::make(__('nova.fields.video.source.name'), Video::ATTRIBUTE_SOURCE)
|
||||
->options(VideoSource::asSelectArray())
|
||||
->displayUsing(fn (?Enum $enum) => $enum?->description)
|
||||
->onlyOnPreview(),
|
||||
|
||||
DateTime::make(__('nova.fields.base.created_at'), BaseModel::ATTRIBUTE_CREATED_AT)
|
||||
->onlyOnPreview(),
|
||||
|
||||
DateTime::make(__('nova.fields.base.updated_at'), BaseModel::ATTRIBUTE_UPDATED_AT)
|
||||
->onlyOnPreview(),
|
||||
|
||||
DateTime::make(__('nova.fields.base.deleted_at'), BaseModel::ATTRIBUTE_DELETED_AT)
|
||||
->onlyOnPreview(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available on the lens.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function actions(NovaRequest $request): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI key for the lens.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function uriKey(): string
|
||||
{
|
||||
return 'video-script-lens';
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,18 @@ use App\Nova\Actions\Models\Wiki\Video\BackfillAudioAction;
|
||||
use App\Nova\Actions\Repositories\Storage\Wiki\Video\ReconcileVideoAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\DeleteVideoAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\MoveVideoAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\Script\UploadScriptAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\UploadVideoAction;
|
||||
use App\Nova\Lenses\Video\VideoAudioLens;
|
||||
use App\Nova\Lenses\Video\VideoResolutionLens;
|
||||
use App\Nova\Lenses\Video\VideoScriptLens;
|
||||
use App\Nova\Lenses\Video\VideoSourceLens;
|
||||
use App\Nova\Lenses\Video\VideoUnlinkedLens;
|
||||
use App\Nova\Metrics\Video\NewVideos;
|
||||
use App\Nova\Metrics\Video\VideosPerDay;
|
||||
use App\Nova\Resources\BaseResource;
|
||||
use App\Nova\Resources\Wiki\Anime\Theme\Entry;
|
||||
use App\Nova\Resources\Wiki\Video\Script;
|
||||
use App\Pivots\BasePivot;
|
||||
use BenSampo\Enum\Enum;
|
||||
use BenSampo\Enum\Rules\EnumValue;
|
||||
@@ -31,6 +34,7 @@ use Laravel\Nova\Fields\BelongsTo;
|
||||
use Laravel\Nova\Fields\BelongsToMany;
|
||||
use Laravel\Nova\Fields\Boolean;
|
||||
use Laravel\Nova\Fields\DateTime;
|
||||
use Laravel\Nova\Fields\HasOne;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Number;
|
||||
use Laravel\Nova\Fields\Select;
|
||||
@@ -205,6 +209,12 @@ class Video extends BaseResource
|
||||
->hideWhenCreating(),
|
||||
]),
|
||||
|
||||
HasOne::make(__('nova.resources.singularLabel.video_script'), VideoModel::RELATION_SCRIPT, Script::class)
|
||||
->hideFromIndex()
|
||||
->sortable()
|
||||
->nullable()
|
||||
->showOnPreview(),
|
||||
|
||||
Panel::make(__('nova.fields.base.file_properties'), $this->fileProperties())
|
||||
->collapsable(),
|
||||
|
||||
@@ -327,6 +337,16 @@ class Video extends BaseResource
|
||||
|
||||
return $user instanceof User && $user->can('create video');
|
||||
}),
|
||||
|
||||
(new UploadScriptAction())
|
||||
->confirmButtonText(__('nova.actions.storage.upload.confirmButtonText'))
|
||||
->cancelButtonText(__('nova.actions.base.cancelButtonText'))
|
||||
->onlyOnDetail()
|
||||
->canSee(function (Request $request) {
|
||||
$user = $request->user();
|
||||
|
||||
return $user instanceof User && $user->can('create video script');
|
||||
}),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -363,7 +383,8 @@ class Video extends BaseResource
|
||||
new VideoResolutionLens(),
|
||||
new VideoSourceLens(),
|
||||
new VideoUnlinkedLens(),
|
||||
]
|
||||
new VideoScriptLens(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Resources\Wiki\Video;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Nova\Actions\Repositories\Storage\Wiki\Video\Script\ReconcileScriptAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\Script\DeleteScriptAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\Script\MoveScriptAction;
|
||||
use App\Nova\Actions\Storage\Wiki\Video\Script\UploadScriptAction;
|
||||
use App\Nova\Resources\BaseResource;
|
||||
use App\Nova\Resources\Wiki\Video;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Nova\Fields\BelongsTo;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Text;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
use Laravel\Nova\Panel;
|
||||
use Laravel\Nova\Query\Search\Column;
|
||||
|
||||
/**
|
||||
* Class Script.
|
||||
*/
|
||||
class Script extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static string $model = VideoScript::class;
|
||||
|
||||
/**
|
||||
* The single value that should be used to represent the resource when being displayed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $title = VideoScript::ATTRIBUTE_PATH;
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function group(): string
|
||||
{
|
||||
return __('nova.resources.group.wiki');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function label(): string
|
||||
{
|
||||
return __('nova.resources.label.video_scripts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function singularLabel(): string
|
||||
{
|
||||
return __('nova.resources.singularLabel.video_script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function uriKey(): string
|
||||
{
|
||||
return 'video-scripts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the searchable columns for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function searchableColumns(): array
|
||||
{
|
||||
return [
|
||||
new Column(VideoScript::ATTRIBUTE_PATH),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a "relatable" query for the given resource.
|
||||
*
|
||||
* This query determines which instances of the model may be attached to other resources.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function indexQuery(NovaRequest $request, $query): Builder
|
||||
{
|
||||
return $query->with(VideoScript::RELATION_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields displayed by the resource.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fields(NovaRequest $request): array
|
||||
{
|
||||
return [
|
||||
BelongsTo::make(__('nova.resources.singularLabel.video'), VideoScript::RELATION_VIDEO, Video::class)
|
||||
->sortable()
|
||||
->filterable()
|
||||
->searchable()
|
||||
->withSubtitles()
|
||||
->nullable()
|
||||
->showOnPreview(),
|
||||
|
||||
ID::make(__('nova.fields.base.id'), VideoScript::ATTRIBUTE_ID)
|
||||
->sortable()
|
||||
->showOnPreview(),
|
||||
|
||||
Text::make(__('nova.fields.video_script.path'), VideoScript::ATTRIBUTE_PATH)
|
||||
->copyable()
|
||||
->showOnPreview()
|
||||
->filterable(),
|
||||
|
||||
Panel::make(__('nova.fields.base.timestamps'), $this->timestamps()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available for the resource.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function actions(NovaRequest $request): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::actions($request),
|
||||
[
|
||||
(new UploadScriptAction())
|
||||
->confirmButtonText(__('nova.actions.storage.upload.confirmButtonText'))
|
||||
->cancelButtonText(__('nova.actions.base.cancelButtonText'))
|
||||
->onlyOnIndex()
|
||||
->standalone()
|
||||
->canSee(function (Request $request) {
|
||||
$user = $request->user();
|
||||
|
||||
return $user instanceof User && $user->can('create video script');
|
||||
}),
|
||||
|
||||
(new MoveScriptAction())
|
||||
->confirmButtonText(__('nova.actions.storage.move.confirmButtonText'))
|
||||
->cancelButtonText(__('nova.actions.base.cancelButtonText'))
|
||||
->exceptOnIndex()
|
||||
->canSee(function (Request $request) {
|
||||
$user = $request->user();
|
||||
|
||||
return $user instanceof User && $user->can('create video script');
|
||||
}),
|
||||
|
||||
(new DeleteScriptAction())
|
||||
->confirmText(__('nova.actions.video_script.delete.confirmText'))
|
||||
->confirmButtonText(__('nova.actions.storage.delete.confirmButtonText'))
|
||||
->cancelButtonText(__('nova.actions.base.cancelButtonText'))
|
||||
->exceptOnIndex()
|
||||
->canSee(function (Request $request) {
|
||||
$user = $request->user();
|
||||
|
||||
return $user instanceof User && $user->can('delete video script');
|
||||
}),
|
||||
|
||||
(new ReconcileScriptAction())
|
||||
->confirmButtonText(__('nova.actions.repositories.confirmButtonText'))
|
||||
->cancelButtonText(__('nova.actions.base.cancelButtonText'))
|
||||
->onlyOnIndex()
|
||||
->standalone()
|
||||
->canSee(function (Request $request) {
|
||||
$user = $request->user();
|
||||
|
||||
return $user instanceof User && $user->can('create video script');
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Policies\Wiki\Video;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Class VideoScriptPolicy.
|
||||
*/
|
||||
class VideoScriptPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->can('view video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function view(User $user): bool
|
||||
{
|
||||
return $user->can('view video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->can('create video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function update(User $user): bool
|
||||
{
|
||||
return $user->can('update video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(User $user): bool
|
||||
{
|
||||
return $user->can('delete video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function restore(User $user): bool
|
||||
{
|
||||
return $user->can('restore video script');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function forceDelete(User $user): bool
|
||||
{
|
||||
return $user->can('force delete video script');
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Constants\Config\DumpConstants;
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
@@ -48,12 +49,17 @@ class AppServiceProvider extends ServiceProvider
|
||||
'Streaming Method' => fn () => Config::get(AudioConstants::STREAMING_METHOD_QUALIFIED),
|
||||
]);
|
||||
|
||||
AboutCommand::add('Dumps', [
|
||||
'Disk' => fn () => Config::get(DumpConstants::DISK_QUALIFIED),
|
||||
]);
|
||||
|
||||
AboutCommand::add('Flags', [
|
||||
'Allow Audio Streams' => fn () => Config::bool(FlagConstants::ALLOW_AUDIO_STREAMS_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
'Allow Discord Notifications' => fn () => Config::bool(FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
'Allow Video Streams' => fn () => Config::bool(FlagConstants::ALLOW_VIDEO_STREAMS_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
'Allow View Recording' => fn () => Config::bool(FlagConstants::ALLOW_VIEW_RECORDING_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
'Allow Dump Downloading' => fn () => Config::bool(FlagConstants::ALLOW_DUMP_DOWNLOADING_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
'Allow Script Downloading' => fn () => Config::bool(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED) ? 'true' : 'false',
|
||||
]);
|
||||
|
||||
AboutCommand::add('Images', [
|
||||
@@ -65,6 +71,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
'Disks' => fn () => implode(',', Config::get(VideoConstants::DISKS_QUALIFIED)),
|
||||
'Encoder Version' => fn () => Config::get(VideoConstants::ENCODER_VERSION_QUALIFIED),
|
||||
'Nginx Redirect' => fn () => Config::get(VideoConstants::NGINX_REDIRECT_QUALIFIED),
|
||||
'Script Disk' => fn () => Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED),
|
||||
'Streaming Method' => fn () => Config::get(VideoConstants::STREAMING_METHOD_QUALIFIED),
|
||||
]);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -51,6 +52,9 @@ class RouteServiceProvider extends ServiceProvider
|
||||
// Anime Theme Resources
|
||||
Route::model('animethemeentry', AnimeThemeEntry::class);
|
||||
|
||||
// Video Resources
|
||||
Route::model('videoscript', VideoScript::class);
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('web')
|
||||
->domain(Config::get('web.url'))
|
||||
@@ -72,6 +76,11 @@ class RouteServiceProvider extends ServiceProvider
|
||||
->prefix(Config::get(DumpConstants::PATH_QUALIFIED))
|
||||
->group(base_path('routes/dump.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->domain(Config::get(VideoConstants::SCRIPT_URL_QUALIFIED))
|
||||
->prefix(Config::get(VideoConstants::SCRIPT_PATH_QUALIFIED))
|
||||
->group(base_path('routes/script.php'));
|
||||
|
||||
Route::middleware('api')
|
||||
->domain(Config::get('api.url'))
|
||||
->prefix(Config::get('api.path'))
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories\Eloquent\Wiki\Video;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Repositories\Eloquent\EloquentRepository;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Class ScriptRepository.
|
||||
*
|
||||
* @extends EloquentRepository<VideoScript>
|
||||
*/
|
||||
class ScriptRepository extends EloquentRepository
|
||||
{
|
||||
/**
|
||||
* Get the underlying query builder.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function builder(): Builder
|
||||
{
|
||||
return VideoScript::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter repository models.
|
||||
*
|
||||
* @param string $filter
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function handleFilter(string $filter, mixed $value = null): void
|
||||
{
|
||||
if ($filter === 'path') {
|
||||
$this->query->where(VideoScript::ATTRIBUTE_PATH, ComparisonOperator::LIKE, "$value%");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories\Storage\Wiki\Video;
|
||||
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Repositories\Storage\StorageRepository;
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use League\Flysystem\StorageAttributes;
|
||||
|
||||
/**
|
||||
* Class ScriptRepository.
|
||||
*
|
||||
* @extends StorageRepository<VideoScript>
|
||||
*/
|
||||
class ScriptRepository extends StorageRepository
|
||||
{
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the callback to filter filesystem contents.
|
||||
*
|
||||
* @return Closure(StorageAttributes): bool
|
||||
*/
|
||||
protected function filterCallback(): Closure
|
||||
{
|
||||
return fn (StorageAttributes $file) => $file->isFile() && File::extension($file->path()) === 'txt';
|
||||
}
|
||||
|
||||
/**
|
||||
* Map filesystem files to model.
|
||||
*
|
||||
* @return Closure(StorageAttributes): VideoScript
|
||||
*/
|
||||
protected function mapCallback(): Closure
|
||||
{
|
||||
return fn (StorageAttributes $file) => new VideoScript([
|
||||
VideoScript::ATTRIBUTE_PATH => $file->path(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,27 @@ return [
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
'scripts' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('SCRIPT_ACCESS_KEY_ID'),
|
||||
'secret' => env('SCRIPT_SECRET_ACCESS_KEY'),
|
||||
'region' => env('SCRIPT_DEFAULT_REGION'),
|
||||
'bucket' => env('SCRIPT_BUCKET'),
|
||||
'endpoint' => env('SCRIPT_ENDPOINT'),
|
||||
'stream_reads' => env('SCRIPT_STREAM_READS'),
|
||||
'disable_asserts' => env('SCRIPT_DISABLE_ASSERTS'),
|
||||
'visibility' => env('SCRIPT_VISIBILITY'),
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
'scripts_local' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('scripts'),
|
||||
'url' => env('APP_URL').'/scripts',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
@@ -175,6 +196,7 @@ return [
|
||||
public_path('images') => env('IMAGE_DISK_ROOT', storage_path('app/images')),
|
||||
public_path('videos') => env('VIDEO_DISK_ROOT', storage_path('app/videos')),
|
||||
public_path('dumps') => env('DUMP_DISK_ROOT', storage_path('app/dumps')),
|
||||
public_path('scripts') => env('SCRIPT_DISK_ROOT', storage_path('app/scripts')),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -69,4 +69,17 @@ return [
|
||||
*/
|
||||
|
||||
'allow_dump_downloading' => (bool) env('ALLOW_DUMP_DOWNLOADING', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allow Script Downloading
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When script downloads are allowed, requests to the videoscript.show route will
|
||||
| download scripts. If disabled, requests to the videoscript.show route will
|
||||
| raise a 403 Forbidden response.
|
||||
|
|
||||
*/
|
||||
|
||||
'allow_script_downloading' => (bool) env('ALLOW_SCRIPT_DOWNLOADING', false),
|
||||
];
|
||||
|
||||
@@ -92,6 +92,7 @@ return [
|
||||
FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED => FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG,
|
||||
FlagConstants::ALLOW_VIEW_RECORDING_FLAG_QUALIFIED => FlagConstants::ALLOW_VIEW_RECORDING_FLAG,
|
||||
FlagConstants::ALLOW_DUMP_DOWNLOADING_FLAG_QUALIFIED => FlagConstants::ALLOW_DUMP_DOWNLOADING_FLAG,
|
||||
FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED => FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG,
|
||||
VideoConstants::ENCODER_VERSION_QUALIFIED => VideoConstants::ENCODER_VERSION,
|
||||
WikiConstants::FEATURED_ENTRY_SETTING_QUALIFIED => WikiConstants::FEATURED_ENTRY_SETTING,
|
||||
WikiConstants::FEATURED_VIDEO_SETTING_QUALIFIED => WikiConstants::FEATURED_VIDEO_SETTING,
|
||||
|
||||
@@ -64,4 +64,48 @@ return [
|
||||
*/
|
||||
|
||||
'encoder_version' => env('VIDEO_ENCODER_VERSION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Video Scripts
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Scripts represent the encoding script used to produce a video.
|
||||
| Generally, a script file will be a list of FFmpeg commands
|
||||
| to be executed in a target runtime environment.
|
||||
|
|
||||
*/
|
||||
|
||||
'script' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Script Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The filesystem disk where video scripts are hosted. By default, it is assumed
|
||||
| that video scripts are hosted in an S3-like bucket.
|
||||
|
|
||||
*/
|
||||
|
||||
'disk' => env('SCRIPT_DISK', 'scripts'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Script Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These values represent the base URL that video scripts are downloaded from.
|
||||
| It is most likely that only one of these values should be set.
|
||||
| If scripts are downloaded from a subdomain, set SCRIPT_URL and leave SCRIPT_PATH null.
|
||||
| Ex: script.animethemes.test
|
||||
| If scripts are NOT downloaded from a subdomain, set SCRIPT_PATH and leave SCRIPT_URL null.
|
||||
| Ex: animethemes.test/script
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('SCRIPT_URL'),
|
||||
|
||||
'path' => env('SCRIPT_PATH'),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\Wiki\Video;
|
||||
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class VideoScriptFactory.
|
||||
*
|
||||
* @method VideoScript createOne($attributes = [])
|
||||
* @method VideoScript makeOne($attributes = [])
|
||||
*
|
||||
* @extends Factory<VideoScript>
|
||||
*/
|
||||
class VideoScriptFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var class-string<VideoScript>
|
||||
*/
|
||||
protected $model = VideoScript::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
VideoScript::ATTRIBUTE_PATH => Str::random(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable(VideoScript::TABLE)) {
|
||||
Schema::create(VideoScript::TABLE, function (Blueprint $table) {
|
||||
$table->id(VideoScript::ATTRIBUTE_ID);
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes(BaseModel::ATTRIBUTE_DELETED_AT, 6);
|
||||
$table->string(VideoScript::ATTRIBUTE_PATH);
|
||||
|
||||
$table->unsignedBigInteger(VideoScript::ATTRIBUTE_VIDEO)->nullable();
|
||||
$table->foreign(VideoScript::ATTRIBUTE_VIDEO)->references(Video::ATTRIBUTE_ID)->on(Video::TABLE)->nullOnDelete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists(VideoScript::TABLE);
|
||||
}
|
||||
};
|
||||
@@ -54,6 +54,7 @@ class PermissionSeeder extends Seeder
|
||||
$this->configureWikiResourcePermissions($admin, $wikiEditor, $wikiViewer, 'song');
|
||||
$this->configureWikiResourcePermissions($admin, $wikiEditor, $wikiViewer, 'studio');
|
||||
$this->configureWikiResourcePermissions($admin, $wikiEditor, $wikiViewer, 'video');
|
||||
$this->configureWikiResourcePermissions($admin, $wikiEditor, $wikiViewer, 'video script');
|
||||
|
||||
// Special Permissions
|
||||
$this->configureSpecialPermissions($admin, $wikiEditor, $wikiViewer);
|
||||
@@ -108,17 +109,19 @@ class PermissionSeeder extends Seeder
|
||||
|
||||
$create = Permission::findOrCreate("create $wikiResource");
|
||||
$adminPermissions[] = $create;
|
||||
if ($wikiResource !== 'video' && $wikiResource !== 'audio') {
|
||||
if ($wikiResource !== 'video' && $wikiResource !== 'audio' && $wikiResource !== 'video script') {
|
||||
$editorPermissions[] = $create;
|
||||
}
|
||||
|
||||
$update = Permission::findOrCreate("update $wikiResource");
|
||||
$adminPermissions[] = $update;
|
||||
$editorPermissions[] = $update;
|
||||
if ($wikiResource !== 'video script') {
|
||||
$editorPermissions[] = $update;
|
||||
}
|
||||
|
||||
$delete = Permission::findOrCreate("delete $wikiResource");
|
||||
$adminPermissions[] = $delete;
|
||||
if ($wikiResource !== 'video' && $wikiResource !== 'audio') {
|
||||
if ($wikiResource !== 'video' && $wikiResource !== 'audio' && $wikiResource !== 'video script') {
|
||||
$editorPermissions[] = $delete;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,16 @@ class SettingSeeder extends Seeder
|
||||
]
|
||||
);
|
||||
|
||||
Setting::query()->firstOrCreate(
|
||||
[
|
||||
Setting::ATTRIBUTE_KEY => FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG,
|
||||
],
|
||||
[
|
||||
Setting::ATTRIBUTE_KEY => FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG,
|
||||
Setting::ATTRIBUTE_VALUE => 'false',
|
||||
]
|
||||
);
|
||||
|
||||
Setting::query()->firstOrCreate(
|
||||
[
|
||||
Setting::ATTRIBUTE_KEY => VideoConstants::ENCODER_VERSION,
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -224,7 +224,7 @@ return [
|
||||
'confirmButtonText' => 'Move',
|
||||
'fields' => [
|
||||
'path' => [
|
||||
'help' => 'The new location of the Audio. Ex: 2009/Summer/Bakemonogatari-OP1.webm.',
|
||||
'help' => 'The new location of the file. Ex: 2009/Summer/Bakemonogatari-OP1.webm.',
|
||||
'name' => 'Path',
|
||||
],
|
||||
],
|
||||
@@ -284,6 +284,18 @@ return [
|
||||
'name' => 'Revoke Role',
|
||||
],
|
||||
],
|
||||
'video_script' => [
|
||||
'delete' => [
|
||||
'confirmText' => 'Remove Video Script from configured storage disks and from the database?',
|
||||
'name' => 'Remove Video Script',
|
||||
],
|
||||
'move' => [
|
||||
'name' => 'Move Video Script',
|
||||
],
|
||||
'upload' => [
|
||||
'name' => 'Upload Video Script',
|
||||
],
|
||||
],
|
||||
'video' => [
|
||||
'backfill' => [
|
||||
'confirmButtonText' => 'Backfill',
|
||||
@@ -586,6 +598,9 @@ return [
|
||||
'email' => 'Email',
|
||||
'name' => 'Name',
|
||||
],
|
||||
'video_script' => [
|
||||
'path' => 'Path',
|
||||
],
|
||||
'video' => [
|
||||
'basename' => [
|
||||
'name' => 'Basename',
|
||||
@@ -693,6 +708,9 @@ return [
|
||||
'resolution' => [
|
||||
'name' => 'Video with Unset Resolution',
|
||||
],
|
||||
'script' => [
|
||||
'name' => 'Video Without Script',
|
||||
],
|
||||
'source' => [
|
||||
'name' => 'Video with Unknown Source Type',
|
||||
],
|
||||
@@ -759,6 +777,7 @@ return [
|
||||
'studios' => 'Studios',
|
||||
'transactions' => 'Transactions',
|
||||
'users' => 'Users',
|
||||
'video_scripts' => 'Video Scripts',
|
||||
'videos' => 'Videos',
|
||||
],
|
||||
'singularLabel' => [
|
||||
@@ -783,6 +802,7 @@ return [
|
||||
'studio' => 'Studio',
|
||||
'transaction' => 'Transaction',
|
||||
'user' => 'User',
|
||||
'video_script' => 'Video Script',
|
||||
'video' => 'Video',
|
||||
],
|
||||
],
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Http\Controllers\Wiki\Video\Script\ScriptController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
$isScriptDownloadingAllowed = Str::of('is_feature_enabled:')
|
||||
->append(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED)
|
||||
->append(',Script Downloading Disabled')
|
||||
->__toString();
|
||||
|
||||
Route::get('/{videoscript}', [ScriptController::class, 'show'])
|
||||
->name('videoscript.show')
|
||||
->middleware([$isScriptDownloadingAllowed, 'without_trashed:videoscript']);
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Actions\Repositories\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\Wiki\Video\Script\ReconcileScriptRepositoriesAction;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Repositories\Eloquent\Wiki\Video\ScriptRepository as ScriptDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\Video\ScriptRepository as ScriptSourceRepository;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptRepositoriesTest.
|
||||
*/
|
||||
class ReconcileScriptRepositoriesTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* If no changes are needed, the Reconcile Script Repository Action shall indicate no changes were made.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNoResults(): void
|
||||
{
|
||||
$this->mock(ScriptSourceRepository::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('get')->once()->andReturn(Collection::make());
|
||||
});
|
||||
|
||||
$action = new ReconcileScriptRepositoriesAction();
|
||||
|
||||
$source = App::make(ScriptSourceRepository::class);
|
||||
$destination = App::make(ScriptDestinationRepository::class);
|
||||
|
||||
$result = $action->reconcileRepositories($source, $destination);
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
static::assertFalse($result->hasChanges());
|
||||
static::assertDatabaseCount(VideoScript::class, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* If video scripts are created, the Reconcile Script Repository Action shall return created video scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreated(): void
|
||||
{
|
||||
$createdScriptCount = $this->faker->numberBetween(2, 9);
|
||||
|
||||
$scripts = VideoScript::factory()->count($createdScriptCount)->make();
|
||||
|
||||
$this->mock(ScriptSourceRepository::class, function (MockInterface $mock) use ($scripts) {
|
||||
$mock->shouldReceive('get')->once()->andReturn($scripts);
|
||||
});
|
||||
|
||||
$action = new ReconcileScriptRepositoriesAction();
|
||||
|
||||
$source = App::make(ScriptSourceRepository::class);
|
||||
$destination = App::make(ScriptDestinationRepository::class);
|
||||
|
||||
$result = $action->reconcileRepositories($source, $destination);
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
static::assertTrue($result->hasChanges());
|
||||
static::assertCount($createdScriptCount, $result->getCreated());
|
||||
static::assertDatabaseCount(VideoScript::class, $createdScriptCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* If video scripts are deleted, the Reconcile Script Repository Action shall return deleted video scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleted(): void
|
||||
{
|
||||
$deletedScriptCount = $this->faker->numberBetween(2, 9);
|
||||
|
||||
$scripts = VideoScript::factory()->count($deletedScriptCount)->create();
|
||||
|
||||
$this->mock(ScriptSourceRepository::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('get')->once()->andReturn(Collection::make());
|
||||
});
|
||||
|
||||
$action = new ReconcileScriptRepositoriesAction();
|
||||
|
||||
$source = App::make(ScriptSourceRepository::class);
|
||||
$destination = App::make(ScriptDestinationRepository::class);
|
||||
|
||||
$result = $action->reconcileRepositories($source, $destination);
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
static::assertTrue($result->hasChanges());
|
||||
static::assertCount($deletedScriptCount, $result->getDeleted());
|
||||
|
||||
static::assertDatabaseCount(VideoScript::class, $deletedScriptCount);
|
||||
foreach ($scripts as $script) {
|
||||
static::assertSoftDeleted($script);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\DeleteScriptAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Testing\File;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptTest.
|
||||
*/
|
||||
class DeleteScriptTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The Delete Script Action shall fail if there are no deletions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDefault(): void
|
||||
{
|
||||
Config::set(VideoConstants::SCRIPT_DISK_QUALIFIED, []);
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$action = new DeleteScriptAction($script);
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue($result->hasFailed());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Delete Script Action shall pass if there are deletions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPassed(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $file->path(),
|
||||
]);
|
||||
|
||||
$action = new DeleteScriptAction($script);
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The Delete Script Action shall delete the file from the configured disks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeletedFromDisk(): void
|
||||
{
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $file->path(),
|
||||
]);
|
||||
|
||||
$action = new DeleteScriptAction($script);
|
||||
|
||||
$action->handle();
|
||||
|
||||
static::assertEmpty($fs->allFiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Delete Video Action shall delete the script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoDeleted(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $file->path(),
|
||||
]);
|
||||
|
||||
$action = new DeleteScriptAction($script);
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$action->then($result);
|
||||
|
||||
static::assertSoftDeleted($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\MoveScriptAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Testing\File;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class MoveScriptTest.
|
||||
*/
|
||||
class MoveScriptTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The Move Script Action shall fail if there are no moves.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDefault(): void
|
||||
{
|
||||
Config::set(VideoConstants::SCRIPT_DISK_QUALIFIED, []);
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$action = new MoveScriptAction($script, $this->faker->word());
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue($result->hasFailed());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Move Script Action shall pass if there are moves.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPassed(): void
|
||||
{
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$directory = $this->faker->word();
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $fs->putFileAs($directory, $file, $file->getClientOriginalName()),
|
||||
]);
|
||||
|
||||
$action = new MoveScriptAction($script, Str::replace($directory, $this->faker->word(), $script->path));
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The Move Script Action shall move the file in the configured disks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMovedInDisk(): void
|
||||
{
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$directory = $this->faker->word();
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $fs->putFileAs($directory, $file, $file->getClientOriginalName()),
|
||||
]);
|
||||
|
||||
$from = $script->path;
|
||||
$to = Str::replace($directory, $this->faker->word(), $script->path);
|
||||
|
||||
$action = new MoveScriptAction($script, $to);
|
||||
|
||||
$action->handle();
|
||||
|
||||
$fs->assertMissing($from);
|
||||
$fs->assertExists($to);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Move Script Action shall move the script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testScriptUpdated(): void
|
||||
{
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$directory = $this->faker->word();
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $fs->putFileAs($directory, $file, $file->getClientOriginalName()),
|
||||
]);
|
||||
|
||||
$to = Str::replace($directory, $this->faker->word(), $script->path);
|
||||
|
||||
$action = new MoveScriptAction($script, $to);
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$action->then($result);
|
||||
|
||||
static::assertDatabaseHas(VideoScript::class, [VideoScript::ATTRIBUTE_PATH => $to]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\UploadScriptAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Testing\File;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class UploadScriptTest.
|
||||
*/
|
||||
class UploadScriptTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The Upload Script Action shall fail if there are no uploads.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDefault(): void
|
||||
{
|
||||
Config::set(VideoConstants::SCRIPT_DISK_QUALIFIED, []);
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$action = new UploadScriptAction($file, $this->faker->word());
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue($result->hasFailed());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Upload Script Action shall pass if given a valid file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPassed(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$action = new UploadScriptAction($file, $this->faker->word());
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
static::assertTrue(ActionStatus::PASSED()->is($result->getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The Upload Script Action shall upload the file to the configured disk.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUploadedToDisk(): void
|
||||
{
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$action = new UploadScriptAction($file, $this->faker->word());
|
||||
|
||||
$action->handle();
|
||||
|
||||
static::assertCount(1, $fs->allFiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Upload Video Action shall upload the file to the configured disk.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatedVideo(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$action = new UploadScriptAction($file, $this->faker->word());
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$action->then($result);
|
||||
|
||||
static::assertDatabaseCount(VideoScript::class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Upload Script Action shall attach the provided video.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAttachesVideo(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$video = Video::factory()->createOne();
|
||||
|
||||
$action = new UploadScriptAction($file, $this->faker->word(), $video);
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$action->then($result);
|
||||
|
||||
static::assertTrue($video->videoscript()->exists());
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Pivots\AnimeThemeEntryVideo;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Testing\File;
|
||||
@@ -131,4 +132,27 @@ class UploadVideoTest extends TestCase
|
||||
|
||||
static::assertDatabaseCount(AnimeThemeEntryVideo::class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Upload Video Action shall attach the provided script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAssociatesScript(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
|
||||
Config::set(VideoConstants::DISKS_QUALIFIED, [Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED)]);
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$file = File::fake()->create($this->faker->word().'.webm', $this->faker->randomDigitNotNull());
|
||||
$script = File::fake()->create($this->faker->word().'.txt', $this->faker->randomDigitNotNull());
|
||||
|
||||
$action = new UploadVideoAction($file, $this->faker->word(), null, $script);
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$action->then($result);
|
||||
|
||||
static::assertDatabaseHas(VideoScript::class, [VideoScript::ATTRIBUTE_VIDEO => 1]);
|
||||
}
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Console\Commands\Repositories\Storage\Wiki\Video;
|
||||
|
||||
use App\Console\Commands\Repositories\Storage\Wiki\Video\ScriptReconcileCommand;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Repositories\Storage\Wiki\Video\ScriptRepository;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptReconcileTest.
|
||||
*/
|
||||
class ScriptReconcileTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* If no changes are needed, the Reconcile Script Command shall output 'No Video Scripts created or deleted or updated'.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNoResults(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$this->baseRefreshDatabase(); // Cannot lazily refresh database within pending command
|
||||
|
||||
$this->mock(ScriptRepository::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('get')->once()->andReturn(Collection::make());
|
||||
});
|
||||
|
||||
$this->artisan(ScriptReconcileCommand::class)
|
||||
->assertSuccessful()
|
||||
->expectsOutput('No Video Scripts created or deleted or updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* If scripts are created, the Reconcile Script Command shall output '{Created Count} Video Scripts created, 0 Video Scripts deleted, 0 Video Scripts updated'.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreated(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$this->baseRefreshDatabase(); // Cannot lazily refresh database within pending command
|
||||
|
||||
$createdScriptCount = $this->faker->numberBetween(2, 9);
|
||||
|
||||
$scripts = VideoScript::factory()->count($createdScriptCount)->make();
|
||||
|
||||
$this->mock(ScriptRepository::class, function (MockInterface $mock) use ($scripts) {
|
||||
$mock->shouldReceive('get')->once()->andReturn($scripts);
|
||||
});
|
||||
|
||||
$this->artisan(ScriptReconcileCommand::class)
|
||||
->assertSuccessful()
|
||||
->expectsOutput("$createdScriptCount Video Scripts created, 0 Video Scripts deleted, 0 Video Scripts updated");
|
||||
}
|
||||
|
||||
/**
|
||||
* If scripts are deleted, the Reconcile Script Command shall output '0 Video Scripts created, {Deleted Count} Video Scripts deleted, 0 Video Scripts updated'.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleted(): void
|
||||
{
|
||||
Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
|
||||
$deletedScriptCount = $this->faker->numberBetween(2, 9);
|
||||
|
||||
VideoScript::factory()->count($deletedScriptCount)->create();
|
||||
|
||||
$this->mock(ScriptRepository::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('get')->once()->andReturn(Collection::make());
|
||||
});
|
||||
|
||||
$this->artisan(ScriptReconcileCommand::class)
|
||||
->assertSuccessful()
|
||||
->expectsOutput("0 Video Scripts created, $deletedScriptCount Video Scripts deleted, 0 Video Scripts updated");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Events\Wiki\Video;
|
||||
|
||||
use App\Events\Wiki\Video\Script\VideoScriptCreated;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptDeleted;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptRestored;
|
||||
use App\Events\Wiki\Video\Script\VideoScriptUpdated;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptTest.
|
||||
*/
|
||||
class ScriptTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* When a Script is created, a VideoScriptCreated event shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptCreatedEventDispatched(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
VideoScript::factory()->createOne();
|
||||
|
||||
Event::assertDispatched(VideoScriptCreated::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Script is deleted, a VideoScriptDeleted event shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptDeletedEventDispatched(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->delete();
|
||||
|
||||
Event::assertDispatched(VideoScriptDeleted::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Script is restored, a VideoScriptRestored event shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptRestoredEventDispatched(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->restore();
|
||||
|
||||
Event::assertDispatched(VideoScriptRestored::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Script is restored, a VideoScriptUpdated event shall not be dispatched.
|
||||
* Note: This is a customization that overrides default framework behavior.
|
||||
* An updated event is fired on restore.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptRestoresQuietly(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->restore();
|
||||
|
||||
Event::assertNotDispatched(VideoScriptUpdated::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Script is updated, a VideoScriptUpdated event shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptUpdatedEventDispatched(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
$changes = VideoScript::factory()->makeOne();
|
||||
|
||||
$script->fill($changes->getAttributes());
|
||||
$script->save();
|
||||
|
||||
Event::assertDispatched(VideoScriptUpdated::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The VideoScriptUpdated event shall contain embed fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoScriptUpdatedEventEmbedFields(): void
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
$changes = VideoScript::factory()->makeOne();
|
||||
|
||||
$script->fill($changes->getAttributes());
|
||||
$script->save();
|
||||
|
||||
Event::assertDispatched(VideoScriptUpdated::class, function (VideoScriptUpdated $event) {
|
||||
$message = $event->getDiscordMessage();
|
||||
|
||||
return ! empty(Arr::get($message->embed, 'fields'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class DumpStoreTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* The Dump Store Endpoint shall require the content field.
|
||||
* The Dump Store Endpoint shall require the path field.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptDestroyTest.
|
||||
*/
|
||||
class ScriptDestroyTest extends TestCase
|
||||
{
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* The Script Destroy Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$response = $this->delete(route('api.videoscript.destroy', ['videoscript' => $script]));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Destroy Endpoint shall delete the script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleted(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$user = User::factory()->withPermission('delete video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->delete(route('api.videoscript.destroy', ['videoscript' => $script]));
|
||||
|
||||
$response->assertOk();
|
||||
static::assertSoftDeleted($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptForceDeleteTest.
|
||||
*/
|
||||
class ScriptForceDeleteTest extends TestCase
|
||||
{
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* The Script Force Destroy Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$response = $this->delete(route('api.videoscript.forceDelete', ['videoscript' => $script]));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Force Destroy Endpoint shall force delete the script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleted(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$user = User::factory()->withPermission('force delete video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->delete(route('api.videoscript.forceDelete', ['videoscript' => $script]));
|
||||
|
||||
$response->assertOk();
|
||||
static::assertModelMissing($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Contracts\Http\Api\Field\SortableField;
|
||||
use App\Enums\Http\Api\Filter\TrashedStatus;
|
||||
use App\Enums\Http\Api\Sort\Direction;
|
||||
use App\Enums\Models\Wiki\VideoOverlap;
|
||||
use App\Enums\Models\Wiki\VideoSource;
|
||||
use App\Http\Api\Criteria\Filter\TrashedCriteria;
|
||||
use App\Http\Api\Criteria\Paging\Criteria;
|
||||
use App\Http\Api\Criteria\Paging\OffsetCriteria;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Parser\FieldParser;
|
||||
use App\Http\Api\Parser\FilterParser;
|
||||
use App\Http\Api\Parser\IncludeParser;
|
||||
use App\Http\Api\Parser\PagingParser;
|
||||
use App\Http\Api\Parser\SortParser;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptReadQuery;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Resources\Wiki\Video\Collection\ScriptCollection;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptIndexTest.
|
||||
*/
|
||||
class ScriptIndexTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* By default, the Script Index Endpoint shall return a collection of Script Resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDefault(): void
|
||||
{
|
||||
$scripts = VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index'));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery()))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall be paginated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPaginated(): void
|
||||
{
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index'));
|
||||
|
||||
$response->assertJsonStructure([
|
||||
ScriptCollection::$wrap,
|
||||
'links',
|
||||
'meta',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall allow inclusion of related resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAllowedIncludePaths(): void
|
||||
{
|
||||
$schema = new ScriptSchema();
|
||||
|
||||
$allowedIncludes = collect($schema->allowedIncludes());
|
||||
|
||||
$selectedIncludes = $allowedIncludes->random($this->faker->numberBetween(1, $allowedIncludes->count()));
|
||||
|
||||
$includedPaths = $selectedIncludes->map(fn (AllowedInclude $include) => $include->path());
|
||||
|
||||
$parameters = [
|
||||
IncludeParser::param() => $includedPaths->join(','),
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with($includedPaths->all())->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall implement sparse fieldsets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSparseFieldsets(): void
|
||||
{
|
||||
$schema = new ScriptSchema();
|
||||
|
||||
$fields = collect($schema->fields());
|
||||
|
||||
$includedFields = $fields->random($this->faker->numberBetween(1, $fields->count()));
|
||||
|
||||
$parameters = [
|
||||
FieldParser::param() => [
|
||||
ScriptResource::$wrap => $includedFields->map(fn (Field $field) => $field->getKey())->join(','),
|
||||
],
|
||||
];
|
||||
|
||||
$scripts = VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Synonym Index Endpoint shall support sorting resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSorts(): void
|
||||
{
|
||||
$schema = new ScriptSchema();
|
||||
|
||||
$sort = collect($schema->fields())
|
||||
->filter(fn (Field $field) => $field instanceof SortableField)
|
||||
->map(fn (SortableField $field) => $field->getSort())
|
||||
->random();
|
||||
|
||||
$parameters = [
|
||||
SortParser::param() => $sort->format(Direction::getRandomInstance()),
|
||||
];
|
||||
|
||||
$query = new ScriptReadQuery($parameters);
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
$query->collection($query->index())
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by created_at.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatedAtFilter(): void
|
||||
{
|
||||
$createdFilter = $this->faker->date();
|
||||
$excludedDate = $this->faker->date();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
BaseModel::ATTRIBUTE_CREATED_AT => $createdFilter,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
Carbon::withTestNow($createdFilter, function () {
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
});
|
||||
|
||||
Carbon::withTestNow($excludedDate, function () {
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
});
|
||||
|
||||
$script = VideoScript::query()->where(BaseModel::ATTRIBUTE_CREATED_AT, $createdFilter)->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by updated_at.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUpdatedAtFilter(): void
|
||||
{
|
||||
$updatedFilter = $this->faker->date();
|
||||
$excludedDate = $this->faker->date();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
BaseModel::ATTRIBUTE_UPDATED_AT => $updatedFilter,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
Carbon::withTestNow($updatedFilter, function () {
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
});
|
||||
|
||||
Carbon::withTestNow($excludedDate, function () {
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
});
|
||||
|
||||
$script = VideoScript::query()->where(BaseModel::ATTRIBUTE_UPDATED_AT, $updatedFilter)->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by trashed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testWithoutTrashedFilter(): void
|
||||
{
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
TrashedCriteria::PARAM_VALUE => TrashedStatus::WITHOUT,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
|
||||
$deleteVideo = VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
$deleteVideo->each(function (VideoScript $script) {
|
||||
$script->delete();
|
||||
});
|
||||
|
||||
$script = VideoScript::withoutTrashed()->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by trashed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testWithTrashedFilter(): void
|
||||
{
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
TrashedCriteria::PARAM_VALUE => TrashedStatus::WITH,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
|
||||
$deleteVideo = VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
$deleteVideo->each(function (VideoScript $script) {
|
||||
$script->delete();
|
||||
});
|
||||
|
||||
$script = VideoScript::withTrashed()->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by trashed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOnlyTrashedFilter(): void
|
||||
{
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
TrashedCriteria::PARAM_VALUE => TrashedStatus::ONLY,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
|
||||
$deleteVideo = VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
$deleteVideo->each(function (VideoScript $script) {
|
||||
$script->delete();
|
||||
});
|
||||
|
||||
$script = VideoScript::onlyTrashed()->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support filtering by deleted_at.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDeletedAtFilter(): void
|
||||
{
|
||||
$deletedFilter = $this->faker->date();
|
||||
$excludedDate = $this->faker->date();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
BaseModel::ATTRIBUTE_DELETED_AT => $deletedFilter,
|
||||
TrashedCriteria::PARAM_VALUE => TrashedStatus::WITH,
|
||||
],
|
||||
PagingParser::param() => [
|
||||
OffsetCriteria::SIZE_PARAM => Criteria::MAX_RESULTS,
|
||||
],
|
||||
];
|
||||
|
||||
Carbon::withTestNow($deletedFilter, function () {
|
||||
$scripts = VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
$scripts->each(function (VideoScript $script) {
|
||||
$script->delete();
|
||||
});
|
||||
});
|
||||
|
||||
Carbon::withTestNow($excludedDate, function () {
|
||||
$scripts = VideoScript::factory()->count($this->faker->randomDigitNotNull())->create();
|
||||
$scripts->each(function (VideoScript $script) {
|
||||
$script->delete();
|
||||
});
|
||||
});
|
||||
|
||||
$script = VideoScript::withTrashed()->where(BaseModel::ATTRIBUTE_DELETED_AT, $deletedFilter)->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by lyrics.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByLyrics(): void
|
||||
{
|
||||
$lyricsFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_LYRICS => $lyricsFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($lyricsFilter) {
|
||||
$query->where(Video::ATTRIBUTE_LYRICS, $lyricsFilter);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by nc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByNc(): void
|
||||
{
|
||||
$ncFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_NC => $ncFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($ncFilter) {
|
||||
$query->where(Video::ATTRIBUTE_NC, $ncFilter);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by overlap.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByOverlap(): void
|
||||
{
|
||||
$overlapFilter = VideoOverlap::getRandomInstance();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_OVERLAP => $overlapFilter->description,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($overlapFilter) {
|
||||
$query->where(Video::ATTRIBUTE_OVERLAP, $overlapFilter->value);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by resolution.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByResolution(): void
|
||||
{
|
||||
$resolutionFilter = $this->faker->randomNumber();
|
||||
$excludedResolution = $resolutionFilter + 1;
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_RESOLUTION => $resolutionFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(
|
||||
Video::factory()->state([
|
||||
Video::ATTRIBUTE_RESOLUTION => $this->faker->boolean() ? $resolutionFilter : $excludedResolution,
|
||||
])
|
||||
)
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($resolutionFilter) {
|
||||
$query->where(Video::ATTRIBUTE_RESOLUTION, $resolutionFilter);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by source.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosBySource(): void
|
||||
{
|
||||
$sourceFilter = VideoSource::getRandomInstance();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_SOURCE => $sourceFilter->description,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($sourceFilter) {
|
||||
$query->where(Video::ATTRIBUTE_SOURCE, $sourceFilter->value);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by subbed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosBySubbed(): void
|
||||
{
|
||||
$subbedFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_SUBBED => $subbedFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($subbedFilter) {
|
||||
$query->where(Video::ATTRIBUTE_SUBBED, $subbedFilter);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Index Endpoint shall support constrained eager loading of videos by uncen.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByUncen(): void
|
||||
{
|
||||
$uncenFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_UNCEN => $uncenFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
VideoScript::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$scripts = VideoScript::with([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($uncenFilter) {
|
||||
$query->where(Video::ATTRIBUTE_UNCEN, $uncenFilter);
|
||||
},
|
||||
])
|
||||
->get();
|
||||
|
||||
$response = $this->get(route('api.videoscript.index', $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptCollection($scripts, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptRestoreTest.
|
||||
*/
|
||||
class ScriptRestoreTest extends TestCase
|
||||
{
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* The Script Restore Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->delete();
|
||||
|
||||
$response = $this->patch(route('api.videoscript.restore', ['videoscript' => $script]));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Restore Endpoint shall restore the script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRestored(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->delete();
|
||||
|
||||
$user = User::factory()->withPermission('restore video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->patch(route('api.videoscript.restore', ['videoscript' => $script]));
|
||||
|
||||
$response->assertOk();
|
||||
static::assertNotSoftDeleted($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Enums\Models\Wiki\VideoOverlap;
|
||||
use App\Enums\Models\Wiki\VideoSource;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Parser\FieldParser;
|
||||
use App\Http\Api\Parser\FilterParser;
|
||||
use App\Http\Api\Parser\IncludeParser;
|
||||
use App\Http\Api\Query\Wiki\Video\Script\ScriptReadQuery;
|
||||
use App\Http\Api\Schema\Wiki\Video\ScriptSchema;
|
||||
use App\Http\Resources\Wiki\Video\Resource\ScriptResource;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptShowTest.
|
||||
*/
|
||||
class ScriptShowTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* By default, the Script Show Endpoint shall return a Script Resource.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDefault(): void
|
||||
{
|
||||
$script = VideoScript::factory()->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script]));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery()))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall return a Script Resource for soft deleted scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSoftDelete(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->delete();
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script]));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery()))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall allow inclusion of related resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAllowedIncludePaths(): void
|
||||
{
|
||||
$schema = new ScriptSchema();
|
||||
|
||||
$allowedIncludes = collect($schema->allowedIncludes());
|
||||
|
||||
$selectedIncludes = $allowedIncludes->random($this->faker->numberBetween(1, $allowedIncludes->count()));
|
||||
|
||||
$includedPaths = $selectedIncludes->map(fn (AllowedInclude $include) => $include->path());
|
||||
|
||||
$parameters = [
|
||||
IncludeParser::param() => $includedPaths->join(','),
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->createOne();
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall implement sparse fieldsets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSparseFieldsets(): void
|
||||
{
|
||||
$schema = new ScriptSchema();
|
||||
|
||||
$fields = collect($schema->fields());
|
||||
|
||||
$includedFields = $fields->random($this->faker->numberBetween(1, $fields->count()));
|
||||
|
||||
$parameters = [
|
||||
FieldParser::param() => [
|
||||
ScriptResource::$wrap => $includedFields->map(fn (Field $field) => $field->getKey())->join(','),
|
||||
],
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()->create();
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by lyrics.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByLyrics(): void
|
||||
{
|
||||
$lyricsFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_LYRICS => $lyricsFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($lyricsFilter) {
|
||||
$query->where(Video::ATTRIBUTE_LYRICS, $lyricsFilter);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by nc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByNc(): void
|
||||
{
|
||||
$ncFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_NC => $ncFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($ncFilter) {
|
||||
$query->where(Video::ATTRIBUTE_NC, $ncFilter);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by overlap.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByOverlap(): void
|
||||
{
|
||||
$overlapFilter = VideoOverlap::getRandomInstance();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_OVERLAP => $overlapFilter->description,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($overlapFilter) {
|
||||
$query->where(Video::ATTRIBUTE_OVERLAP, $overlapFilter->value);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by resolution.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByResolution(): void
|
||||
{
|
||||
$resolutionFilter = $this->faker->randomNumber();
|
||||
$excludedResolution = $resolutionFilter + 1;
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_RESOLUTION => $resolutionFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(
|
||||
Video::factory()->state([
|
||||
Video::ATTRIBUTE_RESOLUTION => $this->faker->boolean() ? $resolutionFilter : $excludedResolution,
|
||||
])
|
||||
)
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($resolutionFilter) {
|
||||
$query->where(Video::ATTRIBUTE_RESOLUTION, $resolutionFilter);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by source.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosBySource(): void
|
||||
{
|
||||
$sourceFilter = VideoSource::getRandomInstance();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_SOURCE => $sourceFilter->description,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($sourceFilter) {
|
||||
$query->where(Video::ATTRIBUTE_SOURCE, $sourceFilter->value);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by subbed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosBySubbed(): void
|
||||
{
|
||||
$subbedFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_SUBBED => $subbedFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($subbedFilter) {
|
||||
$query->where(Video::ATTRIBUTE_SUBBED, $subbedFilter);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Show Endpoint shall support constrained eager loading of videos by uncen.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideosByUncen(): void
|
||||
{
|
||||
$uncenFilter = $this->faker->boolean();
|
||||
|
||||
$parameters = [
|
||||
FilterParser::param() => [
|
||||
Video::ATTRIBUTE_UNCEN => $uncenFilter,
|
||||
],
|
||||
IncludeParser::param() => VideoScript::RELATION_VIDEO,
|
||||
];
|
||||
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->create();
|
||||
|
||||
$script->unsetRelations()->load([
|
||||
VideoScript::RELATION_VIDEO => function (BelongsTo $query) use ($uncenFilter) {
|
||||
$query->where(Video::ATTRIBUTE_UNCEN, $uncenFilter);
|
||||
},
|
||||
]);
|
||||
|
||||
$response = $this->get(route('api.videoscript.show', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
(new ScriptResource($script, new ScriptReadQuery($parameters)))
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptStoreTest.
|
||||
*/
|
||||
class ScriptStoreTest extends TestCase
|
||||
{
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* The Script Store Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$script = VideoScript::factory()->makeOne();
|
||||
|
||||
$response = $this->post(route('api.videoscript.store', $script->toArray()));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Store Endpoint shall require the path field.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRequiredFields(): void
|
||||
{
|
||||
$user = User::factory()->withPermission('create video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->post(route('api.videoscript.store'));
|
||||
|
||||
$response->assertJsonValidationErrors([
|
||||
VideoScript::ATTRIBUTE_PATH,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Store Endpoint shall create a script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreate(): void
|
||||
{
|
||||
$parameters = VideoScript::factory()->raw();
|
||||
|
||||
$user = User::factory()->withPermission('create video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->post(route('api.videoscript.store', $parameters));
|
||||
|
||||
$response->assertCreated();
|
||||
static::assertDatabaseCount(VideoScript::TABLE, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Wiki\Video\Script;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptUpdateTest.
|
||||
*/
|
||||
class ScriptUpdateTest extends TestCase
|
||||
{
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* The Script Update Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$parameters = VideoScript::factory()->raw();
|
||||
|
||||
$response = $this->put(route('api.videoscript.update', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Script Update Endpoint shall update a script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUpdate(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$parameters = VideoScript::factory()->raw();
|
||||
|
||||
$user = User::factory()->withPermission('update video script')->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->put(route('api.videoscript.update', ['videoscript' => $script] + $parameters));
|
||||
|
||||
$response->assertOk();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Sequence;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -114,6 +115,7 @@ class VideoIndexTest extends TestCase
|
||||
Video::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->for(Audio::factory())
|
||||
->has(VideoScript::factory(), 'videoscript')
|
||||
->has(
|
||||
AnimeThemeEntry::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
|
||||
@@ -19,6 +19,7 @@ use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Factories\Sequence;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@@ -103,6 +104,7 @@ class VideoShowTest extends TestCase
|
||||
|
||||
$video = Video::factory()
|
||||
->for(Audio::factory())
|
||||
->has(VideoScript::factory(), 'videoscript')
|
||||
->has(
|
||||
AnimeThemeEntry::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Wiki;
|
||||
namespace Tests\Feature\Http\Wiki\Audio;
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Models\Wiki\Audio;
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Wiki\Video\Script;
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||
use Illuminate\Http\Testing\File;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptTest.
|
||||
*/
|
||||
class ScriptTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
use WithoutEvents;
|
||||
|
||||
/**
|
||||
* If script downloading is disabled through the 'flags.allow_script_downloading' property,
|
||||
* the user shall receive a forbidden exception.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testScriptDownloadingNotAllowedForbidden(): void
|
||||
{
|
||||
Config::set(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED, false);
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$response = $this->get(route('videoscript.show', ['videoscript' => $script]));
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the script is soft-deleted, the user shall receive a forbidden exception.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSoftDeleteScriptDownloadingForbidden(): void
|
||||
{
|
||||
Config::set(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED, false);
|
||||
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
$script->delete();
|
||||
|
||||
$response = $this->get(route('videoscript.show', ['videoscript' => $script]));
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* If script downloading is enabled, the script is downloaded from storage through the response.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDownloadedThroughResponse(): void
|
||||
{
|
||||
Config::set(FlagConstants::ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED, true);
|
||||
|
||||
$fs = Storage::fake(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
$file = File::fake()->create($this->faker->word().'.txt');
|
||||
$fsFile = $fs->putFile('', $file);
|
||||
|
||||
$script = VideoScript::factory()->createOne([
|
||||
VideoScript::ATTRIBUTE_PATH => $fsFile,
|
||||
]);
|
||||
|
||||
$response = $this->get(route('videoscript.show', ['videoscript' => $script]));
|
||||
|
||||
static::assertInstanceOf(StreamedResponse::class, $response->baseResponse);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Wiki;
|
||||
namespace Tests\Feature\Http\Wiki\Video;
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Jobs\Wiki\Video;
|
||||
|
||||
use App\Constants\Config\FlagConstants;
|
||||
use App\Jobs\SendDiscordNotificationJob;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptTest.
|
||||
*/
|
||||
class ScriptTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* When a script is created, a SendDiscordNotification job shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoCreatedSendsDiscordNotification(): void
|
||||
{
|
||||
Config::set(FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED, true);
|
||||
Bus::fake(SendDiscordNotificationJob::class);
|
||||
|
||||
VideoScript::factory()->createOne();
|
||||
|
||||
Bus::assertDispatched(SendDiscordNotificationJob::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a script is deleted, a SendDiscordNotification job shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoDeletedSendsDiscordNotification(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
Config::set(FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED, true);
|
||||
Bus::fake(SendDiscordNotificationJob::class);
|
||||
|
||||
$script->delete();
|
||||
|
||||
Bus::assertDispatched(SendDiscordNotificationJob::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a script is restored, a SendDiscordNotification job shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoRestoredSendsDiscordNotification(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
Config::set(FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED, true);
|
||||
Bus::fake(SendDiscordNotificationJob::class);
|
||||
|
||||
$script->restore();
|
||||
|
||||
Bus::assertDispatched(SendDiscordNotificationJob::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a script is updated, a SendDiscordNotification job shall be dispatched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoUpdatedSendsDiscordNotification(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
Config::set(FlagConstants::ALLOW_DISCORD_NOTIFICATIONS_FLAG_QUALIFIED, true);
|
||||
Bus::fake(SendDiscordNotificationJob::class);
|
||||
|
||||
$changes = VideoScript::factory()->makeOne();
|
||||
|
||||
$script->fill($changes->getAttributes());
|
||||
$script->save();
|
||||
|
||||
Bus::assertDispatched(SendDiscordNotificationJob::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Models\Wiki\Video;
|
||||
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ScriptTest.
|
||||
*/
|
||||
class ScriptTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Dumps shall be nameable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNameable(): void
|
||||
{
|
||||
$script = VideoScript::factory()->createOne();
|
||||
|
||||
static::assertIsString($script->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scripts shall belong to a Video.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideo(): void
|
||||
{
|
||||
$script = VideoScript::factory()
|
||||
->for(Video::factory())
|
||||
->createOne();
|
||||
|
||||
static::assertInstanceOf(BelongsTo::class, $script->video());
|
||||
static::assertInstanceOf(Video::class, $script->video()->first());
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,12 @@ use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Pivots\AnimeThemeEntryVideo;
|
||||
use CyrildeWit\EloquentViewable\View;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
@@ -309,6 +311,21 @@ class VideoTest extends TestCase
|
||||
static::assertInstanceOf(Audio::class, $video->audio()->first());
|
||||
}
|
||||
|
||||
/**
|
||||
* Video shall have a one-to-one relationship with the type Script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testScript(): void
|
||||
{
|
||||
$video = Video::factory()
|
||||
->has(VideoScript::factory(), 'videoscript')
|
||||
->createOne();
|
||||
|
||||
static::assertInstanceOf(HasOne::class, $video->videoscript());
|
||||
static::assertInstanceOf(VideoScript::class, $video->videoscript()->first());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for source priority testing.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user