I’ve encountered the same issue as you did when I try to play mp4 video in <video>
tag embedded in a tiddler under Safari browser or Tauri Webview App (I’m using macOS Ventura 13.7.4 / 22H420).
This is a quick-n-dirty patch on source file tiddlywiki/core/modules/server/server.js
(based on version 5.3.6, around line 300), I hope someone could provide more elegant solution. (PLEASE do not use this solution in production code. It’s written by Perplexity.ai and not tested by seasoned developer.)
// Receive the request body if necessary and hand off to the route handler
if(route.bodyFormat === "stream" || request.method === "GET" || request.method === "HEAD") {
// List filename extensions and MIME names we need as a dictionary.
const MIME_TYPES = {
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4',
'.ogg': 'application/ogg',
'.ogv': 'video/ogg',
'.oga': 'audio/ogg',
'.wav': 'audio/x-wav',
'.webm': 'video/webm'
};
// Assume route.handler is a function that takes (request, response, state)
// We'll wrap or replace it here with range handling for media files
const range = request.headers.range;
const filePath = path.join(process.cwd(), this.boot.wikiPath, request.url);
const ext = path.extname(filePath).toLowerCase();
// Only handle range requests for files witin MIME_TYPES
if ((ext in MIME_TYPES) && range) {
fs.stat(filePath, (err, stats) => {
if (err) {
response.writeHead(404);
return response.end();
}
const total = stats.size;
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : total - 1;
// Validate range
if (start >= total || end >= total) {
response.writeHead(416, {
'Content-Range': `bytes */${total}`
});
return response.end();
}
const chunkSize = (end - start) + 1;
response.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${total}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': MIME_TYPES[ext],
});
const stream = fs.createReadStream(filePath, { start, end });
stream.pipe(response);
});
} else {
// No range header or not a media file, proceed normally
route.handler(request, response, state);
}
} else if(route.bodyFormat === "string" || !route.bodyFormat) {
...