# 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

  1. Queue Empty Detection: System detects when queue runs out
  2. Context Analysis: Analyzes recent playback history
  3. Strategy Selection: Chooses appropriate recommendation algorithm
  4. Track Generation: Fetches recommended tracks
  5. Queue Addition: Automatically adds tracks to queue
  6. 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_history setting
  • 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_history size
  • 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

  1. Start Simple: Begin with basic strategies
  2. Monitor Quality: Track recommendation success
  3. Adjust Gradually: Fine-tune based on user feedback
  4. Provide Controls: Allow users to enable/disable and configure

# Performance

  1. Limit History: Don't analyze too much history
  2. Cache Wisely: Use caching for expensive operations
  3. Batch Operations: Process multiple recommendations together
  4. Monitor Resources: Watch CPU and memory usage

# User Experience

  1. Clear Communication: Inform users when autoplay is active
  2. Quality Control: Skip low-quality recommendations
  3. Fallback Handling: Have backup strategies when primary fails
  4. User Preferences: Respect individual user preferences

# Maintenance

  1. Regular Updates: Keep recommendation algorithms current
  2. Data Cleaning: Remove old or invalid track data
  3. Performance Tuning: Optimize based on usage patterns
  4. Feedback Loop: Learn from user acceptance/rejection

For more advanced usage, check the API documentation or join our Discord server for community support.