Fix WebDAV music seeking functionality

- Add proxy endpoint for WebDAV streaming with authentication
- Implement range request support for audio seeking
- Update streaming endpoint to use proxy for WebDAV, presigned URLs for S3
- Add proper HTTP headers for audio streaming (Accept-Ranges, Content-Range)
- Fix music player seeking issue where clicking progress bar would reset playback

The WebDAV integration now supports proper audio seeking by using a backend
proxy that handles WebDAV authentication and range requests, allowing users
to click anywhere on the progress bar to seek to that position in the track.
This commit is contained in:
Geert Rademakes 2025-09-18 08:58:49 +02:00
parent 9de7564c18
commit e58d42bea2

View File

@ -371,20 +371,75 @@ router.get('/:id/stream', async (req, res) => {
return res.status(404).json({ error: 'Music file not found' });
}
// Use presigned URL for secure access instead of direct URL
const presignedUrl = await storageService.getPresignedUrl(musicFile.s3Key, 3600); // 1 hour expiry
res.json({
streamingUrl: presignedUrl,
musicFile,
contentType: musicFile.contentType || undefined,
});
// For WebDAV, use a proxy endpoint to handle authentication
// For S3, use presigned URL for direct access
const config = await StorageProviderFactory.loadConfig();
if (config.provider === 'webdav') {
// Use proxy endpoint for WebDAV to handle authentication
const proxyUrl = `${req.protocol}://${req.get('host')}/api/music/${musicFile._id}/proxy`;
res.json({
streamingUrl: proxyUrl,
musicFile,
contentType: musicFile.contentType || undefined,
});
} else {
// Use presigned URL for S3
const presignedUrl = await storageService.getPresignedUrl(musicFile.s3Key, 3600); // 1 hour expiry
res.json({
streamingUrl: presignedUrl,
musicFile,
contentType: musicFile.contentType || undefined,
});
}
} catch (error) {
console.error('Streaming error:', error);
res.status(500).json({ error: 'Failed to get streaming URL' });
}
});
/**
* Proxy endpoint for WebDAV streaming with authentication
*/
router.get('/:id/proxy', async (req, res) => {
try {
const musicFile = await MusicFile.findById(req.params.id);
if (!musicFile) {
return res.status(404).json({ error: 'Music file not found' });
}
// Set appropriate headers for audio streaming
res.setHeader('Content-Type', musicFile.contentType || 'audio/mpeg');
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Cache-Control', 'public, max-age=3600');
// Get the file content from WebDAV
const fileBuffer = await storageService.getFileContent(musicFile.s3Key);
// Handle range requests for seeking
const range = req.headers.range;
if (range) {
const fileSize = fileBuffer.length;
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
const chunksize = (end - start) + 1;
const chunk = fileBuffer.slice(start, end + 1);
res.status(206);
res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
res.setHeader('Content-Length', chunksize.toString());
res.end(chunk);
} else {
// No range request, send entire file
res.setHeader('Content-Length', fileBuffer.length.toString());
res.end(fileBuffer);
}
} catch (error) {
console.error('Proxy streaming error:', error);
res.status(500).json({ error: 'Failed to stream music file' });
}
});
/**
* Get presigned URL for secure access
*/