TransWikia.com

Can't play multiple .mp3 files in streaming function using Shouty

Stack Overflow Asked by feners on December 7, 2020

I’m writing some code that connects to an IceCast server using Shouty. My code takes an .mp3 file and streams it using IceCast to a web player I made.

from multiprocessing import Process, JoinableQueue
import queue
import subprocess
import os

from flask import Response
import shouty

class Worker(Process):
    def __init__(self):
        super().__init__()
        self.queue = JoinableQueue()
        self.is_mp3 = True

    def put_queue(self, mp3file):
        self.queue.put(mp3file)

    def join_queue(self):
        self.queue.join()

    def checkMP3(self):
    ''' Check files before streaming'''

    def go_Stream(self):
        with shouty.connect(**params) as connection:
            song_path = 0
            allfiles = os.listdir('app/')
            for i in allfiles:
                if i[:1] == "C":
                    song_path = i
                    return Response(self.streamStart(connection=connection, mp3file=song_path), mimetype="audio/mp3")

    def streamStart(self, connection, mp3file):
        with open('music/' + mp3file, 'rb') as song:
            ffmpeg = None
            src = song
            if self.is_mp3:
                ffmpeg = subprocess.Popen(
                    [   
                        "ffmpeg",
                        "-i",
                        "-",
                        "-f",
                        "mp3",
                        "-ab",
                        "192",
                        "-",
                    ],
                    stdin=song,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.DEVNULL,
                )
                src = ffmpeg.stdout
            chunk_size = 4096
            sent_bytes = 0
            if src:
                while True:
                    chunk = src.read(chunk_size)
                    if not chunk:
                        return "Buffer Empty"
                    yield chunk
                    sent_bytes += len(chunk)
                src.close()
            if ffmpeg:
                ffmpeg.terminate()

    def run(self):
        checkMP3()

def run ():
    w = Worker()
    w.start()

In an HTML page I have set up with Flask, the web player has a play button that triggers go_Stream. It works, but it only plays one of the .mp3 files and I can’t figure out how to keep playing the .mp3 files in the directory. Should I have some loop in place that streamStart for every .mp3 file?

One Answer

I would have the HTML player page make requests for the next song using something like jquery and use an index. If you modify your route for serving songs to your clients, to serve a song at an index, the user could also skip songs or go back to previous songs very simply, and then your client and server side logic can be more simple as well.

Server side in Python: Wait for get request at /songplayurl, upon request look for a path parameter or a form parameter in said request, that has the index of the song desired, and then use icecast to play that song.

Client side: When the user clicks on the next or prev button on your player, send a get request to the server for /songplayurl with parameter index+1 or index-1. Additionally, for autoplay functionality, use the player to detect when a song finishes playing, and make a new request for index+1 then.

The reason I'd suggest this is because web apps are client/server apps. Playing on a loop would require server sent events to change songs, or you'd need to send all the data for all the songs at one time to the client application. So instead of going through all of that effort to configure server sent events, and instead of sending a massive payload, just send data for one song at a time.

Answered by TheFunk on December 7, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP