🎉 Initial commit: AudiOhm - Alternative à Spotify avec streaming YouTube

Features:
- Frontend Flutter avec thème néon cyberpunk
- Backend FastAPI avec streaming YouTube
- Base de données PostgreSQL + Redis
- Authentification JWT complète
- Recherche multi-source (DB + YouTube)
- Playlists CRUD avec drag & drop
- Queue management
- Settings avec audio quality
- Interface adaptative (Desktop + Mobile)

Tech Stack:
- Frontend: Flutter 3.2+, Riverpod
- Backend: Python 3.11+, FastAPI
- Database: PostgreSQL 15+
- Cache: Redis 7+
- Streaming: yt-dlp + FFmpeg

🚀 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
feldenr
2026-01-18 17:08:59 +01:00
commit 9c504d2c3d
128 changed files with 22638 additions and 0 deletions
@@ -0,0 +1,135 @@
import 'package:flutter/material.dart';
import '../../../core/theme/colors.dart';
/// Desktop Top Bar
class DesktopTopBar extends StatelessWidget {
const DesktopTopBar({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
color: AppColors.surface,
border: Border(
bottom: BorderSide(
color: AppColors.cyan.withOpacity(0.1),
width: 1,
),
),
),
child: Row(
children: [
// Search bar
Expanded(
child: _SearchBar(),
),
const SizedBox(width: 16),
// User profile
// TODO: Implement user profile menu
const _UserAvatar(),
],
),
);
}
}
/// Search Bar
class _SearchBar extends StatefulWidget {
@override
State<_SearchBar> createState() => _SearchBarState();
}
class _SearchBarState extends State<_SearchBar> {
final _focusNode = FocusNode();
bool _isFocused = false;
@override
void initState() {
super.initState();
_focusNode.addListener(() {
setState(() {
_isFocused = _focusNode.hasFocus;
});
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: AppColors.surfaceVariant,
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: _isFocused ? AppColors.cyan : AppColors.cyan.withOpacity(0.2),
width: _isFocused ? 2 : 1,
),
boxShadow: _isFocused
? [
BoxShadow(
color: AppColors.cyan.withOpacity(0.3),
blurRadius: 20,
spreadRadius: 0,
),
]
: null,
),
child: TextField(
focusNode: _focusNode,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 14,
),
decoration: InputDecoration(
hintText: 'Search tracks, artists, albums...',
hintStyle: TextStyle(
color: AppColors.muted,
fontSize: 14,
),
prefixIcon: const Icon(
Icons.search,
color: AppColors.cyan,
),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
),
);
}
}
/// User Avatar
class _UserAvatar extends StatelessWidget {
const _UserAvatar();
@override
Widget build(BuildContext context) {
return Container(
width: 40,
height: 40,
decoration: BoxDecoration(
gradient: AppColors.primaryGradient,
borderRadius: BorderRadius.circular(20),
boxShadow: AppColors.cyanGlow,
),
child: const Icon(
Icons.person,
color: AppColors.onBackground,
),
);
}
}