mirror of
https://code.castopod.org/adaures/castopod.git
synced 2024-09-30 05:31:22 +02:00
feat(video-clips): replace hardcoded colors with config's theme colors
This commit is contained in:
parent
827ca03f61
commit
e462abf6d6
@ -203,4 +203,24 @@ class MediaClipper extends BaseConfig
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, string|int[]>>
|
||||||
|
*/
|
||||||
|
public array $themes = [
|
||||||
|
'pine' => [
|
||||||
|
'background' => [0, 86, 74],
|
||||||
|
'text' => [255, 255, 255],
|
||||||
|
// subtitle hex color is BGR (Blue, Green, Red),
|
||||||
|
'subtitles' => 'FFFFFF',
|
||||||
|
// quotes image MUST BE black
|
||||||
|
'quotes' => [0, 148, 134],
|
||||||
|
'episodeNumberingBg' => [0, 61, 11],
|
||||||
|
'episodeNumberingText' => [255, 255, 255],
|
||||||
|
'progressbar' => '009486',
|
||||||
|
'timestampBg' => '00564A',
|
||||||
|
'timestampText' => 'FFFFFF',
|
||||||
|
'soundwaves' => 'F2FAF9',
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -44,32 +44,46 @@ class VideoClip
|
|||||||
|
|
||||||
protected ?string $episodeNumbering = null;
|
protected ?string $episodeNumbering = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, mixed>
|
|
||||||
*/
|
|
||||||
protected array $dimensions = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var 'landscape'|'portrait'|'squared'
|
* @var 'landscape'|'portrait'|'squared'
|
||||||
*/
|
*/
|
||||||
protected string $format = 'landscape';
|
protected string $format = 'landscape';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
protected array $dimensions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var 'pine'|'crimson'|'lake'|'amber'|'jacaranda'|'onyx'
|
||||||
|
*/
|
||||||
|
protected string $theme = 'pine';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
protected array $colors = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param 'landscape'|'portrait'|'squared' $format
|
* @param 'landscape'|'portrait'|'squared' $format
|
||||||
|
* @param 'pine'|'crimson'|'lake'|'amber'|'jacaranda'|'onyx' $theme
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Episode $episode,
|
protected Episode $episode,
|
||||||
protected float $start,
|
protected float $start,
|
||||||
protected float $end,
|
protected float $end,
|
||||||
string $format,
|
string $format,
|
||||||
|
string $theme,
|
||||||
) {
|
) {
|
||||||
$this->duration = $end - $start;
|
$this->duration = $end - $start;
|
||||||
$this->format = $format;
|
$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];
|
||||||
|
$this->colors = config('MediaClipper')
|
||||||
|
->themes[$theme];
|
||||||
|
|
||||||
helper('media');
|
helper(['media']);
|
||||||
|
|
||||||
$this->audioInput = media_path($this->episode->audio_file_path);
|
$this->audioInput = media_path($this->episode->audio_file_path);
|
||||||
$this->episodeCoverPath = media_path($this->episode->cover->path);
|
$this->episodeCoverPath = media_path($this->episode->cover->path);
|
||||||
@ -118,7 +132,7 @@ 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=white,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=0xFFFFFF,format=yuva420p[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]',
|
||||||
@ -128,11 +142,11 @@ class VideoClip
|
|||||||
"[waves_t3]scale={$this->dimensions['soundwaves']['rescaleWidth']}:{$this->dimensions['soundwaves']['rescaleHeight']}[waves_final]",
|
"[waves_t3]scale={$this->dimensions['soundwaves']['rescaleWidth']}:{$this->dimensions['soundwaves']['rescaleHeight']}[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=white:box=1:boxcolor=0x00564A:boxborderw={$this->dimensions['timestamp']['padding']}[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']},format=yuv420p,colormatrix=bt601:bt2020[v3]",
|
||||||
"color=c=0x009486: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']},BorderStyle=1,Outline=0,Shadow=0,MarginL={$this->dimensions['subtitles']['marginL']},MarginR={$this->dimensions['subtitles']['marginR']},MarginV={$this->dimensions['subtitles']['marginV']}'[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']}',format=yuv420p,colormatrix=bt601:bt2020[outv]",
|
||||||
];
|
];
|
||||||
|
|
||||||
$videoClipCmd = [
|
$videoClipCmd = [
|
||||||
@ -142,12 +156,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=white:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
"-f lavfi -i color=0x{$this->colors['soundwaves']}:{$this->dimensions['width']}x{$this->dimensions['height']}",
|
||||||
'-filter_complex "' . implode(';', $filters) . '"',
|
'-filter_complex "' . implode(';', $filters) . '"',
|
||||||
'-map "[outv]"',
|
'-map "[outv]"',
|
||||||
'-map 0:a',
|
'-map 0:a',
|
||||||
'-acodec copy',
|
'-acodec copy',
|
||||||
'-vcodec libx264',
|
'-vcodec libx264',
|
||||||
|
'-pix_fmt yuv420p',
|
||||||
"{$this->videoClipOutput}",
|
"{$this->videoClipOutput}",
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -184,7 +199,7 @@ class VideoClip
|
|||||||
|
|
||||||
private function generateVideoClipBg(): bool
|
private function generateVideoClipBg(): bool
|
||||||
{
|
{
|
||||||
$background = $this->generateColouredBg($this->dimensions['width'], $this->dimensions['height']);
|
$background = $this->generateBackground($this->dimensions['width'], $this->dimensions['height']);
|
||||||
|
|
||||||
if ($background === null) {
|
if ($background === null) {
|
||||||
return false;
|
return false;
|
||||||
@ -265,6 +280,8 @@ class VideoClip
|
|||||||
$this->episodeNumbering,
|
$this->episodeNumbering,
|
||||||
$this->getFont('episodeNumbering'),
|
$this->getFont('episodeNumbering'),
|
||||||
$this->dimensions['episodeNumbering']['fontsize'],
|
$this->dimensions['episodeNumbering']['fontsize'],
|
||||||
|
$this->colors['episodeNumberingText'],
|
||||||
|
$this->colors['episodeNumberingBg'],
|
||||||
$this->dimensions['episodeNumbering']['paddingX'],
|
$this->dimensions['episodeNumbering']['paddingX'],
|
||||||
$this->dimensions['episodeNumbering']['paddingY'],
|
$this->dimensions['episodeNumbering']['paddingY'],
|
||||||
);
|
);
|
||||||
@ -289,6 +306,8 @@ class VideoClip
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imagefilter($quotes, IMG_FILTER_COLORIZE, ...$this->colors['quotes']);
|
||||||
|
|
||||||
$scaledQuotes = $this->scaleImage(
|
$scaledQuotes = $this->scaleImage(
|
||||||
$quotes,
|
$quotes,
|
||||||
$this->dimensions['quotes']['width'],
|
$this->dimensions['quotes']['width'],
|
||||||
@ -319,7 +338,7 @@ class VideoClip
|
|||||||
return config('MediaClipper')->fontsFolder . self::FONTS[$name];
|
return config('MediaClipper')->fontsFolder . self::FONTS[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateColouredBg(int $width, int $height): ?GdImage
|
private function generateBackground(int $width, int $height): ?GdImage
|
||||||
{
|
{
|
||||||
$background = imagecreatetruecolor($width, $height);
|
$background = imagecreatetruecolor($width, $height);
|
||||||
|
|
||||||
@ -327,7 +346,7 @@ class VideoClip
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$coloredBackground = imagecolorallocate($background, 0, 86, 74);
|
$coloredBackground = imagecolorallocate($background, ...$this->colors['background']);
|
||||||
|
|
||||||
if ($coloredBackground === false) {
|
if ($coloredBackground === false) {
|
||||||
return null;
|
return null;
|
||||||
@ -450,9 +469,9 @@ class VideoClip
|
|||||||
int $paragraphIndent = 0,
|
int $paragraphIndent = 0,
|
||||||
): bool {
|
): bool {
|
||||||
// Allocate A Color For The Text
|
// Allocate A Color For The Text
|
||||||
$white = imagecolorallocate($image, 255, 255, 255);
|
$textColor = imagecolorallocate($image, ...$this->colors['text']);
|
||||||
|
|
||||||
if ($white === false) {
|
if ($textColor === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +489,7 @@ class VideoClip
|
|||||||
0,
|
0,
|
||||||
$x + ($paragraphIndent * ($i === 0 ? 1 : 0)),
|
$x + ($paragraphIndent * ($i === 0 ? 1 : 0)),
|
||||||
$y + $fontsize + ($leading * $i),
|
$y + $fontsize + ($leading * $i),
|
||||||
$white,
|
$textColor,
|
||||||
$fontPath,
|
$fontPath,
|
||||||
$line
|
$line
|
||||||
);
|
);
|
||||||
@ -510,6 +529,10 @@ class VideoClip
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int[] $boxTextColor
|
||||||
|
* @param int[] $boxBgColor
|
||||||
|
*/
|
||||||
private function addTextWithBox(
|
private function addTextWithBox(
|
||||||
GdImage $image,
|
GdImage $image,
|
||||||
int $x,
|
int $x,
|
||||||
@ -517,14 +540,16 @@ class VideoClip
|
|||||||
string $text,
|
string $text,
|
||||||
string $fontPath,
|
string $fontPath,
|
||||||
int $fontsize,
|
int $fontsize,
|
||||||
|
array $boxTextColor,
|
||||||
|
array $boxBgColor,
|
||||||
int $paddingX = 0,
|
int $paddingX = 0,
|
||||||
int $paddingY = 0,
|
int $paddingY = 0,
|
||||||
): bool {
|
): bool {
|
||||||
// Create some colors
|
// Create some colors
|
||||||
$white = imagecolorallocate($image, 255, 255, 255);
|
$textColor = imagecolorallocate($image, ...$boxTextColor);
|
||||||
$bgColor = imagecolorallocate($image, 0, 61, 11);
|
$bgColor = imagecolorallocate($image, ...$boxBgColor);
|
||||||
|
|
||||||
if ($white === false || $bgColor === false) {
|
if ($textColor === false || $bgColor === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +565,7 @@ class VideoClip
|
|||||||
$y2 = $y + $bbox['height'] + ($paddingY * 2);
|
$y2 = $y + $bbox['height'] + ($paddingY * 2);
|
||||||
|
|
||||||
imagefilledrectangle($image, $x, $y, $x2, $y2, $bgColor);
|
imagefilledrectangle($image, $x, $y, $x2, $y2, $bgColor);
|
||||||
imagettftext($image, $fontsize, 0, $x1, $y1, $white, $fontPath, $text);
|
imagettftext($image, $fontsize, 0, $x1, $y1, $textColor, $fontPath, $text);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.8 KiB |
6
app/Resources/icons/clapperboard.svg
Normal file
6
app/Resources/icons/clapperboard.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
<path d="M17.998 7l2.31-4h.7c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h3.006l-2.31 4h2.31l2.31-4h3.69l-2.31 4h2.31l2.31-4h3.69l-2.31 4h2.31z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 345 B |
@ -352,15 +352,23 @@ $routes->group(
|
|||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'video-clips',
|
'video-clips',
|
||||||
'ClipsController::videoClips/$1/$2',
|
'VideoClipsController::list/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'video-clips',
|
'as' => 'video-clips-list',
|
||||||
|
'filter' => 'permission:podcast_episodes-edit',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$routes->get(
|
||||||
|
'video-clips/new',
|
||||||
|
'VideoClipsController::generate/$1/$2',
|
||||||
|
[
|
||||||
|
'as' => 'video-clips-generate',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast_episodes-edit',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'video-clips',
|
'video-clips/new',
|
||||||
'ClipsController::generateVideoClip/$1/$2',
|
'VideoClipsController::attemptGenerate/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'video-clips-generate',
|
'as' => 'video-clips-generate',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast_episodes-edit',
|
||||||
|
@ -18,7 +18,7 @@ use CodeIgniter\Exceptions\PageNotFoundException;
|
|||||||
use CodeIgniter\HTTP\RedirectResponse;
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
use MediaClipper\VideoClip;
|
use MediaClipper\VideoClip;
|
||||||
|
|
||||||
class ClipsController extends BaseController
|
class VideoClipsController extends BaseController
|
||||||
{
|
{
|
||||||
protected Podcast $podcast;
|
protected Podcast $podcast;
|
||||||
|
|
||||||
@ -55,7 +55,21 @@ class ClipsController extends BaseController
|
|||||||
return $this->{$method}(...$params);
|
return $this->{$method}(...$params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function videoClips(): string
|
public function list(): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'episode' => $this->episode,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->title,
|
||||||
|
1 => $this->episode->title,
|
||||||
|
]);
|
||||||
|
return view('episode/video_clips_list', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate(): string
|
||||||
{
|
{
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
@ -66,18 +80,19 @@ class ClipsController extends BaseController
|
|||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->title,
|
||||||
1 => $this->episode->slug,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/video_clips', $data);
|
return view('episode/video_clips_new', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateVideoClip(): RedirectResponse
|
public function attemptGenerate(): RedirectResponse
|
||||||
{
|
{
|
||||||
// TODO: add end_time greater than start_time, with minimum ?
|
// TODO: add end_time greater than start_time, with minimum ?
|
||||||
$rules = [
|
$rules = [
|
||||||
'format' => 'required|in_list[landscape,portrait,squared]',
|
|
||||||
'start_time' => 'required|numeric',
|
'start_time' => 'required|numeric',
|
||||||
'end_time' => 'required|numeric|differs[start_time]',
|
'end_time' => 'required|numeric|differs[start_time]',
|
||||||
|
'format' => 'required|in_list[' . implode(',', array_keys(config('MediaClipper')->formats)) . ']',
|
||||||
|
'theme' => 'required|in_list[' . implode(',', array_keys(config('Colors')->themes)) . ']',
|
||||||
];
|
];
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
if (! $this->validate($rules)) {
|
||||||
@ -92,10 +107,11 @@ class ClipsController extends BaseController
|
|||||||
(float) $this->request->getPost('start_time'),
|
(float) $this->request->getPost('start_time'),
|
||||||
(float) $this->request->getPost('end_time',),
|
(float) $this->request->getPost('end_time',),
|
||||||
$this->request->getPost('format'),
|
$this->request->getPost('format'),
|
||||||
|
$this->request->getPost('theme'),
|
||||||
);
|
);
|
||||||
$clipper->generate();
|
$clipper->generate();
|
||||||
|
|
||||||
return redirect()->route('video-clips', [$this->podcast->id, $this->episode->id])->with(
|
return redirect()->route('video-clips-generate', [$this->podcast->id, $this->episode->id])->with(
|
||||||
'message',
|
'message',
|
||||||
lang('Settings.images.regenerationSuccess')
|
lang('Settings.images.regenerationSuccess')
|
||||||
);
|
);
|
@ -15,6 +15,8 @@ return [
|
|||||||
'episode-edit' => 'Edit episode',
|
'episode-edit' => 'Edit episode',
|
||||||
'episode-persons-manage' => 'Manage persons',
|
'episode-persons-manage' => 'Manage persons',
|
||||||
'embed-add' => 'Embeddable player',
|
'embed-add' => 'Embeddable player',
|
||||||
|
'clips' => 'Clips',
|
||||||
'soundbites-edit' => 'Soundbites',
|
'soundbites-edit' => 'Soundbites',
|
||||||
'video-clips' => 'Video clips',
|
'video-clips-list' => 'Video clips',
|
||||||
|
'video-clips-generate' => 'New video clip',
|
||||||
];
|
];
|
||||||
|
@ -15,5 +15,8 @@ return [
|
|||||||
'episode-edit' => 'Modifier l’épisode',
|
'episode-edit' => 'Modifier l’épisode',
|
||||||
'episode-persons-manage' => 'Gestion des intervenants',
|
'episode-persons-manage' => 'Gestion des intervenants',
|
||||||
'embed' => 'Lecteur intégré',
|
'embed' => 'Lecteur intégré',
|
||||||
|
'clips' => 'Extraits',
|
||||||
'soundbites-edit' => 'Extraits sonores',
|
'soundbites-edit' => 'Extraits sonores',
|
||||||
|
'video-clips-list' => 'Extraits video',
|
||||||
|
'video-clips-generate' => 'Nouvel extrait video',
|
||||||
];
|
];
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
$podcastNavigation = [
|
$podcastNavigation = [
|
||||||
'dashboard' => [
|
'dashboard' => [
|
||||||
'icon' => 'dashboard',
|
'icon' => 'dashboard',
|
||||||
'items' => ['episode-view', 'episode-edit', 'episode-persons-manage', 'embed-add', 'soundbites-edit', 'video-clips'],
|
'items' => ['episode-view', 'episode-edit', 'episode-persons-manage', 'embed-add'],
|
||||||
|
],
|
||||||
|
'clips' => [
|
||||||
|
'icon' => 'clapperboard',
|
||||||
|
'items' => ['video-clips-list', 'video-clips-generate', 'soundbites-edit'],
|
||||||
],
|
],
|
||||||
]; ?>
|
]; ?>
|
||||||
|
|
||||||
|
13
themes/cp_admin/episode/video_clips_list.php
Normal file
13
themes/cp_admin/episode/video_clips_list.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?= $this->extend('_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Episode.video_clips.title') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Episode.video_clips.title') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
|
<?= $this->endSection() ?>
|
@ -28,23 +28,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="grid gap-4 grid-cols-colorButtons">
|
||||||
|
<?php foreach (config('Colors')->themes as $themeName => $color): ?>
|
||||||
|
<Forms.ColorRadioButton
|
||||||
|
class="theme-<?= $themeName ?> mx-auto"
|
||||||
|
value="<?= $themeName ?>"
|
||||||
|
name="theme"
|
||||||
|
isChecked="<?= $themeName === 'pine' ? 'true' : 'false' ?>" ><?= lang('Settings.theme.' . $themeName) ?></Forms.ColorRadioButton>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Forms.Field
|
<Forms.Field
|
||||||
type="number"
|
type="number"
|
||||||
name="start_time"
|
name="start_time"
|
||||||
label="START"
|
label="START"
|
||||||
required="true"
|
required="true"
|
||||||
value="0"
|
value="5"
|
||||||
/>
|
/>
|
||||||
<Forms.Field
|
<Forms.Field
|
||||||
type="number"
|
type="number"
|
||||||
name="end_time"
|
name="end_time"
|
||||||
label="END"
|
label="END"
|
||||||
required="true"
|
required="true"
|
||||||
value="15"
|
value="10"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<audio></audio>
|
|
||||||
|
|
||||||
<Button variant="primary" type="submit"><?= lang('Episode.video_clips.submit') ?></Button>
|
<Button variant="primary" type="submit"><?= lang('Episode.video_clips.submit') ?></Button>
|
||||||
|
|
||||||
</form>
|
</form>
|
Loading…
Reference in New Issue
Block a user