Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 22462

Albert Hopkins: Writing autofill plugins for TeamPlayer

$
0
0
Background

TeamPlayer is a Django-based streaming radio app with a twist. A while back it gained a feature called "shake things up" where, instead of dead silence, "DJ Ango" would play tracks from the TeamPlayer Library when no players had any queued songs. Initially this was implemented by creating a queue for DJ Ango and then filling it with random tracks. This worked but after I while I became annoyed by the "randomness" and so went about writing a few other implementations which I call "autofill strategies". These were function definitions and the autofill logic used an if/else clause to select which function to call based on what was set in the Django settings.

Recently I got rid of the if/else's and instead use setuptools entry points. This also allows for third parties to write "autofill plugins" for TeamPlayer. Here's how to do it.

As I said every autofill strategy is a Python function with the following signature:

def my_autofill_strategy(*, queryset, entries_needed, station):  

This function should return a list of teamplayer.models.LibraryItem. The list should ideally have a length of entries_needed but no longer, and the returned list should contain entries from the queryset. The "should"s are emphasized because sometimes a particular strategy can't find enough entries from the queryset so it can either return a smaller list or return entries not in the queryset or both. The station argument is the teamplayer.models.Station instance for which songs are being selected. This is (almost) always Station.main_station().

Idea

Regular terrestrial radio stations often play the same set of songs in rotation over and over again. This is one reason why I rarely listen to them. However I thought this would be an interesting (and easy) autofill strategy to write.

Implementation

Here's the idea: keep a (play)list of songs from the TeamPlayer Library for rotation, store it in a database table, and then write the autofill function to simply pick from that list. Here is the Django database model:

from django.db import models  
from teamplayer.models import LibraryItem

class Song(models.Model):  
    song = models.OneToOneField(LibraryItem)

This table's rows just point to a LibraryItem. We can use the Django admin site to maintain the list. So again the autofill function just points to entries from the list:

from .models import Song

def rotation_autofill(*, queryset, entries_needed, station):  
    songs = Song.objects.order_by('?')[:entries_needed]
    songs = [i.song for i in songs]

    return songs

Now all that we need is some logic to run the commercial breaks and station identification. Just kidding. Now all that is needed is to "package" our plugin.

Packaging

As I've said TeamPlayer now uses setuptools entry points to get autofill strategies. The entry point group name for autofill plugins is aptly called 'teamplayer.autofill_strategy'. So in our setup.py we register our function as such:

# setup.py
from setuptools import setup

setup(  
    name='mypackage',
    ...
    entry_points={
        'teamplayer.autofill_strategy': [
            'rotation = mypackage.autofill:rotation_autofill',
        ]
    }
)

Here the entry_points argument to setup defines the entry points. For this we declare the group teamplayer.autofill_strategy and in that group we have a single entry point called rotation. rotation points to the rotation_autofill function in the module mypackage.autofill (using dots for the module and a colon for the member).

From there all you would need is to pip install your app, add it to INSTALLED_APPS (after TeamPlayer) and change the following setting:

TEAMPLAYER = {  
    'SHAKE_THINGS_UP': 10,
    'AUTOFILL_STRATEGY': 'rotation',
}

The 'SHAKE_THINGS_UP' setting tells TeamPlayer the (maximum) number of Library items to add to DJ Ango's queue at a time (0 to disable) and the AUTOFILL_STRATEGY tells which autofill strategy plugin to load.

A (more) complete implementation of this example is here.


Viewing all articles
Browse latest Browse all 22462

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>