Music tunneling (#7)

* New requirement

* SMall fixes for tunnel

* First step towards cogization

* Tmp chng

* Testing of new functions in conjurer_musician.py and radio_conjurer.liq

* Fixes!

* Fix fix

* Mostly working ?

* Requests

* Fixes

* Fix

* Radio requests

* Radio hardkor

* Fix

* Fixes

* Change log to print

* Clearing fetch failed

* Small fix

* sx

* Fix fix fix

* test

* el fixo stupido

* test

* Maybe fix ?

* FIX!

* Fix fix fix

* FIx of volume
This commit is contained in:
2024-04-23 12:59:33 +02:00
committed by GitHub
parent 6fefede271
commit 082b25c89c
8 changed files with 312 additions and 94 deletions
+75 -21
View File
@@ -44,7 +44,7 @@ import yt_dlp
from spotify_dl import spotify from spotify_dl import spotify
from spotify_dl import youtube as youtube_download from spotify_dl import youtube as youtube_download
from communication_subroutine import comm_subroutine, IN_COMM_Q, OUT_COMM_Q from communication_subroutine import comm_subroutine, IN_COMM_Q, OUT_COMM_Q, QueryControl
# *=========================== Classes definition # *=========================== Classes definition
@@ -62,22 +62,6 @@ Music_Config = TypedDict(
) )
class QueryControl:
"""
This class `QueryControl` is used to manage queries with information about the author, UUID,
content, logger, context, and replies.
"""
def __init__(self, query_author, query_uuid, query_content, uplogger, ctx) -> None:
self.author = query_author
self.uuid = query_uuid
self.content = query_content
self.logger = uplogger
self.stop = False
self.ctx = ctx
self.logger.info(
f"Created Query control for {self.author}, {self.uuid}: {self.content}"
)
self.replies = []
class MusicFileList(object): class MusicFileList(object):
""" """
@@ -155,6 +139,7 @@ GET_MP3 = "/mp3"
SEND_MP3 = "/update_mp3" SEND_MP3 = "/update_mp3"
GET_PLAYLIST = "/get_music" GET_PLAYLIST = "/get_music"
ADD_TO_PRIO_PLAYLIST = "/add_to_priority" ADD_TO_PRIO_PLAYLIST = "/add_to_priority"
REQUEST_MUSIC = "/request_radio_file"
CLEAR_PRIO = "/clear_pr_pls" CLEAR_PRIO = "/clear_pr_pls"
LIBRARIAN_SERVICE_ADDRESS = "http://192.168.1.192:5001" LIBRARIAN_SERVICE_ADDRESS = "http://192.168.1.192:5001"
SEND_QUERY = "/query" SEND_QUERY = "/query"
@@ -695,7 +680,6 @@ async def check_data_q():
entries = [] entries = []
if fresh_data.stop: if fresh_data.stop:
searcher = fresh_data.author searcher = fresh_data.author
ctx = fresh_data.ctx
query = fresh_data.content query = fresh_data.content
l_p = 1 l_p = 1
for doi in fresh_data.entries: for doi in fresh_data.entries:
@@ -705,7 +689,14 @@ async def check_data_q():
entries.append(f"{l_p}. {title} pod linkiem https://www.sci-hub.se/{doi} i jest to {desc['type']}\n") entries.append(f"{l_p}. {title} pod linkiem https://www.sci-hub.se/{doi} i jest to {desc['type']}\n")
l_p+=1 l_p+=1
message = '*Z podłogi wysuwa się winda na książki*' message = '*Z podłogi wysuwa się winda na książki*'
message += f' Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}' if fresh_data.ctx is not None:
ctx = fresh_data.ctx
message += f' Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}'
else:
ctx = client.get_channel(1062047367337095268)
message += f' Mam wynik dla zapytania {fresh_data.uuid} ale że Hammer coś grzebał nie pamiętam kto to chciał i co chciał'
message += 'nasza biblioteka służy Ci odpowiedzią. Przy dźwięku fanfar winda się otwiera a w środku ' message += 'nasza biblioteka służy Ci odpowiedzią. Przy dźwięku fanfar winda się otwiera a w środku '
if len(entries)<1: if len(entries)<1:
message += "niestety nie ma nic" message += "niestety nie ma nic"
@@ -1582,6 +1573,40 @@ async def graj_muzyko(ctx):
logger.info("Press play on tape completed") logger.info("Press play on tape completed")
check_music.start() check_music.start()
@client.hybrid_command(
name="radio_hardkor",
description="Włącza radio Conjurer na kanale #nocna-zmiana.",
guild=discord.Object(id=664789470779932693),
)
async def radio_hardkor(ctx):
"""
Plays the radio hardkor stream in the voice channel.
Args:
ctx: The context object representing the invocation context.
Returns:
None
"""
logger.info("Press play on radio hardkor")
async with ctx.typing():
global MUZYKA # pylint: disable=global-variable-not-assigned
MUZYKA["ctx"] = ctx
if not client.voice_clients:
await connect(ctx=ctx)
voice_client = client.voice_clients[0]
voice_client.play(
discord.PCMVolumeTransformer(
discord.FFmpegPCMAudio("http://95.175.16.246:666/mp3-stream"), volume=0.3
)
)
logger.info("Press play on radio completed")
else:
logger.error("Already playing")
await asyncio.sleep(12)
check_music.start()
@client.hybrid_command( @client.hybrid_command(
name="cisza", name="cisza",
@@ -1757,7 +1782,36 @@ async def dodaj_do_ulubionych(ctx):
timeout=360, timeout=360,
) )
_ = await coroutine _ = await coroutine
@client.hybrid_command(
name="ja_chciol",
description="Dodaje do listy ulubionych w radiu",
guild=discord.Object(id=664789470779932693),
)
async def request_radio(ctx):
"""
Generate a playlist in queue. First word in this command shall be int defining length of the playlist.
Rest of the line are search terms.
:param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It
represents the context in which the command was invoked, including information such as the message,
the channel, the server, and the user who invoked the command. This parameter is required in all
Discord.py commands
"""
async with ctx.typing():
logger.info("Zaczynam szukać timestamp %s", datetime.now())
word_list = ctx.message.content.split()
jrequest = {
"lista_slow": word_list,
"UUID": str(uuid.uuid4()),
}
coroutine = asyncio.to_thread(
requests.post,
f"{FILE_SERVICE_ADDRESS}{REQUEST_MUSIC}",
json=jrequest,
timeout=360,
)
_ = await coroutine
@client.hybrid_command( @client.hybrid_command(
name="wyczysc_ulubione", name="wyczysc_ulubione",
description="Czysci liste ulubionych w radiu", description="Czysci liste ulubionych w radiu",
@@ -2324,13 +2378,13 @@ if __name__ == "__main__":
WRK_CNT = 0 WRK_CNT = 0
for worker in threads: for worker in threads:
WRK_CNT+=1 WRK_CNT+=1
logger.info("Starting discord bot: Starting thread {WRK_CNT}") logger.info("Starting discord bot: Starting thread %s", WRK_CNT)
worker.start() worker.start()
logger.info("Starting discord bot: Joining threads") logger.info("Starting discord bot: Joining threads")
WRK_CNT=0 WRK_CNT=0
for worker in threads: for worker in threads:
WRK_CNT+=1 WRK_CNT+=1
logger.info("Starting discord bot: Joining thread {WRK_CNT}") logger.info("Starting discord bot: Joining thread %s", WRK_CNT)
worker.join() worker.join()
# widok - generuje się 5 stron po 4 linki # widok - generuje się 5 stron po 4 linki
+46
View File
@@ -0,0 +1,46 @@
import discord
from discord.ext import commands
from typing import Optional
class Music(commands.Cog):
def __init__(self, bot):
self.bot = bot # This is so you can access Bot instance in your cog
# You must have this function for `bot.load_extension` to call
def setup(bot):
bot.add_cog(Music(bot))
@commands.hybrid_command(
name="przytul", description="Przytul kogoś - daj mention po komendzie :)"
)
async def przytul(ctx, arg: Optional[discord.Member] = None):
"""
Generate a text about hugging mentioned user.
:param ctx: ctx stands for "context" and is a required parameter in Discord.py commands. It
represents the context in which the command was invoked, including information such as the message,
the channel, the server, and the user who invoked the command
:param arg: arg is a parameter of the function "przytul" that expects a Discord member object. The
parameter is optional, meaning that if no member object is provided, it will default to None
:type arg: Optional[discord.Member]
"""
async with ctx.typing():
nieprzytulac = False
for mention in ctx.message.mentions:
for role in mention.roles:
if role.name == "NIEPRZYTULAĆ!":
nieprzytulac = True
if arg and nieprzytulac:
await ctx.send(
f"Żebym ja Ciebie nie przytulił {ctx.message.author.mention}"
)
elif arg:
await ctx.send(
# trunk-ignore(codespell/misspelled)
f"Już dobrze.... Już dobrze... Ojej.. Biedactwo... :( *W ułamku sekundy {arg.mention} znajduje sie w duszącym uścisku. Żebra trzeszczą - kilka pęka. Pacnięcia po plecach grożą odbiciem nerek, a głaskanie po głowie powoduje wstrząs mózgu*"
)
else:
await ctx.send(
"Kogo mam przytulić? *Wyłamuje kostki i przeciąga się - jego 200 kilowa sylwetka złożona z samych mięśni świadczy o tym że jest gotowy*"
)
+23
View File
@@ -13,6 +13,22 @@ IN_COMM_Q = Queue()
awaiting_q = [] awaiting_q = []
incoming_q = Queue() incoming_q = Queue()
app = Flask(__name__) app = Flask(__name__)
class QueryControl:
"""
This class `QueryControl` is used to manage queries with information about the author, UUID,
content, logger, context, and replies.
"""
def __init__(self, query_author, query_uuid, query_content, uplogger, ctx) -> None:
self.author = query_author
self.uuid = query_uuid
self.content = query_content
self.logger = uplogger
self.stop = False
self.ctx = ctx
self.logger.info(
f"Created Query control for {self.author}, {self.uuid}: {self.content}"
)
self.replies = []
@app.route("/conjurer", methods=["POST"]) @app.route("/conjurer", methods=["POST"])
def answer_external_command(): def answer_external_command():
@@ -91,6 +107,13 @@ def scan_incoming(_logger):
record.stop = True record.stop = True
record.entries = answer[record.uuid] record.entries = answer[record.uuid]
IN_COMM_Q.put(record) IN_COMM_Q.put(record)
else:
for key in answer.keys():
record = QueryControl("Orphaned", key, "Orphan", _logger, None)
record.stop = True
record.entries = answer[record.uuid]
record.uuid = "000000"
IN_COMM_Q.put(record)
except Empty: except Empty:
time.sleep(1) time.sleep(1)
+10 -9
View File
@@ -68,7 +68,7 @@ def producer(out_q, control_q, filename, _logger):
# IMPORTANT!!! ONLY ONE CONSUMER THREAD AS WE ARE NOT PUTTING SENTINELS BACK # IMPORTANT!!! ONLY ONE CONSUMER THREAD AS WE ARE NOT PUTTING SENTINELS BACK
def consumer(in_q, control_q, doi, live_results, result_list, _logger, ): def consumer(in_q, control_q, doi, live_results, result_list, control_dict, _logger):
""" """
Consumes items from an input queue and checks if DOI exists in the live results list. Consumes items from an input queue and checks if DOI exists in the live results list.
@@ -79,7 +79,6 @@ def consumer(in_q, control_q, doi, live_results, result_list, _logger, ):
live_results (list): List to store the search results. live_results (list): List to store the search results.
_logger: Logger object for logging. _logger: Logger object for logging.
""" """
sentinels = 0
for item in doi: for item in doi:
result_list.append({"DOI": item[0], "exists": False, "data": item[1]}) result_list.append({"DOI": item[0], "exists": False, "data": item[1]})
while True: while True:
@@ -87,7 +86,7 @@ def consumer(in_q, control_q, doi, live_results, result_list, _logger, ):
data = in_q.get() data = in_q.get()
if data is _sentinel: if data is _sentinel:
_logger.info("Worker finished") _logger.info("Worker finished")
sentinels += 1 control_dict["sentinels"] += 1
else: else:
for item in result_list: for item in result_list:
if item["DOI"] in data and not item["exists"]: if item["DOI"] in data and not item["exists"]:
@@ -98,7 +97,7 @@ def consumer(in_q, control_q, doi, live_results, result_list, _logger, ):
done_check = done_check and item["exists"] done_check = done_check and item["exists"]
if done_check: if done_check:
control_q.put(_sentinel) control_q.put(_sentinel)
if sentinels >= MAXTHREADS: if control_dict["sentinels"] >= MAXTHREADS:
_logger.info("All workers finished") _logger.info("All workers finished")
break break
@@ -112,15 +111,17 @@ def search_for_doi(doi, live_results, _logger):
live_results (list): List to store the search results. live_results (list): List to store the search results.
_logger: Logger object for logging. _logger: Logger object for logging.
""" """
control_dict = {"sentinels":0}
result_list = [] result_list = []
threads = [] threads = []
work_q = Queue(maxsize=WORK_Q_SIZE) work_q = Queue(maxsize=WORK_Q_SIZE)
control_q = Queue() control_q = Queue()
t_cons = Thread( for _ in range (1, len(doi)//1000+1):
target=consumer, args=(work_q, control_q, doi, live_results, result_list, _logger) t_cons = Thread(
) target=consumer, args=(work_q, control_q, doi, live_results, result_list, control_dict, _logger)
_logger.info("Consumer thread created") )
threads.append(t_cons) _logger.info("Consumer thread created")
threads.append(t_cons)
for i in range(0, MAXTHREADS): for i in range(0, MAXTHREADS):
# TEST DATA # TEST DATA
# filename = "test" + str(i) + CHUNk # filename = "test" + str(i) + CHUNk
+35 -13
View File
@@ -238,13 +238,6 @@ def wyszukaj(word_list, how_many, _logger=None, return_to_bot=True):
fun_logger.info("Wiele plików do zagrania") fun_logger.info("Wiele plików do zagrania")
search_weight.sort(key=lambda x: x[0], reverse=True) search_weight.sort(key=lambda x: x[0], reverse=True)
return_list.extend(search_weight[: int(how_many)]) return_list.extend(search_weight[: int(how_many)])
if not return_to_bot:
with open(
"/home/pi/Conjurer/priority_queue.playlist", "a", encoding="utf-8"
) as s_file:
for item in return_list:
s_file.write(item[1] + "\n")
return return_list return return_list
@@ -386,16 +379,41 @@ def look_for_playlist():
return return_data return return_data
@app.route("/request_radio_file", methods=["POST"])
def add_request():
record = json.loads(request.data)
app.logger.info(record)
app.logger.info(record["lista_slow"])
app.logger.info(record["UUID"])
return_data = wyszukaj(
record["lista_slow"], 0, app.logger, False
)
with open(
"/home/pi/Conjurer/request.playlist", "a", encoding="utf-8"
) as s_file:
for item in return_data:
s_file.write(item[1] + "\n")
return_data = (
jsonify(isError=False, message="Success", statusCode=200, data={"status": "OK"}),
200,
)
return return_data
@app.route("/add_to_priority", methods=["POST"]) @app.route("/add_to_priority", methods=["POST"])
def add_to_priority(): def add_to_priority():
""" """
The function `add_to_priority` receives a POST request with a JSON payload, logs the received The function `add_to_priority` receives a POST request with a JSON payload, logs the received
item, adds it to a music file list, and returns a success message along with the updated record. item, adds it to a music file list, and returns a success message along with the updated record.
:return: A tuple containing a JSON response and a status code. The JSON response includes keys `isError`, :return: A tuple containing a JSON response and a status code.
`message`, `statusCode`, and `data`, with values indicating the success of the operation and the The JSON response includes keys `isError`,
data that was received and added to the `music_file_list`. The status code returned is 200, `message`, `statusCode`, and `data`, with values
indicating a successful response. indicating the success of the operation and the
data that was received and added to the `music_file_list`.
The status code returned is 200,indicating a successful response.
""" """
record = json.loads(request.data) record = json.loads(request.data)
app.logger.info(record) app.logger.info(record)
@@ -405,9 +423,13 @@ def add_to_priority():
return_data = wyszukaj( return_data = wyszukaj(
record["lista_slow"], record["dlugosc_plejlisty"], app.logger, False record["lista_slow"], record["dlugosc_plejlisty"], app.logger, False
) )
with open(
"/home/pi/Conjurer/priority_queue.playlist", "a", encoding="utf-8"
) as s_file:
for item in return_data:
s_file.write(item[1] + "\n")
return_data = ( return_data = (
jsonify(isError=False, message="Success", statusCode=200, data=return_data), jsonify(isError=False, message="Success", statusCode=200, data={"status": "OK"}),
200, 200,
) )
return return_data return return_data
+118 -50
View File
@@ -5,67 +5,135 @@
# Load icecast credentials from a JSON file # Load icecast credentials from a JSON file
let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentials.json") let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentials.json")
# Enable replaygain metadata processing # Enable replaygain metadata processing
enable_replaygain_metadata() enable_replaygain_metadata()
l = playlog(duration = 14400.0, persistency="/home/pi/Conjurer/persistence.log")
def check(r)
m = request.metadata(r)
if l.last(m) < 3600. then
log.info("Rejecting #{m['filename']} (played #{l.last(m)}s ago).")
false
else
l.add(m)
true
end
end
# Define playlists to be used in the stream
s1 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/all_playlist.playlist"))
s2 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/priority_queue.playlist"))
s3 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/hit.playlist"))
# Randomly select a playlist with weights
s = random(id="randomizer", weights=[1, 3, 2], [s1, s2, s3])
# Set up an interactive harbor for controlling the stream # Define playlists to be used in the stream
interactive.harbor(port = 9999) s1 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/all_playlist.playlist"))
s2 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/priority_queue.playlist"))
s3 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/hit.playlist"))
requests_queue = request.queue()
def queue_processing()
f1=file.read("/home/pi/Conjurer/request.playlist")
text = f1()
if text != "" then
requests_queue.push.uri(text)
file.remove("/home/pi/Conjurer/request.playlist")
f = file.open("/home/pi/Conjurer/request.playlist", create=true)
f.close()
log.info("done")
else
log.info("no requests")
end
end
thread.run(every=60., queue_processing)
# Set up interactive controls for bass boost # Randomly select a playlist with weights
f = interactive.float("f", description="Frequency", min=0., max=1000., unit="Hz", 200.) s = random(id="randomizer", weights=[1, 3, 2], [s1, s2, s3])
g = interactive.float("g", description="Gain", min=0., max=20., unit="dB", 8.) #s = delay(duration=5., s)
b = bass_boost(frequency=f, gain=g, s) #radio = add([delay(1800., jingles), music])
s = add([s, b]) #radio = fallback([delay(1800., jingles), music])
#p = predicate.signal()
#radio = switch([(p, jingles), ({true}, music)])
#thread.run(every=1200., {p.signal()})
# def cmd(_)
# p.signal()
# "Jingle inserted!"
# end
# server.register("insert_jingle", cmd)
# Set up interactive control for main volume # Set up an interactive harbor for controlling the stream
a = interactive.float("main_volume", min=0., max=20., 1.) interactive.harbor(port = 9999)
s = compress.multiband.interactive(bands=3, s) # Set up interactive controls for bass boost
f = interactive.float("f", description="Frequency", min=0., max=1000., unit="Hz", 200.)
g = interactive.float("g", description="Gain", min=0., max=20., unit="dB", 8.)
b = bass_boost(frequency=f, gain=g, s)
s = add([s, b])
# Set up interactive control for main volume
a = interactive.float("main_volume", min=0., max=20., 1.)
s = compress.multiband.interactive(bands=3, s)
# Apply audio processing effects # Apply audio processing effects
s = nrj(normalize(s)) s = nrj(normalize(s))
s = crossfade(fade_out=3., fade_in=3., duration=5., s) s = crossfade(fade_out=3., fade_in=3., duration=5., s)
s = amplify(a, s) s = amplify(a, s)
# Skip blank sections in the stream # Skip blank sections in the stream
s = blank.skip(max_blank=2., s) s = blank.skip(max_blank=2., s)
# Configure logging settings # Configure logging settings
log_to_stdout = true log_to_stdout = true
log_to_file = true log_to_file = true
logpath = "/home/pi/Conjurer/radio_log.log" logpath = "/home/pi/Conjurer/radio_log.log"
loglevel = 4 loglevel = 4
set("log.stdout", log_to_stdout) set("log.stdout", log_to_stdout)
set("log.level", loglevel) set("log.level", loglevel)
# Enable logging to file # Enable logging to file
set("log.file", log_to_file) set("log.file", log_to_file)
set("log.file.path", logpath) set("log.file.path", logpath)
# Set up emergency fallback track # Set up emergency fallback track
emergency = single("/home/pi/RetroPie/mp3/Youtube/Dr. Peacock - Trip to Ireland [GvrvQTUbUcA].mp3") emergency = single("/home/pi/RetroPie/mp3/Youtube/Dr. Peacock - Trip to Ireland [GvrvQTUbUcA].mp3")
# Create a fallback stream with the main stream and emergency track # Create a fallback stream with the main stream and emergency track
radio = fallback(id="switcher", track_sensitive=false, [s, emergency]) radio = fallback(id="switcher", track_sensitive=true, [requests_queue, s, emergency])
# Handle metadata events # Handle metadata events
handle_metadata = fun (m) -> begin handle_metadata = fun (m) -> begin
log.info(m["title"]) log.info(m["title"])
log.info(m["artist"]) log.info(m["artist"])
log.info(m["filename"]) log.info(m["filename"])
end end
radio.on_metadata(handle_metadata) radio.on_metadata(handle_metadata)
# Enable persistent script parameters # Enable persistent script parameters
interactive.persistent("script.params") interactive.persistent("script.params")
# Configure output formats and destinations # Configure output formats and destinations
output.file.hls("/tmp/hls", [("mp3-low", %mp3(bitrate=96)), ("mp3-hi", %mp3(bitrate=160))], radio) output.file.hls("/tmp/hls", [("mp3-low", %mp3(bitrate=96)), ("mp3-hi", %mp3(bitrate=160))], radio)
output.icecast(%mp3, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="mp3-stream", radio) output.icecast(%mp3, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="mp3-stream", radio)
# output.icecast(%opus, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="opus-stream", radio)
# output.icecast(%ogg, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="ogg-stream", radio)
# output.icecast(%aac, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="aac-stream", radio)
# output.icecast(%flac, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="flac-stream", radio)
# output.icecast(%vorbis, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="vorbis-stream", radio)
# output.icecast(%speex, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="speex-stream", radio)
# output.icecast(%wav, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="wav-stream", radio)
# output.icecast(%pcm, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="pcm-stream", radio)
# output.icecast(%raw, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="raw-stream", radio)
# output.icecast(%s16l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s16l-stream", radio)
# output.icecast(%s16b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s16b-stream", radio)
# output.icecast(%s24l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s24l-stream", radio)
# output.icecast(%s24b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s24b-stream", radio)
# output.icecast(%s32l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s32l-stream", radio)
# output.icecast(%s32b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s32b-stream", radio)
# output.icecast(%s64l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s64l-stream", radio)
# output.icecast(%s64b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s64b-stream", radio)
# output.icecast(%s128l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s128l-stream", radio)
# output.icecast(%s128b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s128b-stream", radio)
# output.icecast(%s256l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s256l-stream", radio)
# output.icecast(%s256b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s256b-stream", radio)
# output.icecast(%s512l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s512l-stream", radio)
# output.icecast(%s512b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s512b-stream", radio)
# output.icecast(%s1024l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s1024l-stream", radio)
# output.icecast(%s1024b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s1024b-stream", radio)
# output.icecast(%s2048l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s2048l-stream", radio)
# output.icecast(%s2048b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s2048b-stream", radio)
# output.icecast(%s4096l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s4096l-stream", radio)
# output.icecast(%s4096b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s4096b-stream", radio)
# output.icecast(%s8192l, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s8192l-stream", radio)
# output.icecast(%s8192b, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="s8192b-stream", radio)
+2 -1
View File
@@ -1,2 +1,3 @@
flask flask
waitress waitress
sshtunnel
+3
View File
@@ -15,6 +15,9 @@ touch /home/pi/Conjurer/prio_playlist.json
touch /home/pi/Conjurer/all_playlist.playlist touch /home/pi/Conjurer/all_playlist.playlist
touch /home/pi/Conjurer/priority_queue.playlist touch /home/pi/Conjurer/priority_queue.playlist
touch /home/pi/Conjurer/hit.playlist touch /home/pi/Conjurer/hit.playlist
touch /home/pi/Conjurer/request.playlist
touch /home/pi/Conjurer/persistence.log
echo "[]" > /home/pi/Conjurer/persistence.log
touch /home/pi/Conjurer/radio_log.log touch /home/pi/Conjurer/radio_log.log
./env/bin/python3 -m pip install --upgrade pip ./env/bin/python3 -m pip install --upgrade pip