Skip to content

Commit b69a95e

Browse files
authored
Downloading (#271)
2 parents f669ed3 + 6d43def commit b69a95e

File tree

9 files changed

+128
-87
lines changed

9 files changed

+128
-87
lines changed

lib/api/events.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ extension EventsExtension on API {
129129
? int.parse(eventObject['content']['media_id'])
130130
: null,
131131
mediaURL: eventObject.containsKey('content')
132-
? Uri.parse(
133-
(eventObject['content']['content'] as String).replaceAll(
134-
'https://',
135-
'https://${Uri.encodeComponent(server.login)}:${Uri.encodeComponent(server.password)}@',
136-
),
137-
)
132+
? Uri.parse(eventObject['content']['content'] as String
133+
// .replaceAll(
134+
// 'https://',
135+
// 'https://${Uri.encodeComponent(server.login)}:${Uri.encodeComponent(server.password)}@',
136+
// ),
137+
)
138138
: null,
139139
);
140140
return event;

lib/main.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,14 @@ class _UnityAppState extends State<UnityApp>
270270
switch (settings.kStreamOnBackground.value) {
271271
case NetworkUsage.auto:
272272
case NetworkUsage.wifiOnly:
273-
debugPrint('Pausing all streams');
274273
final connectionType = await Connectivity().checkConnectivity();
275274
if ([
276275
ConnectivityResult.bluetooth,
277276
ConnectivityResult.mobile,
278-
].any(connectionType.contains)) UnityPlayers.pauseAll();
277+
].any(connectionType.contains)) {
278+
debugPrint('Pausing all streams');
279+
UnityPlayers.pauseAll();
280+
}
279281

280282
break;
281283
case NetworkUsage.never:

lib/models/event.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ class Event {
206206
}
207207
}
208208

209+
String get mediaPath {
210+
return '${mediaURL!.scheme}://'
211+
'${Uri.encodeComponent(server.login)}'
212+
':'
213+
'${Uri.encodeComponent(server.password)}'
214+
'@'
215+
'${mediaURL!.host}'
216+
':'
217+
'${mediaURL!.port}'
218+
'${mediaURL!.path}'
219+
'?'
220+
'${mediaURL!.query}';
221+
}
222+
209223
Event copyWith({
210224
Server? server,
211225
int? id,

lib/providers/downloads_provider.dart

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,9 @@ class DownloadsManager extends UnityProvider {
119119
}
120120
}
121121

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

298297
/// Downloads the given [event] and returns the path of the downloaded file
299298
Future<String> _downloadEventFile(Event event, String dir) async {
299+
if (event.mediaURL == null) {
300+
throw ArgumentError('The event does not have a mediaURL');
301+
}
302+
300303
if (downloading.entries.any((de) => de.key.id == event.id)) {
301304
return downloading.entries
302305
.firstWhere((de) => de.key.id == event.id)
303306
.value
304307
.$2;
305308
}
306309
writeLogToFile(
307-
'downloads(${event.id}): $dir at ${event.mediaURL!}',
310+
'downloads(${event.id}): $dir at ${event.mediaPath}',
308311
print: true,
309312
);
310313
final home = HomeProvider.instance
@@ -329,22 +332,41 @@ class DownloadsManager extends UnityProvider {
329332
);
330333
}
331334

332-
await Dio().downloadUri(
333-
event.mediaURL!,
334-
downloadPath,
335-
options: Options(
336-
headers: {HttpHeaders.acceptEncodingHeader: '*'}, // disable gzip
337-
),
338-
onReceiveProgress: (received, total) {
339-
if (total != -1) {
340-
downloading[event] = (received / total, fileName);
341-
writeLogToFile('downloads(${event.id}): ${received / total}');
342-
notifyListeners();
343-
}
344-
},
345-
);
346-
347-
home.notLoading(UnityLoadingReason.downloadEvent);
335+
try {
336+
await Dio().download(
337+
event.mediaPath,
338+
downloadPath,
339+
options: Options(
340+
headers: {
341+
HttpHeaders.acceptEncodingHeader: '*', // disable gzip
342+
HttpHeaders.cookieHeader: event.server.cookie!,
343+
},
344+
),
345+
onReceiveProgress: (received, total) {
346+
if (total != -1) {
347+
downloading[event] = (received / total, fileName);
348+
writeLogToFile('downloads(${event.id}): ${received / total}');
349+
notifyListeners();
350+
}
351+
},
352+
);
353+
} on DioException catch (error, stack) {
354+
handleError(
355+
error,
356+
stack,
357+
'Failed to download event file from ${event.mediaPath}'
358+
' (${error.response?.statusCode}): '
359+
'${error.message}. ',
360+
);
361+
} catch (error, stack) {
362+
handleError(
363+
error,
364+
stack,
365+
'Failed to download event file',
366+
);
367+
} finally {
368+
home.notLoading(UnityLoadingReason.downloadEvent);
369+
}
348370

349371
return downloadPath;
350372
}

lib/screens/events_timeline/events_playback.dart

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -95,58 +95,57 @@ class _EventsPlaybackState extends EventsScreenState<EventsPlayback> {
9595
endDate: endDate,
9696
);
9797

98-
final devices = <Device, List<Event>>{};
98+
if (mounted) {
99+
final devices = <Device, List<Event>>{};
99100

100-
final events = eventsProvider.loadedEvents!.filteredEvents
101-
..sort((a, b) {
102-
// Sort the events in a way that the continuous events are displayed first
103-
// Ideally, in the Timeline, the motion events should be displayed on
104-
// top of the continuous events. We need to sort the continuous events
105-
// so that the continuous events don't get on top of the motion events.
106-
final aIsContinuous = a.type == EventType.continuous;
107-
final bIsContinuous = b.type == EventType.continuous;
108-
if (aIsContinuous && !bIsContinuous) return -1;
109-
if (!aIsContinuous && bIsContinuous) return 1;
110-
return 0;
111-
});
112-
for (final event in events) {
113-
if (event.isAlarm || event.mediaURL == null) continue;
101+
final events = eventsProvider.loadedEvents!.filteredEvents
102+
..sort((a, b) {
103+
// Sort the events in a way that the continuous events are displayed first
104+
// Ideally, in the Timeline, the motion events should be displayed on
105+
// top of the continuous events. We need to sort the continuous events
106+
// so that the continuous events don't get on top of the motion events.
107+
final aIsContinuous = a.type == EventType.continuous;
108+
final bIsContinuous = b.type == EventType.continuous;
109+
if (aIsContinuous && !bIsContinuous) return -1;
110+
if (!aIsContinuous && bIsContinuous) return 1;
111+
return 0;
112+
});
113+
for (final event in events) {
114+
if (event.isAlarm || event.mediaURL == null) continue;
114115

115-
if (!DateUtils.isSameDay(event.published, date) ||
116-
!DateUtils.isSameDay(event.published.add(event.duration), date)) {
117-
continue;
118-
}
119-
120-
final device = event.server.devices.firstWhere(
121-
(d) => d.id == event.deviceID,
122-
orElse: () => Device.dump(
123-
name: event.deviceName,
124-
id: event.deviceID,
125-
),
126-
);
127-
devices[device] ??= [];
116+
if (!DateUtils.isSameDay(event.published, date) ||
117+
!DateUtils.isSameDay(event.published.add(event.duration), date)) {
118+
continue;
119+
}
128120

129-
// This ensures that events that happened at the same time are not
130-
// displayed on the same device.
131-
//
132-
// if (devices[device]!.any((e) {
133-
// return e.published.isInBetween(event.published, event.updated,
134-
// allowSameMoment: true) ||
135-
// e.updated.isInBetween(event.published, event.updated,
136-
// allowSameMoment: true) ||
137-
// event.published
138-
// .isInBetween(e.published, e.updated, allowSameMoment: true) ||
139-
// event.updated
140-
// .isInBetween(e.published, e.updated, allowSameMoment: true);
141-
// })) continue;
121+
final device = event.server.devices.firstWhere(
122+
(d) => d.id == event.deviceID,
123+
orElse: () => Device.dump(
124+
name: event.deviceName,
125+
id: event.deviceID,
126+
),
127+
);
128+
devices[device] ??= [];
142129

143-
devices[device]!.add(event);
144-
}
130+
// This ensures that events that happened at the same time are not
131+
// displayed on the same device.
132+
//
133+
// if (devices[device]!.any((e) {
134+
// return e.published.isInBetween(event.published, event.updated,
135+
// allowSameMoment: true) ||
136+
// e.updated.isInBetween(event.published, event.updated,
137+
// allowSameMoment: true) ||
138+
// event.published
139+
// .isInBetween(e.published, e.updated, allowSameMoment: true) ||
140+
// event.updated
141+
// .isInBetween(e.published, e.updated, allowSameMoment: true);
142+
// })) continue;
145143

146-
final parsedTiles =
147-
devices.entries.map((e) => e.buildTimelineTile(context)).toList();
144+
devices[device]!.add(event);
145+
}
148146

149-
if (mounted) {
147+
final parsedTiles =
148+
devices.entries.map((e) => e.buildTimelineTile(context)).toList();
150149
setState(() {
151150
timeline = Timeline(
152151
tiles: parsedTiles,
@@ -367,7 +366,7 @@ extension DevicesMapExtension on MapEntry<Device, Iterable<Event>> {
367366
downloads.getDownloadedPathForEvent(event.id),
368367
windows: isDesktopPlatform && Platform.isWindows,
369368
).toString()
370-
: event.mediaURL!.toString();
369+
: event.mediaPath;
371370

372371
return TimelineEvent(
373372
startTime: event.published,

lib/screens/players/event_player_desktop.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ class _EventPlayerDesktopState extends State<EventPlayerDesktop> {
144144
windows: Platform.isWindows,
145145
).toString();
146146
}
147-
return event.mediaURL.toString();
147+
return event.mediaPath;
148148
}()
149-
: event.mediaURL.toString();
149+
: event.mediaPath;
150150

151151
if (mediaUrl != videoController.dataSource) {
152152
debugPrint(
@@ -193,7 +193,7 @@ class _EventPlayerDesktopState extends State<EventPlayerDesktop> {
193193
Expanded(
194194
child: InteractiveViewer(
195195
child: UnityVideoView(
196-
heroTag: currentEvent.mediaURL,
196+
heroTag: currentEvent.mediaPath,
197197
player: videoController,
198198
fit: fit,
199199
paneBuilder: (context, player) {

lib/screens/players/event_player_mobile.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class __EventPlayerMobileState extends State<_EventPlayerMobile> {
8484
downloads.getDownloadedPathForEvent(widget.event.id),
8585
windows: Platform.isWindows,
8686
).toString()
87-
: widget.event.mediaURL.toString();
87+
: widget.event.mediaPath;
8888

8989
debugPrint(mediaUrl);
9090
if (videoController.dataSource != mediaUrl) {
@@ -120,7 +120,7 @@ class __EventPlayerMobileState extends State<_EventPlayerMobile> {
120120
Expanded(
121121
child: SafeArea(
122122
child: UnityVideoView(
123-
heroTag: widget.event.mediaURL,
123+
heroTag: widget.event.mediaPath,
124124
player: videoController,
125125
fit: settings.kVideoFit.value,
126126
videoBuilder: (context, video) {

lib/utils/logging.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import 'package:logging/logging.dart';
2424
import 'package:path/path.dart' as path;
2525
import 'package:path_provider/path_provider.dart';
2626

27-
void setupLogging() {
27+
late Directory supportDir;
28+
Future<void> setupLogging() async {
2829
Logger.root.level = Level.ALL; // You can set the log level as needed.
2930
Logger.root.onRecord.listen((record) {
3031
debugPrint('${record.level.name}: ${record.time}: ${record.message}');
3132
});
33+
supportDir = await getApplicationSupportDirectory();
3234
}
3335

3436
void handleError(
@@ -44,10 +46,12 @@ void handleError(
4446
}
4547

4648
Future<File> getLogFile() async {
47-
final dir = await getApplicationSupportDirectory();
48-
final file = File(path.join(dir.path, 'logs.txt'));
49-
50-
return file;
49+
try {
50+
return File(path.join(supportDir.path, 'logs.txt'));
51+
} catch (e) {
52+
debugPrint('Error getting log file: $e');
53+
return File('./logs.txt');
54+
}
5155
}
5256

5357
Future<void> writeErrorToFile(

lib/utils/video_player.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class UnityPlayers with ChangeNotifier {
160160
);
161161
},
162162
)
163-
..setDataSource(event.mediaURL!.toString())
163+
..setDataSource(event.mediaPath.toString())
164164
..setVolume(1.0)
165165
..setSpeed(1.0);
166166

0 commit comments

Comments
 (0)