diff --git a/conjurer_musician/conjurer_musician.py b/conjurer_musician/conjurer_musician.py index 11048c5..16e18c4 100644 --- a/conjurer_musician/conjurer_musician.py +++ b/conjurer_musician/conjurer_musician.py @@ -1,9 +1,9 @@ -# This Python file uses the following encoding: utf-8 """ The provided Python script sets up a Flask web server to manage a list of music files, with functions for rescanning the music folder, updating the music list, and serving the music list via API endpoints. """ + import json import logging import os @@ -48,10 +48,10 @@ if platform in ("linux", "linux2"): else: LOGFILE = "/home/pi/Conjurer/discord_mus_service.log" NETRC_FILE = "/home/pi/.netrc" - LOGSTORE = "/home/pi/RetroPie/logs/" + LOGSTORE = "/home/pi/MediaShare/logs/" ENCODING = "utf-8" - MUSIC_FOLDER = "/home/pi/RetroPie/mp3/" - PRIORITY_FOLDER = "/home/pi/RetroPie/mp3/Magiczne i chuj/" + MUSIC_FOLDER = "/home/pi/MediaShare/mp3/" + PRIORITY_FOLDER = "/home/pi/MediaShare/mp3/Magiczne i chuj/" RADIOLOG_PATH = "/home/pi/Conjurer/radio_log.log" PERSISTENCE_PATH = "/home/pi/Conjurer/persistence.log" diff --git a/conjurer_musician/requirements_musician.txt b/conjurer_musician/requirements_musician.txt index e298c25..43eb53b 100644 --- a/conjurer_musician/requirements_musician.txt +++ b/conjurer_musician/requirements_musician.txt @@ -1,3 +1,4 @@ flask waitress -sshtunnel \ No newline at end of file +sshtunnel +requests diff --git a/constants.py b/constants.py index 60cb612..ee4b1bd 100644 --- a/constants.py +++ b/constants.py @@ -78,14 +78,14 @@ if platform in ("linux", "linux2"): MEMORY_FIVE_SIARA = "/home/pi/Conjurer/pamiec.json" SYSTEM_GPT_SETTINGS = "/home/pi/Conjurer/system_gpt_settings.json" MEMORY_FIVE_MUZYKA = "/home/pi/Conjurer/pamiec_muzyki.json" - MUSIC_FOLDER = "/home/pi/RetroPie/mp3/" + MUSIC_FOLDER = "/home/pi/MediaShare/mp3/" SETTINGS_FILE = "/home/pi/Conjurer/settings.json" NETRC_FILE = "/home/pi/.netrc" - LOGSTORE = "/home/pi/RetroPie/logs/" + LOGSTORE = "/home/pi/MediaShara/logs/" ACCIDENT_LOG = "/home/pi/Conjurer/accident_log.json" ENCODING = "utf-8" - GRAPHICS_PATH = "/home/pi/RetroPie/Conjurer_graphics/" - DIR_PATH_SADOX = "/home/pi/RetroPie/Fansadox/" + GRAPHICS_PATH = "/home/pi/MediaShare/Conjurer_graphics/" + DIR_PATH_SADOX = "/home/pi/MediaShare/Fansadox/" elif platform == "win32": @@ -120,6 +120,9 @@ SPOTIFY_CTRL = spotipy.Spotify( client_secret=authTokens[2], ) ) +REMOTE_HOST_NAME = "youtube" +authTokens = netrc_mod.authenticators(REMOTE_HOST_NAME) +YOUTUBE_AUTH = [authTokens[0],authTokens[2]] WORD_REACTIONS = DATA["word_reactions"] CYCLIC_WORDS = DATA["cyclic_words"] diff --git a/ffmpeg.pref b/ffmpeg.pref new file mode 100644 index 0000000..eb72eeb --- /dev/null +++ b/ffmpeg.pref @@ -0,0 +1,3 @@ +Package: ffmpeg libavcodec-dev libavcodec59 libavdevice59 libavfilter8 libavformat-dev libavformat59 libavutil-dev libavutil57 libpostproc56 libswresample-dev libswresample4 libswscale-dev libswscale6 libavdevice-dev libavfilter-dev libpostproc-dev +Pin: origin deb.debian.org +Pin-Priority: 1001 diff --git a/install_main_bot.sh b/install_main_bot.sh index ac58dcb..33c4b22 100755 --- a/install_main_bot.sh +++ b/install_main_bot.sh @@ -1,20 +1,22 @@ #!/bin/bash sudo apt-get install python3-dev +sudo apt-get install portaudio19-dev python3-pyaudio +sudo apt-get install cd /home/pi || exit mdkir Conjurer cd Conjurer ||exit -python3 -m venv /home/pi/Conjurer/.venv +python3 -m venv /home/pi/Conjurer/.env cp /home/pi/conjurer/requirements_bot.txt /home/pi/Conjurer/ # trunk-ignore(shellcheck/SC1091) -source /home/pi/Conjurer/.venv/bin/activate -./env/bin/python3 -m pip install --upgrade pip -./env/bin/python3 -m pip install -r requirements_bot.txt -sed -i -e 's/os.rename/shutil.copy/g' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py -sed -i '1i\import shutil' ./env/lib/python3.11/site-packages/spotify_dl/youtube.py -sed -i '1i\import logging' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py +source /home/pi/Conjurer/.env/bin/activate +./.env/bin/python3 -m pip install --upgrade pip +./.env/bin/python3 -m pip install -r requirements_bot.txt +sed -i -e 's/os.rename/shutil.copy/g' ./.env/lib/python3.11/site-packages/spotify_dl/youtube.py +sed -i '1i\import shutil' ./.env/lib/python3.11/site-packages/spotify_dl/youtube.py +sed -i '1i\import logging' ./.env/lib/python3.11/site-packages/spotify_dl/spotify.py -sed -i '21i\ logger = logging.getLogger("discord")' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py -sed -i '22i\ logger.info("Playlist")' ./env/lib/python3.11/site-packages/spotify_dl/spotify.py +sed -i '21i\ logger = logging.getLogger("discord")' ./.env/lib/python3.11/site-packages/spotify_dl/spotify.py +sed -i '22i\ logger.info("Playlist")' ./.env/lib/python3.11/site-packages/spotify_dl/spotify.py #add sed changing signal registration to catch exception in spotify dl init deactivate sudo cp ./conjurer.service /etc/systemd/system/ diff --git a/install_music_bot.sh b/install_music_bot.sh index a93163a..f1a984c 100755 --- a/install_music_bot.sh +++ b/install_music_bot.sh @@ -25,8 +25,12 @@ sudo usermod -aG pulse-access root sudo usermod -aG audio pi sudo usermod -aG audio root - - +echo "Add exception suppresion to signal handler in spotify __ini__.py" +echo "Add password to youtube opts in spotify_dl youtube.py" +echo "Downgrade ffmpeg by copying ffmpeg.pref from repository to /etc/apt/preferences.d" +echo "Install opam from installation link" +echo "Initialize opam" +echo "Install liquidsoap and its dependencies" touch /home/pi/Conjurer/radio_log.log ./env/bin/python3 -m pip install --upgrade pip diff --git a/manual_set_up_radio.txt b/manual_set_up_radio.txt new file mode 100644 index 0000000..6b825f9 --- /dev/null +++ b/manual_set_up_radio.txt @@ -0,0 +1,101 @@ +# === SYSTEM SETUP === +sudo apt update && sudo apt install -y \ + pulseaudio pulseaudio-utils pavucontrol \ + libgtk-3-dev gtk-3-examples \ + xauth dbus-x11 \ + samba + +# === SAMBA CONFIGURATION === +sudo nano /etc/samba/smb.conf +# Add at the end of the file: +# [RaspberryPiNAS] +# path = /home/pi/Public +# comment = Pi Share +# browseable = yes +# writeable = yes +# guest ok = no +# valid users = pi + +# Set permissions +chmod 755 /home/pi +mkdir -p /home/pi/Public +chmod 777 /home/pi/Public +chown -R pi:pi /home/pi/Public + +# Set Samba password for pi +sudo smbpasswd -a pi + +# Restart Samba +sudo systemctl restart smbd + +# === PULSEAUDIO SYSTEM-WIDE SERVICE === +sudo nano /etc/systemd/system/pulseaudio.service +# Paste this content: +# [Unit] +# Description=PulseAudio System-wide Daemon +# After=sound.target network.target +# +# [Service] +# Type=simple +# ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disallow-module-loading=0 --daemonize=no +# Restart=always +# +# [Install] +# WantedBy=multi-user.target + +# === PULSEAUDIO MODULES FOR LEXICON LAMBDA USB === +sudo nano /etc/pulse/system.pa +# Comment out: +# load-module module-udev-detect +# load-module module-detect +# Add this at the bottom: +# load-module module-alsa-sink device=hw:1,0 sink_name=LambdaOutput sink_properties=device.description="Lexicon_Lambda_USB_Output" +# load-module module-alsa-source device=hw:1,0 source_name=LambdaInput source_properties=device.description="Lexicon_Lambda_USB_Input" +# load-module module-native-protocol-unix auth-anonymous=1 socket=/tmp/pulseaudio.socket + +# Set client default socket +sudo nano /etc/pulse/client.conf +# Add this line: +# default-server = unix:/tmp/pulseaudio.socket + +# Enable and start PulseAudio system-wide +sudo systemctl daemon-reload +sudo systemctl enable pulseaudio.service +sudo systemctl restart pulseaudio.service + +# === X11 & GUI FIXES FOR SSH/MOBAXTERM === +sudo nano /etc/ssh/sshd_config +# Ensure these lines are present: +# X11Forwarding yes +# X11UseLocalhost no +# XAuthLocation /usr/bin/xauth + +# Restart SSH +sudo systemctl restart ssh + +# Ensure xauth is installed +sudo apt install -y xauth + +# === BASHRC: MAKE GUI EXPORTS PERSISTENT === +nano ~/.bashrc +# Add this at the end: +# export GDK_BACKEND=x11 +# export LIBGL_ALWAYS_INDIRECT=1 +# export NO_AT_BRIDGE=1 +# export $(dbus-launch) + +# Reload bashrc immediately +source ~/.bashrc + +# === TESTING GUI === +# (Reconnect via MobaXterm SSH with X11 forwarding enabled before running below) + +# Run pavucontrol GUI +pavucontrol & + +# Optional: Test GTK GUI rendering +gtk3-demo & + +# Check audio devices +pactl list sinks short +pactl list sources short diff --git a/music_functions.py b/music_functions.py index 06bf7fe..a6e2881 100644 --- a/music_functions.py +++ b/music_functions.py @@ -16,6 +16,7 @@ from constants import ( MUSIC_FOLDER, SEND_MP3, SPOTIFY_CTRL, + YOUTUBE_AUTH, ) from spotify_dl import spotify from spotify_dl import youtube as youtube_download @@ -161,6 +162,7 @@ async def get_file(ctx, source, link): file_name_f=file_name_f, multi_core=0, proxy="", + YT_AUTH = YOUTUBE_AUTH ) _ = await coro logger.info("YT-DL done") @@ -203,6 +205,8 @@ async def get_file(ctx, source, link): else: dir_path = "/home/pi/RetroPie/mp3/Youtube" ydl_opts = { + "username": YOUTUBE_AUTH[0], + "password": YOUTUBE_AUTH[1], "proxy": "", "default_search": "ytsearch", "format": "bestaudio/best", @@ -258,7 +262,7 @@ async def get_file(ctx, source, link): ] # TODO: Make sponsorblock work sponsorblock_postprocessor = [] - dir_path = "/home/pi/RetroPie/movies/porn" + dir_path = "/home/pi/MediaShare/movies/porn" ydl_opts = { "postprocessors": sponsorblock_postprocessor, "paths": {"home": dir_path}, diff --git a/other_commands.py b/other_commands.py index 3609f2d..a7c45b4 100644 --- a/other_commands.py +++ b/other_commands.py @@ -66,6 +66,22 @@ class OtherModule(commands.Cog): """ await ctx.send(historia_fabryczki) + @commands.hybrid_command( + name="set_logging_level", + description="Nie intererer bo kici kici", + guild=discord.Object(id=664789470779932693), + ) + @commands.has_any_role('Legenda', 'Jarl', 'Bartender') + async def set_logging_level(self,ctx): + if "DEBUG" in ctx.message.content: + logger = logging.getLogger("discord") + logger.setLevel(logging.DEBUG) + elif "INFO" in ctx.message.content: + logger = logging.getLogger("discord") + logger.setLevel(logging.INFO) + else: + await ctx.send("Weź się kurwa zdecyduj co ?") + @commands.hybrid_command( name="reset_the_clock", description="Resetowanie zegara - dostępne wyłącznie dla Hammera", diff --git a/pulseaudio.service b/pulseaudio.service new file mode 100644 index 0000000..e8362dc --- /dev/null +++ b/pulseaudio.service @@ -0,0 +1,11 @@ +[Unit] +Description=PulseAudio System-wide Daemon +After=sound.target network.target + +[Service] +Type=simple +ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disallow-module-loading=0 --daemonize=no +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/requirements_bot.txt b/requirements_bot.txt index 2831a78..9eeca53 100644 --- a/requirements_bot.txt +++ b/requirements_bot.txt @@ -17,4 +17,4 @@ waitress clickupython assemblyai[extras] SpeechRecognition -git+https://github.com/imayhaveborkedit/discord-ext-voice-recvO \ No newline at end of file +git+https://github.com/imayhaveborkedit/discord-ext-voice-recv diff --git a/spotify_dl/__init__.py b/spotify_dl/__init__.py index 907630e..3de80ef 100644 --- a/spotify_dl/__init__.py +++ b/spotify_dl/__init__.py @@ -8,5 +8,7 @@ def signal_handler(sig, frame): print("\nCaught interrupt(did you press Ctrl+C?), stopping spotify_dl") sys.exit(0) - -signal.signal(signal.SIGINT, signal_handler) +try: + signal.signal(signal.SIGINT, signal_handler) +except ValueError: + print("Exception in signal handler") diff --git a/spotify_dl/constants.py b/spotify_dl/constants.py index 1210085..d9ce9e0 100644 --- a/spotify_dl/constants.py +++ b/spotify_dl/constants.py @@ -3,7 +3,7 @@ from pathlib import Path __all__ = ["VERSION"] -VERSION = "8.8.1" +VERSION = "8.9.0" if os.getenv("XDG_CACHE_HOME") is not None: SAVE_PATH = os.getenv("XDG_CACHE_HOME") + "/spotifydl" diff --git a/spotify_dl/spotify.py b/spotify_dl/spotify.py index 7969a58..4ce8d58 100644 --- a/spotify_dl/spotify.py +++ b/spotify_dl/spotify.py @@ -1,15 +1,17 @@ + import sys +import logging from spotify_dl.scaffold import log from spotify_dl.utils import sanitize from rich.progress import Progress -def fetch_tracks(sp, item_type, url): +def fetch_tracks(sp, item_type, item_id): """ - Fetches tracks from the provided URL. + Fetches tracks from the provided item_id. :param sp: Spotify client :param item_type: Type of item being requested for: album/playlist/track - :param url: URL of the item + :param item_id: id of the item :return Dictionary of song and artist """ songs_list = [] @@ -17,11 +19,14 @@ def fetch_tracks(sp, item_type, url): songs_fetched = 0 if item_type == "playlist": + logger = logging.getLogger("discord") + + logger.info("Playlist") with Progress() as progress: songs_task = progress.add_task(description="Fetching songs from playlist..") while True: items = sp.playlist_items( - playlist_id=url, + playlist_id=item_id, fields="items.track.name,items.track.artists(name, uri)," "items.track.album(name, release_date, total_tracks, images)," "items.track.track_number,total, next,offset," @@ -30,6 +35,7 @@ def fetch_tracks(sp, item_type, url): offset=offset, ) total_songs = items.get("total") + logger.info(total_songs) track_info_task = progress.add_task( description="Fetching track info", total=len(items["items"]) ) @@ -37,16 +43,32 @@ def fetch_tracks(sp, item_type, url): track_info = item.get("track") # If the user has a podcast in their playlist, there will be no track # Without this conditional, the program will fail later on when the metadata is fetched + logger.info(item) if track_info is None: offset += 1 continue + if not track_info.get("artists")[0]["name"]: + offset +=1 + continue track_album_info = track_info.get("album") track_num = track_info.get("track_number") - spotify_id = track_info.get("id") track_name = track_info.get("name") - track_artist = ", ".join( - [artist["name"] for artist in track_info.get("artists")] - ) + spotify_id = track_info.get("id") + track_audio_data = "No audio data" + try: + track_audio_data = sp.audio_analysis(spotify_id) + tempo = track_audio_data.get("track").get("tempo") + except: + log.error("Couldn't fetch audio analysis for %s", track_name) + tempo = None + print(track_info.get("artists")) + print(track_audio_data) + track_artist = "" + for artist in track_info.get("artists"): + if artist["name"]: + track_artist = ", ".join( + [artist["name"]] + ) if track_album_info: track_album = track_album_info.get("name") track_year = ( @@ -86,6 +108,7 @@ def fetch_tracks(sp, item_type, url): "genre": genre, "spotify_id": spotify_id, "track_url": None, + "tempo": tempo, } ) offset += 1 @@ -111,8 +134,8 @@ def fetch_tracks(sp, item_type, url): description="Fetching songs from the album.." ) while True: - album_info = sp.album(album_id=url) - items = sp.album_tracks(album_id=url, offset=offset) + album_info = sp.album(album_id=item_id) + items = sp.album_tracks(album_id=item_id, offset=offset) total_songs = items.get("total") track_album = album_info.get("name") track_year = ( @@ -141,6 +164,12 @@ def fetch_tracks(sp, item_type, url): ) track_num = item["track_number"] spotify_id = item.get("id") + try: + track_audio_data = sp.audio_analysis(spotify_id) + tempo = track_audio_data.get("track").get("tempo") + except: + log.error("Couldn't fetch audio analysis for %s", track_name) + tempo = None songs_list.append( { "name": track_name, @@ -154,6 +183,7 @@ def fetch_tracks(sp, item_type, url): "cover": cover, "genre": genre, "spotify_id": spotify_id, + "tempo": tempo, } ) offset += 1 @@ -168,7 +198,7 @@ def fetch_tracks(sp, item_type, url): break elif item_type == "track": - items = sp.track(track_id=url) + items = sp.track(track_id=item_id) track_name = items.get("name") album_info = items.get("album") track_artist = ", ".join([artist["name"] for artist in items["artists"]]) @@ -182,6 +212,12 @@ def fetch_tracks(sp, item_type, url): album_total = album_info.get("total_tracks") track_num = items["track_number"] spotify_id = items["id"] + try: + track_audio_data = sp.audio_analysis(spotify_id) + tempo = track_audio_data.get("track").get("tempo") + except: + log.error("Couldn't fetch audio analysis for %s", track_name) + tempo = None if len(items["album"]["images"]) > 0: cover = items["album"]["images"][0]["url"] else: @@ -203,6 +239,7 @@ def fetch_tracks(sp, item_type, url): "genre": genre, "track_url": None, "spotify_id": spotify_id, + "tempo": tempo, } ) @@ -219,9 +256,10 @@ def parse_spotify_url(url): if url.startswith("spotify:"): log.error("Spotify URI was provided instead of a playlist/album/track URL.") sys.exit(1) - parsed_url = url.replace("https://open.spotify.com/", "").split("?")[0] - item_type = parsed_url.split("/")[0] - item_id = parsed_url.split("/")[1] + parsed_url = url.replace("https://open.spotify.com/", "").split("?")[0].split("/") + index_adjustment = 1 if parsed_url[0].startswith("intl") else 0 + item_type = parsed_url[0+index_adjustment] + item_id = parsed_url[1+index_adjustment] return item_type, item_id @@ -239,8 +277,6 @@ def get_item_name(sp, item_type, item_id): name = sp.album(album_id=item_id).get("name") elif item_type == "track": 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) diff --git a/spotify_dl/spotify_dl.py b/spotify_dl/spotify_dl.py index d4e3d13..0add158 100644 --- a/spotify_dl/spotify_dl.py +++ b/spotify_dl/spotify_dl.py @@ -198,7 +198,7 @@ def spotify_dl(): ) url_dict["save_path"].mkdir(parents=True, exist_ok=True) log.info("Saving songs to %s directory", directory_name) - url_dict["songs"] = fetch_tracks(sp, item_type, url) + url_dict["songs"] = fetch_tracks(sp, item_type, item_id) url_data["urls"].append(url_dict.copy()) if args.dump_json is True: dump_json(url_dict["songs"]) diff --git a/spotify_dl/youtube.py b/spotify_dl/youtube.py index 3f132af..f1f8ef1 100644 --- a/spotify_dl/youtube.py +++ b/spotify_dl/youtube.py @@ -1,9 +1,9 @@ +import shutil import urllib.request from os import path import os import multiprocessing -import shutil import json import mutagen import csv @@ -65,13 +65,13 @@ def write_tracks(tracks_file, song_dict): i = 0 writer = csv.writer(file_out, delimiter=";") for url_dict in song_dict["urls"]: - # for track in url_dict['songs']: for track in url_dict["songs"]: track_url = track["track_url"] # here track_name = track["name"] track_artist = track["artist"] track_num = track["num"] track_album = track["album"] + track_tempo = track["tempo"] track["save_path"] = url_dict["save_path"] track_db.append(track) track_index = i @@ -82,6 +82,7 @@ def write_tracks(tracks_file, song_dict): track_url, str(track_num), track_album, + str(track_tempo), str(track_index), ] try: @@ -97,8 +98,8 @@ def write_tracks(tracks_file, song_dict): def set_tags(temp, filename, kwargs): """ sets song tags after they are downloaded - :param temp: contains index used to obtain more info about song being edited - :param filename: location of song whose tags are to be edited + :param temp: contains index used to obtain more info about song being editted + :param filename: location of song whose tags are to be editted :param kwargs: a dictionary of extra arguments to be used in tag editing """ song = kwargs["track_db"][int(temp[-1])] @@ -120,6 +121,8 @@ def set_tags(temp, filename, kwargs): ) song_file["genre"] = song.get("genre") + if song.get("tempo") is not None: + song_file["bpm"] = str(song.get("tempo")) song_file.save() song_file = MP3(filename, ID3=ID3) cover = song.get("cover") @@ -162,7 +165,7 @@ def find_and_download_songs(kwargs): print(f"Initiating download for {query}.") file_name = kwargs["file_name_f"]( - name=name, artist=artist, track_num=kwargs["track_db"][i].get("num") + name=name, artist=artist, track_num=kwargs["track_db"][i].get("playlist_num") ) if kwargs["use_sponsorblock"][0].lower() == "y": @@ -197,9 +200,10 @@ def find_and_download_songs(kwargs): ): print(f"File {mp3file_path} already exists, we do not overwrite it ") continue - outtmpl = f"{file_path}.%(ext)s" ydl_opts = { + "username":kwargs["YT_AUTH"][0], + "password":kwargs["YT_AUTH"][1], "proxy": kwargs.get("proxy"), "default_search": "ytsearch", "format": "bestaudio/best", @@ -312,11 +316,7 @@ def download_songs(**kwargs): log.debug("Downloading to %s", url["save_path"]) reference_file = DOWNLOAD_LIST track_db = write_tracks(reference_file, kwargs["songs"]) - try: - shutil.copy(reference_file, kwargs["output_dir"] + "/" + reference_file) - os.remove(reference_file) - except: - os.rename(reference_file, kwargs["output_dir"] + "/" + reference_file) + shutil.copy(reference_file, kwargs["output_dir"] + "/" + reference_file) reference_file = str(kwargs["output_dir"]) + "/" + reference_file kwargs["reference_file"] = reference_file kwargs["track_db"] = track_db diff --git a/thin_client.py b/thin_client.py old mode 100644 new mode 100755 index cbb1c33..efac6cb --- a/thin_client.py +++ b/thin_client.py @@ -75,8 +75,8 @@ async def on_ready(): for com in client.commands: logger.info("Command %s is awejleble", com.qualified_name) - logger.info("Logged in as ---->", client.user) - logger.info("ID:", client.user.id) + logger.info("Logged in as ----> %s", client.user) + logger.info("ID:%s ", client.user.id) logger.info("All systems: operational")