Advertisement
Guest User

Untitled

a guest
Jun 17th, 2025
10
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.87 KB | None | 0 0
  1. import 'package:flutter/material.dart';
  2.  
  3. import 'package:soundscape/shared/managers/backend_manager.dart';
  4. import 'package:soundscape/shared/managers/polygons_manager.dart';
  5. import "package:soundscape/models/backend/songs.dart";
  6. import "package:soundscape/models/backend/assets.dart";
  7. import "package:soundscape/models/backend/artist.dart";
  8.  
  9. class StreamMusicManager {
  10. static final StreamMusicManager _instance = StreamMusicManager._internal();
  11. factory StreamMusicManager() => _instance;
  12. StreamMusicManager._internal();
  13.  
  14. Map<String, dynamic> _currentMusicPath = {};
  15. Map<String, dynamic> get getCurrentMusicPath => _currentMusicPath;
  16.  
  17. String get getCurrentMusicAuthor => _currentMusicPath['author'] ?? '';
  18. String get getCurrentMusicTitle => _currentMusicPath['title'] ?? '';
  19. String get getCurrentMusicImagePath => _currentMusicPath['musicPath'] ?? '';
  20. String get getCurrentMusicCoverPath => _currentMusicPath['coverPath'] ?? '';
  21. int get getCurrentMusicIndex => _currentMusicPath['currentIndex'] ?? 0;
  22.  
  23. final Map<String, Map<String, dynamic>> _musics = {};
  24.  
  25. Function(String?)? onTrackEnded;
  26.  
  27. Future<void> chooseMusic(SoundscapePolygon polygon) async {
  28. debugPrint("🎵 [chooseMusic] Début pour polygone: ${polygon.uuid}");
  29.  
  30. BackendManager backendProvider = BackendManager();
  31. if (backendProvider.backendIsDead) {
  32. debugPrint("❌ [chooseMusic] Backend mort");
  33. _currentMusicPath = {};
  34. return;
  35. }
  36.  
  37. if (_musics.containsKey(polygon.uuid)) {
  38. debugPrint("🎵 [chooseMusic] Musique déjà configurée pour ${polygon.uuid}");
  39. _currentMusicPath = Map<String, dynamic>.from(_musics[polygon.uuid]!);
  40. debugPrint(" - _currentMusicPath: $_currentMusicPath");
  41. return;
  42. }
  43.  
  44. try {
  45. if (polygon.songsUuid.isEmpty) {
  46. debugPrint("❌ [chooseMusic] Aucune musique dans le polygone");
  47. _currentMusicPath = {};
  48. return;
  49. }
  50.  
  51. debugPrint("🎵 [chooseMusic] Chargement de la première musique: ${polygon.songsUuid[0]}");
  52. final firstSongObject = await getSong(polygon.songsUuid[0]);
  53. debugPrint("🎵 [chooseMusic] Info première chanson reçue: $firstSongObject");
  54.  
  55. final firstFileObjectUuid = firstSongObject["file_object_uuid"];
  56. final firstArtistUuid = firstSongObject["artist_uuid"];
  57. final firstCoverUuid = firstSongObject["cover_uuid"];
  58. final firstAlbumUuid = firstSongObject["album_uuid"];
  59.  
  60. if (firstFileObjectUuid == null || firstFileObjectUuid.isEmpty ||
  61. firstArtistUuid == null || firstArtistUuid.isEmpty ||
  62. firstCoverUuid == null || firstCoverUuid.isEmpty ||
  63. firstAlbumUuid == null || firstAlbumUuid.isEmpty) {
  64. debugPrint("❌ [chooseMusic] Données manquantes pour la première musique");
  65. _currentMusicPath = {};
  66. return;
  67. }
  68.  
  69. debugPrint("🎵 [chooseMusic] Récupération premier asset: $firstFileObjectUuid");
  70. final firstResults = await Future.wait([
  71. getMusicAsset(firstFileObjectUuid),
  72. getArtist(firstArtistUuid),
  73. getImgAsset(firstCoverUuid),
  74. ]);
  75.  
  76. final firstAsset = firstResults[0];
  77. final firstArtist = firstResults[1];
  78. final firstCoverAsset = firstResults[2];
  79.  
  80. if (firstAsset == null || !await firstAsset.exists()) {
  81. debugPrint("❌ [chooseMusic] Premier asset invalide");
  82. _currentMusicPath = {};
  83. return;
  84. }
  85.  
  86. final firstMusicData = {
  87. 'polygonId': polygon.uuid,
  88. 'musicPaths': [firstAsset.path],
  89. 'authors': [firstArtist["name"] ?? "Artiste inconnu"],
  90. 'titles': [firstSongObject["name"] ?? "Titre inconnu"],
  91. 'coverPaths': [firstCoverAsset.path],
  92. 'songsUuid': polygon.songsUuid,
  93. 'currentIndex': 0,
  94. 'totalTracks': polygon.songsUuid.length,
  95. 'isFullyLoaded': false,
  96. 'musicPath': firstAsset.path,
  97. 'author': firstArtist["name"] ?? "Artiste inconnu",
  98. 'title': firstSongObject["name"] ?? "Titre inconnu",
  99. 'coverPath': firstCoverAsset.path,
  100. };
  101.  
  102. _musics[polygon.uuid] = firstMusicData;
  103. _currentMusicPath = Map<String, dynamic>.from(firstMusicData);
  104.  
  105. debugPrint("✅ [chooseMusic] Première musique prête: ${firstSongObject["name"]}");
  106. debugPrint(" - Chemin: ${firstAsset.path}");
  107.  
  108. if (polygon.songsUuid.length > 1) {
  109. debugPrint("🎵 [chooseMusic] Démarrage chargement en arrière-plan des ${polygon.songsUuid.length - 1} autres musiques");
  110. _loadRemainingMusicInBackground(polygon);
  111. } else {
  112. _musics[polygon.uuid]!['isFullyLoaded'] = true;
  113. }
  114.  
  115. } catch (error, stackTrace) {
  116. debugPrint("❌ [chooseMusic] Erreur: $error");
  117. debugPrint("❌ [chooseMusic] StackTrace: $stackTrace");
  118. _currentMusicPath = {};
  119. }
  120. }
  121.  
  122. Future<void> _loadRemainingMusicInBackground(SoundscapePolygon polygon) async {
  123. try {
  124. debugPrint("🔄 [_loadRemainingMusicInBackground] Début pour ${polygon.songsUuid.length - 1} musiques");
  125.  
  126. List<String> allMusicPaths = List<String>.from(_musics[polygon.uuid]!['musicPaths']);
  127. List<String> allAuthors = List<String>.from(_musics[polygon.uuid]!['authors']);
  128. List<String> allTitles = List<String>.from(_musics[polygon.uuid]!['titles']);
  129. List<String> allCoverPaths = List<String>.from(_musics[polygon.uuid]!['coverPaths']);
  130.  
  131. for (int i = 1; i < polygon.songsUuid.length; i++) {
  132. try {
  133. String songUuid = polygon.songsUuid[i];
  134. debugPrint("🔄 [Background] Chargement musique $i/${ polygon.songsUuid.length - 1}: $songUuid");
  135.  
  136. final songObject = await getSong(songUuid);
  137. final fileObjectUuid = songObject["file_object_uuid"];
  138. final artistUuid = songObject["artist_uuid"];
  139. final coverUuid = songObject["cover_uuid"];
  140. final albumUuid = songObject["album_uuid"];
  141.  
  142. if (fileObjectUuid == null || fileObjectUuid.isEmpty ||
  143. artistUuid == null || artistUuid.isEmpty ||
  144. coverUuid == null || coverUuid.isEmpty ||
  145. albumUuid == null || albumUuid.isEmpty) {
  146. debugPrint("❌ [Background] Données manquantes pour $songUuid");
  147. continue;
  148. }
  149.  
  150. final results = await Future.wait([
  151. getMusicAsset(fileObjectUuid),
  152. getArtist(artistUuid),
  153. getImgAsset(coverUuid),
  154. ]);
  155.  
  156. final asset = results[0];
  157. final artist = results[1];
  158. final cover = results[2];
  159.  
  160. if (asset == null || !await asset.exists()) {
  161. debugPrint("❌ [Background] Asset invalide pour $songUuid");
  162. continue;
  163. }
  164.  
  165. String coverPath = '';
  166. if (cover != null && await cover.exists()) {
  167. coverPath = cover.path;
  168. } else {
  169. debugPrint("⚠️ [Background] Cover manquante pour $songUuid, utilisation image par défaut");
  170. coverPath = 'assets/images/default_cover.png';
  171. }
  172.  
  173. allMusicPaths.add(asset.path);
  174. allAuthors.add(artist["name"] ?? "Artiste inconnu");
  175. allTitles.add(songObject["name"] ?? "Titre inconnu");
  176. allCoverPaths.add(coverPath);
  177.  
  178. if (_musics.containsKey(polygon.uuid)) {
  179. _musics[polygon.uuid]!['musicPaths'] = allMusicPaths;
  180. _musics[polygon.uuid]!['authors'] = allAuthors;
  181. _musics[polygon.uuid]!['titles'] = allTitles;
  182. _musics[polygon.uuid]!['coverPaths'] = allCoverPaths;
  183. _musics[polygon.uuid]!['totalTracks'] = allMusicPaths.length;
  184. }
  185.  
  186. debugPrint("✅ [Background] Musique ajoutée: ${songObject["name"]} (${allMusicPaths.length}/${polygon.songsUuid.length})");
  187.  
  188. } catch (e) {
  189. debugPrint("❌ [Background] Erreur pour musique $i: $e");
  190. continue;
  191. }
  192. }
  193.  
  194. if (_musics.containsKey(polygon.uuid)) {
  195. _musics[polygon.uuid]!['isFullyLoaded'] = true;
  196. debugPrint("✅ [Background] Chargement terminé: ${allMusicPaths.length} musiques prêtes");
  197. }
  198.  
  199. } catch (error) {
  200. debugPrint("❌ [_loadRemainingMusicInBackground] Erreur générale: $error");
  201. }
  202. }
  203.  
  204. String getCurrentCoverPath(String polygonId) {
  205. if (!_musics.containsKey(polygonId)) return '';
  206.  
  207. final musicData = _musics[polygonId]!;
  208. final currentIndex = musicData['currentIndex'] as int;
  209.  
  210. return getCoverPathByIndex(polygonId, currentIndex);
  211. }
  212.  
  213. List<String> getAllCoverPaths(String polygonId) {
  214. debugPrint("🎵 [getAllCoverPaths] Récupération des chemins de couverture pour le polygone: $polygonId");
  215.  
  216. if (!_musics.containsKey(polygonId)) {
  217. debugPrint("🎵 [getAllCoverPaths] Polygone $polygonId non trouvé");
  218. return [];
  219. }
  220.  
  221. debugPrint("🎵 [getAllCoverPaths] Polygone trouvé, extraction des chemins de couverture");
  222. final musicData = _musics[polygonId]!;
  223.  
  224. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>?;
  225.  
  226. if (coverPathsRaw == null) {
  227. debugPrint("🎵 [getAllCoverPaths] Aucune coverPaths trouvée");
  228. return [];
  229. }
  230.  
  231. final List<String> coverPaths = coverPathsRaw
  232. .where((path) => path != null)
  233. .map((path) => path.toString())
  234. .toList();
  235.  
  236. debugPrint("🎵 [getAllCoverPaths] ${coverPaths.length} chemins de couverture récupérés");
  237. return coverPaths;
  238. }
  239.  
  240. List<Map<String, dynamic>> getFullQueue(String polygonId) {
  241. if (!_musics.containsKey(polygonId)) return [];
  242.  
  243. final musicData = _musics[polygonId]!;
  244.  
  245. final musicPathsRaw = musicData['musicPaths'] as List<dynamic>;
  246. final authorsRaw = musicData['authors'] as List<dynamic>;
  247. final titlesRaw = musicData['titles'] as List<dynamic>;
  248. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>;
  249.  
  250. final musicPaths = musicPathsRaw.cast<String>();
  251. final authors = authorsRaw.cast<String>();
  252. final titles = titlesRaw.cast<String>();
  253. final coverPaths = coverPathsRaw.cast<String>();
  254.  
  255. final currentIndex = musicData['currentIndex'] as int;
  256.  
  257. List<Map<String, dynamic>> queue = [];
  258.  
  259. for (int i = 0; i < musicPaths.length; i++) {
  260. queue.add({
  261. 'musicPath': musicPaths[i],
  262. 'author': authors[i],
  263. 'title': titles[i],
  264. 'coverPath': coverPaths[i],
  265. 'index': i,
  266. 'isCurrent': i == currentIndex,
  267. });
  268. }
  269.  
  270. return queue;
  271. }
  272.  
  273. Future<String?> getNextTrack(String polygonId) async {
  274. if (!_musics.containsKey(polygonId)) {
  275. debugPrint("❌ [getNextTrack] Polygone $polygonId non trouvé");
  276. return null;
  277. }
  278.  
  279. final musicData = _musics[polygonId]!;
  280. final currentIndex = musicData['currentIndex'] as int;
  281.  
  282. final musicPathsRaw = musicData['musicPaths'] as List<dynamic>;
  283. final authorsRaw = musicData['authors'] as List<dynamic>;
  284. final titlesRaw = musicData['titles'] as List<dynamic>;
  285. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>;
  286.  
  287. final musicPaths = musicPathsRaw.cast<String>();
  288. final authors = authorsRaw.cast<String>();
  289. final titles = titlesRaw.cast<String>();
  290. final coverPaths = coverPathsRaw.cast<String>();
  291.  
  292. final isFullyLoaded = musicData['isFullyLoaded'] as bool;
  293.  
  294. int nextIndex = currentIndex + 1;
  295.  
  296. if (nextIndex >= musicPaths.length && !isFullyLoaded) {
  297. debugPrint("🔄 [getNextTrack] Musique suivante pas encore chargée, on reste sur la courante");
  298. return null;
  299. }
  300.  
  301. if (nextIndex >= musicPaths.length) {
  302. nextIndex = 0;
  303. }
  304.  
  305. musicData['currentIndex'] = nextIndex;
  306. musicData['musicPath'] = musicPaths[nextIndex];
  307. musicData['author'] = authors[nextIndex];
  308. musicData['title'] = titles[nextIndex];
  309. musicData['coverPath'] = coverPaths[nextIndex];
  310.  
  311. _currentMusicPath = Map<String, dynamic>.from(musicData);
  312.  
  313. debugPrint("🎵 [getNextTrack] Passage à la musique suivante:");
  314. debugPrint(" - Index: $nextIndex/${musicPaths.length - 1}");
  315. debugPrint(" - Titre: ${titles[nextIndex]}");
  316. debugPrint(" - Cover: ${coverPaths[nextIndex]}");
  317. debugPrint(" - Chargement complet: $isFullyLoaded");
  318.  
  319. return musicPaths[nextIndex];
  320. }
  321.  
  322. Map<String, dynamic>? getTotalNextTrack(String polygonId) {
  323. debugPrint("🎵 [getTotalNextTrack] Récupération des infos de la piste suivante pour: $polygonId");
  324.  
  325. if (!_musics.containsKey(polygonId)) {
  326. debugPrint("❌ [getTotalNextTrack] Polygone $polygonId non trouvé");
  327. return null;
  328. }
  329.  
  330. final musicData = _musics[polygonId]!;
  331. final currentIndex = musicData['currentIndex'] as int;
  332.  
  333. final musicPathsRaw = musicData['musicPaths'] as List<dynamic>;
  334. final authorsRaw = musicData['authors'] as List<dynamic>;
  335. final titlesRaw = musicData['titles'] as List<dynamic>;
  336. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>;
  337.  
  338. final musicPaths = musicPathsRaw.cast<String>();
  339. final authors = authorsRaw.cast<String>();
  340. final titles = titlesRaw.cast<String>();
  341. final coverPaths = coverPathsRaw.cast<String>();
  342.  
  343. final isFullyLoaded = musicData['isFullyLoaded'] as bool;
  344.  
  345. int nextIndex = currentIndex + 1;
  346.  
  347. if (nextIndex >= musicPaths.length && !isFullyLoaded) {
  348. debugPrint("🔄 [getTotalNextTrack] Piste suivante pas encore chargée");
  349. return null;
  350. }
  351.  
  352. if (nextIndex >= musicPaths.length) {
  353. nextIndex = 0;
  354. }
  355.  
  356. final nextTrackInfo = {
  357. 'polygonId': polygonId,
  358. 'musicPath': musicPaths[nextIndex],
  359. 'author': authors[nextIndex],
  360. 'title': titles[nextIndex],
  361. 'coverPath': coverPaths[nextIndex],
  362. 'index': nextIndex,
  363. 'totalTracks': musicPaths.length,
  364. 'isFullyLoaded': isFullyLoaded,
  365. 'isLastTrack': nextIndex == (musicPaths.length - 1),
  366. 'isFirstTrack': nextIndex == 0,
  367. 'willLoop': (currentIndex + 1) >= musicPaths.length
  368. };
  369.  
  370. debugPrint("🎵 [getTotalNextTrack] Piste suivante trouvée:");
  371. debugPrint(" - Index: $nextIndex/${musicPaths.length - 1}");
  372. debugPrint(" - Titre: ${titles[nextIndex]}");
  373. debugPrint(" - Artiste: ${authors[nextIndex]}");
  374. debugPrint(" - Va boucler: ${nextTrackInfo['willLoop']}");
  375.  
  376. return nextTrackInfo;
  377. }
  378.  
  379. String getCoverPathByIndex(String polygonId, int index) {
  380. if (!_musics.containsKey(polygonId)) return '';
  381.  
  382. final musicData = _musics[polygonId]!;
  383. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>;
  384. final coverPaths = coverPathsRaw.cast<String>();
  385.  
  386. if (index >= 0 && index < coverPaths.length) {
  387. return coverPaths[index];
  388. }
  389.  
  390. return 'assets/images/default_cover.png';
  391. }
  392.  
  393. String getNextCoverPath(String polygonId) {
  394. if (!_musics.containsKey(polygonId)) return '';
  395.  
  396. final musicData = _musics[polygonId]!;
  397. final currentIndex = musicData['currentIndex'] as int;
  398. final coverPathsRaw = musicData['coverPaths'] as List<dynamic>;
  399. final coverPaths = coverPathsRaw.cast<String>();
  400.  
  401. int nextIndex = (currentIndex + 1) % coverPaths.length;
  402. return getCoverPathByIndex(polygonId, nextIndex);
  403. }
  404.  
  405. Map<String, dynamic>? getLoadingStatus(String polygonId) {
  406. if (!_musics.containsKey(polygonId)) return null;
  407.  
  408. final musicData = _musics[polygonId]!;
  409. return {
  410. 'isFullyLoaded': musicData['isFullyLoaded'],
  411. 'loadedTracks': (musicData['musicPaths'] as List).length,
  412. 'totalTracks': musicData['totalTracks'],
  413. 'currentTrack': musicData['currentIndex'] + 1,
  414. };
  415. }
  416.  
  417. Future<void> onMusicEnded(String currentPolygonId) async {
  418. debugPrint("🎵 [onMusicEnded] Musique terminée pour polygone: $currentPolygonId");
  419.  
  420. final nextTrack = await getNextTrack(currentPolygonId);
  421. if (nextTrack != null && onTrackEnded != null) {
  422. onTrackEnded!(nextTrack);
  423. }
  424. }
  425.  
  426. bool currentEqualsToNext(SoundscapePolygon? polygon) {
  427. if (polygon == null) {
  428. _currentMusicPath = {};
  429. return false;
  430. }
  431.  
  432. if (_musics[polygon.uuid] == null) {
  433. _currentMusicPath = {};
  434. return false;
  435. }
  436. if (_currentMusicPath["musicPath"] == _musics[polygon.uuid]!["musicPath"]) {
  437. return true;
  438. }
  439. return false;
  440. }
  441.  
  442. Map<String, dynamic>? getQueueInfo(String polygonId) {
  443. if (!_musics.containsKey(polygonId)) return null;
  444.  
  445. final musicData = _musics[polygonId]!;
  446. return {
  447. 'currentIndex': musicData['currentIndex'],
  448. 'totalTracks': musicData['totalTracks'],
  449. 'currentTitle': musicData['title'],
  450. 'currentAuthor': musicData['author'],
  451. 'currentCoverPath': musicData['coverPath'],
  452. };
  453. }
  454. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement