Initial commit - SoundWave v1.0
- Full PWA support with offline capabilities - Comprehensive search across songs, playlists, and channels - Offline playlist manager with download tracking - Pre-built frontend for zero-build deployment - Docker-based deployment with docker compose - Material-UI dark theme interface - YouTube audio download and management - Multi-user authentication support
This commit is contained in:
commit
51679d1943
254 changed files with 37281 additions and 0 deletions
222
docs/AUDIO_SEEKING_FIX.md
Normal file
222
docs/AUDIO_SEEKING_FIX.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
# Audio Seeking Fix - HTTP Range Request Support
|
||||
|
||||
## Issue
|
||||
When users attempted to seek through playing audio files (especially YouTube downloads), the progress bar would reset to the start. This issue only affected downloaded files; local files uploaded by users worked correctly.
|
||||
|
||||
## Root Cause
|
||||
The backend was using Django's default `serve` view to deliver media files, which does not support HTTP Range requests. When a browser seeks in an audio/video file, it sends a Range header requesting specific byte ranges. Without proper Range support:
|
||||
|
||||
1. Browser requests bytes at a specific position (e.g., "Range: bytes=1000000-")
|
||||
2. Server returns entire file with 200 OK instead of partial content with 206 Partial Content
|
||||
3. Browser receives data from the beginning, causing the player to restart
|
||||
|
||||
## Solution
|
||||
Implemented a custom media streaming view (`serve_media_with_range`) with full HTTP Range request support:
|
||||
|
||||
### Key Features
|
||||
|
||||
#### 1. HTTP Range Request Support
|
||||
- **206 Partial Content**: Returns only requested byte ranges
|
||||
- **Accept-Ranges header**: Advertises range support to browsers
|
||||
- **Content-Range header**: Specifies byte range being returned
|
||||
- **416 Range Not Satisfiable**: Properly handles invalid range requests
|
||||
|
||||
#### 2. Security Enhancements
|
||||
- **Path Traversal Prevention**: Blocks `..`, absolute paths, and backslashes
|
||||
- **Symlink Attack Prevention**: Verifies resolved paths stay within document root
|
||||
- **Directory Listing Prevention**: Only serves files, not directories
|
||||
- **Authentication Integration**: Works with Django's authentication middleware
|
||||
- **Security Logging**: Logs suspicious access attempts
|
||||
|
||||
#### 3. Performance Optimizations
|
||||
- **Streaming Iterator**: Processes files in 8KB chunks to avoid memory issues
|
||||
- **Cache Headers**: Sets appropriate caching (1 hour) for better performance
|
||||
- **Last-Modified Headers**: Enables conditional requests
|
||||
|
||||
#### 4. Content Type Detection
|
||||
Automatically detects and sets proper MIME types for audio formats:
|
||||
- `.mp3` → `audio/mpeg`
|
||||
- `.m4a` → `audio/mp4`
|
||||
- `.webm` → `video/webm`
|
||||
- `.ogg` → `audio/ogg`
|
||||
- `.wav` → `audio/wav`
|
||||
- `.flac` → `audio/flac`
|
||||
- `.aac` → `audio/aac`
|
||||
- `.opus` → `audio/opus`
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Backend Changes
|
||||
|
||||
#### 1. `/backend/common/streaming.py` (NEW)
|
||||
Custom streaming view with Range request support. This is the core fix that enables seeking.
|
||||
|
||||
**Key Functions:**
|
||||
- `range_file_iterator()`: Efficiently streams file chunks with offset support
|
||||
- `serve_media_with_range()`: Main view handling Range requests and security
|
||||
|
||||
#### 2. `/backend/config/urls.py`
|
||||
Updated media URL pattern to use the new streaming view:
|
||||
|
||||
```python
|
||||
# Before
|
||||
re_path(r'^media/(?P<path>.*)$', serve, {...})
|
||||
|
||||
# After
|
||||
re_path(r'^media/(?P<path>.*)$', serve_media_with_range, {...})
|
||||
```
|
||||
|
||||
### Security Analysis
|
||||
|
||||
#### Path Security
|
||||
✅ **Directory Traversal**: Blocked by checking for `..`, `/`, and `\\`
|
||||
✅ **Symlink Attacks**: Prevented by verifying resolved path stays in document_root
|
||||
✅ **Directory Listing**: Only files are served, directories return 404
|
||||
|
||||
#### Authentication & Authorization
|
||||
✅ **User Authentication**: Handled by Django middleware before view
|
||||
✅ **User Isolation**: Audio models have `owner` field with proper filtering
|
||||
✅ **Admin Access**: Admins can access all files through middleware
|
||||
|
||||
#### Content Security
|
||||
✅ **Content-Type**: Proper MIME types prevent content sniffing attacks
|
||||
✅ **Inline Disposition**: Files play inline rather than forcing download
|
||||
✅ **File Validation**: Verifies file exists and is readable
|
||||
|
||||
#### Audit Trail
|
||||
✅ **Security Logging**: Suspicious access attempts are logged
|
||||
✅ **Debug Logging**: File not found errors are logged for troubleshooting
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Functional Testing
|
||||
- [x] ✅ Seeking works in YouTube downloaded files
|
||||
- [x] ✅ Seeking works in user-uploaded local files
|
||||
- [x] ✅ Full file playback works (non-Range requests)
|
||||
- [x] ✅ PWA mobile playback with seeking
|
||||
- [x] ✅ Desktop browser playback with seeking
|
||||
|
||||
### Security Testing
|
||||
- [x] ✅ Directory traversal attempts blocked (`../../../etc/passwd`)
|
||||
- [x] ✅ Absolute path attempts blocked (`/etc/passwd`)
|
||||
- [x] ✅ Symlink attacks prevented (resolved path verification)
|
||||
- [x] ✅ Unauthenticated access blocked (middleware)
|
||||
- [x] ✅ User isolation maintained (can't access other users' files)
|
||||
|
||||
### Performance Testing
|
||||
- [x] ✅ Large file streaming (no memory issues)
|
||||
- [x] ✅ Multiple simultaneous streams
|
||||
- [x] ✅ Cache headers work correctly
|
||||
- [x] ✅ Chunk-based delivery efficient
|
||||
|
||||
### Browser Compatibility
|
||||
- [x] ✅ Chrome/Edge (Chromium)
|
||||
- [x] ✅ Firefox
|
||||
- [x] ✅ Safari (iOS/macOS)
|
||||
- [x] ✅ Mobile browsers (PWA)
|
||||
|
||||
## HTTP Range Request Examples
|
||||
|
||||
### Full File Request (No Range)
|
||||
```
|
||||
GET /media/audio/example.mp3
|
||||
→ 200 OK
|
||||
Content-Length: 5000000
|
||||
Content-Type: audio/mpeg
|
||||
Accept-Ranges: bytes
|
||||
```
|
||||
|
||||
### Seek to Middle (Range Request)
|
||||
```
|
||||
GET /media/audio/example.mp3
|
||||
Range: bytes=2500000-
|
||||
→ 206 Partial Content
|
||||
Content-Length: 2500000
|
||||
Content-Range: bytes 2500000-4999999/5000000
|
||||
Content-Type: audio/mpeg
|
||||
Accept-Ranges: bytes
|
||||
```
|
||||
|
||||
### Specific Range Request
|
||||
```
|
||||
GET /media/audio/example.mp3
|
||||
Range: bytes=1000000-2000000
|
||||
→ 206 Partial Content
|
||||
Content-Length: 1000001
|
||||
Content-Range: bytes 1000000-2000000/5000000
|
||||
Content-Type: audio/mpeg
|
||||
```
|
||||
|
||||
### Invalid Range Request
|
||||
```
|
||||
GET /media/audio/example.mp3
|
||||
Range: bytes=9999999-
|
||||
→ 416 Range Not Satisfiable
|
||||
Content-Range: bytes */5000000
|
||||
```
|
||||
|
||||
## User Impact
|
||||
|
||||
### Before Fix
|
||||
❌ Seeking would restart playback from beginning
|
||||
❌ Poor user experience with downloaded files
|
||||
❌ PWA mobile seeking broken
|
||||
❌ Users had to reload entire file to seek
|
||||
|
||||
### After Fix
|
||||
✅ Smooth seeking to any position
|
||||
✅ Instant response to seek operations
|
||||
✅ Works consistently for all file types
|
||||
✅ Better mobile/PWA experience
|
||||
✅ Reduced bandwidth usage (only requested ranges transferred)
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
### Container Restart Required
|
||||
The fix requires restarting the Django application to load the new module:
|
||||
```bash
|
||||
docker compose restart soundwave
|
||||
```
|
||||
|
||||
### No Database Migrations
|
||||
No database changes are required - this is a pure code update.
|
||||
|
||||
### No Configuration Changes
|
||||
Default settings work for all users. No environment variables or settings updates needed.
|
||||
|
||||
### Backwards Compatible
|
||||
- Existing files continue to work
|
||||
- Non-Range requests still supported
|
||||
- No breaking changes to API
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Potential Improvements
|
||||
1. **Rate Limiting**: Add per-user bandwidth throttling
|
||||
2. **Analytics**: Track seeking patterns for insights
|
||||
3. **CDN Integration**: Add support for CDN/proxy caching
|
||||
4. **Compression**: Consider gzip/brotli for text-based formats
|
||||
5. **Adaptive Streaming**: HLS/DASH support for better quality adaptation
|
||||
|
||||
### Monitoring
|
||||
Consider adding metrics for:
|
||||
- Range request success rate
|
||||
- Average seek time
|
||||
- Bandwidth usage by file type
|
||||
- Failed seek attempts
|
||||
|
||||
## References
|
||||
|
||||
- [HTTP Range Requests (RFC 7233)](https://tools.ietf.org/html/rfc7233)
|
||||
- [Django File Serving Best Practices](https://docs.djangoproject.com/en/stable/howto/static-files/deployment/)
|
||||
- [HTML5 Audio/Video Seeking](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking)
|
||||
|
||||
## Date
|
||||
December 16, 2025
|
||||
|
||||
## Status
|
||||
✅ **IMPLEMENTED AND DEPLOYED**
|
||||
|
||||
---
|
||||
|
||||
**Note**: This fix ensures all users (admin and managed users) can seek through audio files without issues. The implementation maintains security, performance, and compatibility while providing a significantly improved user experience.
|
||||
Loading…
Add table
Add a link
Reference in a new issue