Skip to content

Commit

Permalink
✨ V1 downloads complete
Browse files Browse the repository at this point in the history
  • Loading branch information
devaryakjha committed Oct 17, 2023
1 parent f4bbc24 commit 3f7ab62
Show file tree
Hide file tree
Showing 21 changed files with 478 additions and 87 deletions.
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
8 changes: 7 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ PODS:
- Flutter
- audio_session (0.0.1):
- Flutter
- background_downloader (0.0.1):
- Flutter
- Firebase/Auth (10.15.0):
- Firebase/CoreOnly
- FirebaseAuth (~> 10.15.0)
Expand Down Expand Up @@ -83,6 +85,7 @@ PODS:
DEPENDENCIES:
- audio_service (from `.symlinks/plugins/audio_service/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- background_downloader (from `.symlinks/plugins/background_downloader/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- Flutter (from `Flutter`)
Expand Down Expand Up @@ -114,6 +117,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/audio_service/ios"
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
background_downloader:
:path: ".symlinks/plugins/background_downloader/ios"
firebase_auth:
:path: ".symlinks/plugins/firebase_auth/ios"
firebase_core:
Expand All @@ -137,6 +142,7 @@ SPEC CHECKSUMS:
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
background_downloader: 6f55e5548875be2ad4bb91b542558b5be22f339a
Firebase: 66043bd4579e5b73811f96829c694c7af8d67435
firebase_auth: b62e99e6ece589afe88ebe8919eb9563b52c384c
firebase_core: 28e84c2a4fcf6a50ef83f47b145ded8c1fa331e4
Expand All @@ -159,6 +165,6 @@ SPEC CHECKSUMS:
RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a

PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5

COCOAPODS: 1.13.0
132 changes: 65 additions & 67 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
@@ -1,71 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(BUNDLE_DISPLAY_NAME)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(BUNDLE_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>$(LAUNCH_SCREEN)</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UIStatusBarHidden</key>
<false/>
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
<string>$(GOOGLE_REVERSED_CLIENT_ID)</string>
</array>
</dict>
</array>
<!-- End of the Google Sign-in Section -->
</dict>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(BUNDLE_DISPLAY_NAME)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(BUNDLE_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>$(GOOGLE_REVERSED_CLIENT_ID)</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>
<key>UILaunchStoryboardName</key>
<string>$(LAUNCH_SCREEN)</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
6 changes: 4 additions & 2 deletions lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:varanasi_mobile_app/utils/router.dart';
import 'package:varanasi_mobile_app/widgets/responsive_sizer.dart';

import 'cubits/config/config_cubit.dart';
import 'cubits/download/download_cubit.dart';
import 'cubits/player/player_cubit.dart';
import 'utils/theme.dart';

Expand All @@ -19,14 +20,15 @@ class Varanasi extends StatelessWidget {
builder: (context, orientation, screenType) {
return MultiBlocProvider(
providers: [
BlocProvider(lazy: false, create: (_) => DownloadCubit()..init()),
BlocProvider(
lazy: false,
create: (context) => ConfigCubit()..init(),
),
BlocProvider(
lazy: false,
create: (context) =>
MediaPlayerCubit(() => context.read<ConfigCubit>())..init(),
create: (ctx) =>
MediaPlayerCubit(() => ctx.read<ConfigCubit>())..init(),
),
],
child: Builder(builder: (context) {
Expand Down
157 changes: 157 additions & 0 deletions lib/cubits/download/download_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import 'dart:async';

import 'package:background_downloader/background_downloader.dart';
import 'package:equatable/equatable.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:rxdart/rxdart.dart';
import 'package:varanasi_mobile_app/models/download.dart';
import 'package:varanasi_mobile_app/models/media_playlist.dart';
import 'package:varanasi_mobile_app/models/playable_item.dart';
import 'package:varanasi_mobile_app/models/song.dart';
import 'package:varanasi_mobile_app/utils/app_cubit.dart';
import 'package:varanasi_mobile_app/utils/constants/strings.dart';
import 'package:varanasi_mobile_app/utils/logger.dart';

part 'download_state.dart';

class DownloadCubit extends AppCubit<DownloadState> {
DownloadCubit() : super(const DownloadState());

late final Box<DownloadedMedia> _downloadBox;
late final FileDownloader _downloader;

late final Map<String, Song> _expando;

Logger get _logger => Logger.instance;

@override
FutureOr<void> init() async {
_expando = {};
_downloadBox =
await Hive.openBox<DownloadedMedia>(AppStrings.downloadBoxName);
_downloader = FileDownloader();
_downloader.updates.listen((update) {
if (update is TaskStatusUpdate) {
_handleTaskStatusUpdate(update);
} else if (update is TaskProgressUpdate) {
_handleTaskProgressUpdate(update);
}
});
}

void _handleTaskProgressUpdate(TaskProgressUpdate update) {
final item = _downloadBox.get(update.task.taskId);
if (item != null) {
_downloadBox.put(
update.task.taskId,
item.copyWith(progress: update.progress),
);
_logger.i('Dl pro for ${item.id}: ${item.progress}');
}
}

void _handleTaskStatusUpdate(TaskStatusUpdate update) async {
if (update.status == TaskStatus.enqueued) {
_logger.i('Download enqueued for ${update.task.taskId}');
_downloadBox.put(
update.task.taskId,
DownloadedMedia(
id: update.task.taskId,
progress: 0,
downloadComplete: false,
media: _expando[update.task.taskId]!,
path: '',
),
);
return;
}
final item = _downloadBox.get(update.task.taskId);
if (update.status.isNotFinalState && item != null) {
_downloadBox.put(
item.id,
item.copyWith(downloading: update.status == TaskStatus.running),
);
_logger.i('Download status for ${update.task.taskId}: '
'${update.status}');
return;
}

if (item != null) {
if (update.status == TaskStatus.complete) {
final path = await update.task.filePath();
_downloadBox.put(
item.id,
item.copyWith(
downloadComplete: true,
progress: 1,
path: path,
downloading: false,
),
);
_logger.i('Download complete for ${item.id} path: $path');
} else if (update.status.isFinalState) {
_downloadBox.delete(item.id);
_logger.i('Download failed for ${item.id}');
}
}
}

String _fileNameFromSong(Song song) {
final ext = song.itemUrl.split('.').last;
return '${song.itemId}.$ext';
}

DownloadTask _songToTask(Song song) {
final fileName = _fileNameFromSong(song);
return DownloadTask(
taskId: song.itemId,
url: song.itemUrl,
filename: fileName,
updates: Updates.statusAndProgress,
);
}

Future<void> downloadSong(PlayableMedia song) async {
assert(song is Song, 'Only songs can be downloaded');
if (song is! Song) return;
_expando[song.itemId] = song;
final queued = await _downloader.enqueue(_songToTask(song));
_logger.i('Queued ${song.itemId} status: $queued');
}

Future<void> batchDownload(MediaPlaylist playlist) async {
final songs = playlist.mediaItems ?? [];
final filteredsong = songs.whereType<Song>();
final tasks = filteredsong.map(_songToTask).toList();
for (final song in filteredsong) {
_expando[song.itemId] = song;
}
await _downloader.downloadBatch(
tasks,
taskStatusCallback: _handleTaskStatusUpdate,
taskProgressCallback: _handleTaskProgressUpdate,
);
}

Future<void> cancelDownload(PlayableMedia media) =>
_downloader.cancelTaskWithId(media.itemId);

/// Returns a stream of [DownloadedMedia] for the given [song].
///
/// The stream will emit the current download status of the song and
///
/// will continue to emit updates until the download is complete or
Stream<DownloadedMedia?> listen(PlayableMedia song) {
final curr = _downloadBox.get(song.itemId);
return Rx.concat([
if (curr != null) Stream.value(curr),
_downloadBox.watch(key: song.itemId).map((e) => e.value)
]);
}

DownloadedMedia? getDownloadedMedia(PlayableMedia song) =>
_downloadBox.get(song.itemId);

Future<void> deleteDownloadedMedia(PlayableMedia song) =>
_downloadBox.delete(song.itemId);
}
8 changes: 8 additions & 0 deletions lib/cubits/download/download_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
part of 'download_cubit.dart';

class DownloadState extends Equatable {
const DownloadState();

@override
List<Object> get props => [];
}
2 changes: 1 addition & 1 deletion lib/features/home/bloc/home_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ part 'home_state.dart';
class HomeBloc extends Cubit<HomeState> {
HomeBloc() : super(const HomeInitialState());

FutureOr<void> fetchModule({bool refetch = true}) async {
FutureOr<void> fetchModule({bool refetch = false}) async {
try {
emit(const HomeLoadingState());
final modules =
Expand Down
2 changes: 1 addition & 1 deletion lib/features/library/cubit/library_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class LibraryError<T> extends LibraryState {
this.error, {
this.stackTrace,
}) {
Logger.instance.e(error.toString(), error, stackTrace);
Logger.instance.e(error.toString(), error: error, stackTrace: stackTrace);
}

@override
Expand Down
Loading

0 comments on commit 3f7ab62

Please sign in to comment.