Skip to content

Commit

Permalink
重构 taglib 使其与文件系统解耦 & 增加了对 Web 平台的兼容 & 添加 vercel 配置文件
Browse files Browse the repository at this point in the history
  • Loading branch information
MCredbear committed Jan 3, 2025
1 parent be28d45 commit 52ba9a2
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 377 deletions.
38 changes: 13 additions & 25 deletions lib/editor_page.dart → lib/editor_page/editor_page.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:io';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
Expand All @@ -8,28 +7,15 @@ import 'package:music_tools_flutter/search_lyric_page.dart';
import 'package:path/path.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
import 'package:file_picker/file_picker.dart';
import 'package:cross_file/cross_file.dart';

import 'taglib/taglib.dart';

void saveAudioFile(Map<String, dynamic> params) {
final audioFile = params['audioFile'] as AudioFile;
audioFile.setTitle(params['title']);
audioFile.setAlbum(params['album']);
audioFile.setArtist(params['artist']);
audioFile.setAlbumArtist(params['albumArtist']);
audioFile.setCD(params['cd']);
audioFile.setTrack(params['track']);
audioFile.setYear(params['year']);
audioFile.setLyric(params['lyric']);
audioFile.setComment(params['comment']);
audioFile.setCover(params['cover']);
audioFile.save();
}
import 'package:music_tools_flutter/taglib/taglib.dart';
import 'save_file_web.dart' if (dart.library.html) 'save_file_non_web.dart';

class EditorPage extends StatefulWidget {
const EditorPage(this.path, {super.key});
const EditorPage(this.xFile, {super.key});

final String path;
final XFile xFile;

@override
State<EditorPage> createState() => EditorPageState();
Expand All @@ -42,8 +28,10 @@ class EditorPageState extends State<EditorPage> {

@override
void initState() {
_audioFile = AudioFile(widget.path);
_audioFile.read().then((value) {
super.initState();

widget.xFile.readAsBytes().then((bytes) {
_audioFile = AudioFile(bytes);
setState(() {
_titleController.text = _audioFile.getTitle() ?? '';
_artistController.text = _audioFile.getArtist() ?? '';
Expand All @@ -57,7 +45,6 @@ class EditorPageState extends State<EditorPage> {
_cover = _audioFile.getCover();
});
});
super.initState();
}

void downloadCover(BuildContext context) async {
Expand All @@ -69,7 +56,7 @@ class EditorPageState extends State<EditorPage> {
_albumController.text.isNotEmpty &&
_titleController.text.isNotEmpty)
? '${_artistController.text} ${_albumController.text} ${_titleController.text}'
: basenameWithoutExtension(widget.path))));
: basenameWithoutExtension(widget.xFile.name))));
if (data != null) {
setState(() {
_cover = data;
Expand Down Expand Up @@ -117,7 +104,7 @@ class EditorPageState extends State<EditorPage> {
child: Scaffold(
appBar: AppBar(
title: Text(
basename(widget.path),
widget.xFile.name,
maxLines: 2,
textScaler: const TextScaler.linear(0.8),
),
Expand Down Expand Up @@ -329,7 +316,7 @@ class EditorPageState extends State<EditorPage> {
_titleController.text.isNotEmpty)
? '${_artistController.text} ${_albumController.text} ${_titleController.text}'
: basenameWithoutExtension(
widget.path))));
widget.xFile.name))));
if (data != null) {
setState(() {
_lyricController.text = data;
Expand Down Expand Up @@ -374,6 +361,7 @@ class EditorPageState extends State<EditorPage> {
});
await compute(saveAudioFile, {
'audioFile': _audioFile,
'xFile': widget.xFile,
'title': _titleController.text.isNotEmpty ? _titleController.text : null,
'album': _albumController.text.isNotEmpty ? _albumController.text : null,
'artist':
Expand Down
28 changes: 28 additions & 0 deletions lib/editor_page/save_file_non_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:cross_file/cross_file.dart';
import 'package:flutter/foundation.dart';
import 'package:music_tools_flutter/taglib/taglib.dart';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;

void saveAudioFile(Map<String, dynamic> params) {
final audioFile = params['audioFile'] as AudioFile;
final xFile = params['xFile'] as XFile;
audioFile.setTitle(params['title']);
audioFile.setAlbum(params['album']);
audioFile.setArtist(params['artist']);
audioFile.setAlbumArtist(params['albumArtist']);
audioFile.setCD(params['cd']);
audioFile.setTrack(params['track']);
audioFile.setYear(params['year']);
audioFile.setLyric(params['lyric']);
audioFile.setComment(params['comment']);
audioFile.setCover(params['cover']);
final data = audioFile.save();
final blob = html.Blob([Uint8List.fromList(data)]);
final url = html.Url.createObjectUrlFromBlob(blob);
html.AnchorElement(href: url)
..target = 'blank'
..download = xFile.name
..click();
html.Url.revokeObjectUrl(url);
}
25 changes: 25 additions & 0 deletions lib/editor_page/save_file_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:music_tools_flutter/taglib/taglib.dart';

void saveAudioFile(Map<String, dynamic> params) {
final audioFile = params['audioFile'] as AudioFile;
final xFile = params['xFile'] as XFile;
audioFile.setTitle(params['title']);
audioFile.setAlbum(params['album']);
audioFile.setArtist(params['artist']);
audioFile.setAlbumArtist(params['albumArtist']);
audioFile.setCD(params['cd']);
audioFile.setTrack(params['track']);
audioFile.setYear(params['year']);
audioFile.setLyric(params['lyric']);
audioFile.setComment(params['comment']);
audioFile.setCover(params['cover']);
final data = audioFile.save();
final file = File(xFile.path);
final modifiedTime = file.statSync().modified;
final accessedTime = file.statSync().accessed;
file.writeAsBytesSync(data);
file.setLastModifiedSync(modifiedTime);
file.setLastAccessedSync(accessedTime);
}
15 changes: 8 additions & 7 deletions lib/file_manager_page/file_manager_page.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:path/path.dart';
import 'package:flutter/material.dart';

import '../editor_page.dart';
import '../editor_page/editor_page.dart';

import 'file_manager_store.dart';

Expand Down Expand Up @@ -176,10 +177,10 @@ class FileManagerPageState extends State<FileManagerPage> {
context,
MaterialPageRoute(
builder: (context) => EditorPage(
fileManagerStore
XFile(fileManagerStore
.fileSystemEntities
.elementAt(index)
.path)));
.path))));
}
}),
leading: (fileManagerStore.fileSystemEntities
Expand Down Expand Up @@ -268,10 +269,10 @@ class _SearchDialogState extends State<SearchDialog> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditorPage(
builder: (context) => EditorPage(XFile(
_filtedElements
.elementAt(index)
.path)));
.path))));
}
}),
leading:
Expand Down Expand Up @@ -326,10 +327,10 @@ class FileTile extends StatelessWidget {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditorPage(fileManagerStore
builder: (context) => EditorPage(XFile(fileManagerStore
.fileSystemEntities
.elementAt(_index)
.path)));
.path))));
}
}),
leading:
Expand Down
85 changes: 56 additions & 29 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
import 'package:music_tools_flutter/editor_page/editor_page.dart';
import 'package:music_tools_flutter/file_manager_page/file_manager_page.dart';
import 'package:permission_handler/permission_handler.dart';

Expand All @@ -24,13 +26,17 @@ class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
Permission.manageExternalStorage.status.then((value) {
if (value == PermissionStatus.granted) {
setState(() {
_isPermissionGranted = true;
if (!kIsWeb) {
if (Platform.isAndroid) {
Permission.manageExternalStorage.status.then((value) {
if (value == PermissionStatus.granted) {
setState(() {
_isPermissionGranted = true;
});
}
});
}
});
}
}

void requestPermission(BuildContext context) {
Expand Down Expand Up @@ -91,20 +97,24 @@ class _MyAppState extends State<MyApp> {
child: MaterialApp(
title: 'Music tools Flutter',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: false),
home: _isPermissionGranted
? const PickPage()
: Center(
child: ElevatedButton(
onPressed: () {
if (Platform.isAndroid) {
requestPermission(context);
}
},
child: const Text(
'点击获取本应用所需的权限',
textAlign: TextAlign.center,
)),
),
home: switch (kIsWeb) {
true => const PickPage(),
false => switch (Platform.isAndroid) {
true => _isPermissionGranted
? const PickPage()
: Center(
child: ElevatedButton(
onPressed: () {
requestPermission(context);
},
child: const Text(
'点击获取本应用所需的权限',
textAlign: TextAlign.center,
)),
),
false => const PickPage(),
},
},
),
);
}
Expand All @@ -124,20 +134,37 @@ class PickPage extends StatelessWidget {
body: Center(
child: ElevatedButton(
onPressed: () async {
final result = await FilePicker.platform.getDirectoryPath();
if (kIsWeb) {
final result =
await FilePicker.platform.pickFiles(type: FileType.audio);

if (result != null) {
if (context.mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FileManagerPage(result),
),
);
if (result != null) {
if (context.mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
EditorPage(result.files.first.xFile),
),
);
}
}
} else {
final result = await FilePicker.platform.getDirectoryPath();

if (result != null) {
if (context.mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FileManagerPage(result),
),
);
}
}
}
},
child: const Text('选择音频文件夹')),
child: const Text(kIsWeb ? '选择音频文件' : '选择音频文件夹')),
),
);
}
Expand Down
27 changes: 6 additions & 21 deletions lib/search_cover_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart';

Expand Down Expand Up @@ -41,18 +41,9 @@ class _CoverSearchPageState extends State<CoverSearchPage> {

Future<void> search(String keyword, int offset) async {
try {
final httpClient = HttpClient();
final httpRequest = await httpClient.get('music.163.com', 80,
'/api/cloudsearch/pc?s=${Uri.encodeFull(keyword.substring(1))}&type=1&limit=10&offset=$offset&total=true');
final response = await httpRequest.close();
httpClient.close();
final dataStream = response.transform(utf8.decoder);
var jsonData = '';
await for (final data in dataStream) {
jsonData += data;
}
httpClient.close();
final json = jsonDecode(jsonData);
final response = await http.get(Uri.parse(
'https://music.redbear.moe/api/cloudsearch/pc?s=${Uri.encodeFull(keyword.substring(1))}&type=1&limit=10&offset=$offset&total=true'));
final json = jsonDecode(response.body);
final int songCount = json['result']['songCount'];
if (songCount == 0) {
setState(() {
Expand Down Expand Up @@ -197,14 +188,8 @@ class ImageCard extends StatelessWidget {
child: InkWell(
onTap: () async {
try {
var httpClient = HttpClient();
var httpRequest = await httpClient.getUrl(Uri.parse(imageUrl));
var response = await httpRequest.close();
httpClient.close();
List<int> data = [];
await for (var data_ in response) {
data.addAll(data_);
}
final response = await http.get(Uri.parse(imageUrl));
final data = response.bodyBytes;
if (!context.mounted) return;
Navigator.pop(context, Uint8List.fromList(data));
} catch (e) {
Expand Down
Loading

0 comments on commit 52ba9a2

Please sign in to comment.