#
Autoplay
Sonora v1.2.8 features an intelligent autoplay system that automatically continues playback when the queue runs empty.
#
Table of Contents
Overview How It Works Configuration Strategies Customization Monitoring Troubleshooting
#
Overview
The autoplay system analyzes your listening patterns and automatically suggests relevant tracks to keep the music flowing.
#
Key Features
- Context-aware recommendations based on listening history
- Multiple recommendation strategies (artist, genre, popularity)
- Smart fallback when primary recommendations fail
- Configurable behavior to match your preferences
- Real-time adaptation to changing tastes
#
How It Works
#
Basic Flow
- Queue Empty Detection: System detects when queue runs out
- Context Analysis: Analyzes recent playback history
- Strategy Selection: Chooses appropriate recommendation algorithm
- Track Generation: Fetches recommended tracks
- Queue Addition: Automatically adds tracks to queue
- Seamless Playback: Continues without interruption
#
Context Analysis
The system considers:
- Recently played tracks (last 10-50 tracks)
- Current track and its metadata
- User preferences and patterns
- Time of day and listening context
- Guild-specific behavior
#
Configuration
#
Basic Setup
import discord
from discord.ext import commands
from sonora import SonoraClient
bot = commands.Bot(command_prefix='!')
sonora = SonoraClient(
lavalink_nodes=[{
"host": "localhost",
"port": 2333,
"password": "youshallnotpass"
}]
)
# Autoplay is disabled by default
player = await sonora.get_player(guild_id)
player.autoplay.enabled = True
#
Advanced Configuration
# Configure autoplay behavior
player.autoplay.enabled = True
player.autoplay.strategy = "similar_genre"
player.autoplay.max_history = 25 # Tracks to analyze
player.autoplay.smart_shuffle = True # Intelligent shuffling
#
Command Integration
@bot.command()
async def autoplay(ctx, action: str = None):
"""Control autoplay feature"""
player = await sonora.get_player(ctx.guild.id)
if action is None:
# Show current status
status = "ON" if player.autoplay.enabled else "OFF"
strategy = player.autoplay.strategy
await ctx.send(f"🎵 Autoplay: {status} (Strategy: {strategy})")
return
if action.lower() == "on":
player.autoplay.enabled = True
await ctx.send("🎵 Autoplay enabled!")
elif action.lower() == "off":
player.autoplay.enabled = False
await ctx.send("🎵 Autoplay disabled!")
elif action.lower() in ["artist", "genre", "popular", "random"]:
player.autoplay.strategy = f"similar_{action}" if action != "popular" else "popularity_based"
if action == "random":
player.autoplay.strategy = "random"
await ctx.send(f"🎵 Autoplay strategy set to: {action}")
else:
await ctx.send("❌ Use: !autoplay on/off/artist/genre/popular/random")
#
Strategies
#
Similar Artist
Recommends tracks by the same artist or similar artists.
player.autoplay.strategy = "similar_artist"
Best for:
- Building artist-focused playlists
- Discovering similar artists
- Maintaining consistent artist flow
Algorithm:
- Analyzes current/last played artist
- Finds tracks by same artist
- Extends to similar artists based on metadata
#
Similar Genre
Recommends tracks in similar genres based on content analysis.
player.autoplay.strategy = "similar_genre"
Best for:
- Genre-based listening sessions
- Mood consistency
- Thematic playlists
Algorithm:
- Extracts genre information from track metadata
- Matches against genre database
- Scores tracks by genre similarity
#
Popularity Based
Recommends popular tracks from available sources.
player.autoplay.strategy = "popularity_based"
Best for:
- Casual listening
- Discovering trending music
- High-energy sessions
Algorithm:
- Analyzes play counts and ratings
- Prioritizes highly-rated tracks
- Considers recency and engagement
#
Random
Provides truly random track recommendations.
player.autoplay.strategy = "random"
Best for:
- Variety and surprise
- Breaking out of patterns
- Exploratory listening
Algorithm:
- Pure random selection from available tracks
- No analysis or weighting
- Maximum unpredictability
#
Customization
#
Custom Strategies
Create your own recommendation algorithms:
from sonora.autoplay.strategies import RecommendationStrategy
class MoodBasedStrategy(RecommendationStrategy):
def __init__(self):
super().__init__()
self.mood_keywords = {
'happy': ['upbeat', 'dance', 'pop', 'electronic'],
'chill': ['ambient', 'jazz', 'classical', 'lo-fi'],
'energetic': ['rock', 'metal', 'punk', 'hip-hop'],
'melancholic': ['indie', 'folk', 'alternative', 'blues']
}
async def recommend(self, context):
history = context.get('history', [])
if not history:
return []
# Analyze recent tracks for mood
recent_genres = [track.genre for track in history[-5:] if hasattr(track, 'genre')]
detected_mood = self.detect_mood(recent_genres)
# Get tracks matching mood
mood_genres = self.mood_keywords.get(detected_mood, [])
candidates = await self.search_by_genres(mood_genres)
return candidates[:10]
def detect_mood(self, genres):
# Simple mood detection logic
mood_scores = {}
for genre in genres:
for mood, keywords in self.mood_keywords.items():
if any(keyword in genre.lower() for keyword in keywords):
mood_scores[mood] = mood_scores.get(mood, 0) + 1
return max(mood_scores, key=mood_scores.get) if mood_scores else 'neutral'
# Register custom strategy
player.autoplay.register_strategy("mood_based", MoodBasedStrategy())
player.autoplay.strategy = "mood_based"
#
Context Providers
Add external context to recommendations:
class WeatherContextProvider:
async def get_context(self, guild_id):
# Get current weather for guild location
weather = await self.get_weather_for_guild(guild_id)
return {
'weather': weather['condition'],
'temperature': weather['temp'],
'time_of_day': self.get_time_of_day(),
'season': self.get_current_season()
}
# Integrate with autoplay
context_provider = WeatherContextProvider()
# Override context fetching
original_get_context = player.autoplay.get_context
async def enhanced_get_context(guild_id):
base_context = await original_get_context(guild_id)
extra_context = await context_provider.get_context(guild_id)
return {**base_context, **extra_context}
player.autoplay.get_context = enhanced_get_context
#
Manual Recommendations
Get recommendations without enabling autoplay:
@bot.command()
async def recommend(ctx, count: int = 5):
"""Get manual recommendations"""
player = await sonora.get_player(ctx.guild.id)
# Build context
context = {
'history': list(player.queue.history)[-10:],
'current': player.queue.current,
'guild_id': ctx.guild.id,
'user_id': ctx.author.id
}
# Get recommendations
recommendations = await player.autoplay.fetch_next_track(context, count=count)
if not recommendations:
await ctx.send("❌ No recommendations available")
return
# Format response
embed = discord.Embed(title="🎵 Recommendations", color=0x00ff00)
for i, track in enumerate(recommendations, 1):
embed.add_field(
name=f"{i}. {track.title}",
value=f"by {track.author} ({track.length//1000}s)",
inline=False
)
await ctx.send(embed=embed)
#
Monitoring
#
Autoplay Statistics
@bot.command()
async def autoplay_stats(ctx):
"""Show autoplay statistics"""
player = await sonora.get_player(ctx.guild.id)
embed = discord.Embed(title="🎵 Autoplay Statistics", color=0x0099ff)
embed.add_field(
name="Status",
value="Enabled" if player.autoplay.enabled else "Disabled",
inline=True
)
embed.add_field(
name="Strategy",
value=player.autoplay.strategy.replace('_', ' ').title(),
inline=True
)
embed.add_field(
name="History Size",
value=str(player.autoplay.max_history),
inline=True
)
# Get recommendation success rate
success_rate = await player.autoplay.get_success_rate()
embed.add_field(
name="Success Rate",
value=f"{success_rate:.1%}",
inline=True
)
embed.set_footer(text="Autoplay keeps the music flowing! 🎶")
await ctx.send(embed=embed)
#
Debug Information
@bot.command()
async def autoplay_debug(ctx):
"""Show autoplay debug information"""
player = await sonora.get_player(ctx.guild.id)
debug_info = await player.autoplay.get_debug_info()
embed = discord.Embed(title="🔍 Autoplay Debug", color=0xff9900)
embed.add_field(
name="Strategy",
value=debug_info.get('strategy', 'Unknown'),
inline=True
)
embed.add_field(
name="History Tracks",
value=str(debug_info.get('history_count', 0)),
inline=True
)
embed.add_field(
name="Cache Size",
value=str(debug_info.get('cache_size', 0)),
inline=True
)
embed.add_field(
name="Last Recommendation",
value=debug_info.get('last_recommendation', 'None'),
inline=False
)
await ctx.send(embed=embed)
#
Troubleshooting
#
Common Issues
No recommendations generated
- Check if autoplay is enabled:
player.autoplay.enabled = True - Verify strategy is valid
- Ensure sufficient listening history
- Check for network connectivity
Poor recommendation quality
- Try different strategies
- Adjust
max_historysetting - Clear autoplay cache
- Provide more listening data
Autoplay not triggering
- Confirm queue is actually empty
- Check if tracks are being added properly
- Verify autoplay permissions
- Monitor for errors in logs
Too similar recommendations
- Change to different strategy
- Increase history analysis size
- Enable smart shuffle
- Add more variety to listening habits
#
Performance Considerations
High CPU usage
- Reduce
max_historysize - Disable complex strategies
- Clear caches periodically
- Monitor with
sonoractl performance
Memory issues
- Limit cache sizes
- Clear old history
- Restart periodically
- Use lighter strategies
#
Advanced Debugging
# Enable verbose logging
import logging
logging.getLogger('sonora.autoplay').setLevel(logging.DEBUG)
# Monitor recommendation patterns
@bot.command()
async def autoplay_log(ctx, duration: int = 60):
"""Log autoplay activity for debugging"""
player = await sonora.get_player(ctx.guild.id)
original_recommend = player.autoplay.fetch_next_track
recommendations = []
async def logging_recommend(context):
result = await original_recommend(context)
recommendations.append({
'track': result,
'context': context,
'timestamp': time.time()
})
return result
player.autoplay.fetch_next_track = logging_recommend
await ctx.send(f"🎵 Logging autoplay for {duration} seconds...")
await asyncio.sleep(duration)
# Analyze results
embed = discord.Embed(title="📊 Autoplay Log Analysis", color=0x00ff00)
total_recs = len(recommendations)
embed.add_field(name="Total Recommendations", value=str(total_recs), inline=True)
if total_recs > 0:
avg_confidence = sum(r.get('confidence', 0) for r in recommendations) / total_recs
embed.add_field(name="Avg Confidence", value=f"{avg_confidence:.2f}", inline=True)
strategy_counts = {}
for r in recommendations:
strategy = r.get('strategy', 'unknown')
strategy_counts[strategy] = strategy_counts.get(strategy, 0) + 1
strategy_text = "\n".join(f"{k}: {v}" for k, v in strategy_counts.items())
embed.add_field(name="Strategies Used", value=strategy_text, inline=False)
await ctx.send(embed=embed)
# Restore original function
player.autoplay.fetch_next_track = original_recommend
#
Best Practices
#
Configuration
- Start Simple: Begin with basic strategies
- Monitor Quality: Track recommendation success
- Adjust Gradually: Fine-tune based on user feedback
- Provide Controls: Allow users to enable/disable and configure
#
Performance
- Limit History: Don't analyze too much history
- Cache Wisely: Use caching for expensive operations
- Batch Operations: Process multiple recommendations together
- Monitor Resources: Watch CPU and memory usage
#
User Experience
- Clear Communication: Inform users when autoplay is active
- Quality Control: Skip low-quality recommendations
- Fallback Handling: Have backup strategies when primary fails
- User Preferences: Respect individual user preferences
#
Maintenance
- Regular Updates: Keep recommendation algorithms current
- Data Cleaning: Remove old or invalid track data
- Performance Tuning: Optimize based on usage patterns
- Feedback Loop: Learn from user acceptance/rejection
For more advanced usage, check the API documentation or join our Discord server for community support.