Loading app/Resources/js/modules/audio-clipper.ts +256 −122 Original line number Diff line number Diff line Loading @@ -14,6 +14,11 @@ enum ACTIONS { Seek, } interface EventElement { events: string[]; onEvent: EventListener; } @customElement("audio-clipper") export class AudioClipper extends LitElement { @queryAssignedNodes("audio", true) Loading @@ -37,14 +42,17 @@ export class AudioClipper extends LitElement { @query(".slider__segment-progress-handle") _progressNode!: HTMLDivElement; @query(".slider__seeking-placeholder") _seekingNode!: HTMLDivElement; @query("#waveform") _waveformNode!: HTMLDivElement; @property({ type: Number, attribute: "start-time" }) startTime = 0; initStartTime = 0; @property({ type: Number }) duration = 10; @property({ type: Number, attribute: "duration" }) initDuration = 10; @property({ type: Number, attribute: "min-duration" }) minDuration = 5; Loading @@ -52,6 +60,9 @@ export class AudioClipper extends LitElement { @property({ type: Number, attribute: "volume" }) initVolume = 0.5; @property({ type: Number, attribute: "height" }) height = 100; @state() _isPlaying = false; Loading @@ -76,98 +87,159 @@ export class AudioClipper extends LitElement { @state() _volume = 0.5; @state() _isLoading = false; @state() _seekingTime: number | null = null; @state() _wavesurfer!: WaveSurfer; _windowEvents: EventElement[] = [ { events: ["load", "resize"], onEvent: () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }, }, ]; _documentEvents: EventElement[] = [ { events: ["mouseup"], onEvent: () => { if (this._action !== null) { document.body.style.cursor = ""; if (this._action === ACTIONS.Seek && this._seekingTime) { this._audio[0].currentTime = this._seekingTime; this._seekingTime = 0; } this._action = null; } }, }, { events: ["mousemove"], onEvent: (event: Event) => { if (this._action !== null) { this.updatePosition(event as MouseEvent); } }, }, ]; _audioEvents: EventElement[] = [ { events: ["play"], onEvent: () => { this._isPlaying = true; }, }, { events: ["pause"], onEvent: () => { this._isPlaying = false; }, }, { events: ["complete"], onEvent: () => { this._isLoading = false; }, }, { events: ["timeupdate"], onEvent: () => { // TODO: change this this._currentTime = this._audio[0].currentTime; if (this._currentTime > this._clip.endTime) { this.pause(); } else if (this._currentTime < this._clip.startTime) { this._audio[0].currentTime = this._clip.startTime; } else { this.setCurrentTime(this._currentTime); } }, }, ]; connectedCallback(): void { super.connectedCallback(); console.log("connectedCallback_before"); this._clip = { startTime: this.startTime, endTime: this.startTime + this.duration, startTime: this.initStartTime, endTime: this.initStartTime + this.initDuration, }; this._volume = this.initVolume; console.log("connectedCallback_after"); } protected firstUpdated(): void { console.log("firstUpdate"); this._audioDuration = this._audio[0].duration; this._audio[0].volume = this._volume; this._audio[0].currentTime = this._clip.startTime; this._isLoading = true; this._wavesurfer = WaveSurfer.create({ container: this._waveformNode, height: this.height, interact: false, barWidth: 2, barWidth: 4, barHeight: 1, barGap: 4, responsive: true, cursorColor: "transparent", }); this._wavesurfer.load(this._audio[0].src); window.addEventListener("load", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); window.addEventListener("resize", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); this.addEventListeners(); } document.addEventListener("mouseup", () => { if (this._action !== null) { this._action = null; disconnectedCallback(): void { super.disconnectedCallback(); this.removeEventListeners(); } addEventListeners(): void { for (const event of this._windowEvents) { event.events.forEach((name) => { window.addEventListener(name, event.onEvent); }); document.addEventListener("mousemove", (event: MouseEvent) => { if (this._action !== null) { this.updatePosition(event); } }); this._audio[0].addEventListener("play", () => { this._isPlaying = true; for (const event of this._documentEvents) { event.events.forEach((name) => { document.addEventListener(name, event.onEvent); }); this._audio[0].addEventListener("pause", () => { this._isPlaying = false; }); // this._audio[0].addEventListener("timeupdate", () => { // this._currentTime = this._audio[0].currentTime; // }); } disconnectedCallback(): void { console.log("disconnectedCallback"); window.removeEventListener("load", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); for (const event of this._audioEvents) { event.events.forEach((name) => { this._audio[0].addEventListener(name, event.onEvent); }); window.removeEventListener("resize", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); document.removeEventListener("mouseup", () => { if (this._action !== null) { this._action = null; } }); document.removeEventListener("mousemove", (event: MouseEvent) => { if (this._action !== null) { this.updatePosition(event); } removeEventListeners(): void { for (const event of this._windowEvents) { event.events.forEach((name) => { window.removeEventListener(name, event.onEvent); }); } this._audio[0].removeEventListener("play", () => { this._isPlaying = true; for (const event of this._documentEvents) { event.events.forEach((name) => { document.removeEventListener(name, event.onEvent); }); this._audio[0].removeEventListener("pause", () => { this._isPlaying = false; } for (const event of this._audioEvents) { event.events.forEach((name) => { this._audio[0].removeEventListener(name, event.onEvent); }); // this._audio[0].removeEventListener("timeupdate", () => { // this._currentTime = this._audio[0].currentTime; // }); } } setSegmentPosition(): void { Loading @@ -180,42 +252,38 @@ export class AudioClipper extends LitElement { }px`; } getPositionFromSeconds(seconds: number) { private getPositionFromSeconds(seconds: number) { return (seconds * this._sliderWidth) / this._audioDuration; } getSecondsFromPosition(position: number) { private getSecondsFromPosition(position: number) { return (this._audioDuration * position) / this._sliderWidth; } protected updated( _changedProperties: Map<string | number | symbol, unknown> ): void { // console.log("updated", _changedProperties); if (_changedProperties.has("_clip")) { // console.log("CLIP", _changedProperties.get("_clip")); this.pause(); this.setSegmentPosition(); console.log(this._clip.startTime); this._audio[0].currentTime = 58; console.log(this._audio[0].currentTime); this._audio[0].currentTime = this._clip.startTime; } if (_changedProperties.has("_seekingTime")) { if (this._seekingTime) { this._audio[0].currentTime = this._seekingTime; } } } play(): void { this._audio[0].play(); // setTimeout(() => { // this.pause(); // this._audio[0].currentTime = this._clip.startTime; // }, (this._clip.endTime - this._clip.startTime) * 1000); } pause(): void { this._audio[0].pause(); } updatePosition(event: MouseEvent): void { private updatePosition(event: MouseEvent): void { const cursorPosition = event.clientX - (this._sliderNode.getBoundingClientRect().left + Loading Loading @@ -260,7 +328,13 @@ export class AudioClipper extends LitElement { break; } case ACTIONS.Seek: { console.log("seeking"); if (seconds < this._clip.startTime) { this._seekingTime = this._clip.startTime; } else if (seconds > this._clip.endTime) { this._seekingTime = this._clip.endTime; } else { this._seekingTime = seconds; } break; } default: Loading @@ -268,37 +342,66 @@ export class AudioClipper extends LitElement { } } setVolume(event: InputEvent): void { this._volume = parseFloat((event.target as HTMLInputElement).value); this._audio[0].volume = this._volume; } setCurrentTime(event: MouseEvent): void { goTo(event: MouseEvent): void { const cursorPosition = event.clientX - (this._sliderNode.getBoundingClientRect().left + document.documentElement.scrollLeft); const seconds = this.getSecondsFromPosition(cursorPosition); this._audio[0].currentTime = seconds; } setVolume(event: InputEvent): void { this._volume = parseFloat((event.target as HTMLInputElement).value); this._audio[0].volume = this._volume; } setCurrentTime(currentTime: number): void { const seekingTimePosition = this.getPositionFromSeconds(currentTime); const startTimePosition = this.getPositionFromSeconds(this._clip.startTime); const seekingTimeSegmentPosition = seekingTimePosition - startTimePosition; const seekingTimePercentage = (seekingTimeSegmentPosition / this._segmentContentNode.clientWidth) * this._segmentContentNode.clientWidth; this._progressNode.style.transform = `translateX(${seekingTimeSegmentPosition}px)`; this._seekingNode.style.transform = `scaleX(${seekingTimePercentage})`; } setAction(action: ACTIONS): void { switch (action) { case ACTIONS.StretchLeft: case ACTIONS.StretchRight: document.body.style.cursor = "grabbing"; break; default: document.body.style.cursor = "default"; break; } this._action = action; } secondsToHHMMSS(seconds: number): string { private secondsToHHMMSS(seconds: number): string { return new Date(seconds * 1000).toISOString().substr(11, 8); } static styles = css` .slider { .slider-wrapper { position: relative; height: 6rem; width: 100%; background-color: #0f172a; } .slider { position: absolute; z-index: 10; top: 0; left: 0; display: flex; align-items: center; height: 100%; width: 100%; background-color: #0f172a; } .slider__track-placeholder { Loading @@ -309,37 +412,64 @@ export class AudioClipper extends LitElement { .slider__segment--wrapper { position: absolute; height: 100%; } .slider__segment { position: relative; display: flex; height: 100%; } .slider__segment-content { background-color: rgba(255, 255, 255, 0.9); height: 4rem; background-color: rgba(255, 255, 255, 0.5); height: 100%; width: 1px; border: none; } .slider__seeking-placeholder { position: absolute; pointer-events: none; background-color: rgba(255, 255, 255, 0.5); height: 100%; width: 1px; transform-origin: left; } .slider__segment-progress-handle { position: absolute; width: 9px; height: 9px; margin-top: -9px; margin-left: -4px; width: 20px; height: 20px; top: -23px; left: -10px; background-color: #3b82f6; cursor: pointer; border-radius: 50%; } .slider__segment-progress-handle::after { position: absolute; content: ""; width: 0px; height: 0px; bottom: -12px; left: 1px; border: 10px solid transparent; border-top-color: transparent; border-top-style: solid; border-top-width: 10px; border-top: 10px solid #3b82f6; } .slider__segment .slider__segment-handle { position: absolute; cursor: pointer; width: 1rem; height: 100%; height: 120%; background-color: #b91c1c; border: none; margin: auto 0; top: 0; bottom: 0; } .slider__segment .slider__segment-handle::before { Loading @@ -357,12 +487,12 @@ export class AudioClipper extends LitElement { .slider__segment .clipper__handle-left { left: -1rem; border-radius: 0.2rem 0 0 0.2rem; border-radius: 0.2rem 9999px 9999px 0.2rem; } .slider__segment .clipper__handle-right { right: -1rem; border-radius: 0 0.2rem 0.2rem 0; border-radius: 9999px 0.2rem 0.2rem 9999px; } `; Loading @@ -374,6 +504,7 @@ export class AudioClipper extends LitElement { <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" Loading @@ -383,6 +514,7 @@ export class AudioClipper extends LitElement { value="${this._volume}" @change="${this.setVolume}" /> <div class="slider-wrapper" style="height:${this.height}"> <div id="waveform"></div> <div class="slider" role="slider"> <div class="slider__track-placeholder"></div> Loading @@ -391,16 +523,17 @@ export class AudioClipper extends LitElement { class="slider__segment-progress-handle" @mousedown="${() => this.setAction(ACTIONS.Seek)}" ></div> <!-- <div class="slider__segment-progress-handle-bar"></div> --> <div class="slider__segment"> <button class="slider__segment-handle clipper__handle-left" title="${this.secondsToHHMMSS(this._clip.startTime)}" @mousedown="${() => this.setAction(ACTIONS.StretchLeft)}" ></button> <div class="slider__seeking-placeholder"></div> <div class="slider__segment-content" @click="${this.setCurrentTime}" @mousedown="${() => this.setAction(ACTIONS.Seek)}" @click="${(event: MouseEvent) => this.goTo(event)}" ></div> <button class="slider__segment-handle clipper__handle-right" Loading @@ -410,6 +543,7 @@ export class AudioClipper extends LitElement { </div> </div> </div> </div> <button @click="${this._isPlaying ? this.pause : this.play}"> ${this._isPlaying ? html`<svg Loading modules/Admin/Controllers/VideoClipsController.php +2 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ class VideoClipsController extends BaseController 0 => $this->podcast->title, 1 => $this->episode->title, ]); $this->response->setHeader('Accept-Ranges', 'bytes'); return view('episode/video_clips_new', $data); } Loading themes/cp_admin/episode/video_clips_new.php +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ <div class="flex-1 w-full"> <!-- <div class="h-full bg-black"></div> --> <audio-clipper start-time="1000" duration="140" min-duration="10" volume=".25"> <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"> Your browser does not support the <code>audio</code> element. </audio> Loading Loading
app/Resources/js/modules/audio-clipper.ts +256 −122 Original line number Diff line number Diff line Loading @@ -14,6 +14,11 @@ enum ACTIONS { Seek, } interface EventElement { events: string[]; onEvent: EventListener; } @customElement("audio-clipper") export class AudioClipper extends LitElement { @queryAssignedNodes("audio", true) Loading @@ -37,14 +42,17 @@ export class AudioClipper extends LitElement { @query(".slider__segment-progress-handle") _progressNode!: HTMLDivElement; @query(".slider__seeking-placeholder") _seekingNode!: HTMLDivElement; @query("#waveform") _waveformNode!: HTMLDivElement; @property({ type: Number, attribute: "start-time" }) startTime = 0; initStartTime = 0; @property({ type: Number }) duration = 10; @property({ type: Number, attribute: "duration" }) initDuration = 10; @property({ type: Number, attribute: "min-duration" }) minDuration = 5; Loading @@ -52,6 +60,9 @@ export class AudioClipper extends LitElement { @property({ type: Number, attribute: "volume" }) initVolume = 0.5; @property({ type: Number, attribute: "height" }) height = 100; @state() _isPlaying = false; Loading @@ -76,98 +87,159 @@ export class AudioClipper extends LitElement { @state() _volume = 0.5; @state() _isLoading = false; @state() _seekingTime: number | null = null; @state() _wavesurfer!: WaveSurfer; _windowEvents: EventElement[] = [ { events: ["load", "resize"], onEvent: () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }, }, ]; _documentEvents: EventElement[] = [ { events: ["mouseup"], onEvent: () => { if (this._action !== null) { document.body.style.cursor = ""; if (this._action === ACTIONS.Seek && this._seekingTime) { this._audio[0].currentTime = this._seekingTime; this._seekingTime = 0; } this._action = null; } }, }, { events: ["mousemove"], onEvent: (event: Event) => { if (this._action !== null) { this.updatePosition(event as MouseEvent); } }, }, ]; _audioEvents: EventElement[] = [ { events: ["play"], onEvent: () => { this._isPlaying = true; }, }, { events: ["pause"], onEvent: () => { this._isPlaying = false; }, }, { events: ["complete"], onEvent: () => { this._isLoading = false; }, }, { events: ["timeupdate"], onEvent: () => { // TODO: change this this._currentTime = this._audio[0].currentTime; if (this._currentTime > this._clip.endTime) { this.pause(); } else if (this._currentTime < this._clip.startTime) { this._audio[0].currentTime = this._clip.startTime; } else { this.setCurrentTime(this._currentTime); } }, }, ]; connectedCallback(): void { super.connectedCallback(); console.log("connectedCallback_before"); this._clip = { startTime: this.startTime, endTime: this.startTime + this.duration, startTime: this.initStartTime, endTime: this.initStartTime + this.initDuration, }; this._volume = this.initVolume; console.log("connectedCallback_after"); } protected firstUpdated(): void { console.log("firstUpdate"); this._audioDuration = this._audio[0].duration; this._audio[0].volume = this._volume; this._audio[0].currentTime = this._clip.startTime; this._isLoading = true; this._wavesurfer = WaveSurfer.create({ container: this._waveformNode, height: this.height, interact: false, barWidth: 2, barWidth: 4, barHeight: 1, barGap: 4, responsive: true, cursorColor: "transparent", }); this._wavesurfer.load(this._audio[0].src); window.addEventListener("load", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); window.addEventListener("resize", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); this.addEventListeners(); } document.addEventListener("mouseup", () => { if (this._action !== null) { this._action = null; disconnectedCallback(): void { super.disconnectedCallback(); this.removeEventListeners(); } addEventListeners(): void { for (const event of this._windowEvents) { event.events.forEach((name) => { window.addEventListener(name, event.onEvent); }); document.addEventListener("mousemove", (event: MouseEvent) => { if (this._action !== null) { this.updatePosition(event); } }); this._audio[0].addEventListener("play", () => { this._isPlaying = true; for (const event of this._documentEvents) { event.events.forEach((name) => { document.addEventListener(name, event.onEvent); }); this._audio[0].addEventListener("pause", () => { this._isPlaying = false; }); // this._audio[0].addEventListener("timeupdate", () => { // this._currentTime = this._audio[0].currentTime; // }); } disconnectedCallback(): void { console.log("disconnectedCallback"); window.removeEventListener("load", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); for (const event of this._audioEvents) { event.events.forEach((name) => { this._audio[0].addEventListener(name, event.onEvent); }); window.removeEventListener("resize", () => { this._sliderWidth = this._sliderNode.clientWidth; this.setSegmentPosition(); }); document.removeEventListener("mouseup", () => { if (this._action !== null) { this._action = null; } }); document.removeEventListener("mousemove", (event: MouseEvent) => { if (this._action !== null) { this.updatePosition(event); } removeEventListeners(): void { for (const event of this._windowEvents) { event.events.forEach((name) => { window.removeEventListener(name, event.onEvent); }); } this._audio[0].removeEventListener("play", () => { this._isPlaying = true; for (const event of this._documentEvents) { event.events.forEach((name) => { document.removeEventListener(name, event.onEvent); }); this._audio[0].removeEventListener("pause", () => { this._isPlaying = false; } for (const event of this._audioEvents) { event.events.forEach((name) => { this._audio[0].removeEventListener(name, event.onEvent); }); // this._audio[0].removeEventListener("timeupdate", () => { // this._currentTime = this._audio[0].currentTime; // }); } } setSegmentPosition(): void { Loading @@ -180,42 +252,38 @@ export class AudioClipper extends LitElement { }px`; } getPositionFromSeconds(seconds: number) { private getPositionFromSeconds(seconds: number) { return (seconds * this._sliderWidth) / this._audioDuration; } getSecondsFromPosition(position: number) { private getSecondsFromPosition(position: number) { return (this._audioDuration * position) / this._sliderWidth; } protected updated( _changedProperties: Map<string | number | symbol, unknown> ): void { // console.log("updated", _changedProperties); if (_changedProperties.has("_clip")) { // console.log("CLIP", _changedProperties.get("_clip")); this.pause(); this.setSegmentPosition(); console.log(this._clip.startTime); this._audio[0].currentTime = 58; console.log(this._audio[0].currentTime); this._audio[0].currentTime = this._clip.startTime; } if (_changedProperties.has("_seekingTime")) { if (this._seekingTime) { this._audio[0].currentTime = this._seekingTime; } } } play(): void { this._audio[0].play(); // setTimeout(() => { // this.pause(); // this._audio[0].currentTime = this._clip.startTime; // }, (this._clip.endTime - this._clip.startTime) * 1000); } pause(): void { this._audio[0].pause(); } updatePosition(event: MouseEvent): void { private updatePosition(event: MouseEvent): void { const cursorPosition = event.clientX - (this._sliderNode.getBoundingClientRect().left + Loading Loading @@ -260,7 +328,13 @@ export class AudioClipper extends LitElement { break; } case ACTIONS.Seek: { console.log("seeking"); if (seconds < this._clip.startTime) { this._seekingTime = this._clip.startTime; } else if (seconds > this._clip.endTime) { this._seekingTime = this._clip.endTime; } else { this._seekingTime = seconds; } break; } default: Loading @@ -268,37 +342,66 @@ export class AudioClipper extends LitElement { } } setVolume(event: InputEvent): void { this._volume = parseFloat((event.target as HTMLInputElement).value); this._audio[0].volume = this._volume; } setCurrentTime(event: MouseEvent): void { goTo(event: MouseEvent): void { const cursorPosition = event.clientX - (this._sliderNode.getBoundingClientRect().left + document.documentElement.scrollLeft); const seconds = this.getSecondsFromPosition(cursorPosition); this._audio[0].currentTime = seconds; } setVolume(event: InputEvent): void { this._volume = parseFloat((event.target as HTMLInputElement).value); this._audio[0].volume = this._volume; } setCurrentTime(currentTime: number): void { const seekingTimePosition = this.getPositionFromSeconds(currentTime); const startTimePosition = this.getPositionFromSeconds(this._clip.startTime); const seekingTimeSegmentPosition = seekingTimePosition - startTimePosition; const seekingTimePercentage = (seekingTimeSegmentPosition / this._segmentContentNode.clientWidth) * this._segmentContentNode.clientWidth; this._progressNode.style.transform = `translateX(${seekingTimeSegmentPosition}px)`; this._seekingNode.style.transform = `scaleX(${seekingTimePercentage})`; } setAction(action: ACTIONS): void { switch (action) { case ACTIONS.StretchLeft: case ACTIONS.StretchRight: document.body.style.cursor = "grabbing"; break; default: document.body.style.cursor = "default"; break; } this._action = action; } secondsToHHMMSS(seconds: number): string { private secondsToHHMMSS(seconds: number): string { return new Date(seconds * 1000).toISOString().substr(11, 8); } static styles = css` .slider { .slider-wrapper { position: relative; height: 6rem; width: 100%; background-color: #0f172a; } .slider { position: absolute; z-index: 10; top: 0; left: 0; display: flex; align-items: center; height: 100%; width: 100%; background-color: #0f172a; } .slider__track-placeholder { Loading @@ -309,37 +412,64 @@ export class AudioClipper extends LitElement { .slider__segment--wrapper { position: absolute; height: 100%; } .slider__segment { position: relative; display: flex; height: 100%; } .slider__segment-content { background-color: rgba(255, 255, 255, 0.9); height: 4rem; background-color: rgba(255, 255, 255, 0.5); height: 100%; width: 1px; border: none; } .slider__seeking-placeholder { position: absolute; pointer-events: none; background-color: rgba(255, 255, 255, 0.5); height: 100%; width: 1px; transform-origin: left; } .slider__segment-progress-handle { position: absolute; width: 9px; height: 9px; margin-top: -9px; margin-left: -4px; width: 20px; height: 20px; top: -23px; left: -10px; background-color: #3b82f6; cursor: pointer; border-radius: 50%; } .slider__segment-progress-handle::after { position: absolute; content: ""; width: 0px; height: 0px; bottom: -12px; left: 1px; border: 10px solid transparent; border-top-color: transparent; border-top-style: solid; border-top-width: 10px; border-top: 10px solid #3b82f6; } .slider__segment .slider__segment-handle { position: absolute; cursor: pointer; width: 1rem; height: 100%; height: 120%; background-color: #b91c1c; border: none; margin: auto 0; top: 0; bottom: 0; } .slider__segment .slider__segment-handle::before { Loading @@ -357,12 +487,12 @@ export class AudioClipper extends LitElement { .slider__segment .clipper__handle-left { left: -1rem; border-radius: 0.2rem 0 0 0.2rem; border-radius: 0.2rem 9999px 9999px 0.2rem; } .slider__segment .clipper__handle-right { right: -1rem; border-radius: 0 0.2rem 0.2rem 0; border-radius: 9999px 0.2rem 0.2rem 9999px; } `; Loading @@ -374,6 +504,7 @@ export class AudioClipper extends LitElement { <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" Loading @@ -383,6 +514,7 @@ export class AudioClipper extends LitElement { value="${this._volume}" @change="${this.setVolume}" /> <div class="slider-wrapper" style="height:${this.height}"> <div id="waveform"></div> <div class="slider" role="slider"> <div class="slider__track-placeholder"></div> Loading @@ -391,16 +523,17 @@ export class AudioClipper extends LitElement { class="slider__segment-progress-handle" @mousedown="${() => this.setAction(ACTIONS.Seek)}" ></div> <!-- <div class="slider__segment-progress-handle-bar"></div> --> <div class="slider__segment"> <button class="slider__segment-handle clipper__handle-left" title="${this.secondsToHHMMSS(this._clip.startTime)}" @mousedown="${() => this.setAction(ACTIONS.StretchLeft)}" ></button> <div class="slider__seeking-placeholder"></div> <div class="slider__segment-content" @click="${this.setCurrentTime}" @mousedown="${() => this.setAction(ACTIONS.Seek)}" @click="${(event: MouseEvent) => this.goTo(event)}" ></div> <button class="slider__segment-handle clipper__handle-right" Loading @@ -410,6 +543,7 @@ export class AudioClipper extends LitElement { </div> </div> </div> </div> <button @click="${this._isPlaying ? this.pause : this.play}"> ${this._isPlaying ? html`<svg Loading
modules/Admin/Controllers/VideoClipsController.php +2 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ class VideoClipsController extends BaseController 0 => $this->podcast->title, 1 => $this->episode->title, ]); $this->response->setHeader('Accept-Ranges', 'bytes'); return view('episode/video_clips_new', $data); } Loading
themes/cp_admin/episode/video_clips_new.php +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ <div class="flex-1 w-full"> <!-- <div class="h-full bg-black"></div> --> <audio-clipper start-time="1000" duration="140" min-duration="10" volume=".25"> <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"> Your browser does not support the <code>audio</code> element. </audio> Loading