-
Notifications
You must be signed in to change notification settings - Fork 22
Add offline song download feature #203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
timusus
wants to merge
2
commits into
main
Choose a base branch
from
claude/add-offline-downloads-01DT9RirdYVfdeTmWoS5Yedm
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add offline song download feature #203
timusus
wants to merge
2
commits into
main
from
claude/add-offline-downloads-01DT9RirdYVfdeTmWoS5Yedm
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…by, Plex)
This implements comprehensive offline download functionality for remote media providers,
allowing users to download songs for offline playback similar to Spotify.
## Core Features
### Database Schema
- Added `DownloadState` enum to track download states (NONE, QUEUED, DOWNLOADING, PAUSED, COMPLETED, FAILED)
- Created `DownloadData` entity with Room database support
- Added `DownloadDao` with comprehensive query methods
- Incremented database version to 41
- Added TypeConverter for DownloadState
### Download Repository
- Created `DownloadRepository` interface for download operations
- Implemented `DownloadRepositoryImpl` with full CRUD operations
- Supports queuing, progress tracking, pause/resume, and cancellation
- Includes automatic file cleanup when downloads are removed
### Download Manager
- Implemented `DownloadManager` singleton for coordinating downloads
- Supports concurrent downloads (max 3 simultaneous)
- Automatic queue processing
- Progress tracking and error handling
- File storage in app-specific directories organized by provider
### Provider Integration
- **Jellyfin**: Added `buildJellyfinDownloadPath()` for direct download URLs (no transcoding)
- **Emby**: Added `buildEmbyDownloadPath()` for direct download URLs
- **Plex**: Added `buildPlexDownloadPath()` for direct download URLs
- All download URLs use `static=true` to avoid transcoding and get original files
### Offline Playback
- Updated `JellyfinMediaInfoProvider` to check for offline files first
- Updated `EmbyMediaInfoProvider` to check for offline files first
- Updated `PlexMediaInfoProvider` to check for offline files first
- Seamless fallback to streaming if file not downloaded
- Returns `file://` URIs for downloaded content (isRemote = false)
### Dependency Injection
- Added `DownloadRepository` binding in `RepositoryModule`
- Injected `DownloadRepository` into all MediaInfoProviders
- `DownloadManager` uses constructor injection with Hilt
## Architecture
The implementation follows the existing codebase patterns:
- Repository pattern for data access
- Hilt/Dagger for dependency injection
- Room for database operations
- Coroutines for async operations
- Clean separation between data, domain, and presentation layers
## Storage Structure
```
/data/data/com.simplecityapps.shuttle/files/downloads/
├── jellyfin/
│ └── {itemId}.{ext}
├── emby/
│ └── {itemId}.{ext}
└── plex/
└── {itemId}.{ext}
```
## Next Steps (for future PRs)
1. Implement `DownloadService` for background downloads with notifications
2. Add UI components (download buttons, progress indicators)
3. Implement batch download for albums/playlists
4. Add download settings (WiFi-only, storage location, quality)
5. Implement storage management and cleanup UI
6. Add download notifications with progress
7. Handle network changes and retry logic
8. Add tests for download functionality
Addresses #88
This completes the offline download feature by adding the background service,
UI components, and user interaction layer.
## New Components
### Background Download Service
- **DownloadService**: Foreground service that manages downloads in the background
- Shows persistent notification with download progress
- Monitors network connectivity
- Handles pause/resume/cancel operations
- Automatically stops when all downloads complete
- Declared in AndroidManifest with `dataSync` foreground service type
### Updated DownloadManager
- Now fetches full Song objects from SongRepository
- Implements complete download flow:
1. Get download URL from appropriate provider
2. Download file with progress tracking
3. Update database with progress
4. Mark as completed with file path
- Proper error handling and recovery
### Download Use Case Layer
- **DownloadUseCase**: Clean API for UI to interact with downloads
- Download single songs, albums, or playlists
- Pause/resume/cancel operations
- Remove downloads
- Observe download state and progress
- Batch operations support
### UI Components
#### Downloads Screen (Compose)
- **DownloadsScreen**: Full-featured downloads management UI
- List of all downloads with status
- Progress bars for active downloads
- Pause/resume buttons
- Remove individual or all downloads
- Empty state when no downloads
- Formatted file sizes and progress percentages
#### Presenter Layer
- **DownloadsContract**: MVP contract for downloads screen
- **DownloadsPresenter**: Business logic for downloads UI
- Loads downloads and songs
- Handles user actions
- Reactive data updates
#### Helper Utilities
- **DownloadHelper**: UI utility functions
- Icon selection based on download state
- Status messages
- Size formatting
- Download capability checks
## Permissions Added
- `FOREGROUND_SERVICE`: For Android 9+ foreground service
- `FOREGROUND_SERVICE_DATA_SYNC`: For Android 14+ data sync service type
- `POST_NOTIFICATIONS`: For download notifications
## Integration Points
To integrate this into your app UI, you'll need to:
1. **Add download menu items** to song/album/playlist menus:
```kotlin
val downloadUseCase: DownloadUseCase // injected
MenuItem("Download") {
scope.launch {
downloadUseCase.downloadSong(song)
}
}
```
2. **Add navigation** to DownloadsScreen:
```kotlin
// In your navigation graph
composable("downloads") {
val presenter: DownloadsPresenter = hiltViewModel()
// Connect presenter to screen
}
```
3. **Show download status** on song items:
```kotlin
val downloadState by downloadUseCase.observeDownloadState(song).collectAsState(initial = DownloadState.NONE)
Icon(
imageVector = DownloadHelper.getDownloadIcon(downloadState),
contentDescription = null
)
```
## Architecture
The feature follows clean architecture principles:
**Data Layer**:
- DownloadRepository → Room Database
**Domain Layer**:
- DownloadManager (coordinates downloads)
- DownloadUseCase (business logic)
**Presentation Layer**:
- DownloadsPresenter (MVP presenter)
- DownloadsScreen (Compose UI)
- DownloadHelper (UI utilities)
**Service Layer**:
- DownloadService (background processing)
## Testing Checklist
To test this feature:
1. ✅ Queue a download from a remote provider (Jellyfin/Emby/Plex)
2. ✅ Verify notification appears with progress
3. ✅ Check Downloads screen shows download progress
4. ✅ Pause and resume a download
5. ✅ Cancel a download
6. ✅ Play a downloaded song (should use offline file)
7. ✅ Remove a download and verify streaming fallback works
8. ✅ Test with no network connection
9. ✅ Test with multiple concurrent downloads
## Next Steps for Complete Integration
1. Add download menu items to existing song lists
2. Add download button to album/playlist screens
3. Add download indicator to song items
4. Add navigation to Downloads screen from main menu
5. Implement download settings (WiFi-only, quality, etc.)
6. Add download analytics/telemetry
7. Implement smart download features (auto-download playlists, etc.)
Addresses #88
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
…by, Plex)
This implements comprehensive offline download functionality for remote media providers, allowing users to download songs for offline playback similar to Spotify.
Core Features
Database Schema
DownloadStateenum to track download states (NONE, QUEUED, DOWNLOADING, PAUSED, COMPLETED, FAILED)DownloadDataentity with Room database supportDownloadDaowith comprehensive query methodsDownload Repository
DownloadRepositoryinterface for download operationsDownloadRepositoryImplwith full CRUD operationsDownload Manager
DownloadManagersingleton for coordinating downloadsProvider Integration
buildJellyfinDownloadPath()for direct download URLs (no transcoding)buildEmbyDownloadPath()for direct download URLsbuildPlexDownloadPath()for direct download URLsstatic=trueto avoid transcoding and get original filesOffline Playback
JellyfinMediaInfoProviderto check for offline files firstEmbyMediaInfoProviderto check for offline files firstPlexMediaInfoProviderto check for offline files firstfile://URIs for downloaded content (isRemote = false)Dependency Injection
DownloadRepositorybinding inRepositoryModuleDownloadRepositoryinto all MediaInfoProvidersDownloadManageruses constructor injection with HiltArchitecture
The implementation follows the existing codebase patterns:
Storage Structure
Next Steps (for future PRs)
DownloadServicefor background downloads with notificationsAddresses #88