mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-08 00:10:27 +00:00
Allow time seeking in videoparser
This commit is contained in:
parent
da3ef6e615
commit
d0849440d2
@ -2,89 +2,33 @@
|
|||||||
title: $:/core/modules/parsers/audioparser.js
|
title: $:/core/modules/parsers/audioparser.js
|
||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: parser
|
module-type: parser
|
||||||
|
|
||||||
|
The audio parser parses an audio tiddler into an embeddable HTML element
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function () {
|
(function(){
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
/*jslint node: true, browser: true */
|
||||||
/*global $tw: false */
|
/*global $tw: false */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var AudioParser = function (type, text, options) {
|
var AudioParser = function(type,text,options) {
|
||||||
var element = {
|
var element = {
|
||||||
type: "element",
|
|
||||||
tag: "audio",
|
|
||||||
attributes: {
|
|
||||||
controls: { type: "string", value: "controls" },
|
|
||||||
style: { type: "string", value: "width: 100%; object-fit: contain" },
|
|
||||||
preload: { type: "string", value: "auto" }, // Changed to auto for full loading
|
|
||||||
class: { type: "string", value: "tw-audio-element" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set source with range support
|
|
||||||
if (options._canonical_uri) {
|
|
||||||
element.children = [{
|
|
||||||
type: "element",
|
type: "element",
|
||||||
tag: "source",
|
tag: "audio",
|
||||||
attributes: {
|
attributes: {
|
||||||
src: { type: "string", value: options._canonical_uri },
|
controls: {type: "string", value: "controls"},
|
||||||
type: { type: "string", value: type }
|
style: {type: "string", value: "width: 100%; object-fit: contain"}
|
||||||
}
|
}
|
||||||
}];
|
},
|
||||||
} else if (text) {
|
src;
|
||||||
element.attributes.src = { type: "string", value: "data:" + type + ";base64," + text };
|
if(options._canonical_uri) {
|
||||||
|
element.attributes.src = {type: "string", value: options._canonical_uri};
|
||||||
|
} else if(text) {
|
||||||
|
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($tw.browser) {
|
|
||||||
$tw.hooks.addHook("th-page-refreshed", function () {
|
|
||||||
setTimeout(function () {
|
|
||||||
Array.from(document.getElementsByClassName("tw-audio-element")).forEach(function (audio) {
|
|
||||||
// Force aggressive loading
|
|
||||||
audio.preload = "auto";
|
|
||||||
audio.autobuffer = true;
|
|
||||||
|
|
||||||
// Create XMLHttpRequest to load full file
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open('GET', audio.currentSrc, true);
|
|
||||||
xhr.responseType = 'blob';
|
|
||||||
|
|
||||||
xhr.onprogress = function(e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
var percentComplete = (e.loaded / e.total) * 100;
|
|
||||||
console.log("Loading: " + percentComplete.toFixed(2) + "%");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onload = function() {
|
|
||||||
if (xhr.status === 200) {
|
|
||||||
var blob = new Blob([xhr.response], { type: type });
|
|
||||||
var url = URL.createObjectURL(blob);
|
|
||||||
audio.src = url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send();
|
|
||||||
|
|
||||||
// Monitor buffer state
|
|
||||||
audio.addEventListener("progress", function() {
|
|
||||||
var buffered = this.buffered;
|
|
||||||
if(buffered.length > 0) {
|
|
||||||
var total = 0;
|
|
||||||
for(var i = 0; i < buffered.length; i++) {
|
|
||||||
var start = buffered.start(i);
|
|
||||||
var end = buffered.end(i);
|
|
||||||
total += (end - start);
|
|
||||||
console.log(`Buffer ${i}: ${start}-${end} (${((end-start)/this.duration*100).toFixed(2)}%)`);
|
|
||||||
}
|
|
||||||
console.log(`Total buffered: ${(total/this.duration*100).toFixed(2)}%`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tree = [element];
|
this.tree = [element];
|
||||||
|
this.source = text;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -93,4 +37,5 @@ exports["audio/mpeg"] = AudioParser;
|
|||||||
exports["audio/mp3"] = AudioParser;
|
exports["audio/mp3"] = AudioParser;
|
||||||
exports["audio/mp4"] = AudioParser;
|
exports["audio/mp4"] = AudioParser;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -6,29 +6,161 @@ module-type: parser
|
|||||||
The video parser parses a video tiddler into an embeddable HTML element
|
The video parser parses a video tiddler into an embeddable HTML element
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function () {
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
/*jslint node: true, browser: true */
|
||||||
/*global $tw: false */
|
/*global $tw: false */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var VideoParser = function(type,text,options) {
|
var VideoParser = function (type, text, options) {
|
||||||
var element = {
|
var element = {
|
||||||
|
type: "element",
|
||||||
|
tag: "video",
|
||||||
|
attributes: {
|
||||||
|
controls: { type: "string", value: "controls" },
|
||||||
|
style: { type: "string", value: "width: 100%; object-fit: contain" },
|
||||||
|
preload: { type: "string", value: "auto" },
|
||||||
|
class: { type: "string", value: "tw-video-element" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options._canonical_uri) {
|
||||||
|
element.children = [{
|
||||||
type: "element",
|
type: "element",
|
||||||
tag: "video",
|
tag: "source",
|
||||||
attributes: {
|
attributes: {
|
||||||
controls: {type: "string", value: "controls"},
|
src: { type: "string", value: options._canonical_uri },
|
||||||
style: {type: "string", value: "width: 100%; object-fit: contain"}
|
type: { type: "string", value: type }
|
||||||
}
|
}
|
||||||
},
|
}];
|
||||||
src;
|
} else if (text) {
|
||||||
if(options._canonical_uri) {
|
element.attributes.src = { type: "string", value: "data:" + type + ";base64," + text };
|
||||||
element.attributes.src = {type: "string", value: options._canonical_uri};
|
|
||||||
} else if(text) {
|
|
||||||
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($tw.browser) {
|
||||||
|
const processedVideos = new WeakMap();
|
||||||
|
const bufferThreshold = 0.1; // 10% buffered before play
|
||||||
|
|
||||||
|
$tw.hooks.addHook("th-page-refreshed", function() {
|
||||||
|
Array.from(document.getElementsByClassName("tw-video-element")).forEach(function(video) {
|
||||||
|
if (processedVideos.has(video)) return;
|
||||||
|
|
||||||
|
// 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%';
|
||||||
|
video.parentNode.style.position = 'relative';
|
||||||
|
video.parentNode.appendChild(overlay);
|
||||||
|
|
||||||
|
video.preload = "auto";
|
||||||
|
video.autobuffer = true;
|
||||||
|
|
||||||
|
// Prevent play until buffered
|
||||||
|
video.addEventListener('play', function(e) {
|
||||||
|
if (video.buffered.length === 0 || (video.buffered.end(0) / video.duration) < bufferThreshold) {
|
||||||
|
console.log('Waiting for buffer...');
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Monitor buffering
|
||||||
|
video.addEventListener('progress', function() {
|
||||||
|
if (video.buffered.length > 0) {
|
||||||
|
const progress = (video.buffered.end(0) / video.duration * 100).toFixed(2);
|
||||||
|
console.log(`Buffer: ${progress}%`);
|
||||||
|
overlay.innerHTML = `Loading ${progress}%`;
|
||||||
|
|
||||||
|
if ((video.buffered.end(0) / video.duration) >= bufferThreshold) {
|
||||||
|
overlay.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', video.currentSrc, true);
|
||||||
|
xhr.responseType = 'blob';
|
||||||
|
|
||||||
|
xhr.onprogress = function(e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
console.log(`Download: ${(e.loaded / e.total * 100).toFixed(2)}%`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const blob = new Blob([xhr.response], { type: video.type || 'video/mp4' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
video._blob = blob;
|
||||||
|
video.src = url;
|
||||||
|
processedVideos.set(video, {
|
||||||
|
blob: blob,
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle seeking with passive listener
|
||||||
|
video.addEventListener('seeking', function() {
|
||||||
|
if (!video.src || video.src === '') {
|
||||||
|
video.src = URL.createObjectURL(video._blob);
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
const observer = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(mutation) {
|
||||||
|
if ([...mutation.removedNodes].includes(video)) {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
processedVideos.delete(video);
|
||||||
|
observer.disconnect();
|
||||||
|
if (overlay.parentNode) {
|
||||||
|
overlay.parentNode.removeChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(video.parentNode, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
|
||||||
|
video.addEventListener('loadedmetadata', async () => {
|
||||||
|
// Use requestAnimationFrame instead of direct style changes
|
||||||
|
requestAnimationFrame(async () => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('HEAD', video.currentSrc);
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (mutation.addedNodes.length) {
|
||||||
|
const overlay = mutation.target.querySelector('.play-overlay');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.parentNode.removeChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(video.parentNode, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}, { passive: true });
|
||||||
|
});
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
this.tree = [element];
|
this.tree = [element];
|
||||||
this.source = text;
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user