Files
streamyfin/DOWNLOAD_SYSTEM_REFACTOR.md
Fredrik Burmester 8d59065c49 fix: building
2025-10-02 20:54:25 +02:00

8.7 KiB

Download System Refactor - Summary

Overview

The download system has been completely refactored to use a new native iOS module for background downloads, replacing the third-party library with a custom, streamlined solution.

What Was Done

1. Created New Native Module (BackgroundDownloader)

Location: modules/background-downloader/

A complete Expo native module for iOS background file downloads:

  • Swift Implementation: BackgroundDownloaderModule.swift

    • Uses NSURLSession with background configuration
    • Implements URLSessionDownloadDelegate for progress tracking
    • Handles iOS background session events via AppDelegate subscriber
    • Session ID: com.fredrikburmester.streamyfin.backgrounddownloader
  • TypeScript Interface: Full type-safe API with events

    • startDownload(url, destinationPath?) - Start a download
    • cancelDownload(taskId) - Cancel a specific download
    • cancelAllDownloads() - Cancel all downloads
    • getActiveDownloads() - List active downloads
  • Events:

    • onDownloadProgress - Progress updates with bytes/percentage
    • onDownloadComplete - Completion with file path
    • onDownloadError - Error handling
    • onDownloadStarted - Download initiation confirmation
  • Documentation: Complete README with usage examples

2. Rewrote DownloadProvider

Location: providers/DownloadProvider.tsx

A simplified, focused implementation:

Features Included:

  • Video file downloads with progress tracking
  • Background download support
  • Database persistence (same format as before)
  • Movies and TV episodes support
  • Download notifications (success/error)
  • File deletion and management
  • Size calculation
  • Same context API for backward compatibility

Features Removed (for simplicity):

  • Trickplay image downloads
  • Subtitle downloads
  • Queue management with concurrent limits
  • Pause/Resume (can be added back easily)
  • Download speed/ETA calculations
  • Cache directory management

Key Improvements:

  • Event-driven architecture (no polling)
  • Better background handling via native module
  • Cleaner, more maintainable code
  • Proper TypeScript typing throughout
  • Simplified state management

3. Preserved Old Implementation

Location: providers/DownloadProvider.deprecated.tsx

The old implementation has been preserved for reference but should not be used.

4. Documentation

Created comprehensive documentation:

  • Module README: modules/background-downloader/README.md

    • API reference
    • Usage examples
    • Implementation details
  • Migration Guide: providers/Downloads/MIGRATION.md

    • What changed
    • API compatibility matrix
    • Migration steps
    • Troubleshooting guide
  • System README: providers/Downloads/README.md

    • Architecture overview
    • Type definitions
    • Usage examples
    • File storage details

Technical Details

Background Download Implementation

The native module uses iOS's recommended approach:

  1. Background URLSession: Persistent identifier ensures downloads continue
  2. Delegate Pattern: Progress and completion via delegate callbacks
  3. AppDelegate Integration: Handles system wake-ups for download events
  4. Completion Handler: Properly signals iOS when background work is done

State Management

// Active downloads tracked in Jotai atom
const processesAtom = atom<JobStatus[]>([]);

// Task ID to Process ID mapping for event correlation
const taskMap = Map<number, string>();

// Persistent database in MMKV
storage.set('downloads.v2.json', JSON.stringify(database));

Download Flow

User initiates download
  ↓
Create JobStatus & add to processes
  ↓
Generate destination path
  ↓
Start native BackgroundDownloader
  ↓
Map taskId ↔ processId
  ↓
Receive progress events → Update UI
  ↓
Receive completion event
  ↓
Move file to permanent location
  ↓
Save to database
  ↓
Send notification
  ↓
Clean up process

Configuration

iOS Background Modes

Already configured in app.json:

{
  "ios": {
    "infoPlist": {
      "UIBackgroundModes": ["audio", "fetch"]
    }
  }
}

The "fetch" mode enables background URLSession downloads.

Module Integration

The module is:

  • Auto-linked via Expo
  • Exported from modules/index.ts
  • Type-safe with full TypeScript support
  • Registered as AppDelegate subscriber

Breaking Changes

None for Normal Usage

The public API remains the same for the most common operations:

const { 
  startBackgroundDownload,
  cancelDownload,
  getDownloadedItems,
  deleteFile,
  processes
} = useDownload();

No-op Methods

These methods exist but do nothing (for compatibility):

  • pauseDownload()
  • resumeDownload()
  • startDownload() (use startBackgroundDownload instead)
  • deleteFileByType()
  • cleanCacheDirectory()
  • updateDownloadedItem()
  • dumpDownloadDiagnostics()

Testing Checklist

Before deployment, test:

  • Download a movie
  • Download a TV episode
  • Download multiple items simultaneously
  • Cancel an active download
  • Delete a downloaded item
  • View list of downloads
  • Background the app during download
  • Force quit and restart (download should be cancelled)
  • Verify notifications appear
  • Check file integrity and playback
  • Verify database persistence
  • Check storage calculations

Next Steps

Immediate

  1. Rebuild iOS app:

    npx expo prebuild -p ios
    cd ios && pod install && cd ..
    npx expo run:ios
    
  2. Test thoroughly: Use the testing checklist above

  3. Monitor for issues: Check console logs and user reports

Future Enhancements

Priority features to add back:

  1. Pause/Resume (High Priority)

    • Easy to implement with NSURLSession
    • User-requested feature
  2. Queue Management (Medium Priority)

    • Concurrent download limits
    • Automatic queue processing
  3. Progress Persistence (Medium Priority)

    • Resume interrupted downloads after app restart
    • Save download state to database
  4. Trickplay & Subtitles (Low Priority)

    • Re-add auxiliary file downloads
    • Integrate with video playback
  5. Download Analytics (Low Priority)

    • Speed calculation
    • ETA estimation
    • Failure rate tracking

Known Limitations

  1. iOS Only: Android support not yet implemented
  2. No Pause: Cannot pause/resume downloads yet
  3. No Queue: All downloads start immediately
  4. Force Quit: Downloads cancelled if app is force-quit (iOS limitation)
  5. No Persistence: Downloads lost if app crashes or is terminated

Performance Improvements

Over the old system:

  • Better Background Support: True iOS background sessions
  • Event-Driven: No polling, immediate updates
  • Lower Overhead: Removed unused features
  • Native Integration: Tighter iOS system integration
  • Cleaner Code: Easier to maintain and extend

Dependencies Changed

Removed

  • @kesha-antonov/react-native-background-downloader

Added

  • Custom BackgroundDownloader native module (local)

No external dependencies added, reducing maintenance burden.

File Changes Summary

New Files

  • modules/background-downloader/ (entire module)
  • providers/Downloads/MIGRATION.md
  • providers/Downloads/README.md
  • DOWNLOAD_SYSTEM_REFACTOR.md (this file)

Modified Files

  • modules/index.ts (exported new module)
  • providers/DownloadProvider.tsx (complete rewrite)

Renamed Files

  • providers/DownloadProvider.tsxproviders/DownloadProvider.deprecated.tsx

Unchanged

  • providers/Downloads/types.ts (types remain the same)
  • Database format and storage location
  • Public API for most common operations

Rollback Plan

If issues arise:

  1. Rename DownloadProvider.deprecated.tsx back to DownloadProvider.tsx
  2. Remove the background-downloader module export from modules/index.ts
  3. Re-install @kesha-antonov/react-native-background-downloader
  4. Rebuild the app

Note: The database format is unchanged, so existing downloads will work with either version.

Success Metrics

The refactor is successful if:

  • Downloads work in foreground
  • Downloads continue in background
  • Progress updates are accurate
  • Notifications work correctly
  • Files are saved and playable
  • No crashes or memory leaks
  • Performance is equal or better
  • Code is cleaner and more maintainable

Conclusion

This refactor provides a solid foundation for the download system moving forward. The native module approach gives us full control over the download experience and makes it easier to add features in the future.

The simplified DownloadProvider focuses on core functionality while maintaining API compatibility, making this a low-risk, high-reward change.