mirror of
https://code.castopod.org/adaures/castopod
synced 2025-05-25 19:42:02 +00:00
feat(video-clips): add new themes + add castopod logo as a watermark
fix video colors to portray exact rgb values using libx264rgb encoding + image transparency issue when overlaying images with php gd
This commit is contained in:
parent
e462abf6d6
commit
1d1490b06a
@ -14,6 +14,8 @@ class MediaClipper extends BaseConfig
|
|||||||
|
|
||||||
public string $wavesMask = APPPATH . 'Libraries/MediaClipper/waves-mask.png';
|
public string $wavesMask = APPPATH . 'Libraries/MediaClipper/waves-mask.png';
|
||||||
|
|
||||||
|
public string $watermark = APPPATH . 'Libraries/MediaClipper/castopod-logo.png';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, array<string, int|array<string, float|int|string>>>
|
* @var array<string, array<string, int|array<string, float|int|string>>>
|
||||||
*/
|
*/
|
||||||
@ -57,9 +59,15 @@ class MediaClipper extends BaseConfig
|
|||||||
'timestamp' => [
|
'timestamp' => [
|
||||||
'fontsize' => 32,
|
'fontsize' => 32,
|
||||||
'padding' => 10,
|
'padding' => 10,
|
||||||
'x' => 1680,
|
'x' => 1620,
|
||||||
'y' => 985,
|
'y' => 985,
|
||||||
],
|
],
|
||||||
|
'watermark' => [
|
||||||
|
'width' => 90,
|
||||||
|
'height' => 72,
|
||||||
|
'x' => 140,
|
||||||
|
'y' => 960,
|
||||||
|
],
|
||||||
'progressbar' => [
|
'progressbar' => [
|
||||||
'height' => 10,
|
'height' => 10,
|
||||||
],
|
],
|
||||||
@ -117,10 +125,16 @@ class MediaClipper extends BaseConfig
|
|||||||
],
|
],
|
||||||
'timestamp' => [
|
'timestamp' => [
|
||||||
'fontsize' => 48,
|
'fontsize' => 48,
|
||||||
'padding' => 10,
|
'padding' => 14,
|
||||||
'x' => 735,
|
'x' => 734,
|
||||||
'y' => 1800,
|
'y' => 1800,
|
||||||
],
|
],
|
||||||
|
'watermark' => [
|
||||||
|
'width' => 120,
|
||||||
|
'height' => 96,
|
||||||
|
'x' => 130,
|
||||||
|
'y' => 1770,
|
||||||
|
],
|
||||||
'progressbar' => [
|
'progressbar' => [
|
||||||
'height' => 10,
|
'height' => 10,
|
||||||
],
|
],
|
||||||
@ -183,6 +197,12 @@ class MediaClipper extends BaseConfig
|
|||||||
'x' => 855,
|
'x' => 855,
|
||||||
'y' => 1070,
|
'y' => 1070,
|
||||||
],
|
],
|
||||||
|
'watermark' => [
|
||||||
|
'width' => 120,
|
||||||
|
'height' => 96,
|
||||||
|
'x' => 130,
|
||||||
|
'y' => 1040,
|
||||||
|
],
|
||||||
'progressbar' => [
|
'progressbar' => [
|
||||||
'height' => 10,
|
'height' => 10,
|
||||||
],
|
],
|
||||||
@ -209,6 +229,8 @@ class MediaClipper extends BaseConfig
|
|||||||
*/
|
*/
|
||||||
public array $themes = [
|
public array $themes = [
|
||||||
'pine' => [
|
'pine' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '174 100% 29%',
|
||||||
'background' => [0, 86, 74],
|
'background' => [0, 86, 74],
|
||||||
'text' => [255, 255, 255],
|
'text' => [255, 255, 255],
|
||||||
// subtitle hex color is BGR (Blue, Green, Red),
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
@ -220,7 +242,93 @@ class MediaClipper extends BaseConfig
|
|||||||
'progressbar' => '009486',
|
'progressbar' => '009486',
|
||||||
'timestampBg' => '00564A',
|
'timestampBg' => '00564A',
|
||||||
'timestampText' => 'FFFFFF',
|
'timestampText' => 'FFFFFF',
|
||||||
'soundwaves' => 'F2FAF9',
|
'watermarkBg' => '00564A',
|
||||||
|
'soundwaves' => [231, 249, 228],
|
||||||
|
],
|
||||||
|
'crimson' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '350 87% 61%',
|
||||||
|
'background' => [179, 31, 57],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [242, 70, 100],
|
||||||
|
'episodeNumberingBg' => [152, 16, 43],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => 'F24664',
|
||||||
|
'timestampBg' => 'B31F39',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'watermarkBg' => 'B31F39',
|
||||||
|
'soundwaves' => [253, 206, 215],
|
||||||
|
],
|
||||||
|
'lake' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '194 100% 44%',
|
||||||
|
'background' => [0, 86, 113],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [0, 171, 225],
|
||||||
|
'episodeNumberingBg' => [0, 43, 57],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => '00ABE1',
|
||||||
|
'timestampBg' => '005671',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'watermarkBg' => '005671',
|
||||||
|
'soundwaves' => [214, 245, 255],
|
||||||
|
],
|
||||||
|
'amber' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '17 100% 57%',
|
||||||
|
'background' => [177, 50, 0],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [255, 96, 34],
|
||||||
|
'episodeNumberingBg' => [121, 34, 0],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => 'FF6022',
|
||||||
|
'timestampBg' => 'B13200',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'watermarkBg' => 'B13200',
|
||||||
|
'soundwaves' => [255, 213, 197],
|
||||||
|
],
|
||||||
|
'jacaranda' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '254 72% 52%',
|
||||||
|
'background' => [47, 21, 132],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [86, 45, 221],
|
||||||
|
'episodeNumberingBg' => [30, 14, 84],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => '562DDD',
|
||||||
|
'timestampBg' => '2F1584',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'watermarkBg' => '2F1584',
|
||||||
|
'soundwaves' => [199, 185, 244],
|
||||||
|
],
|
||||||
|
'onyx' => [
|
||||||
|
// Preview must be a HSL colorscheme string
|
||||||
|
'preview' => '240 17% 2%',
|
||||||
|
'background' => [5, 5, 7],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [38, 38, 49],
|
||||||
|
'episodeNumberingBg' => [0, 0, 0],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => 'D5D5E1',
|
||||||
|
'timestampBg' => '050507',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'watermarkBg' => '050507',
|
||||||
|
'soundwaves' => [213, 213, 225],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@ namespace MediaClipper;
|
|||||||
use App\Entities\Episode;
|
use App\Entities\Episode;
|
||||||
use GdImage;
|
use GdImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: refactor this by splitting image manipulations + video generation
|
||||||
|
*
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
*/
|
||||||
class VideoClip
|
class VideoClip
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -44,21 +49,11 @@ class VideoClip
|
|||||||
|
|
||||||
protected ?string $episodeNumbering = null;
|
protected ?string $episodeNumbering = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var 'landscape'|'portrait'|'squared'
|
|
||||||
*/
|
|
||||||
protected string $format = 'landscape';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, mixed>
|
* @var array<string, mixed>
|
||||||
*/
|
*/
|
||||||
protected array $dimensions = [];
|
protected array $dimensions = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var 'pine'|'crimson'|'lake'|'amber'|'jacaranda'|'onyx'
|
|
||||||
*/
|
|
||||||
protected string $theme = 'pine';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, mixed>
|
* @var array<string, mixed>
|
||||||
*/
|
*/
|
||||||
@ -72,11 +67,10 @@ class VideoClip
|
|||||||
protected Episode $episode,
|
protected Episode $episode,
|
||||||
protected float $start,
|
protected float $start,
|
||||||
protected float $end,
|
protected float $end,
|
||||||
string $format,
|
protected string $format = 'landscape',
|
||||||
string $theme,
|
protected string $theme = 'pine',
|
||||||
) {
|
) {
|
||||||
$this->duration = $end - $start;
|
$this->duration = $end - $start;
|
||||||
$this->format = $format;
|
|
||||||
$this->episodeNumbering = $this->episodeNumbering($this->episode->number, $this->episode->season_number);
|
$this->episodeNumbering = $this->episodeNumbering($this->episode->number, $this->episode->season_number);
|
||||||
$this->dimensions = config('MediaClipper')
|
$this->dimensions = config('MediaClipper')
|
||||||
->formats[$format];
|
->formats[$format];
|
||||||
@ -95,8 +89,8 @@ class VideoClip
|
|||||||
|
|
||||||
$this->soundbiteOutput = $podcastFolder . "/{$this->episode->slug}-soundbite-{$this->start}-to-{$this->end}.mp3";
|
$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->subtitlesClipOutput = $podcastFolder . "/{$this->episode->slug}-subtitles-clip-{$this->start}-to-{$this->end}.srt";
|
||||||
$this->videoClipBgOutput = $podcastFolder . "/{$this->episode->slug}-clip-bg-{$this->format}.png";
|
$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}.mp4";
|
$this->videoClipOutput = $podcastFolder . "/{$this->episode->slug}-clip-{$this->start}-to-{$this->end}-{$this->format}-{$this->theme}.mp4";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function soundbite(): void
|
public function soundbite(): void
|
||||||
@ -132,23 +126,30 @@ class VideoClip
|
|||||||
{
|
{
|
||||||
// @phpstan-ignore
|
// @phpstan-ignore
|
||||||
$filters = [
|
$filters = [
|
||||||
"[0:a]aformat=channel_layouts=mono,showwaves=s={$this->dimensions['soundwaves']['width']}x{$this->dimensions['soundwaves']['height']}:mode=cline:rate=10:colors=0xFFFFFF,format=yuva420p[waves]",
|
"[0:a]aformat=channel_layouts=mono,showwaves=s={$this->dimensions['soundwaves']['width']}x{$this->dimensions['soundwaves']['height']}:mode=cline:rate=10:colors=white,format=rgb24[waves]",
|
||||||
"[waves]scale={$this->dimensions['width']}:{$this->dimensions['height']}:flags=neighbor[resizedwaves]",
|
"[waves]scale={$this->dimensions['width']}:{$this->dimensions['height']}:flags=neighbor[resizedwaves]",
|
||||||
'[resizedwaves][3:v][4:v][5:v]threshold[cleanwaves]',
|
'[resizedwaves][3:v][4:v][5:v]threshold[cleanwaves]',
|
||||||
'[cleanwaves][2:v]alphamerge[waves_t]',
|
'[cleanwaves][2:v]alphamerge[waves_t]',
|
||||||
'[4:v][waves_t]overlay=x=0:y=0:shortest=1[waves_t2]',
|
'[4:v][waves_t]overlay=x=0:y=0:shortest=1[waves_t2]',
|
||||||
'[waves_t2]split[m][a]',
|
'[waves_t2]split[m][a]',
|
||||||
'[m][a]alphamerge[waves_t3]',
|
'[m][a]alphamerge[waves_t3]',
|
||||||
"[waves_t3]scale={$this->dimensions['soundwaves']['rescaleWidth']}:{$this->dimensions['soundwaves']['rescaleHeight']}[waves_final]",
|
"[waves_t3]scale={$this->dimensions['soundwaves']['rescaleWidth']}:{$this->dimensions['soundwaves']['rescaleHeight']},lutrgb=r='if(gt(val,100),{$this->colors['soundwaves'][0]},val)':g='if(gt(val,100),{$this->colors['soundwaves'][1]},val)':b='if(gt(val,100),{$this->colors['soundwaves'][2]},val)'[waves_final]",
|
||||||
"[1:v][waves_final]overlay=x={$this->dimensions['soundwaves']['x']}:y={$this->dimensions['soundwaves']['y']}:shortest=1,drawtext=fontfile=" . $this->getFont(
|
"[1:v][waves_final]overlay=x={$this->dimensions['soundwaves']['x']}:y={$this->dimensions['soundwaves']['y']}:shortest=1,drawtext=fontfile=" . $this->getFont(
|
||||||
'timestamp'
|
'timestamp'
|
||||||
) . ":text='%{pts\:gmtime\:{$this->start}\:%H\\\\\\\\\\:%M\\\\\\\\\\:%S\}':x={$this->dimensions['timestamp']['x']}:y={$this->dimensions['timestamp']['y']}:fontsize={$this->dimensions['timestamp']['fontsize']}:fontcolor=0x{$this->colors['timestampText']}:box=1:boxcolor=0x{$this->colors['timestampBg']}:boxborderw={$this->dimensions['timestamp']['padding']},format=yuv420p,colormatrix=bt601:bt2020[v3]",
|
) . ":text='%{pts\:gmtime\:{$this->start}\:%H\\\\\\\\\\:%M\\\\\\\\\\:%S\}':x={$this->dimensions['timestamp']['x']}:y={$this->dimensions['timestamp']['y']}:fontsize={$this->dimensions['timestamp']['fontsize']}:fontcolor=0x{$this->colors['timestampText']}:box=1:boxcolor=0x{$this->colors['timestampBg']}:boxborderw={$this->dimensions['timestamp']['padding']}[v3]",
|
||||||
"color=c=0x{$this->colors['progressbar']}:s={$this->dimensions['width']}x{$this->dimensions['progressbar']['height']}[progressbar]",
|
"color=c=0x{$this->colors['progressbar']}:s={$this->dimensions['width']}x{$this->dimensions['progressbar']['height']}[progressbar]",
|
||||||
"[v3][progressbar]overlay=-w+(w/{$this->duration})*t:0:shortest=1:format=rgb,subtitles={$this->subtitlesClipOutput}:fontsdir=" . config(
|
"[v3][progressbar]overlay=-w+(w/{$this->duration})*t:0:shortest=1:format=rgb,subtitles={$this->subtitlesClipOutput}:fontsdir=" . config(
|
||||||
'MediaClipper'
|
'MediaClipper'
|
||||||
)->fontsFolder . ":force_style='Fontname=" . self::FONTS['subtitles'] . ",Alignment=5,Fontsize={$this->dimensions['subtitles']['fontsize']},PrimaryColour=&H{$this->colors['subtitles']}&,BorderStyle=1,Outline=0,Shadow=0,MarginL={$this->dimensions['subtitles']['marginL']},MarginR={$this->dimensions['subtitles']['marginR']},MarginV={$this->dimensions['subtitles']['marginV']}',format=yuv420p,colormatrix=bt601:bt2020[outv]",
|
)->fontsFolder . ":force_style='Fontname=" . self::FONTS['subtitles'] . ",Alignment=5,Fontsize={$this->dimensions['subtitles']['fontsize']},PrimaryColour=&H{$this->colors['subtitles']}&,BorderStyle=1,Outline=0,Shadow=0,MarginL={$this->dimensions['subtitles']['marginL']},MarginR={$this->dimensions['subtitles']['marginR']},MarginV={$this->dimensions['subtitles']['marginV']}'[outv]",
|
||||||
|
"[6:v]scale={$this->dimensions['watermark']['width']}:{$this->dimensions['watermark']['height']}[watermark]",
|
||||||
|
"color=0x{$this->colors['watermarkBg']}:{$this->dimensions['watermark']['width']}x{$this->dimensions['watermark']['height']}[over]",
|
||||||
|
'[over][watermark]overlay=x=0:y=0:shortest=1[watermark_box]',
|
||||||
|
"[outv][watermark_box]overlay=x={$this->dimensions['watermark']['x']}:y={$this->dimensions['watermark']['y']}:shortest=1[watermarked]",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$watermark = config('MediaClipper')
|
||||||
|
->watermark;
|
||||||
|
|
||||||
$videoClipCmd = [
|
$videoClipCmd = [
|
||||||
'ffmpeg -y',
|
'ffmpeg -y',
|
||||||
"-i {$this->soundbiteOutput}",
|
"-i {$this->soundbiteOutput}",
|
||||||
@ -156,13 +157,13 @@ class VideoClip
|
|||||||
"-loop 1 -framerate 30 -i {$this->dimensions['soundwaves']['mask']}",
|
"-loop 1 -framerate 30 -i {$this->dimensions['soundwaves']['mask']}",
|
||||||
"-f lavfi -i color=gray:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
"-f lavfi -i color=gray:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
||||||
"-f lavfi -i color=black:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
"-f lavfi -i color=black:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
||||||
"-f lavfi -i color=0x{$this->colors['soundwaves']}:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
"-f lavfi -i color=white:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
||||||
|
"-loop 1 -framerate 1 -i {$watermark}",
|
||||||
'-filter_complex "' . implode(';', $filters) . '"',
|
'-filter_complex "' . implode(';', $filters) . '"',
|
||||||
'-map "[outv]"',
|
'-map "[watermarked]"',
|
||||||
'-map 0:a',
|
'-map 0:a',
|
||||||
'-acodec copy',
|
'-acodec copy',
|
||||||
'-vcodec libx264',
|
'-vcodec libx264rgb',
|
||||||
'-pix_fmt yuv420p',
|
|
||||||
"{$this->videoClipOutput}",
|
"{$this->videoClipOutput}",
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -306,10 +307,16 @@ class VideoClip
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
imagefilter($quotes, IMG_FILTER_COLORIZE, ...$this->colors['quotes']);
|
$cleanedQuotes = $this->cleanTransparency($quotes);
|
||||||
|
|
||||||
|
if (! $cleanedQuotes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
imagefilter($cleanedQuotes, IMG_FILTER_CONTRAST, 255);
|
||||||
|
imagefilter($cleanedQuotes, IMG_FILTER_COLORIZE, ...$this->colors['quotes']);
|
||||||
|
|
||||||
$scaledQuotes = $this->scaleImage(
|
$scaledQuotes = $this->scaleImage(
|
||||||
$quotes,
|
$cleanedQuotes,
|
||||||
$this->dimensions['quotes']['width'],
|
$this->dimensions['quotes']['width'],
|
||||||
$this->dimensions['quotes']['height']
|
$this->dimensions['quotes']['height']
|
||||||
);
|
);
|
||||||
@ -318,6 +325,7 @@ class VideoClip
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imagesavealpha($scaledQuotes, true);
|
||||||
$this->overlayImages(
|
$this->overlayImages(
|
||||||
$background,
|
$background,
|
||||||
$scaledQuotes,
|
$scaledQuotes,
|
||||||
@ -570,6 +578,32 @@ class VideoClip
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This helps getting a truly transparent background for images with transparency:
|
||||||
|
* https://stackoverflow.com/a/2611911
|
||||||
|
*/
|
||||||
|
private function cleanTransparency(GdImage $image): GdImage | false
|
||||||
|
{
|
||||||
|
$imageBg = imagecolorallocate($image, 0, 0, 0);
|
||||||
|
if ($imageBg === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// removing the black from the image
|
||||||
|
imagecolortransparent($image, $imageBg);
|
||||||
|
|
||||||
|
// turning off alpha blending (to ensure alpha channel information
|
||||||
|
// is preserved, rather than removed (blending with the rest of the
|
||||||
|
// image in the form of black))
|
||||||
|
imagealphablending($image, false);
|
||||||
|
|
||||||
|
// turning on alpha channel information saving (to ensure the full range
|
||||||
|
// of transparency is preserved)
|
||||||
|
imagesavealpha($image, true);
|
||||||
|
|
||||||
|
return $image;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, string>|false
|
* @return array<int, string>|false
|
||||||
*/
|
*/
|
||||||
|
BIN
app/Libraries/MediaClipper/castopod-logo.png
Normal file
BIN
app/Libraries/MediaClipper/castopod-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
@ -8,6 +8,8 @@ class ColorRadioButton extends FormComponent
|
|||||||
{
|
{
|
||||||
protected bool $isChecked = false;
|
protected bool $isChecked = false;
|
||||||
|
|
||||||
|
protected string $style = '';
|
||||||
|
|
||||||
public function setIsChecked(string $value): void
|
public function setIsChecked(string $value): void
|
||||||
{
|
{
|
||||||
$this->isChecked = $value === 'true';
|
$this->isChecked = $value === 'true';
|
||||||
@ -26,7 +28,7 @@ class ColorRadioButton extends FormComponent
|
|||||||
);
|
);
|
||||||
|
|
||||||
return <<<HTML
|
return <<<HTML
|
||||||
<div class="{$this->class}">
|
<div class="{$this->class}" style="{$this->style}">
|
||||||
{$radioInput}
|
{$radioInput}
|
||||||
<label for="{$this->value}" title="{$this->slot}" data-tooltip="bottom"></label>
|
<label for="{$this->value}" title="{$this->slot}" data-tooltip="bottom"></label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,12 +29,13 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="grid gap-4 grid-cols-colorButtons">
|
<div class="grid gap-4 grid-cols-colorButtons">
|
||||||
<?php foreach (config('Colors')->themes as $themeName => $color): ?>
|
<?php foreach (config('MediaClipper')->themes as $themeName => $colors): ?>
|
||||||
<Forms.ColorRadioButton
|
<Forms.ColorRadioButton
|
||||||
class="theme-<?= $themeName ?> mx-auto"
|
class="mx-auto"
|
||||||
value="<?= $themeName ?>"
|
value="<?= $themeName ?>"
|
||||||
name="theme"
|
name="theme"
|
||||||
isChecked="<?= $themeName === 'pine' ? 'true' : 'false' ?>" ><?= lang('Settings.theme.' . $themeName) ?></Forms.ColorRadioButton>
|
isChecked="<?= $themeName === 'pine' ? 'true' : 'false' ?>"
|
||||||
|
style="--color-accent-base: <?= $colors['preview']?>"><?= lang('Settings.theme.' . $themeName) ?></Forms.ColorRadioButton>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user