АААААА СУКО ПЛЕИР МУЗЫКО

This commit is contained in:
needle10
2025-11-22 17:29:31 +03:00
parent 51168a6481
commit bf995d8358
6 changed files with 1632 additions and 40 deletions

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'dart:io' show File;
import 'dart:convert' show base64Decode;
import 'dart:convert' show base64Decode, jsonDecode, jsonEncode;
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
@@ -25,6 +25,7 @@ import 'package:gwid/full_screen_video_player.dart';
import 'package:just_audio/just_audio.dart';
import 'package:gwid/services/cache_service.dart';
import 'package:video_player/video_player.dart';
import 'package:gwid/services/music_player_service.dart';
bool _currentIsDark = false;
@@ -2117,17 +2118,34 @@ class ChatMessageBubble extends StatelessWidget {
final fileName = file['name'] ?? 'Файл';
final fileSize = file['size'] as int? ?? 0;
widgets.add(
_buildFileWidget(
context,
fileName,
fileSize,
file,
textColor,
isUltraOptimized,
chatId,
),
);
final preview = file['preview'] as Map<String, dynamic>?;
final isMusic = preview != null && preview['_type'] == 'MUSIC';
if (isMusic) {
widgets.add(
_buildMusicFileWidget(
context,
fileName,
fileSize,
file,
textColor,
isUltraOptimized,
chatId,
),
);
} else {
widgets.add(
_buildFileWidget(
context,
fileName,
fileSize,
file,
textColor,
isUltraOptimized,
chatId,
),
);
}
widgets.add(const SizedBox(height: 6));
}
@@ -2302,6 +2320,406 @@ class ChatMessageBubble extends StatelessWidget {
);
}
Widget _buildMusicFileWidget(
BuildContext context,
String fileName,
int fileSize,
Map<String, dynamic> fileData,
Color textColor,
bool isUltraOptimized,
int? chatId,
) {
final borderRadius = BorderRadius.circular(isUltraOptimized ? 8 : 12);
final preview = fileData['preview'] as Map<String, dynamic>?;
final fileId = fileData['fileId'] as int?;
final token = fileData['token'] as String?;
final title = preview?['title'] as String? ?? fileName;
final artist = preview?['artistName'] as String? ?? 'Unknown Artist';
final album = preview?['albumName'] as String?;
final albumArtUrl = preview?['baseUrl'] as String?;
final duration = preview?['duration'] as int?;
String durationText = '';
if (duration != null) {
final durationSeconds = (duration / 1000).round();
final minutes = durationSeconds ~/ 60;
final seconds = durationSeconds % 60;
durationText = '$minutes:${seconds.toString().padLeft(2, '0')}';
}
final sizeStr = _formatFileSize(fileSize);
return GestureDetector(
onTap: () async {
final prefs = await SharedPreferences.getInstance();
final fileIdMap = prefs.getStringList('file_id_to_path_map') ?? [];
final fileIdString = fileId?.toString();
bool isDownloaded = false;
String? filePath;
if (fileIdString != null) {
for (final mapping in fileIdMap) {
if (mapping.startsWith('$fileIdString:')) {
filePath = mapping.substring(fileIdString.length + 1);
final file = io.File(filePath);
if (await file.exists()) {
isDownloaded = true;
break;
}
}
}
}
if (!isDownloaded) {
await _handleFileDownload(context, fileId, token, fileName, chatId);
await Future.delayed(const Duration(seconds: 1));
if (fileIdString != null) {
final updatedFileIdMap =
prefs.getStringList('file_id_to_path_map') ?? [];
for (final mapping in updatedFileIdMap) {
if (mapping.startsWith('$fileIdString:')) {
filePath = mapping.substring(fileIdString.length + 1);
final file = io.File(filePath);
if (await file.exists()) {
isDownloaded = true;
break;
}
}
}
}
}
if (isDownloaded && filePath != null) {
final track = MusicTrack(
id:
fileId?.toString() ??
DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
artist: artist,
album: album,
albumArtUrl: albumArtUrl,
duration: duration,
filePath: filePath,
fileId: fileId,
token: token,
chatId: chatId,
);
final musicMetadataJson = prefs.getString('music_metadata') ?? '{}';
final musicMetadata =
jsonDecode(musicMetadataJson) as Map<String, dynamic>;
musicMetadata[fileIdString ?? ''] = track.toJson();
await prefs.setString('music_metadata', jsonEncode(musicMetadata));
final musicPlayer = MusicPlayerService();
await musicPlayer.playTrack(track);
}
},
child: Container(
decoration: BoxDecoration(
color: textColor.withOpacity(0.05),
borderRadius: borderRadius,
border: Border.all(color: textColor.withOpacity(0.1), width: 1),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
width: 56,
height: 56,
color: textColor.withOpacity(0.1),
child: albumArtUrl != null
? Image.network(
albumArtUrl,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => Icon(
Icons.music_note,
color: textColor.withOpacity(0.8),
size: 24,
),
)
: Icon(
Icons.music_note,
color: textColor.withOpacity(0.8),
size: 24,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: TextStyle(
color: textColor,
fontSize: 14,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
artist,
style: TextStyle(
color: textColor.withOpacity(0.7),
fontSize: 12,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (album != null) ...[
const SizedBox(height: 2),
Text(
album,
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
const SizedBox(height: 4),
if (fileId != null)
ValueListenableBuilder<double>(
valueListenable: FileDownloadProgressService()
.getProgress(fileId.toString()),
builder: (context, progress, child) {
if (progress < 0) {
return Row(
children: [
if (durationText.isNotEmpty) ...[
Text(
durationText,
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
const SizedBox(width: 8),
Text(
'',
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
const SizedBox(width: 8),
],
Text(
sizeStr,
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
],
);
} else if (progress < 1.0) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LinearProgressIndicator(
value: progress,
minHeight: 3,
backgroundColor: textColor.withOpacity(0.1),
),
const SizedBox(height: 4),
Text(
'${(progress * 100).toStringAsFixed(0)}%',
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
],
);
} else {
return Row(
children: [
Icon(
Icons.check_circle,
size: 12,
color: Colors.green.withOpacity(0.8),
),
const SizedBox(width: 4),
Text(
'Загружено',
style: TextStyle(
color: Colors.green.withOpacity(0.8),
fontSize: 11,
),
),
],
);
}
},
)
else
Row(
children: [
if (durationText.isNotEmpty) ...[
Text(
durationText,
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
const SizedBox(width: 8),
Text(
'',
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
const SizedBox(width: 8),
],
Text(
sizeStr,
style: TextStyle(
color: textColor.withOpacity(0.6),
fontSize: 11,
),
),
],
),
],
),
),
if (fileId != null)
ValueListenableBuilder<double>(
valueListenable: FileDownloadProgressService().getProgress(
fileId.toString(),
),
builder: (context, progress, child) {
if (progress >= 0 && progress < 1.0) {
return const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
);
}
return IconButton(
onPressed: () async {
final prefs = await SharedPreferences.getInstance();
final fileIdMap =
prefs.getStringList('file_id_to_path_map') ?? [];
final fileIdString = fileId.toString();
bool isDownloaded = false;
String? filePath;
for (final mapping in fileIdMap) {
if (mapping.startsWith('$fileIdString:')) {
filePath = mapping.substring(
fileIdString.length + 1,
);
final file = io.File(filePath);
if (await file.exists()) {
isDownloaded = true;
break;
}
}
}
if (!isDownloaded) {
await _handleFileDownload(
context,
fileId,
token,
fileName,
chatId,
);
await Future.delayed(const Duration(seconds: 1));
final updatedFileIdMap =
prefs.getStringList('file_id_to_path_map') ?? [];
for (final mapping in updatedFileIdMap) {
if (mapping.startsWith('$fileIdString:')) {
filePath = mapping.substring(
fileIdString.length + 1,
);
final file = io.File(filePath);
if (await file.exists()) {
isDownloaded = true;
break;
}
}
}
}
if (isDownloaded && filePath != null) {
final track = MusicTrack(
id: fileId.toString(),
title: title,
artist: artist,
album: album,
albumArtUrl: albumArtUrl,
duration: duration,
filePath: filePath,
fileId: fileId,
token: token,
chatId: chatId,
);
final musicMetadataJson =
prefs.getString('music_metadata') ?? '{}';
final musicMetadata =
jsonDecode(musicMetadataJson)
as Map<String, dynamic>;
musicMetadata[fileIdString] = track.toJson();
await prefs.setString(
'music_metadata',
jsonEncode(musicMetadata),
);
final musicPlayer = MusicPlayerService();
await musicPlayer.playTrack(track);
}
},
icon: const Icon(Icons.play_arrow),
style: IconButton.styleFrom(
backgroundColor: textColor.withOpacity(0.1),
foregroundColor: textColor,
),
);
},
)
else
IconButton(
onPressed: () async {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Не удалось загрузить файл'),
backgroundColor: Colors.red,
),
);
}
},
icon: const Icon(Icons.play_arrow),
style: IconButton.styleFrom(
backgroundColor: textColor.withOpacity(0.1),
foregroundColor: textColor,
),
),
],
),
),
),
);
}
String _getFileExtension(String fileName) {
final parts = fileName.split('.');
if (parts.length > 1) {