mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-15 14:22:13 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 439c5c753c | |||
| cc11d3f75e | |||
| a35d7da6e3 | |||
| 2c50318f1c | |||
| addc534c36 | |||
| 430b4de995 | |||
| 9d00e40b41 | |||
| 7d29c1d397 | |||
| ce3ad9f8f0 | |||
| abef18f4ad | |||
| 5beb1c6fdd | |||
| ff69091495 | |||
| 69d3c8849c | |||
| b2aab28460 | |||
| 082b25c89c | |||
| 6fefede271 |
@@ -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,14 +689,23 @@ 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(1062047571557744721)
|
||||||
|
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"
|
||||||
|
await ctx.send(message)
|
||||||
elif len(entries)>=1 and len(entries)<5:
|
elif len(entries)>=1 and len(entries)<5:
|
||||||
message += " znajduje się coś:\n"
|
message += " znajduje się coś:\n"
|
||||||
for item in entries:
|
for item in entries:
|
||||||
message+=item
|
message+=item
|
||||||
|
await ctx.send(message)
|
||||||
else:
|
else:
|
||||||
message+= " znajduje się cholernie dużo:\n"
|
message+= " znajduje się cholernie dużo:\n"
|
||||||
for item in entries:
|
for item in entries:
|
||||||
@@ -721,7 +714,6 @@ async def check_data_q():
|
|||||||
await ctx.send(message)
|
await ctx.send(message)
|
||||||
message = ""
|
message = ""
|
||||||
|
|
||||||
await ctx.send(message)
|
|
||||||
#Kept for sentimental reasons
|
#Kept for sentimental reasons
|
||||||
#await ctx.send(f"O. A tak będzie wyglądało coś ciekawego w przyszłości: {data}")
|
#await ctx.send(f"O. A tak będzie wyglądało coś ciekawego w przyszłości: {data}")
|
||||||
except Empty:
|
except Empty:
|
||||||
@@ -1073,11 +1065,15 @@ async def get_file(ctx, source, link):
|
|||||||
if source == "Spotify":
|
if source == "Spotify":
|
||||||
dir_path = None
|
dir_path = None
|
||||||
item_type, item_id = spotify.parse_spotify_url(link)
|
item_type, item_id = spotify.parse_spotify_url(link)
|
||||||
if item_id:
|
if item_type in ["album", "track", "playlist"] and item_id:
|
||||||
coro = asyncio.to_thread(spotify.fetch_tracks, args=(spotify_ctrl, item_type, link))
|
logger.info(item_type)
|
||||||
file_list = await coro
|
logger.info(item_id)
|
||||||
coro = asyncio.to_thread(spotify.get_item_name, args=(spotify_ctrl, item_type, item_id))
|
logger.info("Spotify link provided")
|
||||||
directory_name = await coro
|
file_list = await asyncio.to_thread(spotify.fetch_tracks, spotify_ctrl, item_type, link)
|
||||||
|
logger.info(file_list)
|
||||||
|
directory_name = await asyncio.to_thread(spotify.get_item_name, spotify_ctrl, item_type, item_id)
|
||||||
|
logger.info(directory_name)
|
||||||
|
logger.info("Spotify scrape done")
|
||||||
url_data = {"urls": []}
|
url_data = {"urls": []}
|
||||||
url_dict = {}
|
url_dict = {}
|
||||||
url_dict["save_path"] = Path(
|
url_dict["save_path"] = Path(
|
||||||
@@ -1087,6 +1083,7 @@ async def get_file(ctx, source, link):
|
|||||||
url_dict["songs"] = file_list
|
url_dict["songs"] = file_list
|
||||||
url_data["urls"].append(url_dict.copy())
|
url_data["urls"].append(url_dict.copy())
|
||||||
file_name_f = youtube_download.default_filename
|
file_name_f = youtube_download.default_filename
|
||||||
|
logger.info("YT-DL")
|
||||||
coro = asyncio.to_thread(
|
coro = asyncio.to_thread(
|
||||||
youtube_download.download_songs,
|
youtube_download.download_songs,
|
||||||
songs=url_data,
|
songs=url_data,
|
||||||
@@ -1102,6 +1099,7 @@ async def get_file(ctx, source, link):
|
|||||||
proxy="",
|
proxy="",
|
||||||
)
|
)
|
||||||
_ = await coro
|
_ = await coro
|
||||||
|
logger.info("YT-DL done")
|
||||||
dir_path = (url_dict["save_path"].resolve()).as_posix()
|
dir_path = (url_dict["save_path"].resolve()).as_posix()
|
||||||
if platform == "win32":
|
if platform == "win32":
|
||||||
dir_path = dir_path.replace("/", "\\")
|
dir_path = dir_path.replace("/", "\\")
|
||||||
@@ -1582,6 +1580,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 +1789,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",
|
||||||
@@ -1775,9 +1836,8 @@ async def wyczysc_ulubione(ctx):
|
|||||||
"""
|
"""
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
coroutine = asyncio.to_thread(
|
coroutine = asyncio.to_thread(
|
||||||
requests.post,
|
requests.get,
|
||||||
f"{FILE_SERVICE_ADDRESS}{CLEAR_PRIO}",
|
f"{FILE_SERVICE_ADDRESS}{CLEAR_PRIO}",
|
||||||
json={},
|
|
||||||
timeout=360,
|
timeout=360,
|
||||||
)
|
)
|
||||||
_ = await coroutine
|
_ = await coroutine
|
||||||
@@ -2324,13 +2384,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
|
||||||
|
|||||||
@@ -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*"
|
||||||
|
)
|
||||||
@@ -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():
|
||||||
@@ -86,8 +102,16 @@ def scan_incoming(_logger):
|
|||||||
try:
|
try:
|
||||||
answer = incoming_q.get(block=False)
|
answer = incoming_q.get(block=False)
|
||||||
_logger.info("DATA FOUND")
|
_logger.info("DATA FOUND")
|
||||||
|
record_stored = False
|
||||||
for record in awaiting_q:
|
for record in awaiting_q:
|
||||||
if record.uuid in answer.keys():
|
if record.uuid in answer.keys():
|
||||||
|
record_stored = True
|
||||||
|
record.stop = True
|
||||||
|
record.entries = answer[record.uuid]
|
||||||
|
IN_COMM_Q.put(record)
|
||||||
|
if not record_stored:
|
||||||
|
for key in answer.keys():
|
||||||
|
record = QueryControl("Orphaned", key, "Orphan", _logger, None)
|
||||||
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)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
habanero
|
habanero
|
||||||
waitress
|
waitress
|
||||||
flask
|
flask[async]
|
||||||
@@ -35,7 +35,7 @@ DATABASE_PATH = r"C:\\Database\\chunks\\"
|
|||||||
ENCODING = "utf-8"
|
ENCODING = "utf-8"
|
||||||
CHUNK = "_chunk.txt"
|
CHUNK = "_chunk.txt"
|
||||||
_sentinel = object()
|
_sentinel = object()
|
||||||
WORK_Q_SIZE = 15500000
|
WORK_Q_SIZE = 35500000
|
||||||
|
|
||||||
|
|
||||||
def producer(out_q, control_q, filename, _logger):
|
def producer(out_q, control_q, filename, _logger):
|
||||||
@@ -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, no, _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,30 +79,34 @@ 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
|
_logger.info("Consumer thread started:%s", no)
|
||||||
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:
|
||||||
done_check = True
|
done_check = True
|
||||||
data = in_q.get()
|
try:
|
||||||
if data is _sentinel:
|
data = in_q.get(block=False)
|
||||||
_logger.info("Worker finished")
|
if data is _sentinel:
|
||||||
sentinels += 1
|
_logger.info("Worker finished")
|
||||||
else:
|
control_dict["sentinels"] += 1
|
||||||
for item in result_list:
|
else:
|
||||||
if item["DOI"] in data and not item["exists"]:
|
for item in result_list:
|
||||||
_logger.info(data)
|
if item["DOI"] in data and not item["exists"]:
|
||||||
_logger.info("HIT")
|
_logger.info(data)
|
||||||
item["exists"] = True
|
_logger.info("HIT")
|
||||||
live_results.append(item)
|
item["exists"] = True
|
||||||
done_check = done_check and item["exists"]
|
live_results.append(item)
|
||||||
if done_check:
|
done_check = done_check and item["exists"]
|
||||||
control_q.put(_sentinel)
|
if done_check:
|
||||||
if sentinels >= MAXTHREADS:
|
control_q.put(_sentinel)
|
||||||
|
except Empty:
|
||||||
|
pass
|
||||||
|
if control_dict["sentinels"] >= MAXTHREADS:
|
||||||
_logger.info("All workers finished")
|
_logger.info("All workers finished")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def search_for_doi(doi, live_results, _logger):
|
def search_for_doi(doi, live_results, _logger):
|
||||||
"""
|
"""
|
||||||
Search for DOI in live_results using _logger for logging.
|
Search for DOI in live_results using _logger for logging.
|
||||||
@@ -112,15 +116,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 i in range (0, (len(doi)//1000)+2):
|
||||||
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, i, _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
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from json.decoder import JSONDecodeError
|
||||||
|
from logging import handlers
|
||||||
|
import requests
|
||||||
|
|
||||||
|
MAIN_BOT_ADDRESS = "http://192.168.1.191:5000"
|
||||||
|
SEND_RESULTS = "/conjurer"
|
||||||
|
|
||||||
|
async def send_results():
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
h1 = handlers.RotatingFileHandler(
|
||||||
|
filename="D:\\logs\\librarian.log",
|
||||||
|
encoding="utf-8",
|
||||||
|
mode="a",
|
||||||
|
maxBytes=6 * 1024 * 1024,
|
||||||
|
backupCount=6,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.addHandler(h1)
|
||||||
|
|
||||||
|
with open("s_results.json", "r+", encoding="utf-8") as s_file:
|
||||||
|
try:
|
||||||
|
database = json.load(s_file)
|
||||||
|
except JSONDecodeError:
|
||||||
|
pass
|
||||||
|
result = database["4bacbca0-ce3b-45c6-90a1-05c70b46b740"]
|
||||||
|
result = {"4bacbca0-ce3b-45c6-90a1-05c70b46b740":result}
|
||||||
|
|
||||||
|
coroutine = asyncio.to_thread(
|
||||||
|
requests.post,
|
||||||
|
f"{MAIN_BOT_ADDRESS}{SEND_RESULTS}",
|
||||||
|
json=result,
|
||||||
|
timeout=360,
|
||||||
|
)
|
||||||
|
logger.info("SENT")
|
||||||
|
result = await coroutine
|
||||||
|
logger.info(result.status_code)
|
||||||
|
logger.info("SEND CONFIRMED")
|
||||||
|
async def main():
|
||||||
|
await send_results()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -41,41 +41,6 @@ if platform in ("linux", "linux2"):
|
|||||||
music_file_list = []
|
music_file_list = []
|
||||||
priority_list = []
|
priority_list = []
|
||||||
|
|
||||||
|
|
||||||
def create_playlist():
|
|
||||||
"""
|
|
||||||
Reads a JSON file containing a playlist and writes the playlist items to a text file.
|
|
||||||
|
|
||||||
The JSON file path is '/home/pi/Conjurer/playlist.json',
|
|
||||||
and the text file path is '/home/pi/Conjurer/all_playlist.playlist'.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
JSONDecodeError: If the JSON file cannot be decoded.
|
|
||||||
|
|
||||||
"""
|
|
||||||
with open("/home/pi/Conjurer/playlist.json", "r+", encoding="utf-8") as r_file:
|
|
||||||
with open(
|
|
||||||
"/home/pi/Conjurer/all_playlist.playlist", "w", encoding="utf-8"
|
|
||||||
) as w_file:
|
|
||||||
try:
|
|
||||||
playlist = json.load(r_file)
|
|
||||||
for item in playlist:
|
|
||||||
w_file.write(item)
|
|
||||||
w_file.write("\n")
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
with open("/home/pi/Conjurer/prio_playlist.json", "r+", encoding="utf-8") as r_file:
|
|
||||||
with open("/home/pi/Conjurer/hit.playlist", "w", encoding="utf-8") as w_file:
|
|
||||||
try:
|
|
||||||
playlist = json.load(r_file)
|
|
||||||
for item in playlist:
|
|
||||||
w_file.write(item)
|
|
||||||
w_file.write("\n")
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def rescan():
|
def rescan():
|
||||||
"""
|
"""
|
||||||
The `rescan` function logs a message, scans for mp3 files in a specified folder,
|
The `rescan` function logs a message, scans for mp3 files in a specified folder,
|
||||||
@@ -95,18 +60,22 @@ def rescan():
|
|||||||
temp_music_file = temp_music_file.replace("/", "\\")
|
temp_music_file = temp_music_file.replace("/", "\\")
|
||||||
priority_list.append(temp_music_file)
|
priority_list.append(temp_music_file)
|
||||||
|
|
||||||
with open("/home/pi/Conjurer/playlist.json", "r+", encoding="utf-8") as s_file:
|
with open(
|
||||||
database = music_file_list
|
"/home/pi/Conjurer/all_playlist.playlist", "w", encoding="utf-8"
|
||||||
# self.app.logger.info("DUMPING DATA")
|
) as w_file:
|
||||||
s_file.truncate(0)
|
try:
|
||||||
s_file.seek(0)
|
for item in music_file_list:
|
||||||
json.dump(database, s_file)
|
w_file.write(item)
|
||||||
with open("/home/pi/Conjurer/prio_playlist.json", "r+", encoding="utf-8") as s_file:
|
w_file.write("\n")
|
||||||
# self.app.logger.info("DUMPING DATA")
|
except json.JSONDecodeError:
|
||||||
s_file.truncate(0)
|
pass
|
||||||
s_file.seek(0)
|
with open("/home/pi/Conjurer/hit.playlist", "w", encoding="utf-8") as w_file:
|
||||||
json.dump(priority_list, s_file)
|
try:
|
||||||
create_playlist()
|
for item in priority_list:
|
||||||
|
w_file.write(item)
|
||||||
|
w_file.write("\n")
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def thread_rescan():
|
def thread_rescan():
|
||||||
@@ -238,13 +207,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
|
||||||
|
|
||||||
|
|
||||||
@@ -320,10 +282,10 @@ def clear_pr_pls():
|
|||||||
|
|
||||||
:return: A JSON response indicating the success of the operation.
|
:return: A JSON response indicating the success of the operation.
|
||||||
"""
|
"""
|
||||||
with open(
|
app.logger.info("CLEARING PLAYLIST")
|
||||||
"/home/pi/Conjurer/priority_queue.playlist", "w", encoding="utf-8"
|
with open('/home/pi/Conjurer/priority_queue.playlist', 'w', encoding='utf-8') as cleared_pl:
|
||||||
) as s_file:
|
cleared_pl.write('')
|
||||||
s_file.truncate(0)
|
|
||||||
return_data = jsonify(isError=False, message="Success", statusCode=200, data=[])
|
return_data = jsonify(isError=False, message="Success", statusCode=200, data=[])
|
||||||
return return_data, 200
|
return return_data, 200
|
||||||
|
|
||||||
@@ -386,16 +348,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 +392,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
|
||||||
|
|||||||
@@ -8,13 +8,52 @@ let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentia
|
|||||||
# Enable replaygain metadata processing
|
# Enable replaygain metadata processing
|
||||||
enable_replaygain_metadata()
|
enable_replaygain_metadata()
|
||||||
|
|
||||||
|
# Set up a playlog for tracking played tracks
|
||||||
|
l = playlog(duration = 14400.0, persistency="/home/pi/Conjurer/persistence.log")
|
||||||
|
|
||||||
|
# Function to check if a track can be played based on its metadata
|
||||||
|
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
|
# Define playlists to be used in the stream
|
||||||
s1 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/all_playlist.playlist"))
|
s1 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/all_playlist.playlist"))
|
||||||
s2 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/priority_queue.playlist"))
|
s2 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/priority_queue.playlist"))
|
||||||
s3 = replaygain(playlist(reload_mode="watch", "/home/pi/Conjurer/hit.playlist"))
|
s3 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/hit.playlist"))
|
||||||
|
|
||||||
|
# Create a request queue for user-generated requests
|
||||||
|
requests_queue = request.queue()
|
||||||
|
|
||||||
|
# Function to process the request queue and add new requests
|
||||||
|
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
|
||||||
|
|
||||||
# Randomly select a playlist with weights
|
# Randomly select a playlist with weights
|
||||||
s = random(id="randomizer", weights=[1, 3, 2], [s1, s2, s3])
|
s4 = random(id="randomizer", weights=[1, 3, 2], [s1, s2, s3])
|
||||||
|
|
||||||
|
# Load jingles playlist
|
||||||
|
jingles = (playlist(reload_mode="watch", "/home/pi/Conjurer/jingles.playlist"))
|
||||||
|
|
||||||
|
# Create the main stream with random playlist and jingles
|
||||||
|
s = rotate(id="randomizer", weights=[20, 1], [s4, jingles])
|
||||||
|
|
||||||
# Set up an interactive harbor for controlling the stream
|
# Set up an interactive harbor for controlling the stream
|
||||||
interactive.harbor(port = 9999)
|
interactive.harbor(port = 9999)
|
||||||
@@ -31,7 +70,6 @@ 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 = amplify(a, s)
|
s = amplify(a, s)
|
||||||
|
|
||||||
# Skip blank sections in the stream
|
# Skip blank sections in the stream
|
||||||
@@ -53,7 +91,26 @@ set("log.file.path", logpath)
|
|||||||
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])
|
cross_s = crossfade(fade_out=3., fade_in=3., duration=5., fallback(id="switcher", track_sensitive=false, [requests_queue, s]))
|
||||||
|
radio = fallback(id="switcher2", track_sensitive=false, [cross_s, emergency])
|
||||||
|
|
||||||
|
# Function to process the request queue and skip the current track if requested
|
||||||
|
def check_skip()
|
||||||
|
if p() then
|
||||||
|
log.info("Skipping current track.")
|
||||||
|
radio.skip()
|
||||||
|
p.set(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the queue processing function every 60 seconds
|
||||||
|
thread.run(every=60., queue_processing)
|
||||||
|
|
||||||
|
# Set up an interactive control for skipping tracks
|
||||||
|
p = interactive.bool("r1", false)
|
||||||
|
|
||||||
|
# Run the check_skip function every 5 seconds
|
||||||
|
thread.run(every=5., check_skip)
|
||||||
|
|
||||||
# Handle metadata events
|
# Handle metadata events
|
||||||
handle_metadata = fun (m) -> begin
|
handle_metadata = fun (m) -> begin
|
||||||
@@ -69,3 +126,35 @@ 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)
|
||||||
|
# Uncomment the following lines to enable additional output formats
|
||||||
|
# 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)
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
flask
|
flask
|
||||||
waitress
|
waitress
|
||||||
|
sshtunnel
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -204,6 +204,13 @@
|
|||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
],
|
],
|
||||||
|
"same plusy\\b": [
|
||||||
|
"Jak na cmentarzu Szefie. Jak na cmentarzu",
|
||||||
|
15.0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
],
|
||||||
"kld\\b": [
|
"kld\\b": [
|
||||||
"KLD? Mieli tam burdel większy niż u Hammera. Ale o ile Hammer sprzątnął już nawet całkiem nieźle - to tam śmieci wyniosły się same. Uczcijmy ich minutą śmiechu.",
|
"KLD? Mieli tam burdel większy niż u Hammera. Ale o ile Hammer sprzątnął już nawet całkiem nieźle - to tam śmieci wyniosły się same. Uczcijmy ich minutą śmiechu.",
|
||||||
15.0,
|
15.0,
|
||||||
|
|||||||
@@ -239,6 +239,8 @@ def get_item_name(sp, item_type, item_id):
|
|||||||
name = sp.album(album_id=item_id).get("name")
|
name = sp.album(album_id=item_id).get("name")
|
||||||
elif item_type == "track":
|
elif item_type == "track":
|
||||||
name = sp.track(track_id=item_id).get("name")
|
name = sp.track(track_id=item_id).get("name")
|
||||||
|
elif item_type == "artist_top_tracks":
|
||||||
|
name = sp.artist_top_tracks(artist_id=item_id).get("name")
|
||||||
return sanitize(name)
|
return sanitize(name)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user