import 'package:flutter/material.dart'; /// Life events Badaga songs belong to. `id` matches the `occasion` column in Supabase. enum Occasion { huttu('huttu', 'Huttu', 'Birth & naming', Color(0xFF3B6E8F), Icons.spa_outlined), habba('habba', 'Habba', 'Festival celebrations', Color(0xFFB5541E), Icons.celebration_outlined), madhuvae('madhuvae', 'Madhuvae', 'Wedding songs', Color(0xFF9C2F4A), Icons.favorite_border), devotional('devotional', 'Devotional', 'Hethai & temple songs', Color(0xFF6B4FA0), Icons.temple_hindu_outlined), folk('folk', 'Folk songs', 'Traditional melodies', Color(0xFF2E7D5B), Icons.music_note_outlined), saavu('saavu', 'Saavu', 'Remembrance', Color(0xFF4A4F57), Icons.local_florist_outlined), instrumental('instrumental', 'Instrumental', 'Flute, drums and more', Color(0xFF8A5A2B), Icons.graphic_eq); const Occasion(this.id, this.label, this.subtitle, this.color, this.icon); final String id; final String label; final String subtitle; final Color color; final IconData icon; static Occasion fromId(String? id) => Occasion.values.firstWhere((o) => o.id == id, orElse: () => Occasion.folk); } enum Tier { free, premium } class Artist { const Artist({required this.id, required this.name, this.hatti, this.photoUrl, this.bio}); final String id; final String name; final String? hatti; /// Only set once the singer (or family) has given photo permission. final String? photoUrl; final String? bio; String get initials { final parts = name.trim().split(RegExp(r'\s+')).where((p) => p.isNotEmpty).toList(); if (parts.isEmpty) return '?'; if (parts.length == 1) return parts.first.substring(0, 1).toUpperCase(); return (parts.first[0] + parts.last[0]).toUpperCase(); } factory Artist.fromJson(Map j) => Artist( id: j['id'] as String, name: j['name'] as String, hatti: j['hatti'] as String?, photoUrl: j['photoUrl'] as String?, bio: j['bio'] as String?, ); } /// A cassette, CD or folder from the collection. class Album { const Album({required this.id, required this.title, this.year, this.coverUrl}); final String id; final String title; final int? year; final String? coverUrl; } class Track { const Track({ required this.id, required this.titleLatin, required this.artistIds, required this.occasion, required this.duration, required this.audioUrl, this.titleTamil, this.meaning, this.albumId, this.coverAsset, this.coverUrl, this.tier = Tier.free, }); final String id; final String titleLatin; final String? titleTamil; /// English meaning of the title, also searchable. final String? meaning; final List artistIds; final String? albumId; final Occasion occasion; final Duration duration; /// `asset:///...` for bundled audio, otherwise an https URL (signed R2 URL later). final String audioUrl; final String? coverAsset; final String? coverUrl; final Tier tier; factory Track.fromJson(Map j) => Track( id: j['id'] as String, titleLatin: j['titleLatin'] as String, titleTamil: j['titleTamil'] as String?, meaning: j['meaning'] as String?, artistIds: (j['artistIds'] as List).cast(), albumId: j['albumId'] as String?, occasion: Occasion.fromId(j['occasion'] as String?), duration: Duration(milliseconds: (j['durationMs'] as num?)?.toInt() ?? 0), audioUrl: j['audioUrl'] as String, coverAsset: j['coverAsset'] as String?, coverUrl: j['coverUrl'] as String?, tier: j['tier'] == 'premium' ? Tier.premium : Tier.free, ); } String formatDuration(Duration d) { final m = d.inMinutes; final s = d.inSeconds.remainder(60).toString().padLeft(2, '0'); return '$m:$s'; }