Skip to content

Commit

Permalink
fixed pause button and caching
Browse files Browse the repository at this point in the history
  • Loading branch information
christopher-besch committed Nov 3, 2021
1 parent 00a84d5 commit 7781c8a
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 77 deletions.
2 changes: 1 addition & 1 deletion manim_editor/app/main/templates/edit_project.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ <h1>{{ name }}</h1>
<div class="btn-group me-2 btn-group mt-3" role="group">
<button id="previous-section" type="button" class="btn btn-secondary"><i class="bi-caret-left"></i></button>
<button id="restart-section" type="button" class="btn btn-secondary"><i class="bi-arrow-counterclockwise"></i></button>
<button id="pause" type="button" class="btn btn-secondary" data-status="pause"><i class="bi-pause"></i></button>
<button id="pause" type="button" class="btn btn-secondary"><i class="bi-pause"></i></button>
<button id="next-section" type="button" class="btn btn-secondary"><i class="bi-caret-right"></i></button>
</div>
<div class="btn-group btn-group me-3 mt-3" role="group">
Expand Down
57 changes: 10 additions & 47 deletions web_src/ts/edit_project.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { spin_button } from "./utils"
import { spin_button } from "./utils";
import { Presentation } from "./presenter/presentation";
import { BufferPresentation } from "./buffer_presenter/buffer_presentation";
import { FallbackPresentation } from "./fallback_presenter/fallback_presentation";
Expand Down Expand Up @@ -44,56 +44,17 @@ function create_presentation(): Presentation {
}
}

function toggle_pause(presentation: Presentation): void {
let button = document.getElementById("pause") as HTMLButtonElement;
let status = button.dataset.status as string;
if (status == "pause") {
presentation.pause();
button.dataset.status = "play";
button.innerHTML = '<i class="bi-play"></i>';
} else if (status == "play") {
presentation.play();
button.dataset.status = "pause";
button.innerHTML = '<i class="bi-pause"></i>';
}
else {
console.error(`Broken status '${status}' in dataset of pause button.`);
}
}

function attach_ui(presentation: Presentation): void {
let previous = document.getElementById("previous-section") as HTMLButtonElement;
let restart = document.getElementById("restart-section") as HTMLButtonElement;
let next = document.getElementById("next-section") as HTMLButtonElement;
let pause = document.getElementById("pause") as HTMLButtonElement;
let fullscreen = document.getElementById("fullscreen") as HTMLButtonElement;
let cache = document.getElementById("cache") as HTMLButtonElement;
let update_settings = document.getElementById("update-settings") as HTMLButtonElement;

// add callbacks
previous.addEventListener("click", presentation.play_previous_section.bind(presentation));
restart.addEventListener("click", presentation.restart_current_section.bind(presentation));
next.addEventListener("click", presentation.play_next_section.bind(presentation));
pause.addEventListener("click", () => {
toggle_pause(presentation);
});
fullscreen.addEventListener("click", presentation.enter_fullscreen.bind(presentation));
cache.addEventListener("click", () => {
spin_button(cache);
presentation.cache(() => {
cache.remove();
});
});

// set player setting values
function attach_settings(): void {
let cache_batch_size = document.getElementById("cache-batch-size") as HTMLInputElement;
let past_sections_to_buffer = document.getElementById("past-sections-to-buffer") as HTMLInputElement;
let future_sections_to_buffer = document.getElementById("future-sections-to-buffer") as HTMLInputElement;
let use_fallback_loader = document.getElementById("fallback-loader-selected") as HTMLInputElement;
let update_settings = document.getElementById("update-settings") as HTMLButtonElement;

cache_batch_size.value = CACHE_BATCH_SIZE.toString();
past_sections_to_buffer.value = PAST_SECTIONS_TO_BUFFER.toString();
future_sections_to_buffer.value = FUTURE_SECTIONS_TO_BUFFER.toString();
// use_buffer_loader.checked = !USE_FALLBACK_LOADER;
// automatically set use_buffer_loader
use_fallback_loader.checked = USE_FALLBACK_LOADER;

// set callback
Expand All @@ -109,8 +70,9 @@ function attach_ui(presentation: Presentation): void {
USE_FALLBACK_LOADER = use_fallback_loader.checked;
update_url_params();
});
}

// export
function attach_export(): void {
let export_presentation = document.getElementById("export-presentation") as HTMLButtonElement | null;
if (export_presentation !== null) {
let target = export_presentation.dataset.target as string;
Expand Down Expand Up @@ -160,7 +122,7 @@ function attach_keyboard_ui(presentation: Presentation): void {
else if (next_keys.includes(e.code))
presentation.play_next_section();
else if (pause_keys.includes(e.code))
toggle_pause(presentation);
presentation.toggle_pause();
else if (fullscreen_keys.includes(e.code))
presentation.toggle_fullscreen();
});
Expand All @@ -169,6 +131,7 @@ function attach_keyboard_ui(presentation: Presentation): void {
document.body.onload = () => {
load_url_params();
let presentation = create_presentation();
attach_ui(presentation);
attach_settings();
attach_export();
attach_keyboard_ui(presentation);
}
113 changes: 86 additions & 27 deletions web_src/ts/presenter/presentation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get_json } from "../utils";
import { spin_button, get_json } from "../utils";
import { Section, SectionJson, SectionType } from "./section";

export abstract class Presentation {
Expand All @@ -8,6 +8,10 @@ export abstract class Presentation {
private videos_div: HTMLDivElement;
private timeline_sections: HTMLCollectionOf<HTMLDivElement>;
private timeline_indicators: HTMLCollectionOf<HTMLElement>;
private pause_button: HTMLButtonElement;

// switch between play and pause
private button_should_pause = true;

private cache_batch_size: number;
// gets flipped when displaying first video
Expand All @@ -29,6 +33,7 @@ export abstract class Presentation {
this.videos_div = document.getElementById("videos-div") as HTMLDivElement;
this.timeline_sections = document.getElementsByClassName("timeline-element") as HTMLCollectionOf<HTMLDivElement>;
this.timeline_indicators = document.getElementsByClassName("timeline-indicator") as HTMLCollectionOf<HTMLDivElement>;
this.pause_button = document.getElementById("pause") as HTMLButtonElement;

// load_sections
let project_file = this.videos_div.dataset.project_file as string;
Expand All @@ -42,12 +47,15 @@ export abstract class Presentation {
console.log(`All ${sections.length} sections have been parsed successfully.`)

this.attach_timeline();
this.attach_buttons();
// start the action
this.play_section(0);
});
}

// update currently playing video in html video element
private update_video(): void {
this.set_button_pause();
// correct section already current
if (this.current_section == this.previous_section) {
// restart video
Expand Down Expand Up @@ -111,16 +119,6 @@ export abstract class Presentation {
this.previous_section = this.current_section;
}

public pause(): void {
console.log("Stopped.");
this.get_current_video().pause();
}

public play(): void {
console.log("Started.");
this.get_current_video().play();
}

// skip_complete_loop can be used in the timeline or as a forced continue
public play_section(section: number, skip_complete_loop = false): void {
if (section < 0 || section >= this.sections.length) {
Expand Down Expand Up @@ -153,21 +151,6 @@ export abstract class Presentation {
this.play_section(this.current_section - 1, true);
}

private attach_timeline(): void {
for (let i = 0; i < this.timeline_sections.length; ++i) {
this.timeline_sections[i].addEventListener("click", () => {
this.play_section(i, true);
});
}
}

private update_timeline(): void {
if (this.previous_section != -1)
this.timeline_indicators[this.previous_section].innerHTML = `<i class="timeline-indicators bi-check-circle" role="img"></i>`;
this.timeline_indicators[this.current_section].innerHTML = `<i class="timeline-indicators bi-circle-fill" role="img"></i>`;
this.timeline_sections[this.current_section].scrollIntoView({ behavior: "smooth", block: "center" });
}

public get_current_section(): number { return this.current_section; }

private get_current_video(): HTMLVideoElement {
Expand All @@ -177,6 +160,9 @@ export abstract class Presentation {
return this.video1;
}

/////////////
// caching //
/////////////
// asynchronous, recursive; downloads everything after offset in batches
private cache_batch(offset: number, on_finished: { (): void; }): void {
let finished = offset;
Expand All @@ -203,6 +189,9 @@ export abstract class Presentation {
this.cache_batch(0, on_finished);
}

////////////////
// fullscreen //
////////////////
// TODO: doesn't work on safari
public enter_fullscreen(): void {
if (this.videos_div.requestFullscreen)
Expand All @@ -226,7 +215,7 @@ export abstract class Presentation {
// document.msExitFullscreen();
}

public fullscreen_status(): boolean {
private fullscreen_status(): boolean {
// return document.fullscreenElement != null ||
// document.webkitFullscreenElement != null ||
// document.mozFullScreenElement != null;
Expand All @@ -240,6 +229,76 @@ export abstract class Presentation {
this.enter_fullscreen();
}

////////////////////
// user interface //
////////////////////
private attach_timeline(): void {
for (let i = 0; i < this.timeline_sections.length; ++i) {
this.timeline_sections[i].addEventListener("click", () => {
this.play_section(i, true);
});
}
}

private update_timeline(): void {
if (this.previous_section != -1)
this.timeline_indicators[this.previous_section].innerHTML = `<i class="timeline-indicators bi-check-circle" role="img"></i>`;
this.timeline_indicators[this.current_section].innerHTML = `<i class="timeline-indicators bi-circle-fill" role="img"></i>`;
this.timeline_sections[this.current_section].scrollIntoView({ behavior: "smooth", block: "center" });
}

// update icon on button
private set_button_play(): void {
this.button_should_pause = false;
this.pause_button.innerHTML = '<i class="bi-play"></i>';
}
private set_button_pause(): void {
this.button_should_pause = true;
this.pause_button.innerHTML = '<i class="bi-pause"></i>';
}

public pause(): void {
console.log("Stopped.");
this.get_current_video().pause();
this.set_button_play();
}
public play(): void {
console.log("Started.");
this.get_current_video().play();
this.set_button_pause();
}
public toggle_pause(): void {
if (this.button_should_pause)
this.pause();
else
this.play();
}

public attach_buttons(): void {
let previous = document.getElementById("previous-section") as HTMLButtonElement;
let restart = document.getElementById("restart-section") as HTMLButtonElement;
let next = document.getElementById("next-section") as HTMLButtonElement;
let pause = document.getElementById("pause") as HTMLButtonElement;
let fullscreen = document.getElementById("fullscreen") as HTMLButtonElement;
let cache = document.getElementById("cache") as HTMLButtonElement;

// add callbacks
previous.addEventListener("click", this.play_previous_section.bind(this));
restart.addEventListener("click", this.restart_current_section.bind(this));
next.addEventListener("click", this.play_next_section.bind(this));
pause.addEventListener("click", this.toggle_pause.bind(this));
fullscreen.addEventListener("click", this.enter_fullscreen.bind(this));
cache.addEventListener("click", () => {
spin_button(cache);
this.cache(() => {
cache.remove();
});
});
}

////////////////////////////////
// to be defined by inheritor //
////////////////////////////////
protected abstract add_section(section: SectionJson, video: string): void;

// called after section changed
Expand Down
13 changes: 11 additions & 2 deletions web_src/ts/presenter/section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,24 @@ export abstract class Section {
public cache(on_cached: () => void): void {
let request = new XMLHttpRequest();
request.onload = () => {
if (request.status == 200) {
if (request.status == 200 || request.status == 206) {
console.log(`Cached section '${this.name}'`)
on_cached();
}
else
else {
console.error(`Section '${this.name}' failed to be cached with status ${request.status}`);
// another attempt in 10 sec
window.setTimeout(() => {
this.cache(on_cached);
}, 10000);
}
};
request.onerror = () => {
console.error(`Section '${this.name}' failed to be cached`);
// another attempt in 10 sec
window.setTimeout(() => {
this.cache(on_cached);
}, 10000);
};
request.open("GET", this.video, true);
request.send();
Expand Down

0 comments on commit 7781c8a

Please sign in to comment.