Initial commit: AudiOhm - Alternative Spotify avec streaming YouTube
Backend: - FastAPI avec PostgreSQL et Redis - Authentification JWT complète - API REST pour musique, playlists, recherche - Streaming audio via yt-dlp - SQLAlchemy 2.0 async Frontend: - Flutter avec thème néon cyberpunk - State management Riverpod - Layout adaptatif desktop/mobile - Lecteur audio avec mini-player Infrastructure: - Docker Compose (PostgreSQL + Redis) - Scripts d'installation automatisés - Scripts de build pour exécutables Fichiers ajoutés: - BUILD_CLIENT_*.bat/sh: Scripts de compilation - BUILD_CLIENT_README.md: Documentation compilation - CHECK_FLUTTER.sh: Vérificateur d'environnement - requirements.txt mis à jour pour Python 3.13 - Modèles SQLAlchemy corrigés (metadata -> extra_metadata) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/// Search Provider - Search state management
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../infrastructure/datasources/remote/music_api_service.dart';
|
||||
import '../../../domain/entities/track.dart';
|
||||
import '../../../domain/entities/artist.dart';
|
||||
import '../../../domain/entities/album.dart';
|
||||
|
||||
/// Search state
|
||||
class SearchState {
|
||||
final String query;
|
||||
final bool isSearching;
|
||||
final List<Track> tracks;
|
||||
final List<Artist> artists;
|
||||
final List<Album> albums;
|
||||
final String? error;
|
||||
|
||||
const SearchState({
|
||||
this.query = '',
|
||||
this.isSearching = false,
|
||||
this.tracks = const [],
|
||||
this.artists = const [],
|
||||
this.albums = const [],
|
||||
this.error,
|
||||
});
|
||||
|
||||
SearchState copyWith({
|
||||
String? query,
|
||||
bool? isSearching,
|
||||
List<Track>? tracks,
|
||||
List<Artist>? artists,
|
||||
List<Album>? albums,
|
||||
String? error,
|
||||
}) {
|
||||
return SearchState(
|
||||
query: query ?? this.query,
|
||||
isSearching: isSearching ?? this.isSearching,
|
||||
tracks: tracks ?? this.tracks,
|
||||
artists: artists ?? this.artists,
|
||||
albums: albums ?? this.albums,
|
||||
error: error,
|
||||
);
|
||||
}
|
||||
|
||||
int get totalResults => tracks.length + artists.length + albums.length;
|
||||
}
|
||||
|
||||
/// Search notifier with debouncing
|
||||
class SearchNotifier extends StateNotifier<SearchState> {
|
||||
SearchNotifier(this._musicApiService) : super(const SearchState());
|
||||
|
||||
final MusicApiService _musicApiService;
|
||||
Timer? _debounceTimer;
|
||||
|
||||
static const _debounceDuration = Duration(milliseconds: 500);
|
||||
|
||||
void search(String query) {
|
||||
if (query.trim().isEmpty) {
|
||||
state = const SearchState();
|
||||
_debounceTimer?.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
_debounceTimer?.cancel();
|
||||
state = state.copyWith(query: query, isSearching: true);
|
||||
|
||||
_debounceTimer = Timer(_debounceDuration, () => _performSearch(query));
|
||||
}
|
||||
|
||||
Future<void> _performSearch(String query) async {
|
||||
try {
|
||||
final results = await _musicApiService.search(
|
||||
query,
|
||||
type: 'all',
|
||||
limit: 20,
|
||||
);
|
||||
|
||||
state = SearchState(
|
||||
query: query,
|
||||
tracks: (results['tracks'] as List?)
|
||||
?.map((json) => Track.fromJson(json as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
artists: (results['artists'] as List?)
|
||||
?.map((json) => Artist.fromJson(json as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
albums: (results['albums'] as List?)
|
||||
?.map((json) => Album.fromJson(json as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
} catch (e) {
|
||||
state = SearchState(
|
||||
query: query,
|
||||
error: e.toString(),
|
||||
);
|
||||
} finally {
|
||||
// Keep isSearching false if this was the latest search
|
||||
if (state.query == query) {
|
||||
state = state.copyWith(isSearching: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_debounceTimer?.cancel();
|
||||
state = const SearchState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debounceTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Search provider
|
||||
final searchProvider = StateNotifierProvider<SearchNotifier, SearchState>((ref) {
|
||||
final musicApiService = ref.watch(musicApiServiceProvider);
|
||||
return SearchNotifier(musicApiService);
|
||||
});
|
||||
Reference in New Issue
Block a user