Commit 7f7c878c authored by Yassine Doghri's avatar Yassine Doghri
Browse files

fix(video-clips): create unique temporary files for resources to be deleted after generation

- tempfile uniqueness ensures that each process lives in its independent context
- add
writable/temp folder to store video clips temporary resources
- add videoClipWorkers config to
Admin for specifying the number of ffmpeg processes to run in parallel
- update video clip preview
background to better suit the end result
parent 482b47ba
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -60,6 +60,9 @@ writable/logs/*
writable/session/*
!writable/session/index.html

writable/temp/*
!writable/temp/index.html

writable/uploads/*
!writable/uploads/index.html

+13 −1
Original line number Diff line number Diff line
@@ -229,8 +229,10 @@ class MediaClipper extends BaseConfig
     */
    public array $themes = [
        'pine' => [
            // Preview must be a HSL colorscheme string
            // Previews must be a HSL colorscheme string
            'preview' => '174 100% 29%',
            'preview-background' => '172 100% 17%',
            // arrays are rgb
            'background' => [0, 86, 74],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
@@ -248,6 +250,8 @@ class MediaClipper extends BaseConfig
        'crimson' => [
            // Preview must be a HSL colorscheme string
            'preview' => '350 87% 61%',
            'preview-background' => '348 75% 40%',
            // arrays are rgb
            'background' => [179, 31, 57],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
@@ -265,6 +269,8 @@ class MediaClipper extends BaseConfig
        'lake' => [
            // Preview must be a HSL colorscheme string
            'preview' => '194 100% 44%',
            'preview-background' => '194 100% 22%',
            // arrays are rgb
            'background' => [0, 86, 113],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
@@ -282,6 +288,8 @@ class MediaClipper extends BaseConfig
        'amber' => [
            // Preview must be a HSL colorscheme string
            'preview' => '17 100% 57%',
            'preview-background' => '17 100% 35%',
            // arrays are rgb
            'background' => [177, 50, 0],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
@@ -299,6 +307,8 @@ class MediaClipper extends BaseConfig
        'jacaranda' => [
            // Preview must be a HSL colorscheme string
            'preview' => '254 72% 52%',
            'preview-background' => '254 73% 30%',
            // arrays are rgb
            'background' => [47, 21, 132],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
@@ -316,6 +326,8 @@ class MediaClipper extends BaseConfig
        'onyx' => [
            // Preview must be a HSL colorscheme string
            'preview' => '240 17% 2%',
            'preview-background' => '240 17% 2%',
            // arrays are rgb
            'background' => [5, 5, 7],
            'text' => [255, 255, 255],
            // subtitle hex color is BGR (Blue, Green, Red),
+17 −3
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ class VideoClipper

    protected ?string $episodeNumbering = null;

    protected string $tempFileOutput;

    /**
     * @var array<string, mixed>
     */
@@ -90,11 +92,22 @@ class VideoClipper

        $podcastFolder = media_path("podcasts/{$this->episode->podcast->handle}");

        $this->soundbiteOutput = $podcastFolder . "/{$this->episode->slug}-soundbite-{$this->start}-to-{$this->end}.mp3";
        $this->subtitlesClipOutput = $podcastFolder . "/{$this->episode->slug}-subtitles-clip-{$this->start}-to-{$this->end}.srt";
        $this->videoClipBgOutput = $podcastFolder . "/{$this->episode->slug}-clip-bg-{$this->format}-{$this->theme}.png";
        $this->videoClipOutput = $podcastFolder . "/{$this->episode->slug}-clip-{$this->start}-to-{$this->end}-{$this->format}-{$this->theme}.mp4";
        $this->videoClipFilePath = "podcasts/{$this->episode->podcast->handle}/{$this->episode->slug}-clip-{$this->start}-to-{$this->end}-{$this->format}-{$this->theme}.mp4";

        // Temporary files to generate clip
        $tempFile = tempnam(WRITEPATH . 'temp', "{$this->episode->slug}-soundbite-{$this->start}-to-{$this->end}");

        if (! $tempFile) {
            throw new Exception(
                'Could not create temporary files, check for permissions on your ' . WRITEPATH . 'temp folder.'
            );
        }

        $this->tempFileOutput = $tempFile;
        $this->soundbiteOutput = $tempFile . '.mp3';
        $this->subtitlesClipOutput = $tempFile . '.srt';
        $this->videoClipBgOutput = $tempFile . '.png';
    }

    public function soundbite(): void
@@ -178,6 +191,7 @@ class VideoClipper
    public function cleanTempFiles(): void
    {
        // delete generated video background image, soundbite & subtitlesClip
        unlink($this->tempFileOutput);
        unlink($this->soundbiteOutput);
        unlink($this->subtitlesClipOutput);
        unlink($this->videoClipBgOutput);
+14 −0
Original line number Diff line number Diff line
@@ -130,6 +130,20 @@ class ClipModel extends Model
        return $found;
    }

    public function getRunningVideoClipsCount(): int
    {
        $result = $this
            ->select('COUNT(*) as `running_count`')
            ->where([
                'type' => 'video',
                'status' => 'running',
            ])
            ->get()
            ->getResultArray();

        return (int) $result[0]['running_count'];
    }

    public function deleteVideoClip(int $podcastId, int $episodeId, int $clipId): BaseResult | bool
    {
        $this->clearVideoClipCache($clipId);
+4 −3
Original line number Diff line number Diff line
@@ -36,15 +36,16 @@ const VideoClipBuilder = (): void => {

      let theme = form
        .querySelector('input[name="theme"]:checked')
        ?.parentElement?.style.getPropertyValue("--color-accent-base");
        ?.parentElement?.style.getPropertyValue("--color-background-preview");
      videoClipPreviewer.setAttribute("theme", theme || "");

      const watchThemeChange = (event: Event) => {
        theme =
          (
            event.target as HTMLInputElement
          ).parentElement?.style.getPropertyValue("--color-accent-base") ??
          theme;
          ).parentElement?.style.getPropertyValue(
            "--color-background-preview"
          ) ?? theme;
        videoClipPreviewer.setAttribute("theme", theme || "");
      };
      for (let i = 0; i < themeOptions.length; i++) {
Loading