From 9bcf3ddb60d94e963fe2cc373ab944205ee54333 Mon Sep 17 00:00:00 2001 From: Wesley Neuhaus Date: Tue, 6 Aug 2024 20:52:02 -0400 Subject: [PATCH] Initial migration --- crontab_file.txt | 25 ++++ display1.html | 112 ++++++++++++++++ display2.html | 112 ++++++++++++++++ htmltest.html | 90 +++++++++++++ install.sh | 22 +++ main1.py | 137 +++++++++++++++++++ main2.py | 154 +++++++++++++++++++++ manage1.html | 328 +++++++++++++++++++++++++++++++++++++++++++++ manage2.html | 227 +++++++++++++++++++++++++++++++ playtest.py | 55 ++++++++ pull_from_cloud.sh | 36 +++++ requirements.txt | 0 style.css | 174 ++++++++++++++++++++++++ update.sh | 35 +++++ version.txt | 1 + 15 files changed, 1508 insertions(+) create mode 100644 crontab_file.txt create mode 100644 display1.html create mode 100644 display2.html create mode 100644 htmltest.html create mode 100644 install.sh create mode 100644 main1.py create mode 100644 main2.py create mode 100644 manage1.html create mode 100644 manage2.html create mode 100644 playtest.py create mode 100644 pull_from_cloud.sh create mode 100644 requirements.txt create mode 100755 style.css create mode 100644 update.sh create mode 100644 version.txt diff --git a/crontab_file.txt b/crontab_file.txt new file mode 100644 index 0000000..9710a3d --- /dev/null +++ b/crontab_file.txt @@ -0,0 +1,25 @@ +# Edit this file to introduce tasks to be run by cron. +# +# Each task to run has to be defined through a single line +# indicating with different fields when the task will be run +# and what command to run for the task +# +# To define the time you can provide concrete values for +# minute (m), hour (h), day of month (dom), month (mon), +# and day of week (dow) or use '*' in these fields (for 'any'). +# +# Notice that tasks will be started based on the cron's system +# daemon's notion of time and timezones. +# +# Output of the crontab jobs (including errors) is sent through +# email to the user the crontab file belongs to (unless redirected). +# +# For example, you can run a backup of all your user accounts +# at 5 a.m every week with: +# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ +# +# For more information see the manual pages of crontab(5) and cron(8) +# +# m h dom mon dow command +@reboot $HOME/update.sh >> $HOME/logfile.log +@reboot sleep 10 && DISPLAY=:0 firefox --kiosk http://localhost:5000/display diff --git a/display1.html b/display1.html new file mode 100644 index 0000000..7383614 --- /dev/null +++ b/display1.html @@ -0,0 +1,112 @@ + + + + + Now Playing + + + +

Now Playing

+ Album Art +
Nothing is playing
+

+
+ 0:00 +
+
+
+ 4:00 +
+ + + diff --git a/display2.html b/display2.html new file mode 100644 index 0000000..37b78c7 --- /dev/null +++ b/display2.html @@ -0,0 +1,112 @@ + + + + + Now Playing + + + + +

Now Playing

+ +
Nothing is playing
+

+
+ 0:00 +
+
+
+ 4:00 +
+ + + + diff --git a/htmltest.html b/htmltest.html new file mode 100644 index 0000000..8fb1e77 --- /dev/null +++ b/htmltest.html @@ -0,0 +1,90 @@ + + + + + + Simple Music Player + + + +
+
+ Song Title +
+
+ 0:00 +
+
+
+ 4:00 +
+
+ + + + diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..c064550 --- /dev/null +++ b/install.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Use /bin/bash to run this script, even when called manually + +SOURCE_DIR="$HOME/GR-Jukebox" +TARGET_DIR="$HOME" + +for file in $SOURCE_DIR/*; do + filename=$(basename "$file") + if [ "$filename" != "install.sh" ]; then + # Move the file + cp -f "$file" "$TARGET_DIR/" + + # Check if the file should be executable (modify this condition to fit your needs) + if [[ "$filename" == *.sh || "$filename" == *.py ]]; then + chmod +x "$TARGET_DIR/$filename" + fi + fi +done + +crontab crontab_file.txt + +echo "Installation complete. All files have been moved to $TARGET_DIR." diff --git a/main1.py b/main1.py new file mode 100644 index 0000000..df6cf4b --- /dev/null +++ b/main1.py @@ -0,0 +1,137 @@ +from flask import Flask, render_template, request, redirect, url_for, jsonify +from flask_cors import CORS +import yt_dlp +import vlc +import time +import os +import eyed3 + +home_directory = os.path.expanduser("~") +#app = Flask(__name__,template_folder=home_directory) +app = Flask(__name__,template_folder=(home_directory+"/Code/GR/GR-Jukebox")) +CORS(app, resources={r"/*": {"origins": "*"}}) +player = vlc.MediaPlayer() +music_queue = [] +title_queue = [] +music_directory = os.path.expanduser('~/Music') # Adjust path as necessary + +@app.route('/') +def index(): + return render_template('manage1.html') + +@app.route('/display') +def display(): + return render_template('display1.html') + +@app.route('/music') +def list_music(): + music_files = [] + for filename in os.listdir(music_directory): + if filename.endswith('.mp3'): + path = os.path.join(music_directory, filename) + audiofile = eyed3.load(path) + title = audiofile.tag.title if audiofile.tag else 'Unknown Title' + music_files.append({'id': filename.split('.')[0], 'title': title}) + return jsonify(music_files) + +@app.route('/download', methods=['POST']) +def download(): + url = request.form['youtube_url'] + if 'youtube.com' in url: + video_id = url.split('=')[1][:11] # Extract the video ID from the YouTube URL + elif 'youtu.be' in url: + video_id = url.split('youtu.be/')[1][:11] + else: + return jsonify({'status': 'error', 'message': 'Invalid YouTube URL'}) + output_path = os.path.expanduser(f'{home_directory}/Music/{video_id}.mp3') # Path where the file will be saved + + if not os.path.exists(output_path): + print("Downloading...") + # Configure yt-dlp to download best audio quality + ydl_opts = { + 'format': 'bestaudio/best', + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }], + 'noplaylist': True, + 'quiet': True, + 'outtmpl': output_path[:-4] # Use the custom path as the output template + } + + # Use yt-dlp to extract information and download the audio file + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(url, download=True) + title = info.get('title') # Extract the title from video information + + # Load the downloaded file using eyed3 and add ID3 tags + audiofile = eyed3.load(output_path) + if audiofile.tag is None: # Create an ID3 tag if none exist + audiofile.tag = eyed3.id3.Tag() + audiofile.tag.file_info = eyed3.id3.FileInfo(output_path) + + print(f"Downloaded: {title}") + audiofile.tag.title = title + audiofile.tag.save() # Save the metadata + #return redirect(url_for('index')) # Redirect back to the main page + return jsonify({'status': 'success', 'message': 'Song added successfully'}) + +playing_thread = False +@app.route('/play', methods=['POST']) +def play(): + global music_queue + global title_queue + global playing_thread + global player + music_queue.append(request.json['songId']) + title_queue.append(request.json['songTitle']) + if not playing_thread: + while len(music_queue) > 0: + playing_thread = True + player = vlc.MediaPlayer(f'{home_directory}/Music/{music_queue[0]}.mp3') + player.play() + # Wait for the audio to start playing and check periodically if it is still playing + time.sleep(1) # Short initial delay to allow playback to start + while player.is_playing(): + print('Playing...') + time.sleep(0.5) # Check every second if it is still playing + player.stop() + music_queue.pop(0) + title_queue.pop(0) + playing_thread = False + return jsonify({'status': 'success', 'message': 'Playing song'}) + +@app.route('/queue') +def get_queue(): + global title_queue + return jsonify([{'id': idx, 'title': title} for idx, title in enumerate(title_queue)]) + +@app.route('/remove_from_queue', methods=['POST']) +def remove_from_queue(): + global music_queue + global title_queue + global player + data = request.json + song_id = int(data['songId']) + try: + #del music_queue[song_id] + #del title_queue[song_id] + if song_id == 0: + player.stop() + else: + music_queue.pop(song_id) + title_queue.pop(song_id) + return jsonify({'status': 'success', 'message': 'Song removed successfully'}) + except IndexError: + return jsonify({'status': 'error', 'message': 'Invalid song ID'}), 400 + +@app.route('/current') +def current(): + global title_queue + if len(title_queue) > 0: + return {'title': title_queue[0]} + return {'title': 'Nothing is playing'} + +if __name__ == "__main__": + app.run(host='0.0.0.0', debug=True, port=5000) diff --git a/main2.py b/main2.py new file mode 100644 index 0000000..ef88b68 --- /dev/null +++ b/main2.py @@ -0,0 +1,154 @@ +from flask import Flask, render_template, jsonify +from flask_socketio import SocketIO, emit +from flask_cors import CORS +import yt_dlp +import vlc +import os +import eyed3 +import time + +home_directory = os.path.expanduser("~") +music_directory = os.path.expanduser('~/Music') # Adjust path as necessary + +#app = Flask(__name__,template_folder=home_directory) +app = Flask(__name__, template_folder=f"{home_directory}/Code/GR/GR-Jukebox") +CORS(app, resources={r"/*": {"origins": "*"}}) +socketio = SocketIO(app) +player = vlc.MediaPlayer() +music_queue = [] +title_queue = [] + +@socketio.on('connect') +def handle_connect(): + emit('response', {'message': 'Connected to WebSocket server'}) + +@socketio.on('list_music') +def handle_list_music(): + music_files = [] + for filename in os.listdir(music_directory): + if filename.endswith('.mp3'): + path = os.path.join(music_directory, filename) + audiofile = eyed3.load(path) + title = audiofile.tag.title if audiofile.tag else 'Unknown Title' + music_files.append({'id': filename.split('.')[0], 'title': title}) + print({'music_files':music_files}) + emit('music_list', {'music_files': music_files}) + +@socketio.on('download') +def handle_download(data): + url = data['youtube_url'] + if 'youtube.com' in url: + video_id = url.split('=')[1][:11] # Extract the video ID from the YouTube URL + elif 'youtu.be' in url: + video_id = url.split('youtu.be/')[1][:11] + else: + #return jsonify({'status': 'error', 'message': 'Invalid YouTube URL'}) + emit('download_status', {'status': 'error', 'message': 'Invalid YouTube URL'}) + output_path = os.path.expanduser(f'{home_directory}/Music/{video_id}.mp3') # Path where the file will be saved + + if not os.path.exists(output_path): + print("Downloading...") + # Configure yt-dlp to download best audio quality + ydl_opts = { + 'format': 'bestaudio/best', + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }], + 'noplaylist': True, + 'quiet': True, + 'outtmpl': output_path[:-4] # Use the custom path as the output template + } + + # Use yt-dlp to extract information and download the audio file + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(url, download=True) + title = info.get('title') # Extract the title from video information + + # Load the downloaded file using eyed3 and add ID3 tags + audiofile = eyed3.load(output_path) + if audiofile.tag is None: # Create an ID3 tag if none exist + audiofile.tag = eyed3.id3.Tag() + audiofile.tag.file_info = eyed3.id3.FileInfo(output_path) + + print(f"Downloaded: {title}") + audiofile.tag.title = title + audiofile.tag.save() # Save the metadata + emit('download_status', {'status': 'success', 'message': 'Song added successfully'}) + +playing_thread = False +@socketio.on('play') +def handle_play(data): + global music_queue + global title_queue + global playing_thread + global player + music_queue.append(data['songId']) + title_queue.append(data['songTitle']) + if not playing_thread: + while len(music_queue) > 0: + playing_thread = True + player = vlc.MediaPlayer(f'{home_directory}/Music/{music_queue[0]}.mp3') + player.play() + print(f"Playing: {title_queue[0]}") + emit('current', {'title': title_queue[0]}) + emit('queue', [{'id': idx, 'title': title} for idx, title in enumerate(title_queue)]) + # Wait for the audio to start playing and check periodically if it is still playing + time.sleep(1) # Short initial delay to allow playback to start + while player.is_playing(): + #print('Playing...') + time.sleep(0.5) # Check every second if it is still playing + #emit('time', {'time': player.get_time()}) + #print(player.get_time()) + #print(player.get_length()) + #print((player.get_time()/player.get_length())) + emit('time', {'time': (player.get_time()/player.get_length())}) + player.stop() + music_queue.pop(0) + title_queue.pop(0) + playing_thread = False + emit('play_return', {'status': 'success', 'message': 'Playing song'}) + +@socketio.on('get_queue') +def handle_get_queue(): + emit('queue', [{'id': idx, 'title': title} for idx, title in enumerate(title_queue)]) + +@socketio.on('remove_from_queue') +def handle_remove_from_queue(data): + global music_queue + global title_queue + global player + #data = request.json + song_id = int(data['songId']) + try: + #del music_queue[song_id] + #del title_queue[song_id] + if song_id == 0: + player.stop() + else: + music_queue.pop(song_id) + title_queue.pop(song_id) + #return jsonify({'status': 'success', 'message': 'Song removed successfully'}) + emit('remove_return', {'status': 'success', 'message': 'Song removed successfully'}) + except IndexError: + #return jsonify({'status': 'error', 'message': 'Invalid song ID'}), 400 + emit('remove_return', {'status': 'error', 'message': 'Invalid song ID'}) + +@socketio.on('current_song') +def handle_current_song(): + if title_queue: + emit('current', {'title': title_queue[0]}) + else: + emit('current', {'title': 'Nothing is playing'}) + +@app.route('/') +def index(): + return render_template('manage2.html') + +@app.route('/display') +def display(): + return render_template('display2.html') + +if __name__ == '__main__': + socketio.run(app, host='0.0.0.0', debug=True, port=5000) diff --git a/manage1.html b/manage1.html new file mode 100644 index 0000000..78abcdc --- /dev/null +++ b/manage1.html @@ -0,0 +1,328 @@ + + + + + Manage Queue + + + +
+

Download New Song

+
+ + + +
+
+ Album Art +
+ 0:00 +
+
+
+ 4:00 +
+
+
+ +
+

Music Queue

+ +
+ +
+
+ +
+

All Music

+ +
+ +
+
+ + + + diff --git a/manage2.html b/manage2.html new file mode 100644 index 0000000..7ced405 --- /dev/null +++ b/manage2.html @@ -0,0 +1,227 @@ + + + + + Manage Queue + + + +
+

Download New Song

+ + + +
+ +
+

Music Queue

+
+
+ +
+

All Music

+ +
+
+ + + + + diff --git a/playtest.py b/playtest.py new file mode 100644 index 0000000..3c93435 --- /dev/null +++ b/playtest.py @@ -0,0 +1,55 @@ +import os +import yt_dlp +import vlc +import time +import eyed3 + +def download_and_play(url): + video_id = url.split('=')[1][:11] # Extract the video ID from the YouTube URL + output_path = os.path.expanduser(f'/home/generalized/Music/{video_id}.mp3') # Path where the file will be saved + + if not os.path.exists(output_path): + print("Downloading...") + # Configure yt-dlp to download best audio quality + ydl_opts = { + 'format': 'bestaudio/best', + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }], + 'noplaylist': True, + 'quiet': True, + 'outtmpl': output_path[:-4] # Use the custom path as the output template + } + + # Use yt-dlp to extract information and download the audio file + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(url, download=True) + title = info.get('title') # Extract the title from video information + + # Load the downloaded file using eyed3 and add ID3 tags + audiofile = eyed3.load(output_path) + if audiofile.tag is None: # Create an ID3 tag if none exist + audiofile.tag = eyed3.id3.Tag() + audiofile.tag.file_info = eyed3.id3.FileInfo(output_path) + + print(f"Downloaded: {title}") + audiofile.tag.title = title + audiofile.tag.save() # Save the metadata + + # Use VLC to play the downloaded audio file + player = vlc.MediaPlayer(output_path) + player.play() + + # Wait for the audio to start playing and check periodically if it is still playing + time.sleep(1) # Short initial delay to allow playback to start + while player.is_playing(): + time.sleep(1) # Check every second if it is still playing + + player.stop() # Stop playing after the audio is done + +if __name__ == "__main__": + #youtube_url = "https://www.youtube.com/watch?v=szeXkBYq5HU" + youtube_url = "https://www.youtube.com/watch?v=BYiraKCTViY&list=PLdQb1ybxFDih_SbfzdjGIlf3Tylztpc4Q" + download_and_play(youtube_url) diff --git a/pull_from_cloud.sh b/pull_from_cloud.sh new file mode 100644 index 0000000..992fa73 --- /dev/null +++ b/pull_from_cloud.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Define directories and files +REPO_DIR="$HOME/GR-Jukebox" +VERSION_FILE_PATH="$REPO_DIR/version.txt" +INSTALLED_VERSION_FILE="$HOME/version.txt" + +# Function to get version from a file +get_version() { + if [[ -f "$1" ]]; then + cat "$1" + else + echo "0" + fi +} + +# Read the currently installed version and the version in the repository +INSTALLED_VERSION=$(get_version "$INSTALLED_VERSION_FILE") +REPO_VERSION=$(get_version "$VERSION_FILE_PATH") + +# Check network connectivity to github.com +while ! ping -c 1 -W 1 git-codecommit.us-east-2.amazonaws.com > /dev/null; do + echo "Waiting for amazonaws.com - network interface might be down..." + sleep 1 +done + +# Fetch and compare the latest version on GitHub without pulling all files +cd "$REPO_DIR" +git fetch origin +REMOTE_VERSION=$(git show origin/master:version.txt) + +if [[ "$INSTALLED_VERSION" != "$REMOTE_VERSION" ]]; then + echo "New version available on AWS. Starting download in background." + # Pull the latest changes from the remote repository + ( git pull origin master & ) +fi \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/style.css b/style.css new file mode 100755 index 0000000..03f7d24 --- /dev/null +++ b/style.css @@ -0,0 +1,174 @@ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +body { + background-repeat: no-repeat; + background-size: cover; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:svgjs='http://svgjs.com/svgjs' width='1440' height='560' preserveAspectRatio='none' viewBox='0 0 1440 560'%3e%3cg mask='url(%26quot%3b%23SvgjsMask1549%26quot%3b)' fill='none'%3e%3crect width='1440' height='560' x='0' y='0' fill='url(%23SvgjsLinearGradient1550)'%3e%3c/rect%3e%3cuse xlink:href='%23SvgjsSymbol1557' x='0' y='0'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsSymbol1557' x='720' y='0'%3e%3c/use%3e%3c/g%3e%3cdefs%3e%3cmask id='SvgjsMask1549'%3e%3crect width='1440' height='560' fill='white'%3e%3c/rect%3e%3c/mask%3e%3clinearGradient x1='15.28%25' y1='139.29%25' x2='84.72%25' y2='-39.29%25' gradientUnits='userSpaceOnUse' id='SvgjsLinearGradient1550'%3e%3cstop stop-color='%230e2a47' offset='0'%3e%3c/stop%3e%3cstop stop-color='rgba(0%2c 7%2c 213%2c 1)' offset='1'%3e%3c/stop%3e%3c/linearGradient%3e%3cpath d='M-1 0 a1 1 0 1 0 2 0 a1 1 0 1 0 -2 0z' id='SvgjsPath1556'%3e%3c/path%3e%3cpath d='M-3 0 a3 3 0 1 0 6 0 a3 3 0 1 0 -6 0z' id='SvgjsPath1554'%3e%3c/path%3e%3cpath d='M-5 0 a5 5 0 1 0 10 0 a5 5 0 1 0 -10 0z' id='SvgjsPath1551'%3e%3c/path%3e%3cpath d='M2 -2 L-2 2z' id='SvgjsPath1552'%3e%3c/path%3e%3cpath d='M6 -6 L-6 6z' id='SvgjsPath1553'%3e%3c/path%3e%3cpath d='M30 -30 L-30 30z' id='SvgjsPath1555'%3e%3c/path%3e%3c/defs%3e%3csymbol id='SvgjsSymbol1557'%3e%3cuse xlink:href='%23SvgjsPath1551' x='30' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='30' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='30' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='30' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='30' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='30' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='30' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='30' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='30' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='30' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='90' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='90' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='90' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='90' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='90' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='90' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='90' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='90' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='90' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='90' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='150' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='150' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='150' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='150' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='150' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='150' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1555' x='150' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)' stroke-width='3'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='150' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='150' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='150' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='210' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='210' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='210' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='210' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='210' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='210' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='210' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='210' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='210' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='210' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='270' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='270' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='270' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='270' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='270' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='270' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='270' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='270' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='270' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1555' x='270' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)' stroke-width='3'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='330' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='330' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='330' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='330' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='330' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='330' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='330' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='330' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='330' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='330' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='390' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='390' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='390' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1555' x='390' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)' stroke-width='3'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='390' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='390' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='390' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='390' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='390' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='390' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='450' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='450' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='450' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='450' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='450' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='450' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='450' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='450' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='450' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='450' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='510' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='510' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='510' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='510' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='510' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1555' x='510' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)' stroke-width='3'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='510' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='510' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='510' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='510' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='570' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='570' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='570' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='570' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='570' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='570' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='570' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='570' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='570' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1554' x='570' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='630' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='630' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='630' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1552' x='630' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='630' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='630' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='630' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='630' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='630' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='630' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='30' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='90' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='150' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1555' x='690' y='210' stroke='rgba(11%2c 102%2c 200%2c 1)' stroke-width='3'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='270' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='690' y='330' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1556' x='690' y='390' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='450' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1551' x='690' y='510' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3cuse xlink:href='%23SvgjsPath1553' x='690' y='570' stroke='rgba(11%2c 102%2c 200%2c 1)'%3e%3c/use%3e%3c/symbol%3e%3c/svg%3e"); + +} + +body * { + font-family: 'Roboto', sans-serif; +} + +#boxes * { + border: 1px solid transparent; +} + +#boxes { + display: grid; + grid-template-columns: max-content max-content; + grid-template-areas: + 'A B' + 'A C'; + gap: 32px; + place-content: center; + height: 100vh; +} + +#player01 { + grid-area: A; + padding: 50px 38px; +} + +#player01 .wrapper{ + width: 190px; +} + + +#player01 img{ + width: 190px; + height: 190px; + object-fit: cover; + border-radius: 10px; +} + +#player01 .info { + padding-top: 28px; +} + +#player02 { + grid-area: B; + height: fit-content; +} + +#player02 .controls { + display: flex; + justify-content: space-around; +} + +#player03 { + grid-area: C; +} + +#player03 .controls { + display: flex; + justify-content: space-around; +} + +.player { + background-color: #080747; + padding: 28px; + border-radius: 20px; +} + +.player img { + width: 84px; + height: 84px; + object-fit: cover; + border-radius: 10px; +} + + +.info { + color: #E1E1E6; +} + +.info p { + opacity: 0.68; + font-size: 19px; +} + +.info-wrapper { + display: flex; + align-items: center; + gap: 30px; +} + +.player h1 { + font-size: 27px; + color: #E1E1E6; + padding-bottom: 7px; +} + + +.controls { + display: flex; + justify-content: space-between; + padding-top: 20px; +} + +.track { + padding-top: 28px; + position: relative; + +} + +.track::before { +content: ''; +height: 6px; +width: 100%; +display: block; +background: #D9D9D9; +opacity: 0.3; +border-radius: 10px; +position: absolute; +} + +.track::after { + content: ''; +height: 6px; +width: 85%; +display: block; +background: #D9D9D9; +border-radius: 10px; + + +} + +.time { + opacity: 0.7; + font-size: 14px; + color: gainsboro; + + display: flex; + justify-content: space-between; + padding-top: 9.6px; +} + +@media (max-width: 670px) { + #boxes { + display:flex; + flex-direction: column; + + max-width: 270px; + margin: auto; + + + height: auto; + padding-block: 60px; + } + + + +} + + + + diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..4923401 --- /dev/null +++ b/update.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Define directories and files +REPO_DIR="$HOME/GR-Jukebox" +VERSION_FILE_PATH="$REPO_DIR/version.txt" +INSTALLED_VERSION_FILE="$HOME/version.txt" + +# Function to get version from a file +get_version() { + if [[ -f "$1" ]]; then + cat "$1" + else + echo "0" + fi +} + +# Read the currently installed version and the version in the repository +INSTALLED_VERSION=$(get_version "$INSTALLED_VERSION_FILE") +REPO_VERSION=$(get_version "$VERSION_FILE_PATH") + +echo "Installed version: $INSTALLED_VERSION" + +# Check if installed version matches the version in the repository directory +if [[ "$INSTALLED_VERSION" != "$REPO_VERSION" ]]; then + echo "Version mismatch. Running install script and exiting updater." + echo "New version: $REPO_VERSION" + /bin/bash "$REPO_DIR/install.sh" + ( /bin/bash "$HOME/update.sh" & ) + exit 0 +fi + +( /bin/bash "$HOME/pull_from_cloud.sh" & ) + +source "$HOME/jukebox/bin/activate" +python3 "$HOME/main1.py" diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..e6d5cb8 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.2 \ No newline at end of file