-
-
Notifications
You must be signed in to change notification settings - Fork 18
Fix sign language tool for draggable videos (BL-16131) #7836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JohnThomson
wants to merge
2
commits into
Version6.4a
Choose a base branch
from
fixDraggableVideos
base: Version6.4a
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
| import { | ||
| removeTransientVideoTimestampParams, | ||
| stripTransientVideoTimestampParam, | ||
| } from "./bloomVideo"; | ||
|
|
||
| describe("bloomVideo transient video timestamp cleanup", () => { | ||
| it("removes only the transient video timestamp param", () => { | ||
| const cleaned = stripTransientVideoTimestampParam( | ||
| "video.mp4?persist=true&bloomVideoTransientTimestamp=123#t=1,2", | ||
| ); | ||
|
|
||
| expect(cleaned).toBe("video.mp4?persist=true#t=1,2"); | ||
| }); | ||
|
|
||
| it("leaves urls without the transient param unchanged", () => { | ||
| const cleaned = stripTransientVideoTimestampParam( | ||
| "video.mp4?persist=true#t=1,2", | ||
| ); | ||
|
|
||
| expect(cleaned).toBe("video.mp4?persist=true#t=1,2"); | ||
| }); | ||
|
|
||
| it("cleans transient params from video sources in the page", () => { | ||
| document.body.innerHTML = | ||
| '<div><video src="movie.mp4?bloomVideoTransientTimestamp=7"></video><video><source src="clip.mp4?keep=1&bloomVideoTransientTimestamp=8#t=2,4"></source></video></div>'; | ||
|
|
||
| removeTransientVideoTimestampParams(document.body); | ||
|
|
||
| const videos = Array.from(document.body.querySelectorAll("video")); | ||
| expect(videos[0].getAttribute("src")).toBe("movie.mp4"); | ||
| expect(videos[1].querySelector("source")?.getAttribute("src")).toBe( | ||
| "clip.mp4?keep=1#t=2,4", | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,45 +18,47 @@ import { getReplayIcon } from "../img/replayIcon"; | |||||||
| import { kCanvasElementSelector } from "../toolbox/canvas/canvasElementUtils"; | ||||||||
| import $ from "jquery"; | ||||||||
|
|
||||||||
| export function SetupVideoEditing(container) { | ||||||||
| const kBloomVideoTransientTimestampParam = "bloomVideoTransientTimestamp"; | ||||||||
|
|
||||||||
| export function SetupVideoEditing(container: HTMLElement) { | ||||||||
| $(container) | ||||||||
| .find(".bloom-videoContainer") | ||||||||
| .each((index, vc) => { | ||||||||
| SetupVideoContainer(vc); | ||||||||
| }); | ||||||||
| Array.from(container.getElementsByTagName("video")).forEach( | ||||||||
| (videoElement: HTMLVideoElement) => { | ||||||||
| videoElement.removeAttribute("controls"); | ||||||||
| // I don't think we need to do this in normal operation, but it's useful when | ||||||||
| // debugging, and just might prevent a problem in normal operation. | ||||||||
| videoElement.parentElement?.classList.remove("playing"); | ||||||||
| videoElement.parentElement?.classList.remove("paused"); | ||||||||
| videoElement.addEventListener("click", handleVideoClick); | ||||||||
| const playButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| // Alternatively, we could import the Material UI icon, make this file a TSX, and use | ||||||||
| // ReactDom.render to render the icon into the div. But just creating the SVG | ||||||||
| // ourselves (as these methods do) seems more natural to me. We would not be using | ||||||||
| // React for anything except to make use of an image which unfortunately is only | ||||||||
| // available by default as a component. | ||||||||
| getPlayIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoPlayIcon", | ||||||||
| ); | ||||||||
| playButton.addEventListener("click", handlePlayClick); | ||||||||
| const pauseButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| getPauseIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoPauseIcon", | ||||||||
| ); | ||||||||
| pauseButton.addEventListener("click", handlePauseClick); | ||||||||
| const replayButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| getReplayIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoReplayIcon", | ||||||||
| ); | ||||||||
| replayButton.addEventListener("click", handleReplayClick); | ||||||||
| }, | ||||||||
| ); | ||||||||
| Array.from<HTMLVideoElement>( | ||||||||
| container.getElementsByTagName("video"), | ||||||||
| ).forEach((videoElement: HTMLVideoElement) => { | ||||||||
| videoElement.removeAttribute("controls"); | ||||||||
| // I don't think we need to do this in normal operation, but it's useful when | ||||||||
| // debugging, and just might prevent a problem in normal operation. | ||||||||
| videoElement.parentElement?.classList.remove("playing"); | ||||||||
| videoElement.parentElement?.classList.remove("paused"); | ||||||||
| videoElement.addEventListener("click", handleVideoClick); | ||||||||
| const playButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| // Alternatively, we could import the Material UI icon, make this file a TSX, and use | ||||||||
| // ReactDom.render to render the icon into the div. But just creating the SVG | ||||||||
| // ourselves (as these methods do) seems more natural to me. We would not be using | ||||||||
| // React for anything except to make use of an image which unfortunately is only | ||||||||
| // available by default as a component. | ||||||||
| getPlayIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoPlayIcon", | ||||||||
| ); | ||||||||
| playButton.addEventListener("click", handlePlayClick); | ||||||||
| const pauseButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| getPauseIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoPauseIcon", | ||||||||
| ); | ||||||||
| pauseButton.addEventListener("click", handlePauseClick); | ||||||||
| const replayButton = wrapVideoIcon( | ||||||||
| videoElement, | ||||||||
| getReplayIcon("#ffffff", videoElement), | ||||||||
| "bloom-videoReplayIcon", | ||||||||
| ); | ||||||||
| replayButton.addEventListener("click", handleReplayClick); | ||||||||
| }); | ||||||||
| } | ||||||||
|
|
||||||||
| function SetupVideoContainer(videoContainerDiv: Element) { | ||||||||
|
|
@@ -92,7 +94,7 @@ function videoPlayingEventHandler(e: Event) { | |||||||
| currentVideoElement = video; | ||||||||
| let end: number = getVideoEndSeconds(video); | ||||||||
| const untrimmedEndPoint: number = 0.0; | ||||||||
| if (end == untrimmedEndPoint) { | ||||||||
| if (end === untrimmedEndPoint) { | ||||||||
| // We can't just set the endpoint to equal the duration of the video here, because | ||||||||
| // we will be testing that the current playback time is greater than the endpoint. | ||||||||
| // Since we test for the end of the video every 1/10th of a second, set the endpoint | ||||||||
|
|
@@ -292,13 +294,59 @@ export function updateVideoInContainer(container: Element, url: string): void { | |||||||
| video.appendChild(source); | ||||||||
| } | ||||||||
| if (source) { | ||||||||
| source.setAttribute("src", url); | ||||||||
| source.setAttribute("src", addTransientVideoTimestampParam(url)); | ||||||||
| video.load(); | ||||||||
| // Transparent background videos allow the placeholder to show. See BL-13918. | ||||||||
| container.classList.remove("bloom-noVideoSelected"); | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| function addTransientVideoTimestampParam(url: string): string { | ||||||||
| const hashIndex = url.indexOf("#"); | ||||||||
| const hash = hashIndex >= 0 ? url.substring(hashIndex) : ""; | ||||||||
| const beforeHash = hashIndex >= 0 ? url.substring(0, hashIndex) : url; | ||||||||
| const queryIndex = beforeHash.indexOf("?"); | ||||||||
| const baseUrl = | ||||||||
| queryIndex >= 0 ? beforeHash.substring(0, queryIndex) : beforeHash; | ||||||||
| const query = queryIndex >= 0 ? beforeHash.substring(queryIndex + 1) : ""; | ||||||||
| const searchParams = new URLSearchParams(query); | ||||||||
| searchParams.set(kBloomVideoTransientTimestampParam, Date.now().toString()); | ||||||||
| const search = searchParams.toString(); | ||||||||
|
|
||||||||
| return `${baseUrl}${search ? `?${search}` : ""}${hash}`; | ||||||||
| } | ||||||||
|
|
||||||||
| export function stripTransientVideoTimestampParam(url: string): string { | ||||||||
| const hashIndex = url.indexOf("#"); | ||||||||
| const hash = hashIndex >= 0 ? url.substring(hashIndex) : ""; | ||||||||
| const beforeHash = hashIndex >= 0 ? url.substring(0, hashIndex) : url; | ||||||||
| const queryIndex = beforeHash.indexOf("?"); | ||||||||
| if (queryIndex < 0) { | ||||||||
| return url; | ||||||||
| } | ||||||||
|
|
||||||||
| const baseUrl = beforeHash.substring(0, queryIndex); | ||||||||
| const query = beforeHash.substring(queryIndex + 1); | ||||||||
| const searchParams = new URLSearchParams(query); | ||||||||
| searchParams.delete(kBloomVideoTransientTimestampParam); | ||||||||
| const search = searchParams.toString(); | ||||||||
|
|
||||||||
| return `${baseUrl}${search ? `?${search}` : ""}${hash}`; | ||||||||
| } | ||||||||
|
|
||||||||
| export function removeTransientVideoTimestampParams(root: ParentNode): void { | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Missing comment on exported function AGENTS.md mandates: "All public methods should have a comment." The exported function
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||
| for (const element of Array.from( | ||||||||
| root.querySelectorAll<HTMLElement>("video[src], video source[src]"), | ||||||||
| )) { | ||||||||
| const src = element.getAttribute("src"); | ||||||||
| if (!src) { | ||||||||
| continue; | ||||||||
| } | ||||||||
| element.setAttribute("src", stripTransientVideoTimestampParam(src)); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| // configure one of the icons we display over videos. We put a div around it and apply | ||||||||
| // various classes and append it to the parent of the video. | ||||||||
| function wrapVideoIcon( | ||||||||
|
|
||||||||
17 changes: 17 additions & 0 deletions
17
src/BloomBrowserUI/bookEdit/js/dragActivityRuntimeUtils.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { copyContentToTarget, getTarget } from "bloom-player"; | ||
|
|
||
| // Keep UI-only selection state out of target shadow content. | ||
| export function copyContentToTargetAndCleanup( | ||
| draggableElement: HTMLElement, | ||
| ): void { | ||
| copyContentToTarget(draggableElement); | ||
|
|
||
| const target = getTarget(draggableElement); | ||
| if (!target) { | ||
| return; | ||
| } | ||
|
|
||
| target.querySelectorAll(".bloom-selected").forEach((el) => { | ||
| el.classList.remove("bloom-selected"); | ||
| }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Missing comment on exported function
stripTransientVideoTimestampParamAGENTS.md mandates: "All public methods should have a comment." The exported function
stripTransientVideoTimestampParamhas no comment.Was this helpful? React with 👍 or 👎 to provide feedback.