Skip to content

Commit

Permalink
Downloading (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Sep 28, 2024
2 parents f669ed3 + 6d43def commit b69a95e
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 87 deletions.
12 changes: 6 additions & 6 deletions lib/api/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ extension EventsExtension on API {
? int.parse(eventObject['content']['media_id'])
: null,
mediaURL: eventObject.containsKey('content')
? Uri.parse(
(eventObject['content']['content'] as String).replaceAll(
'https://',
'https://${Uri.encodeComponent(server.login)}:${Uri.encodeComponent(server.password)}@',
),
)
? Uri.parse(eventObject['content']['content'] as String
// .replaceAll(
// 'https://',
// 'https://${Uri.encodeComponent(server.login)}:${Uri.encodeComponent(server.password)}@',
// ),
)
: null,
);
return event;
Expand Down
6 changes: 4 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,14 @@ class _UnityAppState extends State<UnityApp>
switch (settings.kStreamOnBackground.value) {
case NetworkUsage.auto:
case NetworkUsage.wifiOnly:
debugPrint('Pausing all streams');
final connectionType = await Connectivity().checkConnectivity();
if ([
ConnectivityResult.bluetooth,
ConnectivityResult.mobile,
].any(connectionType.contains)) UnityPlayers.pauseAll();
].any(connectionType.contains)) {
debugPrint('Pausing all streams');
UnityPlayers.pauseAll();
}

break;
case NetworkUsage.never:
Expand Down
14 changes: 14 additions & 0 deletions lib/models/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,20 @@ class Event {
}
}

String get mediaPath {
return '${mediaURL!.scheme}://'
'${Uri.encodeComponent(server.login)}'
':'
'${Uri.encodeComponent(server.password)}'
'@'
'${mediaURL!.host}'
':'
'${mediaURL!.port}'
'${mediaURL!.path}'
'?'
'${mediaURL!.query}';
}

Event copyWith({
Server? server,
int? id,
Expand Down
64 changes: 43 additions & 21 deletions lib/providers/downloads_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ class DownloadsManager extends UnityProvider {
}
}

if (dir == null) {
final docsDir = await getApplicationSupportDirectory();
dir = Directory(path.join(docsDir.path, 'downloads'));
}
dir ??= Directory(
path.join((await getApplicationSupportDirectory()).path, 'downloads'),
);
} on StateError catch (e) {
debugPrint('Failed to get default downloads directory: $e');
} catch (error, stack) {
Expand Down Expand Up @@ -297,14 +296,18 @@ class DownloadsManager extends UnityProvider {

/// Downloads the given [event] and returns the path of the downloaded file
Future<String> _downloadEventFile(Event event, String dir) async {
if (event.mediaURL == null) {
throw ArgumentError('The event does not have a mediaURL');
}

if (downloading.entries.any((de) => de.key.id == event.id)) {
return downloading.entries
.firstWhere((de) => de.key.id == event.id)
.value
.$2;
}
writeLogToFile(
'downloads(${event.id}): $dir at ${event.mediaURL!}',
'downloads(${event.id}): $dir at ${event.mediaPath}',
print: true,
);
final home = HomeProvider.instance
Expand All @@ -329,22 +332,41 @@ class DownloadsManager extends UnityProvider {
);
}

await Dio().downloadUri(
event.mediaURL!,
downloadPath,
options: Options(
headers: {HttpHeaders.acceptEncodingHeader: '*'}, // disable gzip
),
onReceiveProgress: (received, total) {
if (total != -1) {
downloading[event] = (received / total, fileName);
writeLogToFile('downloads(${event.id}): ${received / total}');
notifyListeners();
}
},
);

home.notLoading(UnityLoadingReason.downloadEvent);
try {
await Dio().download(
event.mediaPath,
downloadPath,
options: Options(
headers: {
HttpHeaders.acceptEncodingHeader: '*', // disable gzip
HttpHeaders.cookieHeader: event.server.cookie!,
},
),
onReceiveProgress: (received, total) {
if (total != -1) {
downloading[event] = (received / total, fileName);
writeLogToFile('downloads(${event.id}): ${received / total}');
notifyListeners();
}
},
);
} on DioException catch (error, stack) {
handleError(
error,
stack,
'Failed to download event file from ${event.mediaPath}'
' (${error.response?.statusCode}): '
'${error.message}. ',
);
} catch (error, stack) {
handleError(
error,
stack,
'Failed to download event file',
);
} finally {
home.notLoading(UnityLoadingReason.downloadEvent);
}

return downloadPath;
}
Expand Down
93 changes: 46 additions & 47 deletions lib/screens/events_timeline/events_playback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,58 +95,57 @@ class _EventsPlaybackState extends EventsScreenState<EventsPlayback> {
endDate: endDate,
);

final devices = <Device, List<Event>>{};
if (mounted) {
final devices = <Device, List<Event>>{};

final events = eventsProvider.loadedEvents!.filteredEvents
..sort((a, b) {
// Sort the events in a way that the continuous events are displayed first
// Ideally, in the Timeline, the motion events should be displayed on
// top of the continuous events. We need to sort the continuous events
// so that the continuous events don't get on top of the motion events.
final aIsContinuous = a.type == EventType.continuous;
final bIsContinuous = b.type == EventType.continuous;
if (aIsContinuous && !bIsContinuous) return -1;
if (!aIsContinuous && bIsContinuous) return 1;
return 0;
});
for (final event in events) {
if (event.isAlarm || event.mediaURL == null) continue;
final events = eventsProvider.loadedEvents!.filteredEvents
..sort((a, b) {
// Sort the events in a way that the continuous events are displayed first
// Ideally, in the Timeline, the motion events should be displayed on
// top of the continuous events. We need to sort the continuous events
// so that the continuous events don't get on top of the motion events.
final aIsContinuous = a.type == EventType.continuous;
final bIsContinuous = b.type == EventType.continuous;
if (aIsContinuous && !bIsContinuous) return -1;
if (!aIsContinuous && bIsContinuous) return 1;
return 0;
});
for (final event in events) {
if (event.isAlarm || event.mediaURL == null) continue;

if (!DateUtils.isSameDay(event.published, date) ||
!DateUtils.isSameDay(event.published.add(event.duration), date)) {
continue;
}

final device = event.server.devices.firstWhere(
(d) => d.id == event.deviceID,
orElse: () => Device.dump(
name: event.deviceName,
id: event.deviceID,
),
);
devices[device] ??= [];
if (!DateUtils.isSameDay(event.published, date) ||
!DateUtils.isSameDay(event.published.add(event.duration), date)) {
continue;
}

// This ensures that events that happened at the same time are not
// displayed on the same device.
//
// if (devices[device]!.any((e) {
// return e.published.isInBetween(event.published, event.updated,
// allowSameMoment: true) ||
// e.updated.isInBetween(event.published, event.updated,
// allowSameMoment: true) ||
// event.published
// .isInBetween(e.published, e.updated, allowSameMoment: true) ||
// event.updated
// .isInBetween(e.published, e.updated, allowSameMoment: true);
// })) continue;
final device = event.server.devices.firstWhere(
(d) => d.id == event.deviceID,
orElse: () => Device.dump(
name: event.deviceName,
id: event.deviceID,
),
);
devices[device] ??= [];

devices[device]!.add(event);
}
// This ensures that events that happened at the same time are not
// displayed on the same device.
//
// if (devices[device]!.any((e) {
// return e.published.isInBetween(event.published, event.updated,
// allowSameMoment: true) ||
// e.updated.isInBetween(event.published, event.updated,
// allowSameMoment: true) ||
// event.published
// .isInBetween(e.published, e.updated, allowSameMoment: true) ||
// event.updated
// .isInBetween(e.published, e.updated, allowSameMoment: true);
// })) continue;

final parsedTiles =
devices.entries.map((e) => e.buildTimelineTile(context)).toList();
devices[device]!.add(event);
}

if (mounted) {
final parsedTiles =
devices.entries.map((e) => e.buildTimelineTile(context)).toList();
setState(() {
timeline = Timeline(
tiles: parsedTiles,
Expand Down Expand Up @@ -367,7 +366,7 @@ extension DevicesMapExtension on MapEntry<Device, Iterable<Event>> {
downloads.getDownloadedPathForEvent(event.id),
windows: isDesktopPlatform && Platform.isWindows,
).toString()
: event.mediaURL!.toString();
: event.mediaPath;

return TimelineEvent(
startTime: event.published,
Expand Down
6 changes: 3 additions & 3 deletions lib/screens/players/event_player_desktop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ class _EventPlayerDesktopState extends State<EventPlayerDesktop> {
windows: Platform.isWindows,
).toString();
}
return event.mediaURL.toString();
return event.mediaPath;
}()
: event.mediaURL.toString();
: event.mediaPath;

if (mediaUrl != videoController.dataSource) {
debugPrint(
Expand Down Expand Up @@ -193,7 +193,7 @@ class _EventPlayerDesktopState extends State<EventPlayerDesktop> {
Expanded(
child: InteractiveViewer(
child: UnityVideoView(
heroTag: currentEvent.mediaURL,
heroTag: currentEvent.mediaPath,
player: videoController,
fit: fit,
paneBuilder: (context, player) {
Expand Down
4 changes: 2 additions & 2 deletions lib/screens/players/event_player_mobile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class __EventPlayerMobileState extends State<_EventPlayerMobile> {
downloads.getDownloadedPathForEvent(widget.event.id),
windows: Platform.isWindows,
).toString()
: widget.event.mediaURL.toString();
: widget.event.mediaPath;

debugPrint(mediaUrl);
if (videoController.dataSource != mediaUrl) {
Expand Down Expand Up @@ -120,7 +120,7 @@ class __EventPlayerMobileState extends State<_EventPlayerMobile> {
Expanded(
child: SafeArea(
child: UnityVideoView(
heroTag: widget.event.mediaURL,
heroTag: widget.event.mediaPath,
player: videoController,
fit: settings.kVideoFit.value,
videoBuilder: (context, video) {
Expand Down
14 changes: 9 additions & 5 deletions lib/utils/logging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';

void setupLogging() {
late Directory supportDir;
Future<void> setupLogging() async {
Logger.root.level = Level.ALL; // You can set the log level as needed.
Logger.root.onRecord.listen((record) {
debugPrint('${record.level.name}: ${record.time}: ${record.message}');
});
supportDir = await getApplicationSupportDirectory();
}

void handleError(
Expand All @@ -44,10 +46,12 @@ void handleError(
}

Future<File> getLogFile() async {
final dir = await getApplicationSupportDirectory();
final file = File(path.join(dir.path, 'logs.txt'));

return file;
try {
return File(path.join(supportDir.path, 'logs.txt'));
} catch (e) {
debugPrint('Error getting log file: $e');
return File('./logs.txt');
}
}

Future<void> writeErrorToFile(
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class UnityPlayers with ChangeNotifier {
);
},
)
..setDataSource(event.mediaURL!.toString())
..setDataSource(event.mediaPath.toString())
..setVolume(1.0)
..setSpeed(1.0);

Expand Down

0 comments on commit b69a95e

Please sign in to comment.