From d60d41a119a489edf77a38bb4e87c7d51f4e5046 Mon Sep 17 00:00:00 2001 From: Achraf Labidi Date: Sat, 3 Feb 2024 20:42:26 +0100 Subject: [PATCH] fix downloading type error --- lib/l10n/app_de.arb | 5 +- lib/l10n/app_en.arb | 5 +- lib/l10n/app_es.arb | 5 +- lib/l10n/app_fr.arb | 5 +- lib/models/download/download_state_model.dart | 23 ++++- lib/utils/tools.dart | 1 - lib/view_models/download_view_model.dart | 9 +- .../components/small_stream_card.dart | 4 +- .../video_view/utils/download_service.dart | 87 ++++++++++++++++++ lib/views/video_view/video_player.dart | 91 +++++-------------- 10 files changed, 149 insertions(+), 86 deletions(-) create mode 100644 lib/views/video_view/utils/download_service.dart diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index eab6a60..57502ef 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -95,5 +95,8 @@ "enter_your_password": "Geben Sie Ihr Passwort ein", "home": "Startseite", "language_selection": "Sprache", - "language_selection_description": "Wählen Sie Ihre bevorzugte Sprache aus" + "language_selection_description": "Wählen Sie Ihre bevorzugte Sprache aus", + "download_completed": "Download abgeschlossen", + "download_failed": "Download fehlgeschlagen", + "download_canceled": "Download abgebrochen" } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ec2a657..cc6c536 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -95,5 +95,8 @@ "enter_your_password": "Enter your password", "home": "Home", "language_selection": "Language", - "language_selection_description": "Select your preferred language" + "language_selection_description": "Select your preferred language", + "download_completed": "Download Completed", + "download_failed": "Download Failed", + "download_cancelled": "Download Cancelled" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 9d554f8..7741a49 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -95,5 +95,8 @@ "enter_your_password": "Introduce tu contraseña", "home": "Inicio", "language_selection": "Idioma", - "language_selection_description": "Selecciona tu idioma preferido." + "language_selection_description": "Selecciona tu idioma preferido.", + "download_complete": "Descarga completa", + "download_failed": "Error de descarga", + "download_cancelled": "Descarga cancelada" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 0490725..a95708b 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -95,5 +95,8 @@ "enter_your_password": "Entrez votre mot de passe", "home": "Accueil", "language_selection": "Langue", - "language_selection_description": "Choisissez votre langue préférée." + "language_selection_description": "Choisissez votre langue préférée.", + "download_competed": "Téléchargement terminé", + "download_failed": "Échec du téléchargement", + "download_cancelled": "Téléchargement annulé" } diff --git a/lib/models/download/download_state_model.dart b/lib/models/download/download_state_model.dart index 3e6e0f0..1519156 100644 --- a/lib/models/download/download_state_model.dart +++ b/lib/models/download/download_state_model.dart @@ -9,10 +9,10 @@ class DownloadState { }); DownloadState copyWith({ - Map? downloadedVideos, + required Map downloadedVideos, }) { return DownloadState( - downloadedVideos: downloadedVideos ?? this.downloadedVideos, + downloadedVideos: downloadedVideos, ); } } @@ -21,7 +21,7 @@ class DownloadState { class VideoDetails { final String filePath; final String name; - final String duration; // Duration in seconds or your preferred unit + final String duration; final String description; final String date; @@ -48,4 +48,21 @@ class VideoDetails { date: date ?? this.date, ); } + + VideoDetails.fromJson(Map json) + : filePath = json['filePath'], + name = json['name'], + duration = json['duration'], + description = json['description'], + date = json['date']; + + Map toJson() => { + 'filePath': filePath, + 'name': name, + 'duration': duration, + 'description': description, + 'date': date, + }; + + } diff --git a/lib/utils/tools.dart b/lib/utils/tools.dart index 8846976..29d0c73 100644 --- a/lib/utils/tools.dart +++ b/lib/utils/tools.dart @@ -1,4 +1,3 @@ -import 'dart:ui'; import 'package:flutter/material.dart'; diff --git a/lib/view_models/download_view_model.dart b/lib/view_models/download_view_model.dart index 8f072e6..369c66a 100644 --- a/lib/view_models/download_view_model.dart +++ b/lib/view_models/download_view_model.dart @@ -65,12 +65,9 @@ class DownloadViewModel extends StateNotifier { 'date': streamDate, }; - // Convert video details map to JSON string - final videoDetailsJson = json.encode(videoDetailsMap); - // Save the JSON string in your SharedPreferences - final downloadedVideosJson = Map.from(state.downloadedVideos) - ..[streamIdInt] = videoDetailsJson; + final downloadedVideosJson = Map.from(state.downloadedVideos) + ..[streamIdInt] = VideoDetails.fromJson(videoDetailsMap); await prefs.setString( 'downloadedVideos', @@ -80,7 +77,7 @@ class DownloadViewModel extends StateNotifier { // Convert the JSON strings back to VideoDetails objects for the state final downloadedVideos = downloadedVideosJson.map((key, value) { - final videoDetailsMap = json.decode(value); + final videoDetailsMap = value.toJson(); final videoDetails = VideoDetails( filePath: videoDetailsMap['filePath'], name: videoDetailsMap['name'], diff --git a/lib/views/course_view/components/small_stream_card.dart b/lib/views/course_view/components/small_stream_card.dart index 4109b67..d9e0fbd 100644 --- a/lib/views/course_view/components/small_stream_card.dart +++ b/lib/views/course_view/components/small_stream_card.dart @@ -197,9 +197,9 @@ class SmallStreamCard extends StatelessWidget { // Determine the URL based on the availability of roomNumber final Uri url = roomNumber?.isNotEmpty ?? false ? Uri.parse( - 'https://nav.tum.de/room/$roomNumber') // Use roomNumber in URL if available + 'https://nav.tum.de/room/$roomNumber',) // Use roomNumber in URL if available : Uri.parse( - 'https://nav.tum.de/search?q=${Uri.encodeComponent(roomName ?? '')}'); // Fall back to search URL using roomName + 'https://nav.tum.de/search?q=${Uri.encodeComponent(roomName ?? '')}',); // Fall back to search URL using roomName return Align( alignment: Alignment.centerRight, diff --git a/lib/views/video_view/utils/download_service.dart b/lib/views/video_view/utils/download_service.dart new file mode 100644 index 0000000..c2a4dbc --- /dev/null +++ b/lib/views/video_view/utils/download_service.dart @@ -0,0 +1,87 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:gocast_mobile/providers.dart'; + + +class DownloadService { + final WidgetRef ref; + final bool Function() isWidgetMounted; + final Function(String message) onShowSnackBar; + final Function() showDownloadConfirmationDialog; + final Function() showMobileDataNotAllowedDialog; + final String startingDownloadMessage; + final String downloadNotAvailableMessage; + final String downloadCompletedMessage; + final String downloadFailedMessage; + final String donwloadCancelledMessage; + + + + DownloadService({ + required this.ref, + required this.isWidgetMounted, + required this.onShowSnackBar, + required this.startingDownloadMessage, + required this.downloadNotAvailableMessage, + required this.downloadCompletedMessage, + required this.downloadFailedMessage, + required this.donwloadCancelledMessage, + required this.showDownloadConfirmationDialog, + required this.showMobileDataNotAllowedDialog, + }); + + Future downloadVideo(Stream stream, String type, String streamName, String streamDate) async { + bool canDownload = await _handleDownloadConnectivity(stream, type); + if (!canDownload) return; + + String? downloadUrl; + for (var download in stream.downloads) { + if (download.friendlyName == type) { + downloadUrl = download.downloadURL; + break; + } + } + + if (downloadUrl == null) { + if (!isWidgetMounted()) return; + onShowSnackBar(downloadNotAvailableMessage); + return; + } + + onShowSnackBar(startingDownloadMessage); + + ref.read(downloadViewModelProvider.notifier) + .downloadVideo(downloadUrl, stream, streamName, streamDate) + .then((localPath) { + if (!isWidgetMounted()) return; + onShowSnackBar(localPath.isNotEmpty ? downloadCompletedMessage : downloadFailedMessage); + }); + } + + +Future _handleDownloadConnectivity(Stream stream, String type) async { + final isDownloadWithWifiOnly = ref + .watch(settingViewModelProvider) + .isDownloadWithWifiOnly; + var connectivityResult = await (Connectivity().checkConnectivity()); + // If 'Download Over Wi-Fi Only' is enabled and connected to mobile, show a dialog + if (connectivityResult == ConnectivityResult.mobile && isDownloadWithWifiOnly) { + if (!isWidgetMounted()) return false; + showMobileDataNotAllowedDialog(); + return false; + } + // If on mobile data and 'Download Over Wi-Fi Only' is disabled, ask for confirmation + if (connectivityResult == ConnectivityResult.mobile && !isDownloadWithWifiOnly) { + bool shouldProceed = await showDownloadConfirmationDialog(); + if (!isWidgetMounted()) return false; + if (!shouldProceed) { + onShowSnackBar(donwloadCancelledMessage); + return false; + } + } + return true; +} + + +} \ No newline at end of file diff --git a/lib/views/video_view/video_player.dart b/lib/views/video_view/video_player.dart index 216e61f..7e6f1c9 100644 --- a/lib/views/video_view/video_player.dart +++ b/lib/views/video_view/video_player.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; @@ -10,6 +9,7 @@ import 'package:gocast_mobile/views/chat_view/chat_view.dart'; import 'package:gocast_mobile/views/chat_view/inactive_view.dart'; import 'package:gocast_mobile/views/chat_view/poll_view.dart'; import 'package:gocast_mobile/views/video_view/utils/custom_video_control_bar.dart'; +import 'package:gocast_mobile/views/video_view/utils/download_service.dart'; import 'package:gocast_mobile/views/video_view/utils/video_player_handler.dart'; import 'package:gocast_mobile/views/video_view/video_player_controller.dart'; import 'package:intl/intl.dart'; @@ -270,51 +270,28 @@ class VideoPlayerPageState extends ConsumerState { } Future _downloadVideo(Stream stream, String type) async { - // Extract the "Combined" download URL from the Stream object - bool canDownload = await _handleDownloadConnectivity(stream, type); - if (!canDownload) return; // Exit if download should not proceed - - String? downloadUrl; - for (var download in stream.downloads) { - if (download.friendlyName == type) { - downloadUrl = download.downloadURL; - break; - } - } - if (downloadUrl == null) { - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text( - 'Download type "$type" not available for this lecture',),), - ); - return; - } - // Use the extracted URL for downloading - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(AppLocalizations.of(context)!.starting_download)), + final downloadService = DownloadService( + ref: ref, + isWidgetMounted: () => mounted, + onShowSnackBar: (message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)), + ); + }, + startingDownloadMessage: AppLocalizations.of(context)!.starting_download, + downloadNotAvailableMessage: AppLocalizations.of(context)!.download_not_allowed, + downloadCompletedMessage: AppLocalizations.of(context)!.download_completed, + downloadFailedMessage: AppLocalizations.of(context)!.download_failed, + donwloadCancelledMessage: AppLocalizations.of(context)!.download_cancelled, + showDownloadConfirmationDialog: _showDownloadConfirmationDialog, + showMobileDataNotAllowedDialog: _showMobileDataNotAllowedDialog, ); - // Call the download function from the StreamViewModel String streamName = stream.name != '' ? stream.name : 'Lecture: ${DateFormat('EEEE. dd', Localizations.localeOf(context).toString()).format(stream.start.toDateTime())}'; String streamDate = DateFormat('dd MMMM yyyy', Localizations.localeOf(context).toString()).format(stream.start.toDateTime()); - ref - .read(downloadViewModelProvider.notifier) - .downloadVideo(downloadUrl, stream, streamName, streamDate) - .then((localPath) { - if (localPath.isNotEmpty) { - // Download successful - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Video Downloaded')), - ); - } else { - // Download failed, but not due to Wi-Fi (since it would throw an exception) - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Download failed')), - ); - } - }); + downloadService.downloadVideo(stream, type, streamName, streamDate); } + Future _showDownloadConfirmationDialog() async { return await showDialog( context: context, @@ -342,6 +319,8 @@ class VideoPlayerPageState extends ConsumerState { }, ) ?? false; // If dialog is dismissed, return false } + + void _showMobileDataNotAllowedDialog() { showDialog( context: context, @@ -363,34 +342,6 @@ class VideoPlayerPageState extends ConsumerState { }, ); } - - Future _handleDownloadConnectivity(Stream stream, String type) async { - final isDownloadWithWifiOnly = ref - .watch(settingViewModelProvider) - .isDownloadWithWifiOnly; - - var connectivityResult = await (Connectivity().checkConnectivity()); - - // If 'Download Over Wi-Fi Only' is enabled and connected to mobile, show a dialog - if (connectivityResult == ConnectivityResult.mobile && isDownloadWithWifiOnly) { - if (!mounted) return false; - _showMobileDataNotAllowedDialog(); - return false; - } - - // If on mobile data and 'Download Over Wi-Fi Only' is disabled, ask for confirmation - if (connectivityResult == ConnectivityResult.mobile && !isDownloadWithWifiOnly) { - bool shouldProceed = await _showDownloadConfirmationDialog(); - if (!mounted) return false; - if (!shouldProceed) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Download cancelled')), - ); - return false; - } - } - - return true; // Proceed with download if all checks pass - } + }