From 802c3645251fe85c88c08cdb620f5ff78377f37f Mon Sep 17 00:00:00 2001 From: zuckerberg <5-zuckerberg@users.noreply.git.neet.dev> Date: Tue, 1 Jun 2021 19:30:59 -0400 Subject: [PATCH] radio api --- default.nix | 2 +- downloader.py | 2 +- nullsrc.py | 22 ++++++++++ radio.py | 113 +++++++++++++++++++++++++++++++++++++++++--------- setup.py | 23 ++++++---- transcoder.py | 2 +- 6 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 nullsrc.py diff --git a/default.nix b/default.nix index 9610e5f..b4041ff 100644 --- a/default.nix +++ b/default.nix @@ -4,5 +4,5 @@ pkgs.python3Packages.buildPythonApplication { pname = "radio"; src = self; version = "0.1"; - propagatedBuildInputs = with pkgs.python3Packages; [ pip ffmpeg-python ]; + propagatedBuildInputs = with pkgs.python3Packages; [ pip ffmpeg-python flask ]; } \ No newline at end of file diff --git a/downloader.py b/downloader.py index 27eb26a..20123a4 100644 --- a/downloader.py +++ b/downloader.py @@ -64,7 +64,7 @@ class Downloader(Thread): if self.isAlive(): os.killpg(os.getpgid(self.popen.pid), signal.SIGTERM) self.popen.stdout.close() - self.popen.wait() + # self.popen.wait() self.exit = True # checks to see if the current download has finished diff --git a/nullsrc.py b/nullsrc.py new file mode 100644 index 0000000..d0b1741 --- /dev/null +++ b/nullsrc.py @@ -0,0 +1,22 @@ +import ffmpeg +from logger import logger + +# A null audio source + +class NullSrc(object): + + def __init__(self): + self.process = ( ffmpeg + .input('anullsrc', format='lavfi') + .output('pipe:', format='mp3') + .run_async(pipe_stdout=True, pipe_stderr=True) + ) + logger.add(self.process.stderr, "nullsrc.log") + + def stop(self): + self.process.stdout.close() + self.process.stderr.close() + # self.process.wait() + + def getStream(self): + return self.process.stdout \ No newline at end of file diff --git a/radio.py b/radio.py index f789645..5dc17d8 100644 --- a/radio.py +++ b/radio.py @@ -1,29 +1,102 @@ import downloader import uploader import transcoder +import nullsrc from time import sleep +from flask import Flask, request +from queue import Queue + +app = Flask(__name__) + +class Radio(object): + def __init__(self): + self.downloader = None + self.transcoder = None + self.uploader = uploader.Uploader() + self.playingUrl = None + self.nullsrc = False + self.queue = Queue() + + # plays the next song in the queue + def play(self): + self.stopPlaying() + self.nullsrc = False + self.playingUrl = self.queue.get() + self.downloader = downloader.Downloader(self.playingUrl, self.downloadFinished) + self.transcoder = transcoder.Transcoder(self.downloader) + self.uploader.setUpstream(self.transcoder) + + def playNullSrc(self): + self.stopPlaying() + self.nullsrc = True + self.playingUrl = None + self.downloader = nullsrc.NullSrc() + self.transcoder = transcoder.Transcoder(self.downloader) + self.uploader.setUpstream(self.transcoder) + + def isPlaying(self): + return not self.playingUrl is None + + # blocks the caller until the uploader and trancoder recieve no more data + # if this is a livestream, the end might never come! + def blockUntilDonePlaying(self): + if not self.nullsrc: + self.transcoder.listener.blockUntilEmpty() + self.uploader.listener.blockUntilEmpty() + + # add to queue or play right now if queue is empty + def addToQueue(self,url): + self.queue.put(url) + + def playIfSongAvailable(self): + if not self.isPlaying(): + if self.queue.empty(): + self.playNullSrc() + else: + self.play() + + # stops playing immediately and cleans up + def stopPlaying(self): + if not self.downloader is None: + self.downloader.stop() + if not self.transcoder is None: + self.transcoder.stop() + self.playingUrl = None + + # downloader callback function, called when the downloader is finished + # but may still have bytes left that need to be read and played + def downloadFinished(self): + self.blockUntilDonePlaying() + self.stopPlaying() + self.playIfSongAvailable() + +r = Radio() + +@app.route('/play', methods=['POST']) +def queueNext(): + next = request.form['url'] + r.addToQueue(next) + r.playIfSongAvailable() + return "Added" + +@app.route('/skip', methods=['POST']) +def skipCurrent(): + r.stopPlaying() + r.playIfSongAvailable() + return "Skipped" + +@app.route('/current', methods=['GET']) +def current(): + return r.playingUrl or 'nothing' + +@app.route('/queue', methods=['GET']) +def queue(): + return str(list(r.queue.queue)) def run(): - def cb(): - global d - global t - global u - print("----------------FINISHED-----------") - t.listener.blockUntilEmpty() - u.listener.blockUntilEmpty() - d.stop() - t.stop() - d = downloader.Downloader('https://www.youtube.com/watch?v=BQQ3qZ9FC70', cb) - t = transcoder.Transcoder(d) - u.setUpstream(t) - - u = uploader.Uploader() - d = downloader.Downloader('https://www.youtube.com/watch?v=kgBcg4uBd9Q', cb) - t = transcoder.Transcoder(d) - u.setUpstream(t) - - while True: - sleep(1) + r.addToQueue('https://www.youtube.com/watch?v=BaW_jenozKc') + r.playIfSongAvailable() + app.run(host="0.0.0.0") if __name__ == "__main__": run() \ No newline at end of file diff --git a/setup.py b/setup.py index 5ceca59..117bd7e 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,21 @@ from setuptools import setup -requires = ["pip","ffmpeg-python"] +requires = ["pip","ffmpeg-python","flask"] setup( - name='radio', - version='0.1', - py_modules=['radio','downloader','uploader','logger','util','stream_listener','transcoder'], - entry_points={ - 'console_scripts': ['radio = radio:run'] - }, + name='radio', + version='0.1', + py_modules=[ + 'radio', + 'downloader', + 'uploader', + 'logger', + 'util', + 'stream_listener', + 'transcoder', + 'nullsrc' + ], + entry_points={ + 'console_scripts': ['radio = radio:run'] + }, ) \ No newline at end of file diff --git a/transcoder.py b/transcoder.py index e82938d..baba0c1 100644 --- a/transcoder.py +++ b/transcoder.py @@ -20,7 +20,7 @@ class Transcoder(object): self.process.stdin.close() self.process.stdout.close() self.process.stderr.close() - self.process.wait() + # self.process.wait() def getStream(self): return self.process.stdout