diff --git a/default.nix b/default.nix index cf81d8b..1ee0035 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 flask requests pkgs.ffmpeg ]; + propagatedBuildInputs = with pkgs.python3Packages; [ pip ffmpeg-python flask requests pkgs.ffmpeg pkgs.btfs ]; } diff --git a/downloader.py b/downloader.py index 18d7bd9..1a84573 100644 --- a/downloader.py +++ b/downloader.py @@ -12,6 +12,8 @@ import os import pip import signal import json +import shutil +import glob from logger import logger from threading import Thread, main_thread from time import sleep @@ -81,6 +83,37 @@ def getVideoInfo(url): except: return None +fuseTorrentLocation = "~/torrent" +btfsDataLocation = "~/btfs-data" + +def mountTorrent(magnet): + umountTorrent() + os.mkdir(fuseTorrentLocation) + o = subprocess.Popen([ + "btfs", + "-o", "auto_unmount", # unmount if process is killed + "--data-directory=" + btfsDataLocation, + magnet, + fuseTorrentLocation + ]) + o.communicate() # wait for it to finish + +def umountTorrent(): + o = subprocess.Popen([ + "fusermount", + "-u", fuseTorrentLocation + ]) + o.communicate() # wait for it to finish + os.rmdir(fuseTorrentLocation) + shutil.rmtree(btfsDataLocation) + +def getTorrentMedia(): + files = glob.glob(fuseTorrentLocation + '/**/*', recursive=True) + files = [f for f in files if f.endswith(".acc") or f.endswith(".avi") or f.endswith(".mid") or f.endswith(".midi") or f.endswith(".mp3") or f.endswith(".mp4") or f.endswith(".mpeg") or f.endswith(".oga") or f.endswith(".ogv") or f.endswith(".opus") or f.endswith(".ts") or f.endswith(".wav") or f.endswith(".weba") or f.endswith(".webm") or f.endswith(".3gp") or f.endswith(".3g2")] + if len(files) == 0: + return None + return files[0] # just the first one I guess... + # Downloads using yt-dlp class YtdlpDownloader(Thread, StreamSource): def __init__(self, url, cb): diff --git a/radio.py b/radio.py index 9b1c023..52d1d6f 100644 --- a/radio.py +++ b/radio.py @@ -26,13 +26,31 @@ class Radio(object): # plays the next song in the queue def play(self): self.playingUrl = self.queue.get() - info = downloader.getVideoInfo(self.playingUrl) - if info is None: - return self.play() - elif ("direct" in info and info["direct"] == True) or ("format_id" in info and info["format_id"] == "rtmp"): # stdout for rtmp in ytdl is broken - self.downloader = downloader.DirectDownloader(self.playingUrl, self.downloadFinished) + + # determine what downloader needs to be used and create the downloader + if self.playingUrl.startswith("magnet:?"): + # it's a torrent + downloader.mountTorrent(self.playingUrl) + self.playingUrl = downloader.getTorrentMedia() + if self.playingUrl is None: + # this torrent is unplayable, skip it + downloader.umountTorrent() + return self.play() + else: + self.downloader = downloader.DirectDownloader(self.playingUrl, self.downloadFinished) else: - self.downloader = downloader.YtdlpDownloader(self.playingUrl, self.downloadFinished) + # assume http/https + info = downloader.getVideoInfo(self.playingUrl) + if info is None: + # this url is unplayable, skip it + return self.play() + elif ("direct" in info and info["direct"] == True) or ("format_id" in info and info["format_id"] == "rtmp"): # stdout for rtmp in ytdl is broken + # direct source + self.downloader = downloader.DirectDownloader(self.playingUrl, self.downloadFinished) + else: + # requires youtube-dl + self.downloader = downloader.YtdlpDownloader(self.playingUrl, self.downloadFinished) + self.transcoder = transcoder.Transcoder(self.downloader) self.buffer = buffer.Buffer(self.transcoder) self.uploader.setUpstream(self.buffer) @@ -60,6 +78,7 @@ class Radio(object): self.transcoder.stop() self.buffer.stop() self.playingUrl = None + downloader.umountTorrent() # make sure torrent is unmounted # downloader callback function, called when the downloader is finished # but may still have bytes left that need to be read and played