added audio output for demo songs since spotify api doesnt support audio streaming

This commit is contained in:
Leon Astner 2025-08-02 05:41:50 +02:00
parent 6b93f1206d
commit a91654df03
14 changed files with 1261 additions and 77 deletions

View file

@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/music_queue_service.dart';
import '../services/spam_protection_service.dart';
import '../services/genre_filter_service.dart';
import '../models/spotify_track.dart';
import '../widgets/user_activity_status.dart';
class VotingPage extends StatefulWidget {
const VotingPage({super.key});
@ -30,6 +33,33 @@ class _VotingPageState extends State<VotingPage> {
Future<void> _searchSpotify(String query) async {
if (query.isEmpty) return;
// Check if search query is appropriate
if (!GenreFilterService.isSearchQueryAppropriate(query)) {
final suggestions = GenreFilterService.getAlternativeSearchSuggestions(query);
setState(() {
_isLoading = false;
_statusMessage = 'Search term not suitable for the peaceful Lido atmosphere. Try: ${suggestions.join(', ')}';
_searchResults = [];
});
return;
}
final spamService = Provider.of<SpamProtectionService>(context, listen: false);
final userId = spamService.getCurrentUserId();
// Check spam protection for suggestions
if (!spamService.canSuggest(userId)) {
final cooldown = spamService.getSuggestionCooldownRemaining(userId);
final blockMessage = spamService.getBlockMessage(userId);
setState(() {
_isLoading = false;
_statusMessage = blockMessage ?? 'Please wait $cooldown seconds before searching again.';
_searchResults = [];
});
return;
}
setState(() {
_isLoading = true;
_statusMessage = 'Searching for "$query"...';
@ -39,12 +69,24 @@ class _VotingPageState extends State<VotingPage> {
final queueService = Provider.of<MusicQueueService>(context, listen: false);
final results = await queueService.searchTracks(query);
// Filter results based on genre appropriateness
final filteredResults = GenreFilterService.filterSearchResults(results);
// Record the suggestion attempt
spamService.recordSuggestion(userId);
setState(() {
_searchResults = results;
_searchResults = filteredResults;
_isLoading = false;
_statusMessage = results.isEmpty
? 'No tracks found for "$query"'
: 'Found ${results.length} tracks';
if (filteredResults.isEmpty && results.isNotEmpty) {
_statusMessage = 'No tracks found that match the peaceful Lido atmosphere. Try searching for chill, ambient, or relaxing music.';
} else if (filteredResults.isEmpty) {
_statusMessage = 'No tracks found for "$query"';
} else {
final filtered = results.length - filteredResults.length;
_statusMessage = 'Found ${filteredResults.length} tracks' +
(filtered > 0 ? ' ($filtered filtered for atmosphere)' : '');
}
});
} catch (e) {
setState(() {
@ -56,9 +98,43 @@ class _VotingPageState extends State<VotingPage> {
}
void _addToQueue(SpotifyTrack track) {
final spamService = Provider.of<SpamProtectionService>(context, listen: false);
final userId = spamService.getCurrentUserId();
// Check if user can suggest (add to queue)
if (!spamService.canSuggest(userId)) {
final cooldown = spamService.getSuggestionCooldownRemaining(userId);
final blockMessage = spamService.getBlockMessage(userId);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(blockMessage ?? 'Please wait $cooldown seconds before adding another song.'),
duration: const Duration(seconds: 3),
backgroundColor: Colors.orange,
),
);
return;
}
// Check if track is appropriate for atmosphere
final rejectionReason = GenreFilterService.getRejectionReason(track);
if (rejectionReason != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(rejectionReason),
duration: const Duration(seconds: 4),
backgroundColor: Colors.orange,
),
);
return;
}
final queueService = Provider.of<MusicQueueService>(context, listen: false);
queueService.addToQueue(track);
// Record the suggestion
spamService.recordSuggestion(userId);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Added "${track.name}" to queue'),
@ -77,6 +153,13 @@ class _VotingPageState extends State<VotingPage> {
body: SafeArea(
child: Column(
children: [
// User Activity Status
Consumer<SpamProtectionService>(
builder: (context, spamService, child) {
return UserActivityStatus(spamService: spamService);
},
),
// Header with Search
Container(
padding: const EdgeInsets.all(20),
@ -149,6 +232,81 @@ class _VotingPageState extends State<VotingPage> {
onSubmitted: _searchSpotify,
),
// Atmosphere Info
Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF6366F1).withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF6366F1).withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.spa, color: Color(0xFF6366F1), size: 20),
SizedBox(width: 8),
Text(
'Lido Atmosphere',
style: TextStyle(
color: Color(0xFF6366F1),
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
),
const SizedBox(height: 8),
Text(
GenreFilterService.getAtmosphereDescription(),
style: const TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
const SizedBox(height: 12),
const Text(
'Try these searches:',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 4,
children: GenreFilterService.getSuggestedSearchTerms()
.take(8)
.map((term) => InkWell(
onTap: () {
_searchController.text = term;
_searchSpotify(term);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF6366F1).withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Text(
term,
style: const TextStyle(
color: Color(0xFF6366F1),
fontSize: 11,
),
),
),
))
.toList(),
),
],
),
),
// Status Message
if (_statusMessage.isNotEmpty)
Padding(
@ -458,13 +616,25 @@ class _VotingPageState extends State<VotingPage> {
// Voting Buttons
Column(
children: [
IconButton(
onPressed: () => queueService.upvote(index),
icon: const Icon(
Icons.keyboard_arrow_up,
color: Colors.green,
size: 28,
),
Consumer<SpamProtectionService>(
builder: (context, spamService, child) {
final userId = spamService.getCurrentUserId();
final canVote = spamService.canVote(userId);
final cooldown = spamService.getVoteCooldownRemaining(userId);
return IconButton(
onPressed: canVote ? () {
queueService.upvote(index);
spamService.recordVote(userId);
} : null,
icon: Icon(
Icons.keyboard_arrow_up,
color: canVote ? Colors.green : Colors.grey,
size: 28,
),
tooltip: canVote ? 'Upvote' : 'Wait $cooldown seconds',
);
}
),
Text(
'${queueItem.votes}',
@ -473,13 +643,25 @@ class _VotingPageState extends State<VotingPage> {
fontWeight: FontWeight.bold,
),
),
IconButton(
onPressed: () => queueService.downvote(index),
icon: const Icon(
Icons.keyboard_arrow_down,
color: Colors.red,
size: 28,
),
Consumer<SpamProtectionService>(
builder: (context, spamService, child) {
final userId = spamService.getCurrentUserId();
final canVote = spamService.canVote(userId);
final cooldown = spamService.getVoteCooldownRemaining(userId);
return IconButton(
onPressed: canVote ? () {
queueService.downvote(index);
spamService.recordVote(userId);
} : null,
icon: Icon(
Icons.keyboard_arrow_down,
color: canVote ? Colors.red : Colors.grey,
size: 28,
),
tooltip: canVote ? 'Downvote' : 'Wait $cooldown seconds',
);
}
),
],
),