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,130 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'track.dart';
|
||||
|
||||
/// Playlist entity
|
||||
class Playlist extends Equatable {
|
||||
final String id;
|
||||
final String userId;
|
||||
final String name;
|
||||
final String? description;
|
||||
final String? imageUrl;
|
||||
final bool isPublic;
|
||||
final bool isCollaborative;
|
||||
final bool isSmart;
|
||||
final int trackCount;
|
||||
final int totalDuration;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final List<PlaylistTrack>? tracks;
|
||||
|
||||
const Playlist({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.name,
|
||||
this.description,
|
||||
this.imageUrl,
|
||||
this.isPublic = false,
|
||||
this.isCollaborative = false,
|
||||
this.isSmart = false,
|
||||
this.trackCount = 0,
|
||||
this.totalDuration = 0,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.tracks,
|
||||
});
|
||||
|
||||
/// Format total duration as Xh Ym or Ym Zs
|
||||
String get formattedDuration {
|
||||
final hours = totalDuration ~/ 3600;
|
||||
final minutes = (totalDuration % 3600) ~/ 60;
|
||||
final seconds = totalDuration % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return '${hours}h ${minutes}m';
|
||||
} else if (minutes > 0) {
|
||||
return '${minutes}m ${seconds}s';
|
||||
} else {
|
||||
return '${seconds}s';
|
||||
}
|
||||
}
|
||||
|
||||
/// Create Playlist from JSON
|
||||
factory Playlist.fromJson(Map<String, dynamic> json) {
|
||||
return Playlist(
|
||||
id: json['id'] as String,
|
||||
userId: json['user_id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String?,
|
||||
imageUrl: json['image_url'] as String?,
|
||||
isPublic: json['is_public'] as bool? ?? false,
|
||||
isCollaborative: json['is_collaborative'] as bool? ?? false,
|
||||
isSmart: json['is_smart'] as bool? ?? false,
|
||||
trackCount: json['track_count'] as int? ?? 0,
|
||||
totalDuration: json['total_duration'] as int? ?? 0,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String)
|
||||
: DateTime.now(),
|
||||
updatedAt: json['updated_at'] != null
|
||||
? DateTime.parse(json['updated_at'] as String)
|
||||
: DateTime.now(),
|
||||
tracks: json['tracks'] != null
|
||||
? (json['tracks'] as List)
|
||||
.map((item) => PlaylistTrack.fromJson(item as Map<String, dynamic>))
|
||||
.toList()
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
userId,
|
||||
name,
|
||||
isPublic,
|
||||
isCollaborative,
|
||||
trackCount,
|
||||
totalDuration,
|
||||
];
|
||||
}
|
||||
|
||||
/// Playlist track association
|
||||
class PlaylistTrack extends Equatable {
|
||||
final String id;
|
||||
final String playlistId;
|
||||
final String trackId;
|
||||
final int position;
|
||||
final DateTime addedAt;
|
||||
final String? addedBy;
|
||||
final Track? track;
|
||||
|
||||
const PlaylistTrack({
|
||||
required this.id,
|
||||
required this.playlistId,
|
||||
required this.trackId,
|
||||
required this.position,
|
||||
required this.addedAt,
|
||||
this.adddedBy,
|
||||
this.track,
|
||||
});
|
||||
|
||||
/// Create PlaylistTrack from JSON
|
||||
factory PlaylistTrack.fromJson(Map<String, dynamic> json) {
|
||||
return PlaylistTrack(
|
||||
id: json['id'] as String,
|
||||
playlistId: json['playlist_id'] as String,
|
||||
trackId: json['track_id'] as String,
|
||||
position: json['position'] as int,
|
||||
addedAt: json['added_at'] != null
|
||||
? DateTime.parse(json['added_at'] as String)
|
||||
: DateTime.now(),
|
||||
addedBy: json['added_by'] as String?,
|
||||
track: json['track'] != null
|
||||
? Track.fromJson(json['track'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, playlistId, trackId, position, addedAt];
|
||||
}
|
||||
Reference in New Issue
Block a user