From 7781c8a0e314cde6e0b4009dc3122992c3261987 Mon Sep 17 00:00:00 2001 From: christopher-besch Date: Wed, 3 Nov 2021 20:35:16 +0100 Subject: [PATCH] fixed pause button and caching --- .../app/main/templates/edit_project.html | 2 +- web_src/ts/edit_project.ts | 57 ++------- web_src/ts/presenter/presentation.ts | 113 +++++++++++++----- web_src/ts/presenter/section.ts | 13 +- 4 files changed, 108 insertions(+), 77 deletions(-) diff --git a/manim_editor/app/main/templates/edit_project.html b/manim_editor/app/main/templates/edit_project.html index 521482de..484599ec 100644 --- a/manim_editor/app/main/templates/edit_project.html +++ b/manim_editor/app/main/templates/edit_project.html @@ -129,7 +129,7 @@

{{ name }}

- +
diff --git a/web_src/ts/edit_project.ts b/web_src/ts/edit_project.ts index 7018cb74..a050654c 100644 --- a/web_src/ts/edit_project.ts +++ b/web_src/ts/edit_project.ts @@ -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"; @@ -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 = ''; - } else if (status == "play") { - presentation.play(); - button.dataset.status = "pause"; - button.innerHTML = ''; - } - 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 @@ -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; @@ -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(); }); @@ -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); } diff --git a/web_src/ts/presenter/presentation.ts b/web_src/ts/presenter/presentation.ts index 9778f81c..3898db14 100644 --- a/web_src/ts/presenter/presentation.ts +++ b/web_src/ts/presenter/presentation.ts @@ -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 { @@ -8,6 +8,10 @@ export abstract class Presentation { private videos_div: HTMLDivElement; private timeline_sections: HTMLCollectionOf; private timeline_indicators: HTMLCollectionOf; + 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 @@ -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; this.timeline_indicators = document.getElementsByClassName("timeline-indicator") as HTMLCollectionOf; + this.pause_button = document.getElementById("pause") as HTMLButtonElement; // load_sections let project_file = this.videos_div.dataset.project_file as string; @@ -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 @@ -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) { @@ -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 = ``; - this.timeline_indicators[this.current_section].innerHTML = ``; - 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 { @@ -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; @@ -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) @@ -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; @@ -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 = ``; + this.timeline_indicators[this.current_section].innerHTML = ``; + 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 = ''; + } + private set_button_pause(): void { + this.button_should_pause = true; + this.pause_button.innerHTML = ''; + } + + 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 diff --git a/web_src/ts/presenter/section.ts b/web_src/ts/presenter/section.ts index be7583d5..c5a9f8f0 100644 --- a/web_src/ts/presenter/section.ts +++ b/web_src/ts/presenter/section.ts @@ -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();