Macro to display constantly updating info about a currently playing video (current position in H:M:S, duration in H:M:S, playback progress in percent)

In a previous post link here, solved by Eric Shulman, I recieved help in creating a macro to control the playback speed of a video that is playing in a tiddler.

I was able to use that macro as a template to create other macros for video controls, such as for playing, or pausing, or setting the volume of a video. Here is a macro for setting a video’s playback volume:

/*\
title: mymacros/setvideovolume.js
type: application/javascript
module-type: macro
\*/

"use strict";

/*
Custom macro to set video volume for myVideo element
*/

exports.name = "setvideovolume";

exports.params = [
	{name: "vol"}
];

/*
Run the macro
*/
exports.run = function(vol) {
	const video = document.getElementById('myVideo');
	video.volume = vol;
	return vol;
};

Example usage, a mute button:

<$button class='btn btn-sm btn-light' actions="<<setvideovolume 0>>">mute</$button>

However, I am now at the limit of my abilities to further adapt the macro code!

I would like to write a macro that, when called, displays constantly updating information about a currently playing video (its current position in H:M:S, the video duration in H:M:S, the progress of playback in percent).

With the help of AI, I have created standalone HTML code that performs this desired function, although I am stuck in trying to figure out how to adapt the code to make a callable tiddly wiki macro that does the same thing inside a tiddler.

Here is the HTML code, would anyone be able to help me turn it into a macro?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Video Player</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        #videoContainer {
            margin-bottom: 10px;
        }
    </style>
</head>
<body>

<div id="videoContainer">
    <video playsinline id="myVideo" width="640" height="360" controls>
        <source src="file.mp4" type="video/mp4">
        Your browser does not support the video tag.
    </video>
</div>
<div>
    <span style='font-family : monospace;'>
    <span id="currentTime">[0:00:00]</span> / 
    <span id="duration">[0:00:00]</span> 
    <span id="progress">0%</span>

</div>

<script>
    const video = document.getElementById('myVideo');
    const currentTimeDisplay = document.getElementById('currentTime');
    const durationDisplay = document.getElementById('duration');
    const progressDisplay = document.getElementById('progress');


    function formatTime(seconds) {
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        return `${hours}:${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`;
    }

    video.addEventListener('loadedmetadata', () => {
        durationDisplay.textContent = '['+formatTime(video.duration)+']';
    });

    video.addEventListener('timeupdate', () => {
        currentTimeDisplay.textContent = '[' + formatTime(video.currentTime) + ']';
        const progress = (video.currentTime / video.duration) * 100;
        progressDisplay.textContent = `${progress.toFixed(0)}%`;
    });
</script>

</body>
</html>

Create a tiddler (e.g., “mymacros/videostatus.js”), containing:

/*\
title: mymacros/videostatus.js
type: application/javascript
module-type: macro
\*/

/*
Macro to show video status (current time, duration, progress)
*/

"use strict";
exports.name = "videostatus";
exports.params = [{name: "id"}];
exports.run = function(id) {
	const video = document.getElementById(id);

	function formatTime(seconds) {
		const hours = Math.floor(seconds / 3600);
		const minutes = Math.floor((seconds % 3600) / 60);
		const secs = Math.floor(seconds % 60);
		return `${hours}:${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`;
	}

	video.addEventListener('loadedmetadata', () => {
		document.getElementById(id+'_duration').textContent = '['+formatTime(video.duration)+']';
	});

	video.addEventListener('timeupdate', () => {
		document.getElementById(id+'_currentTime').textContent = '[' + formatTime(video.currentTime) + ']';
		document.getElementById(id+'_progress').textContent = `${((video.currentTime / video.duration) * 100).toFixed(0)}%`;
	});
	return "";
};

REMINDER: make sure to set the tiddler’s type field to application/javascript and add a module-type field set to macro . Then save-and-reload for this custom javascript macro to be available.

Then, your “video_player” tiddler should contain:

<$select tiddler=video_tiddler field=currentvideo>
<option value="file1.mp4"> video 1 </option>
<option value="file2.mp4"> video 2 </option>
</$select>

<$let vid={{!!currentvideo}}>
<video id="myVideo" controls>
	<source src=<<vid>>>
	Your browser does not support the video tag.
</video>
<<videostatus myVideo>>
<div>
	<$button actions="<<videospeed myVideo 0.5>>">0.5x</$button>
	<$button actions="<<videospeed myVideo 1>>">1x</$button>
	<$button actions="<<videospeed myVideo 1.5>>">1.5x</$button>
	<$button actions="<<videospeed myVideo 2>>">2x</$button>
	<span style='font-family:monospace;'>
		<span id="myVideo_currentTime">[0:00:00]</span> / 
		<span id="myVideo_duration">[0:00:00]</span> 
		<span id="myVideo_progress">0%</span>
	</span>
</div>
</$let>

Notes:

  • Most of the “video_player” tiddler content is the same as before.
  • However, note the addition of <<videostatus myVideo>>. This is what sets up the event listeners for loadedmetadata and timeupdate.
  • Then, following the <<videospeed>> buttons, add the display elements for myVideo_currentTime, myVideo_duration, and myVideo_progress. When the event listeners are triggered, the textContent of these display elements are updated.

enjoy,
-e

2 Likes

Thank you so much, Eric! I have tested your solution, and it works perfectly in my wiki!