Compare commits

..

15 Commits

Author SHA1 Message Date
gitea 439c5c753c Comments in radio script 2024-04-24 20:15:39 +02:00
gitea cc11d3f75e This language is officially insane 2024-04-24 20:06:29 +02:00
gitea a35d7da6e3 testing skip 2024-04-24 20:02:37 +02:00
gitea 2c50318f1c Skipping tracks 2024-04-24 19:58:23 +02:00
gitea addc534c36 JINGLES! 2024-04-24 18:57:14 +02:00
gitea 430b4de995 Search headless (#8)
* Fix

* Final fix to headless

* test

* Fix

* Ready for deployment
2024-04-24 17:31:52 +02:00
gitea 9d00e40b41 Starting work on librarian headless mode 2024-04-24 12:00:56 +02:00
gitea 7d29c1d397 Fix 2024-04-23 23:58:55 +02:00
gitea ce3ad9f8f0 Crossfadeing ctnd 2024-04-23 23:40:44 +02:00
gitea abef18f4ad Fix crossfade 2024-04-23 23:36:45 +02:00
gitea 5beb1c6fdd Optimization bug fix #1 2024-04-23 21:37:44 +02:00
gitea ff69091495 Fine tuning 2024-04-23 18:23:12 +02:00
gitea 69d3c8849c test requests 2024-04-23 16:10:14 +02:00
gitea b2aab28460 Even more logging in spotify download
Fix #1

Test fix 2

Fixing part 2

Fixing ctnd

Final fix

Fixing spotipy get bug

Same plusy kurwa

Fixing fucking spotipy

test

More logging
2024-04-23 15:09:51 +02:00
gitea 082b25c89c 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
2024-04-23 12:59:33 +02:00
9 changed files with 257 additions and 201 deletions
+15 -9
View File
@@ -693,17 +693,19 @@ async def check_data_q():
ctx = fresh_data.ctx ctx = fresh_data.ctx
message += f' Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}' message += f' Dzielny poszukiwaczu @{searcher} w odpowiedzi na twoje zapytanie {query} nr {fresh_data.uuid}'
else: else:
ctx = client.get_channel(1062047367337095268) 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 += 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:
@@ -712,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:
@@ -1064,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, 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, 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(
@@ -1078,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,
@@ -1093,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("/", "\\")
@@ -1829,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
+3 -2
View File
@@ -102,17 +102,18 @@ 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.stop = True
record.entries = answer[record.uuid] record.entries = answer[record.uuid]
IN_COMM_Q.put(record) IN_COMM_Q.put(record)
else: if not record_stored:
for key in answer.keys(): for key in answer.keys():
record = QueryControl("Orphaned", key, "Orphan", _logger, None) record = QueryControl("Orphaned", key, "Orphan", _logger, None)
record.stop = True record.stop = True
record.entries = answer[record.uuid] record.entries = answer[record.uuid]
record.uuid = "000000"
IN_COMM_Q.put(record) IN_COMM_Q.put(record)
except Empty: except Empty:
time.sleep(1) time.sleep(1)
@@ -1,3 +1,3 @@
habanero habanero
waitress waitress
flask flask[async]
+10 -5
View File
@@ -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, control_dict, _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,11 +79,13 @@ def consumer(in_q, control_q, doi, live_results, result_list, control_dict, _log
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.
""" """
_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:
data = in_q.get(block=False)
if data is _sentinel: if data is _sentinel:
_logger.info("Worker finished") _logger.info("Worker finished")
control_dict["sentinels"] += 1 control_dict["sentinels"] += 1
@@ -97,11 +99,14 @@ def consumer(in_q, control_q, doi, live_results, result_list, control_dict, _log
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)
except Empty:
pass
if control_dict["sentinels"] >= MAXTHREADS: 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.
@@ -116,9 +121,9 @@ def search_for_doi(doi, live_results, _logger):
threads = [] threads = []
work_q = Queue(maxsize=WORK_Q_SIZE) work_q = Queue(maxsize=WORK_Q_SIZE)
control_q = Queue() control_q = Queue()
for _ in range (1, len(doi)//1000+1): for i in range (0, (len(doi)//1000)+2):
t_cons = Thread( t_cons = Thread(
target=consumer, args=(work_q, control_q, doi, live_results, result_list, control_dict, _logger) target=consumer, args=(work_q, control_q, doi, live_results, result_list, control_dict, i, _logger)
) )
_logger.info("Consumer thread created") _logger.info("Consumer thread created")
threads.append(t_cons) threads.append(t_cons)
+45
View File
@@ -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())
+20 -51
View File
@@ -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():
@@ -313,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
+90 -69
View File
@@ -5,10 +5,14 @@
# 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) # 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) m = request.metadata(r)
if l.last(m) < 3600. then if l.last(m) < 3600. then
log.info("Rejecting #{m['filename']} (played #{l.last(m)}s ago).") log.info("Rejecting #{m['filename']} (played #{l.last(m)}s ago).")
@@ -17,16 +21,18 @@ let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentia
l.add(m) l.add(m)
true true
end end
end end
# Define playlists to be used in the stream
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"))
# Create a request queue for user-generated requests
requests_queue = request.queue()
# Define playlists to be used in the stream # Function to process the request queue and add new requests
s1 = replaygain(playlist(reload_mode="watch", check_next=check, "/home/pi/Conjurer/all_playlist.playlist")) def queue_processing()
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") f1=file.read("/home/pi/Conjurer/request.playlist")
text = f1() text = f1()
if text != "" then if text != "" then
@@ -38,74 +44,89 @@ let json.parse credentials = file.contents("/home/pi/Conjurer/icecast_credentia
else else
log.info("no requests") log.info("no requests")
end end
end
# Randomly select a playlist with weights
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
interactive.harbor(port = 9999)
# 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
s = nrj(normalize(s))
s = amplify(a, s)
# Skip blank sections in the stream
s = blank.skip(max_blank=2., s)
# Configure logging settings
log_to_stdout = true
log_to_file = true
logpath = "/home/pi/Conjurer/radio_log.log"
loglevel = 4
set("log.stdout", log_to_stdout)
set("log.level", loglevel)
# Enable logging to file
set("log.file", log_to_file)
set("log.file.path", logpath)
# Set up emergency fallback track
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
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
thread.run(every=60., queue_processing) end
# Randomly select a playlist with weights # Run the queue processing function every 60 seconds
s = random(id="randomizer", weights=[1, 3, 2], [s1, s2, s3]) thread.run(every=60., queue_processing)
#s = delay(duration=5., s)
#radio = add([delay(1800., jingles), music])
#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 an interactive harbor for controlling the stream # Set up an interactive control for skipping tracks
interactive.harbor(port = 9999) p = interactive.bool("r1", false)
# 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 # Run the check_skip function every 5 seconds
s = nrj(normalize(s)) thread.run(every=5., check_skip)
s = crossfade(fade_out=3., fade_in=3., duration=5., s)
s = amplify(a, s)
# Skip blank sections in the stream # Handle metadata events
s = blank.skip(max_blank=2., s) handle_metadata = fun (m) -> begin
# Configure logging settings
log_to_stdout = true
log_to_file = true
logpath = "/home/pi/Conjurer/radio_log.log"
loglevel = 4
set("log.stdout", log_to_stdout)
set("log.level", loglevel)
# Enable logging to file
set("log.file", log_to_file)
set("log.file.path", logpath)
# Set up emergency fallback track
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
radio = fallback(id="switcher", track_sensitive=true, [requests_queue, s, emergency])
# Handle metadata events
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)
# 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(%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(%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(%aac, host="retropie", port=8000, password=credentials.password, icy_metadata="true", mount="aac-stream", radio)
+7
View File
@@ -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,
+2
View File
@@ -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)