inital version, simple layout and functionality

This commit is contained in:
2024-04-20 18:30:12 -07:00
commit b1c36bd22c
6 changed files with 340 additions and 0 deletions

Binary file not shown.

1
code Normal file
View File

@@ -0,0 +1 @@
AQA8p4IgPYH0j9TIUFR4m5zAKPgvPzhvW07iCLAUYq-7sWyhxTTDRiWZaocH01HomhYoGZnVarc2SGUH4Buu9ox22lQ0e7z4AnglirOqF8I24v4zZveuiOPuLjet4YFY9rCY9TdtGmaUE1Y710tdgF7_ZBT4V2fuFSM0i9qXML37rZBS_DVmWlbVyLi-RKOeT-EIktYsTNfV45ajoe-GCB7eL5eFit8nGGjBokIXM0w14m004GtnvjfcazBaJqS6XlqU80VB82OTQauN7fyckTEYMUalhfM20ZE2p8D7qCg

150
spotifycontroller.py Normal file
View File

@@ -0,0 +1,150 @@
from flask import Flask, render_template, request, url_for, redirect
import time
import requests
from urllib.parse import urlencode
import webbrowser
import base64
import json
import os
app = Flask(__name__)
client_id = '1cb8bc27872c4bcaaad0e95f123b4f7d'
client_secret = '9893dfb6d9eb43eebf082f9173ce937c'
redirect_uri = 'http://127.0.0.1:8888/callback'
encoded_creds = base64.b64encode(client_id.encode() + b':' + client_secret.encode()).decode("utf-8")
headers = {
"client_id": client_id,
"response_type": "code",
"redirect_uri": redirect_uri,
"scope": "user-read-playback-state,user-modify-playback-state,user-library-read,user-library-modify"
}
token_headers = {
"Authorization": "Basic " + encoded_creds,
"Content-Type": "application/x-www-form-urlencoded"
}
song_info = {
'name': "None",
'artist': "None",
'album': "None",
'image': "None"
}
@app.route('/')
def index():
global code
if os.path.exists('code'):
with open('code', 'r') as file:
code = file.read()
return redirect(url_for('webapp'))
else:
return redirect("https://accounts.spotify.com/authorize?" + urlencode(headers))
# return render_template('index.html')
# @app.route('/update')
# def update():
# # Generate new information
# new_info = generate_new_info()
# # Wait for 1 second
# time.sleep(1)
# return new_info
@app.route('/webapp', methods=['GET', 'POST'])
def webapp():
global access_token
# NEED TO FIND A BETTER WAY THAT DOESN'T INVOLVE A TRY EXCEPT BLOCK
if 'code' not in globals() or not code:
return redirect(url_for('index'))
token_data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": redirect_uri
}
access_object = requests.post("https://accounts.spotify.com/api/token", data=token_data, headers=token_headers)
if "error" in access_object.json():
if os.path.exists('code'):
os.remove('code')
return redirect(url_for('index'))
access_token = access_object.json()["access_token"]
return render_template('webapp.html', song_info=song_info)
@app.route('/callback', methods=['GET'])
def callback():
global code
code = request.args.get('code')
with open('code', 'w') as file:
file.write(code)
return redirect(url_for('webapp'))
@app.route('/appdata')
def appdata():
global access_token
user_headers = {
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json"
}
currently_playing = requests.get("https://api.spotify.com/v1/me/player/currently-playing", headers=user_headers)
if currently_playing.json()["is_playing"] == True:
return { 'id': currently_playing.json()["item"]["id"],
'name': currently_playing.json()["item"]["name"],
'artist': currently_playing.json()["item"]["artists"][0]["name"],
'album': currently_playing.json()["item"]["album"]["name"],
'image': currently_playing.json()["item"]["album"]["images"][0]["url"],
'is_playing': currently_playing.json()["is_playing"],
'progress_ms': currently_playing.json()["progress_ms"],
'duration_ms': currently_playing.json()["item"]["duration_ms"],
'is_liked': requests.get("https://api.spotify.com/v1/me/tracks/contains?ids=" + currently_playing.json()["item"]["id"], headers=user_headers).json()[0]
}
elif currently_playing.json()["is_playing"] == False:
return { 'name': "Not Playing",
'artist': "Not Playing",
'album': "Not Playing",
'image': "Not Playing"
}
else:
return { 'name': "Error",
'artist': "Error",
'album': "Error",
'image': "Error"
}
@app.route('/control', methods=['POST'])
def control():
global access_token
user_headers = {
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json"
}
if request.form.get('command') == "pause":
requests.put("https://api.spotify.com/v1/me/player/pause", headers=user_headers)
elif request.form.get('command') == "play":
requests.put("https://api.spotify.com/v1/me/player/play", headers=user_headers)
elif request.form.get('command') == "next":
requests.post("https://api.spotify.com/v1/me/player/next", headers=user_headers)
elif request.form.get('command') == "previous":
requests.post("https://api.spotify.com/v1/me/player/previous", headers=user_headers)
elif request.form.get('command') == "restart":
requests.put("https://api.spotify.com/v1/me/player/seek?position_ms=0", headers=user_headers)
elif request.form.get('command') == "like":
requests.put("https://api.spotify.com/v1/me/tracks?ids=" + request.form.get('id'), headers=user_headers)
elif request.form.get('command') == "unlike":
requests.delete("https://api.spotify.com/v1/me/tracks?ids=" + request.form.get('id'), headers=user_headers)
print(request.form.get('command'))
return ('', 204)
if __name__ == '__main__':
app.run(port=8888, debug=True)

158
templates/webapp.html Normal file
View File

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Updating Value</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
.progress-container {
width: 100%;
background-color: #ddd;
}
.progress-bar {
width: 0%;
height: 4px;
background-color: #d734e9;
text-align: center;
line-height: 4px;
color: white;
}
.song-text {
font-size: 20px;
font-weight: bold;
text-align: center;
padding-left: 20px;
padding-right: 20px;
}
.artist-text {
font-size: 15px;
text-align: center;
padding-left: 20px;
padding-right: 20px;
}
.middle {
text-align: center;
display: inline-block;
vertical-align: top;
padding-left: 20px;
padding-right: 20px;
}
</style>
</head>
<body>
<img id="image" src="{{ song_info['image'] }}" alt="Song Image" type="image/jpeg" style="max-width: 300px; max-height: 300px;">
<div class="middle">
<p id="name" class="song-text">{{ song_info['name'] }}</p>
<p id="artist" class="artist-text">{{ song_info['artist'] }}</p>
<!-- <p id="album" class="song-info">{{ song_info['album'] }}</p>
<p id="progress_ms">{{ song_info['progress_ms'] }}</p>
<p id="duration_ms">{{ song_info['duration_ms'] }}</p> -->
<p>
<button id="playpause">Play/Pause</button>
<button id="previous">Previous</button>
<button id="next">Next</button>
<button id="like">Like/Unlike</button>
</p>
</div>
<div class="progress-container">
<div id="progress_bar" class="progress-bar"></div>
</div>
<script>
var is_playing = false
var id = ''
// Function to update the values every second
function updateValues() {
$.ajax({
url: '/appdata',
success: function(data) {
$('#name').text(data['name']);
$('#artist').text(data['artist']);
$('#album').text(data['album']);
$('#image').attr('src', data['image']);
$('#is_playing').text(data['is_playing']);
$('#progress_ms').text(data['progress_ms']);
$('#duration_ms').text(data['duration_ms']);
// $('#progress_min').text(Math.floor(data['progress_ms'] / (1000 * 60)) % 60);
// $('#progress_sec').text(Math.floor((data['progress_ms'] / 1000) % 60));
// $('#duration_min').text(Math.floor(data['duration_ms'] / (1000 * 60) % 60));
// $('#duration_sec').text(Math.floor(data['duration_ms'] / 1000) % 60);
$('#is_liked').text(data['is_liked']);
id = data['id'];
is_playing = data['is_playing'];
is_liked = data['is_liked'];
document.getElementById('progress_bar').style.width = (data['progress_ms'] / data['duration_ms']) * 100 + '%';
// document.getElementById('progress_bar').textContent = (Math.floor(data['progress_ms'] / (1000 * 60)) % 60) + ':' + (Math.floor((data['progress_ms'] / 1000) % 60)).toString().padStart(2, '0');
if (is_playing) {
// Change button to play icon
document.getElementById('playpause').textContent = 'Pause';
} else {
// Change button to pause icon
document.getElementById('playpause').textContent = 'Play';
}
if (is_liked) {
// Change button to unlike text
document.getElementById('like').textContent = 'Unlike';
} else {
// Change button to like text
document.getElementById('like').textContent = 'Like';
}
}
});
}
// Update values every second
setInterval(updateValues, 3000);
// BUTTONS
$('#playpause').click(function() {
$.ajax({
type: 'POST',
url: '/control',
data: {command: (is_playing ? 'pause' : 'play')}, // Predefined command
success: function(response) {
}
});
});
$('#pause').click(function() {
$.ajax({
type: 'POST',
url: '/control',
data: {command: 'pause'}, // Predefined command
success: function(response) {
}
});
});
$('#next').click(function() {
$.ajax({
type: 'POST',
url: '/control',
data: {command: 'next'}, // Predefined command
success: function(response) {
}
});
});
$('#previous').click(function() {
$.ajax({
type: 'POST',
url: '/control',
data: {command: (progress_ms < 5000) ? 'restart' : 'previous'},
success: function(response) {
}
});
});
$('#like').click(function() {
$.ajax({
type: 'POST',
url: '/control',
data: {command: (is_liked ? 'unlike' : 'like'), id: id},
success: function(response) {
}
});
});
</script>
</body>
</html>

12
todo.txt Normal file
View File

@@ -0,0 +1,12 @@
song name is successfully on the website.
next task:
have this update every 1 or 2 seconds.
04-18-24:
make it so when the js app calls the /appdata endpoint it sends the song id,
ONLY if the song id is different then the fetched one should the endpoint
provide updated song info, otherwise have it return just song progress ONLY.
include color calculation, probably in python then give color value
to js app, but maybe have js app do it from album art, explore which is better.

19
yamlcon.py Normal file
View File

@@ -0,0 +1,19 @@
import yaml
def load(path):
with open(path, 'r') as file:
try:
data = yaml.safe_load(file)
return data
except yaml.YAMLError as e:
print(f"Error reading YAML file {path}: {e}")
return None
def save(path, data):
with open(path, 'w') as file:
try:
yaml.dump(data, file)
return True
except yaml.YAMLError as e:
print(f"Error writing to YAML file {path}: {e}")
return None