Adding Jazz to your Python scripts

Posted by StuffonmyMind on September 21, 2020
img
Come with me and find safe haven in a warm bathtub full of my jazz

Jazzit 🎷 : https://github.com/Sangarshanan/jazzit

Brain is weird, cause when you are relaxed enough it convinces you that the stupidest thing ever is the most amazing idea you ever idea, this is a story of one such idea.

It was a lazy weekend, I was building a docker image for some dumb work stuff and it was taking too long (Typical) one of these days I should stop bloating my PC with humongous docker images. So I said screw it kept my PC running and hopped into the shower and as it happens when you shower your mind goes places. I though to myself Hmmm what if my computer was playing a tune to indicate that it was still running which lead to, what if it could tell me if it fails with or when it succeeds with background music. So kinda like a background score but for your code

TAN TAN TAN

I was like Hell Yeah !! Best idea ever maybe I could hire Hans Zimmer to produce it for me. Now comes the cool part where I had to think about implementation. It was easy I just start a music playing daemon thread/ process in the background and stop it the execution is done. I implemented this for python scripts using decorators cause I use python a lot and using decorators makes me feel smart. I then decided to call this jazzit cause you know it added jazz to my code.

I put in up on hackernews cause I am clout chaser https://news.ycombinator.com/item?id=24485447 and boy was I happy when the stars starting kicking in. Daddy loves that social currency induced dopamine hit (I apologize for that comment)

img

As it went trending, issues were opened and so were some wounds and by that I meant some blatantly stupid things I overlooked cause it was working for me and I didn’t care but when an issue was opened to support ipython notebook I realized I could do something a little cooler, I decided to go old school and use the HTML Audio tag with jupyter notebooks. I could then also use jupyer magic and get rids of the need to wrap code in functions cause the real ones don’t need subroutines also this implementation is OS agnostic cause its on the browser

We can use the Audio tag on jupyter notebooks using IPython display

1
2
3
4
5
from IPython.display import Audio
Audio(
    "https://www.free-stock-music.com/music/musicbyaden-mondays.mp3", 
    autoplay=True
)

Runnning this will result in an audio element popping and the music starts autoplaying

This is just a HTML Audio tag and is inherently not blocking so we can continue to run other cells in our notebook as the music keeps playing in the background till we pause the music by using the clicking the pause object on the Audio element

Now we wanna achieve two things with this

  • Audio tag should be invisible to users, we need to emulate the effect of background music and an ugly audio tag will ruin the whole magic

  • We need a way to programmatically pause and unpause the music and make it run in a loop

Here is how to do it, First I write my own version of IPython Audio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class JupyterAudio(Audio):
    def __init__(self, *args, **kwargs):
        self.loop = kwargs.pop("loop", False)
        super().__init__(*args, **kwargs)

    def _repr_html_(self):
        audio = super()._repr_html_()
        if self.loop:
            loop = "controls loop"
        else:
            loop = ""
        audio = audio.replace(
            "<audio", 
            f'<audio {loop} onended="this.parentNode.removeChild(this)"'
        )
        return f'<div style="display:none">{audio}</div>'

I am overloading _repr_html_ with my own version of Audio tag that is invisible cause the display:none and I am also adding an optional controls loop to loop the audio track when needed

Now onto playing and pausing the music with our own JupyterPlayer

1
2
3
4
5
6
7
8
9
10
11
12
class JupyterPlayer:
    def __init__(self, track):
        self.track = track

    def start_music(self, loop):
        sound = JupyterAudio(self.track, autoplay=True, loop=loop)
        return display(sound, display_id="123")

    @staticmethod
    def stop_music(display):
        """Stop Music."""
        display.update(HTML(""))

So now we are gonna use the Custom Audio tag to write a Player which can play and pause music, maybe more methods can be added to make it closer to a real music player but keeping it super simple for now, also kinda feel like stop_music is a hack cause it replaces the existing audio display with an empty html tag inplace making it kinda like a destructor.

So yeah now we can instantiate our Player to start and stop music at will, all of it happens in the background and none of em are blocking, Time to go crazy

1
2
3
4
5
audio = JupyterPlayer(
    "https://www.free-stock-music.com/music/musicbyaden-mondays.mp3"
)
player = audio.start_music(loop=False)
audio.stop_music(player)

:wq