Skip to content
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

Allow seeking throughout video files #8844

Closed
wants to merge 4 commits into from

Conversation

well-noted
Copy link
Contributor

@well-noted well-noted commented Dec 22, 2024

Parser which allows buffering and scrubbing through video files

Copy link

Confirmed: well-noted has already signed the Contributor License Agreement (see contributing.md)

Copy link

netlify bot commented Dec 22, 2024

Deploy Preview for tiddlywiki-previews ready!

Name Link
🔨 Latest commit a53fe9d
🔍 Latest deploy log https://app.netlify.com/sites/tiddlywiki-previews/deploys/677ac814055fc50008908948
😎 Deploy Preview https://deploy-preview-8844--tiddlywiki-previews.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@well-noted well-noted closed this Dec 22, 2024
@well-noted well-noted reopened this Dec 22, 2024
@well-noted well-noted marked this pull request as draft December 22, 2024 20:53
Copy link

Confirmed: well-noted has already signed the Contributor License Agreement (see contributing.md)

@well-noted well-noted changed the title Allow seeking throughout audio files Allow seeking throughout video files Dec 23, 2024
@well-noted well-noted marked this pull request as ready for review December 23, 2024 00:59
@arunnbabu81
Copy link

arunnbabu81 commented Dec 23, 2024

@well-noted would you please tell what changes are made to the video parser in this pull request that are relevant to an end user. I am interested in knowing because I have a video annotation system based on wikitext as mentioned in this talk tiddlywiki post and share demo

Copy link
Member

@pmario pmario left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is something conceptually wrong with this PR. Parsers should not manipulate the DOM. --

}

if ($tw.browser) {
const processedVideos = new WeakMap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jermolene -- Do we allow WeakMap ES6 (ES2015) and const, async and arrow => functions?


/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

var VideoParser = function(type,text,options) {
var VideoParser = function (type, text, options) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no spaces between params

@@ -6,29 +6,161 @@ module-type: parser
The video parser parses a video tiddler into an embeddable HTML element

\*/
(function(){
(function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no spaces

}, { passive: true });

// Cleanup
const observer = new MutationObserver(function(mutations) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no const no let atm

// Cleanup
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if ([...mutation.removedNodes].includes(video)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no spread atm


// Cleanup
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use $tw.utils.each()


xhr.send();

video.addEventListener('loadedmetadata', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no async atm

if (processedVideos.has(video)) return;

// Create loading overlay
const overlay = document.createElement('div');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do use double quotes "div" for strings

// Create loading overlay
const overlay = document.createElement('div');
overlay.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;color:white;';
overlay.innerHTML = 'Loading 0%';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, innerHTML -- I am not sure if a "parser" should modify the DOM structure. Ususally widgets need to be used to create the DOM

@buggyj
Copy link
Contributor

buggyj commented Jan 4, 2025

@well-noted do you have an example showing what functionality this adds?

@well-noted
Copy link
Contributor Author

I think there is something conceptually wrong with this PR. Parsers should not manipulate the DOM. --

Having sat with this for some time, it does occur to me that some of these features are likely outside the realm of a "parser" and might be better suited for a widget.

@well-noted
Copy link
Contributor Author

well-noted commented Jan 4, 2025

@well-noted do you have an example showing what functionality this adds?

https://talk.tiddlywiki.org/t/playing-with-parsers/11518/9?u=well-noted

vivaldi_5dRJIm1rID

@well-noted
Copy link
Contributor Author

well-noted commented Jan 5, 2025

Added new commit that improves upon original commit and also in version previewed yesterday:

vivaldi_J7wOsRNP5b-ezgif com-resize

Known limitation, timestamps do not restore on mobile, probably something to do with buffering/loading times

@well-noted
Copy link
Contributor Author

Additional note, one will need to transclude the src tiddler into another tiddler for this to work, because if one is viewing the video within the src, then, when the timestamp updates, the src is temporarily interrupted -- if this were to merged, therefore, we would need to make additional changes so that, if the current tiddler is the source tiddler, it would not store timestamps.

(function () {

/*jslint node: true, browser: true */
/*global $tw: false */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation problem. It is safe to remove the (function(){ wrapper. So your "prettyfier" does not create redundant indents.

@Jermolene
Copy link
Member

Hi @well-noted thank you for this, and apologies for being late to engage thanks to the holidays.

Firstly, this is an interesting and determined attempt to solve a significant problem, and bring considerable benefits to users.

I have some questions and possible reservations about the approach of preloading the entire media file using XMLHttpRequest. (I first saw an earlier version of the code that was much simpler, and haven't been able to fully review the new code and so its possible these points no longer apply).

  • I think that XHR and the raw video/audio tags have different behaviour with respect to CORS which would present a backwards compatibility issue
  • My understanding is that seeking through remote media on the web is supposed to be done by the player using HTTP range requests. Doesn't loading the entire file at once break this mechanism?

There are also a few issues with the implementation, most of which I think have already been discussed:

  • Parsers have no business interacting with the DOM. They should be pure functions that convert a string/type combination into a parse tree
  • The "th-page-refreshed" hook is a very low level hack that is only provided for 3rd party plugins to use as a last resort, and is not appropriate for use by the core
  • Note that "th-page-refreshed" only gets called for the main window, and not for secondary windows

If we were to focus on core changes to enable this functionality to be safely shipped as a plugin, a possible approach would be to refactor the video and audio parsers so that they generate special <$video> and <$audio> widgets. The core implementation would simply produce the associated HTML element, but you'd be able to ship a plugin that overrode those widgets with a more sophisticated JS implementation. We could later consider that implementation for the core.

Stepping back, the fundamental problem with offering users a reliable and consistently good experience for video playback is really quite fundamental: TiddlyWiki works on the basis that any part of the DOM tree can be destroyed and recreated at any time which is utterly at odds with the needs of stateful HTML elements such as <video>.

In many cases, we can work around the problem by ensuring that all the required state is stored in the wiki and so if the element is destroyed it can be recreated with the same state. This is accomplished using a custom widget rather than the generic HTML element widget. For example, that is how the edit text widget doesn't lose content when the page is refreshed; a plain <input> element on the other hand will lose its content when the page is refreshed.

However, that technique isn't good enough for media playback. Humans are sensitive to very short glitches, particularly in audio. So at the back of my mind has been the idea of a separate media player JS component that renders the <video>/<audio> element outside of the main render tree (the controls can still be rendered in the usual way). With this approach the <$video> element would be a button that send a widget message to initiate playback of a particular tiddler.

@pmario
Copy link
Member

pmario commented Jan 6, 2025

However, that technique isn't good enough for media playback. Humans are sensitive to very short glitches, particularly in audio. So at the back of my mind has been the idea of a separate media player JS component that renders the

I was also thinking about "interrupt free" state. My first thought was to render the video/audio elements in the root-widget, so it will be "outside" the main refresh tree. (That's similar to what Jeremy suggested)

The next thought was: Let's open a new window for the video or audio file. -- This may be able to keep the window alive even if the tiddler is closed in the story river. (That's my personal favourite)

Or the other way around: Let root handle the video-tag and create a new small window with the controls only. This would allow us to move the controls around. IMO that's handy for audio-only UI. Where the UI would be completely out of the way.

@well-noted
Copy link
Contributor Author

well-noted commented Jan 6, 2025

Parsers have no business interacting with the DOM

Heard, I have actually been discussing with etardiff storing the values as statetiddlers, which I believe would remove most of the need for DOM manipulation.

Let's open a new window for the video or audio file.

Personally, this solution would not work for me unless the time-values could still be accessed from within the wiki (And, still not quite as elegant, to my mind) as having the media element as an overlay on the page that allows for closing the story tiddler, as the audioparser modifications I have made currently allow for. I believe it is core to the philosophy of tiddlywiki that all values should be able to be referenced and transcluded in any context.

a possible approach would be to refactor the video and audio parsers so that they generate special <$video> and <$audio> widgets.

Yes, I believe that, using this as a proof-of concept, this PR could be refactored into a plugin if it were possible to intercept the video rendering through the widget system. and agree that it has the benefits of maintaining backward compatibility, while also allowing for additional features that might be helpful options to end-users but would unnecessarily bloat the core.

I am satisfied with this as a direction and am happy to begin proceeding in refactoring as a plugin, unless there is a more elegant core solution on the horizon?

@well-noted well-noted closed this Jan 6, 2025
@Jermolene
Copy link
Member

Yes, I believe that, using this as a proof-of concept, this PR could be refactored into a plugin if it were possible to intercept the video rendering through the widget system. and agree that it has the benefits of maintaining backward compatibility, while also allowing for additional features that might be helpful options to end-users but would unnecessarily bloat the core.

Hi @well-noted would you be interested in drafting a PR for this? Let me know if you need any more information beyond my comments above.

@well-noted
Copy link
Contributor Author

@Jermolene, lmk if this would be appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants