Skip to content

Commit 07d7860

Browse files
committed
feat: Implement export as M3U8
1 parent cbb10e5 commit 07d7860

File tree

5 files changed

+60
-6
lines changed

5 files changed

+60
-6
lines changed

lib/utils/context_menu/collection_item_context_menu.dart

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import 'dart:io';
22

3+
import 'package:file_selector/file_selector.dart';
4+
import 'package:path_provider/path_provider.dart';
35
import 'package:provider/provider.dart';
46
import 'package:fluent_ui/fluent_ui.dart';
57
import 'package:material_symbols_icons/symbols.dart';
8+
import 'package:rune/utils/context_menu/utils/build_m3u8.dart';
69

710
import '../../utils/api/get_all_mixes.dart';
811
import '../../utils/api/add_item_to_mix.dart';
@@ -268,14 +271,35 @@ MenuFlyout buildLargeScreenCollectionItemContextMenu(
268271
text: Text(S.of(context).exportM3u8),
269272
onPressed: () async {
270273
Flyout.of(context).close();
274+
275+
final Directory appDocumentsDir =
276+
await getApplicationDocumentsDirectory();
277+
278+
final FileSaveLocation? path = await getSaveLocation(
279+
suggestedName: '$title.m3u8',
280+
initialDirectory: appDocumentsDir.path,
281+
acceptedTypeGroups: const [
282+
XTypeGroup(
283+
label: 'playlist',
284+
extensions: <String>['m3u8'],
285+
)
286+
],
287+
);
288+
289+
if (path == null) return;
290+
291+
final playlist = await buildM3u8(type, id);
292+
293+
final file = File(path.path);
294+
await file.writeAsString(playlist);
271295
},
272296
),
273297
MenuFlyoutItem(
274298
leading: const Icon(Symbols.wall_art),
275299
text: Text(S.of(context).exportCoverWall),
276300
onPressed: () async {
277301
Flyout.of(context).close();
278-
showExportCoverWallDialog(context, type, id);
302+
showExportCoverWallDialog(context, type, title, id);
279303
},
280304
),
281305
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import '../../../utils/query_list.dart';
2+
import '../../../messages/all.dart';
3+
4+
import '../../build_query.dart';
5+
import '../../api/query_mix_tracks.dart';
6+
7+
Future<String> buildM3u8(
8+
CollectionType type,
9+
int id,
10+
) async {
11+
final queries = await buildQuery(type, id);
12+
final newItems = await queryMixTracks(
13+
QueryList(queries),
14+
0,
15+
999,
16+
);
17+
18+
final filePaths = newItems.map((x) => x.path).join('\r\n');
19+
20+
return filePaths;
21+
}

lib/utils/dialogs/export_cover_wall/export_cover_wall_dialog.dart

+11-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:ui' as ui;
44
import 'package:fluent_ui/fluent_ui.dart';
55
import 'package:file_selector/file_selector.dart';
66
import 'package:material_symbols_icons/material_symbols_icons.dart';
7+
import 'package:path_provider/path_provider.dart';
78

89
import '../../../utils/l10n.dart';
910
import '../../../utils/dialogs/unavailable_dialog_on_band.dart';
@@ -90,12 +91,14 @@ List<SelectItem> frameItem(BuildContext context) => <SelectItem>[
9091
class ExportCoverWallDialog extends StatefulWidget {
9192
final CollectionType type;
9293
final int id;
94+
final String title;
9395
final void Function(void) $close;
9496

9597
const ExportCoverWallDialog({
9698
super.key,
9799
required this.type,
98100
required this.id,
101+
required this.title,
99102
required this.$close,
100103
});
101104

@@ -178,8 +181,12 @@ class ExportCoverWallDialogState extends State<ExportCoverWallDialog> {
178181
: Colors.white,
179182
);
180183

181-
final FileSaveLocation? result = await getSaveLocation(
182-
suggestedName: 'cover_wall.png',
184+
final Directory appDocumentsDir =
185+
await getApplicationDocumentsDirectory();
186+
187+
final FileSaveLocation? path = await getSaveLocation(
188+
suggestedName: '${widget.title}.png',
189+
initialDirectory: appDocumentsDir.path,
183190
acceptedTypeGroups: const [
184191
XTypeGroup(
185192
label: 'images',
@@ -188,13 +195,13 @@ class ExportCoverWallDialogState extends State<ExportCoverWallDialog> {
188195
],
189196
);
190197

191-
if (result == null) return;
198+
if (path == null) return;
192199

193200
final pngBytes = await image.toByteData(
194201
format: ui.ImageByteFormat.png,
195202
);
196203

197-
File(result.path).writeAsBytesSync(
204+
File(path.path).writeAsBytesSync(
198205
pngBytes!.buffer.asInt8List(),
199206
);
200207

lib/utils/dialogs/export_cover_wall/show_export_cover_wall_dialog.dart

+2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import 'export_cover_wall_dialog.dart';
99
void showExportCoverWallDialog(
1010
BuildContext context,
1111
CollectionType type,
12+
String title,
1213
int id,
1314
) async {
1415
await $showModal<void>(
1516
context,
1617
(context, $close) => ExportCoverWallDialog(
1718
type: type,
1819
id: id,
20+
title: title,
1921
$close: $close,
2022
),
2123
dismissWithEsc: true,

lib/utils/dialogs/export_cover_wall/utils/render_cover_wall.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import 'package:fluent_ui/fluent_ui.dart';
55
import 'package:rune/utils/query_list.dart';
66

77
import '../../../../messages/all.dart';
8-
import '../../../../widgets/cover_wall_background/utils/calculate_cover_wall_size.dart';
98
import '../../../../widgets/cover_wall_background/utils/generate_tiles_of_size.dart';
9+
import '../../../../widgets/cover_wall_background/utils/calculate_cover_wall_size.dart';
1010
import '../../../../widgets/cover_wall_background/utils/cover_wall_background_painter.dart';
1111
import '../../../../widgets/cover_wall_background/constants/max_random_grid_config_size.dart';
1212

0 commit comments

Comments
 (0)