mirror of
https://code.castopod.org/adaures/castopod
synced 2025-05-24 02:52:01 +00:00
feat: add audio-clipper toolbar + add video-clip-previewer
This commit is contained in:
parent
01a09dc447
commit
02557539e6
@ -18,6 +18,7 @@ import Soundbites from "./modules/Soundbites";
|
|||||||
import ThemePicker from "./modules/ThemePicker";
|
import ThemePicker from "./modules/ThemePicker";
|
||||||
import Time from "./modules/Time";
|
import Time from "./modules/Time";
|
||||||
import Tooltip from "./modules/Tooltip";
|
import Tooltip from "./modules/Tooltip";
|
||||||
|
import "./modules/video-clip-previewer";
|
||||||
import "./modules/xml-editor";
|
import "./modules/xml-editor";
|
||||||
|
|
||||||
Dropdown();
|
Dropdown();
|
||||||
|
@ -48,6 +48,9 @@ export class AudioClipper extends LitElement {
|
|||||||
@query("#waveform")
|
@query("#waveform")
|
||||||
_waveformNode!: HTMLDivElement;
|
_waveformNode!: HTMLDivElement;
|
||||||
|
|
||||||
|
@query(".buffering-bar")
|
||||||
|
_bufferingBarNode!: HTMLCanvasElement;
|
||||||
|
|
||||||
@property({ type: Number, attribute: "start-time" })
|
@property({ type: Number, attribute: "start-time" })
|
||||||
initStartTime = 0;
|
initStartTime = 0;
|
||||||
|
|
||||||
@ -87,15 +90,15 @@ export class AudioClipper extends LitElement {
|
|||||||
@state()
|
@state()
|
||||||
_volume = 0.5;
|
_volume = 0.5;
|
||||||
|
|
||||||
@state()
|
|
||||||
_isLoading = false;
|
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
_seekingTime: number | null = null;
|
_seekingTime: number | null = null;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
_wavesurfer!: WaveSurfer;
|
_wavesurfer!: WaveSurfer;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
_isBuffering = false;
|
||||||
|
|
||||||
_windowEvents: EventElement[] = [
|
_windowEvents: EventElement[] = [
|
||||||
{
|
{
|
||||||
events: ["load", "resize"],
|
events: ["load", "resize"],
|
||||||
@ -144,21 +147,46 @@ export class AudioClipper extends LitElement {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
events: ["complete"],
|
events: ["progress"],
|
||||||
onEvent: () => {
|
onEvent: () => {
|
||||||
this._isLoading = false;
|
const context = this._bufferingBarNode.getContext("2d");
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
context.fillStyle = "lightgray";
|
||||||
|
context.fillRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this._bufferingBarNode.width,
|
||||||
|
this._bufferingBarNode.height
|
||||||
|
);
|
||||||
|
context.fillStyle = "#04AC64";
|
||||||
|
|
||||||
|
const inc = this._bufferingBarNode.width / this._audio[0].duration;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._audio[0].buffered.length; i++) {
|
||||||
|
const startX = this._audio[0].buffered.start(i) * inc;
|
||||||
|
const endX = this._audio[0].buffered.end(i) * inc;
|
||||||
|
const width = endX - startX;
|
||||||
|
|
||||||
|
context.fillRect(startX, 0, width, this._bufferingBarNode.height);
|
||||||
|
context.rect(startX, 0, width, this._bufferingBarNode.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
events: ["timeupdate"],
|
events: ["timeupdate"],
|
||||||
onEvent: () => {
|
onEvent: () => {
|
||||||
// TODO: change this
|
// TODO: change this?
|
||||||
this._currentTime = this._audio[0].currentTime;
|
this._currentTime = parseFloat(this._audio[0].currentTime.toFixed(3));
|
||||||
if (this._currentTime > this._clip.endTime) {
|
if (this._currentTime > this._clip.endTime) {
|
||||||
this.pause();
|
this.pause();
|
||||||
|
this._audio[0].currentTime = this._clip.endTime;
|
||||||
} else if (this._currentTime < this._clip.startTime) {
|
} else if (this._currentTime < this._clip.startTime) {
|
||||||
|
this._isBuffering = true;
|
||||||
this._audio[0].currentTime = this._clip.startTime;
|
this._audio[0].currentTime = this._clip.startTime;
|
||||||
} else {
|
} else {
|
||||||
|
this._isBuffering = false;
|
||||||
this.setCurrentTime(this._currentTime);
|
this.setCurrentTime(this._currentTime);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -178,17 +206,18 @@ export class AudioClipper extends LitElement {
|
|||||||
protected firstUpdated(): void {
|
protected firstUpdated(): void {
|
||||||
this._audioDuration = this._audio[0].duration;
|
this._audioDuration = this._audio[0].duration;
|
||||||
this._audio[0].volume = this._volume;
|
this._audio[0].volume = this._volume;
|
||||||
this._audio[0].currentTime = this._clip.startTime;
|
this._startTimeInput[0].hidden = true;
|
||||||
this._isLoading = true;
|
this._durationInput[0].hidden = true;
|
||||||
|
|
||||||
this._wavesurfer = WaveSurfer.create({
|
this._wavesurfer = WaveSurfer.create({
|
||||||
container: this._waveformNode,
|
container: this._waveformNode,
|
||||||
height: this.height,
|
height: this.height,
|
||||||
interact: false,
|
interact: false,
|
||||||
barWidth: 4,
|
barWidth: 2,
|
||||||
barHeight: 1,
|
barHeight: 1,
|
||||||
barGap: 4,
|
// barGap: 4,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
waveColor: "hsl(0 5% 85%)",
|
||||||
cursorColor: "transparent",
|
cursorColor: "transparent",
|
||||||
});
|
});
|
||||||
this._wavesurfer.load(this._audio[0].src);
|
this._wavesurfer.load(this._audio[0].src);
|
||||||
@ -266,6 +295,11 @@ export class AudioClipper extends LitElement {
|
|||||||
if (_changedProperties.has("_clip")) {
|
if (_changedProperties.has("_clip")) {
|
||||||
this.pause();
|
this.pause();
|
||||||
this.setSegmentPosition();
|
this.setSegmentPosition();
|
||||||
|
|
||||||
|
this._startTimeInput[0].value = this._clip.startTime.toString();
|
||||||
|
this._durationInput[0].value = (
|
||||||
|
this._clip.endTime - this._clip.startTime
|
||||||
|
).toFixed(3);
|
||||||
this._audio[0].currentTime = this._clip.startTime;
|
this._audio[0].currentTime = this._clip.startTime;
|
||||||
}
|
}
|
||||||
if (_changedProperties.has("_seekingTime")) {
|
if (_changedProperties.has("_seekingTime")) {
|
||||||
@ -293,18 +327,16 @@ export class AudioClipper extends LitElement {
|
|||||||
|
|
||||||
switch (this._action) {
|
switch (this._action) {
|
||||||
case ACTIONS.StretchLeft: {
|
case ACTIONS.StretchLeft: {
|
||||||
let startTime;
|
let startTime = 0;
|
||||||
if (seconds > 0) {
|
if (seconds > 0) {
|
||||||
if (seconds > this._clip.endTime - this.minDuration) {
|
if (seconds > this._clip.endTime - this.minDuration) {
|
||||||
startTime = this._clip.endTime - this.minDuration;
|
startTime = this._clip.endTime - this.minDuration;
|
||||||
} else {
|
} else {
|
||||||
startTime = seconds;
|
startTime = seconds;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
startTime = 0;
|
|
||||||
}
|
}
|
||||||
this._clip = {
|
this._clip = {
|
||||||
startTime,
|
startTime: parseFloat(startTime.toFixed(3)),
|
||||||
endTime: this._clip.endTime,
|
endTime: this._clip.endTime,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@ -323,7 +355,7 @@ export class AudioClipper extends LitElement {
|
|||||||
|
|
||||||
this._clip = {
|
this._clip = {
|
||||||
startTime: this._clip.startTime,
|
startTime: this._clip.startTime,
|
||||||
endTime,
|
endTime: parseFloat(endTime.toFixed(3)),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -333,7 +365,7 @@ export class AudioClipper extends LitElement {
|
|||||||
} else if (seconds > this._clip.endTime) {
|
} else if (seconds > this._clip.endTime) {
|
||||||
this._seekingTime = this._clip.endTime;
|
this._seekingTime = this._clip.endTime;
|
||||||
} else {
|
} else {
|
||||||
this._seekingTime = seconds;
|
this._seekingTime = parseFloat(seconds.toFixed(3));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -386,6 +418,20 @@ export class AudioClipper extends LitElement {
|
|||||||
return new Date(seconds * 1000).toISOString().substr(11, 8);
|
return new Date(seconds * 1000).toISOString().substr(11, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trim(side: "start" | "end") {
|
||||||
|
if (side === "start") {
|
||||||
|
this._clip = {
|
||||||
|
startTime: this._audio[0].currentTime,
|
||||||
|
endTime: this._clip.endTime,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._clip = {
|
||||||
|
startTime: this._clip.startTime,
|
||||||
|
endTime: this._currentTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.slider-wrapper {
|
.slider-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -393,6 +439,15 @@ export class AudioClipper extends LitElement {
|
|||||||
background-color: #0f172a;
|
background-color: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buffering-bar {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: gray;
|
||||||
|
bottom: -4px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
@ -404,12 +459,6 @@ export class AudioClipper extends LitElement {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider__track-placeholder {
|
|
||||||
width: 100%;
|
|
||||||
height: 8px;
|
|
||||||
background-color: #64748b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider__segment--wrapper {
|
.slider__segment--wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -418,14 +467,17 @@ export class AudioClipper extends LitElement {
|
|||||||
.slider__segment {
|
.slider__segment {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 120%;
|
||||||
|
top: -10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider__segment-content {
|
.slider__segment-content {
|
||||||
|
box-sizing: border-box;
|
||||||
background-color: rgba(255, 255, 255, 0.5);
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
border: none;
|
border-top: 2px dashed #b91c1c;
|
||||||
|
border-bottom: 2px dashed #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider__seeking-placeholder {
|
.slider__seeking-placeholder {
|
||||||
@ -441,8 +493,9 @@ export class AudioClipper extends LitElement {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
top: -23px;
|
top: -50%;
|
||||||
left: -10px;
|
left: -10px;
|
||||||
|
margin-top: -2px;
|
||||||
background-color: #3b82f6;
|
background-color: #3b82f6;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
@ -453,7 +506,7 @@ export class AudioClipper extends LitElement {
|
|||||||
width: 0px;
|
width: 0px;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
bottom: -12px;
|
bottom: -12px;
|
||||||
left: 1px;
|
left: 0;
|
||||||
border: 10px solid transparent;
|
border: 10px solid transparent;
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
@ -464,7 +517,7 @@ export class AudioClipper extends LitElement {
|
|||||||
.slider__segment .slider__segment-handle {
|
.slider__segment .slider__segment-handle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 120%;
|
height: 100%;
|
||||||
background-color: #b91c1c;
|
background-color: #b91c1c;
|
||||||
border: none;
|
border: none;
|
||||||
margin: auto 0;
|
margin: auto 0;
|
||||||
@ -475,7 +528,7 @@ export class AudioClipper extends LitElement {
|
|||||||
.slider__segment .slider__segment-handle::before {
|
.slider__segment .slider__segment-handle::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 3rem;
|
height: 50%;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -487,12 +540,79 @@ export class AudioClipper extends LitElement {
|
|||||||
|
|
||||||
.slider__segment .clipper__handle-left {
|
.slider__segment .clipper__handle-left {
|
||||||
left: -1rem;
|
left: -1rem;
|
||||||
border-radius: 0.2rem 9999px 9999px 0.2rem;
|
border-radius: 0.2rem 0 0 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider__segment .clipper__handle-right {
|
.slider__segment .clipper__handle-right {
|
||||||
right: -1rem;
|
right: -1rem;
|
||||||
border-radius: 9999px 0.2rem 0.2rem 9999px;
|
border-radius: 0 0.2rem 0.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem 0.5rem 0.25rem 0.5rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: hsl(var(--color-background-elevated));
|
||||||
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||||
|
border-radius: 0 0 0.25rem 0.25rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar__audio-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar .toolbar__play-button {
|
||||||
|
padding: 0.5rem;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar__trim-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar button {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: hsl(var(--color-accent-base));
|
||||||
|
color: hsl(var(--color-accent-contrast));
|
||||||
|
border-radius: 9999px;
|
||||||
|
border: none;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-spin {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume {
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: hsl(var(--color-accent-base));
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-slider {
|
||||||
|
accent-color: hsl(var(--color-accent-base));
|
||||||
|
width: 100px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -501,23 +621,9 @@ export class AudioClipper extends LitElement {
|
|||||||
<slot name="audio"></slot>
|
<slot name="audio"></slot>
|
||||||
<slot name="start_time"></slot>
|
<slot name="start_time"></slot>
|
||||||
<slot name="duration"></slot>
|
<slot name="duration"></slot>
|
||||||
<div>${this.secondsToHHMMSS(this._clip.startTime)}</div>
|
|
||||||
<div>${this.secondsToHHMMSS(this._currentTime)}</div>
|
|
||||||
<div>${this.secondsToHHMMSS(this._clip.endTime)}</div>
|
|
||||||
<div>${this._isLoading ? "loading..." : "not loading"}</div>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
id="volume"
|
|
||||||
min="0"
|
|
||||||
max="1"
|
|
||||||
step="0.1"
|
|
||||||
value="${this._volume}"
|
|
||||||
@change="${this.setVolume}"
|
|
||||||
/>
|
|
||||||
<div class="slider-wrapper" style="height:${this.height}">
|
<div class="slider-wrapper" style="height:${this.height}">
|
||||||
<div id="waveform"></div>
|
<div id="waveform"></div>
|
||||||
<div class="slider" role="slider">
|
<div class="slider" role="slider">
|
||||||
<div class="slider__track-placeholder"></div>
|
|
||||||
<div class="slider__segment--wrapper">
|
<div class="slider__segment--wrapper">
|
||||||
<div
|
<div
|
||||||
class="slider__segment-progress-handle"
|
class="slider__segment-progress-handle"
|
||||||
@ -543,9 +649,36 @@ export class AudioClipper extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<canvas class="buffering-bar"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<button @click="${this._isPlaying ? this.pause : this.play}">
|
<div class="toolbar">
|
||||||
${this._isPlaying
|
<div class="toolbar__audio-controls">
|
||||||
|
<button
|
||||||
|
class="toolbar__play-button"
|
||||||
|
@click="${this._isPlaying ? this.pause : this.play}"
|
||||||
|
>
|
||||||
|
${this._isBuffering
|
||||||
|
? html`<svg
|
||||||
|
class="animate-spin"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
opacity="0.25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
opacity="0.75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>`
|
||||||
|
: this._isPlaying
|
||||||
? html`<svg
|
? html`<svg
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@ -569,6 +702,37 @@ export class AudioClipper extends LitElement {
|
|||||||
/>
|
/>
|
||||||
</svg>`}
|
</svg>`}
|
||||||
</button>
|
</button>
|
||||||
|
<div class="volume">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M8.889 16H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387L8.89 16zm9.974.591l-1.422-1.422A3.993 3.993 0 0 0 19 12c0-1.43-.75-2.685-1.88-3.392l1.439-1.439A5.991 5.991 0 0 1 21 12c0 1.842-.83 3.49-2.137 4.591z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<input
|
||||||
|
class="range-slider"
|
||||||
|
type="range"
|
||||||
|
id="volume"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.1"
|
||||||
|
value="${this._volume}"
|
||||||
|
@change="${this.setVolume}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar__trim-controls">
|
||||||
|
<button @click="${() => this.trim("start")}">Trim start</button>
|
||||||
|
<button @click="${() => this.trim("end")}">Trim end</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
58
app/Resources/js/modules/video-clip-previewer.ts
Normal file
58
app/Resources/js/modules/video-clip-previewer.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, queryAssignedNodes } from "lit/decorators.js";
|
||||||
|
import { styleMap } from "lit/directives/style-map.js";
|
||||||
|
|
||||||
|
enum VideoFormats {
|
||||||
|
Landscape = "landscape",
|
||||||
|
Portrait = "portrait",
|
||||||
|
Squared = "squared",
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatMap = {
|
||||||
|
[VideoFormats.Landscape]: "16/9",
|
||||||
|
[VideoFormats.Portrait]: "9/16",
|
||||||
|
[VideoFormats.Squared]: "1/1",
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("video-clip-previewer")
|
||||||
|
export class VideoClipPreviewer extends LitElement {
|
||||||
|
@queryAssignedNodes("preview_image", true)
|
||||||
|
_previewImage!: NodeListOf<HTMLImageElement>;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
format: VideoFormats = VideoFormats.Landscape;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
theme = "#009486";
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.video-background {
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: black;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-format {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
render(): TemplateResult<1> {
|
||||||
|
const styles = {
|
||||||
|
aspectRatio: formatMap[this.format],
|
||||||
|
backgroundColor: this.theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
return html`<div class="video-background">
|
||||||
|
<div class="video-format" style=${styleMap(styles)}>
|
||||||
|
<slot name="preview_image"></slot>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
@ -10,12 +10,14 @@
|
|||||||
|
|
||||||
<?= $this->section('content') ?>
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
<form action="<?= route_to('video-clips-create', $podcast->id, $episode->id) ?>" method="POST" class="flex gap-4">
|
<form action="<?= route_to('video-clips-create', $podcast->id, $episode->id) ?>" method="POST" class="flex flex-col items-center gap-4 xl:items-start xl:flex-row">
|
||||||
|
|
||||||
<div class="flex-1 w-full">
|
<div class="flex-1 w-full">
|
||||||
<!-- <div class="h-full bg-black"></div> -->
|
<video-clip-previewer format="portrait">
|
||||||
|
<img slot="preview_image" src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->cover->description ?>" />
|
||||||
|
</video-clip-previewer>
|
||||||
<audio-clipper start-time="15" duration="10" min-duration="10" volume=".25" height="50">
|
<audio-clipper start-time="15" duration="10" min-duration="10" volume=".25" height="50">
|
||||||
<audio slot="audio" src="<?= $episode->audio->file_url ?>" class="w-full">
|
<audio slot="audio" src="<?= $episode->audio->file_url ?>" class="w-full" preload="auto">
|
||||||
Your browser does not support the <code>audio</code> element.
|
Your browser does not support the <code>audio</code> element.
|
||||||
</audio>
|
</audio>
|
||||||
<input slot="start_time" type="number" name="start_time" placeholder="<?= lang('VideoClip.form.start_time') ?>" step="0.001" />
|
<input slot="start_time" type="number" name="start_time" placeholder="<?= lang('VideoClip.form.start_time') ?>" step="0.001" />
|
||||||
@ -23,7 +25,7 @@
|
|||||||
</audio-clipper>
|
</audio-clipper>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <Forms.Section title="<?= lang('VideoClip.form.params_section_title') ?>" >
|
<Forms.Section title="<?= lang('VideoClip.form.params_section_title') ?>" >
|
||||||
|
|
||||||
<Forms.Field
|
<Forms.Field
|
||||||
name="label"
|
name="label"
|
||||||
@ -62,7 +64,7 @@
|
|||||||
|
|
||||||
<Button variant="primary" type="submit" iconRight="arrow-right" class="self-end"><?= lang('VideoClip.form.submit') ?></Button>
|
<Button variant="primary" type="submit" iconRight="arrow-right" class="self-end"><?= lang('VideoClip.form.submit') ?></Button>
|
||||||
|
|
||||||
</Forms.Section> -->
|
</Forms.Section>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user