From ad989ad50b708c3c27f18bd3bcea9c594466a124 Mon Sep 17 00:00:00 2001 From: brandon Date: Thu, 25 Apr 2024 16:11:17 -0700 Subject: [PATCH] auto syncing lyrics, canvas replaces album art (if available), fixed layout (margins, padding, etc.), refactored some code --- .DS_Store | Bin 0 -> 6148 bytes .syncedlyrics/musixmatch_token.json | 1 + __pycache__/_canvas.cpython-312.pyc | Bin 0 -> 2361 bytes _canvas.py | 57 ++++++++ code | 2 +- protos/__pycache__/canvas_pb2.cpython-310.pyc | Bin 0 -> 1835 bytes protos/__pycache__/canvas_pb2.cpython-312.pyc | Bin 0 -> 2413 bytes protos/canvas_pb2.py | 37 +++++ spotifycontroller.py | 46 +++++-- templates/webapp.html | 128 ++++++++++++++---- todo.txt | 7 +- 11 files changed, 236 insertions(+), 42 deletions(-) create mode 100644 .DS_Store create mode 100644 .syncedlyrics/musixmatch_token.json create mode 100644 __pycache__/_canvas.cpython-312.pyc create mode 100755 _canvas.py create mode 100755 protos/__pycache__/canvas_pb2.cpython-310.pyc create mode 100644 protos/__pycache__/canvas_pb2.cpython-312.pyc create mode 100755 protos/canvas_pb2.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8c47c95f2d79b0a757cafad4c2addc2a4e6bf64a GIT binary patch literal 6148 zcmeHK&2G~`5S~rl)@g*4Ly>wxvc$D2p+P{!#e{O;iV++D1-mw_h3k!MhqQ_!`3&#C zD{$pWco$Ca&F)sR-Ecz)p&e`X+n<^J`NrP$5|Nn9}D6}LKbZrMhQa%T3YZ_zAE z%W5?GE_xfz=GL~m?GD_x!I@kHRani-Nm#t*(hDiG=qevZFVkc(9qvAnc@?I4QYeQs zPGIupRhq|gF_H5;E|qVf8{EFzpAPqy%klW(zIX5N!D`=I9vzO5AKzcC`tHr!cOE^T ze#p*q`AIQN0{f|w8wRi7Gcv6;eGG~`lldv?*=LtZu*2M>^j<}Z8Lbpw8D)rAfoLO! zZH^Uh&1m%Dlh8RVGsK$nU8Z>^O%E4V9A}CCE4t{uP2xw#eh0G)F+ygdzGhZEUR84p z6s`O*2FyF=@*ZHtGGH073^W)k=_8zy3gvD}cViS|e&8 zLc0RBt1wp#VQvmWyQAGV_O(XsPC|{0e$2?i+)#wMcnD9Klkhd#)G}ZhXfv>(i$gyD zpZ@;--*&Pk%YbFzzhXc*$HDOgOEPEcwZ-vS>p|Z^S=g@Ds9cbk<5*?*DBgoAL7T@J Wpl`6&h#H9f5l}SP#4_+#8TbXFdc`yV literal 0 HcmV?d00001 diff --git a/.syncedlyrics/musixmatch_token.json b/.syncedlyrics/musixmatch_token.json new file mode 100644 index 0000000..2818718 --- /dev/null +++ b/.syncedlyrics/musixmatch_token.json @@ -0,0 +1 @@ +{"token": "240425a54f5df91735a14b241dcfb1e5df28f942d95b9cad8ea6a4", "expiration_time": 1714086845} \ No newline at end of file diff --git a/__pycache__/_canvas.cpython-312.pyc b/__pycache__/_canvas.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2289500414aa9c2f54fd2c0fabd89328e3c69a40 GIT binary patch literal 2361 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 IQ8WMi54Oz&rT_o{ literal 0 HcmV?d00001 diff --git a/protos/__pycache__/canvas_pb2.cpython-312.pyc b/protos/__pycache__/canvas_pb2.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d276bc3e3779a3a3805740a830ab1cfd19d55b33 GIT binary patch literal 2413 zcmaJ?OK%!i6rKm>aWNQ=jYFKIc5rOKkwCKZa-JnNc2E=B0h}tR(rDmbJL3*BbY_T( zyFuMFQhz|DuDa-=RW|(zU9?iAZbC!l(MXY6RjTAgC0E&{o1U40!Etd%h&kVPzkANP zhjR}>qKxMF64Qcu&`V zlQ4-qhf`ROofX=>;Gm=5B#eEaV;zpB2)jFXbltJB|HC9jsh{Bh3G?HrMkV zIY8%duC5tq22yACrq+l|R6!?rx~?j^QdOksN@IJQsMA#ilk|ag?i6hzOth*h)^$Y% zG}1?WT~Q>9(weo3BJo(zg^HjNpnR2vC}E;%Kfv#ZhO2`7RM66P817V6sin2LqKn(j zw7s%d6{=52d=YuGsxE3e7es7B6=U=?oeXfp$Ri6i5@T!>Mqc451X5$Hh2wuRXgnwD zqTXD#g6tItxRy1-ts=h@mT2IJO$HY(rB8^Q9^@eH_~Rx}Sl1<9lzEL*6&Y)GBJAW} zH1c*O2r(k=W^1P|b(0-IzHLz=JgnN;au<NWd!-j$8|}pih9fkTm0M@3O4GJB4C2~N;BrS{X&t6F6_ht zHiF1b?d-@wi;F*_(b(H{`lvzRx5_~PD@)O>^Wup)4cOnU{p39II;+Qy?e>73p4|iU z1>~^{6hK4y2idh;gyO(_lwZkhL}+dlg=~|*zp=5IUtfzbTnvrBMe&8~`pU+G2+R2p zyOv*#c391*umSR@XIaP7K~47NFzJA-?o@Gberx_CX4?rjO4S zO8H{x-~&JhyhTO%+dP!^ZJ{CQkPGni zGdX^%nB$lEev~43SL(V5lf)c@i{Hz}B}b2v8jK^|963r68GlQ?hNDt%9%Z(`ftIPL zf{Yb8v!YZRHMlZbX0xe3QRL}ORoPL6nwBX#Lveapk#$v(B$&j>_emv{v3pR^tjP5IS*i|%|ts)!kA0CVAi;Dt(ANcjJKJY4s+SKbhVEbja%M%Eq>$iI7S7zyOit=1k6Hv>40Vdgj AM*si- literal 0 HcmV?d00001 diff --git a/protos/canvas_pb2.py b/protos/canvas_pb2.py new file mode 100755 index 0000000..5f93d98 --- /dev/null +++ b/protos/canvas_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: canvas.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x63\x61nvas.proto\x12\x17\x63om.spotify.canvazcache\"3\n\x06\x41rtist\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06\x61vatar\x18\x03 \x01(\t\"\xe6\x02\n\x14\x45ntityCanvazResponse\x12\x46\n\x08\x63\x61nvases\x18\x01 \x03(\x0b\x32\x34.com.spotify.canvazcache.EntityCanvazResponse.Canvaz\x12\x16\n\x0ettl_in_seconds\x18\x02 \x01(\x03\x1a\xed\x01\n\x06\x43\x61nvaz\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0f\n\x07\x66ile_id\x18\x03 \x01(\t\x12+\n\x04type\x18\x04 \x01(\x0e\x32\x1d.com.spotify.canvazcache.Type\x12\x12\n\nentity_uri\x18\x05 \x01(\t\x12/\n\x06\x61rtist\x18\x06 \x01(\x0b\x32\x1f.com.spotify.canvazcache.Artist\x12\x10\n\x08\x65xplicit\x18\x07 \x01(\x08\x12\x13\n\x0buploaded_by\x18\x08 \x01(\t\x12\x0c\n\x04\x65tag\x18\t \x01(\t\x12\x12\n\ncanvas_uri\x18\x0b \x01(\t\"\x88\x01\n\x13\x45ntityCanvazRequest\x12\x45\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x33.com.spotify.canvazcache.EntityCanvazRequest.Entity\x1a*\n\x06\x45ntity\x12\x12\n\nentity_uri\x18\x01 \x01(\t\x12\x0c\n\x04\x65tag\x18\x02 \x01(\t*R\n\x04Type\x12\t\n\x05IMAGE\x10\x00\x12\t\n\x05VIDEO\x10\x01\x12\x11\n\rVIDEO_LOOPING\x10\x02\x12\x18\n\x14VIDEO_LOOPING_RANDOM\x10\x03\x12\x07\n\x03GIF\x10\x04\x42\x16\n\x12\x63om.spotify.canvazH\x02\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'canvas_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\022com.spotify.canvazH\002' + _globals['_TYPE']._serialized_start=594 + _globals['_TYPE']._serialized_end=676 + _globals['_ARTIST']._serialized_start=41 + _globals['_ARTIST']._serialized_end=92 + _globals['_ENTITYCANVAZRESPONSE']._serialized_start=95 + _globals['_ENTITYCANVAZRESPONSE']._serialized_end=453 + _globals['_ENTITYCANVAZRESPONSE_CANVAZ']._serialized_start=216 + _globals['_ENTITYCANVAZRESPONSE_CANVAZ']._serialized_end=453 + _globals['_ENTITYCANVAZREQUEST']._serialized_start=456 + _globals['_ENTITYCANVAZREQUEST']._serialized_end=592 + _globals['_ENTITYCANVAZREQUEST_ENTITY']._serialized_start=550 + _globals['_ENTITYCANVAZREQUEST_ENTITY']._serialized_end=592 +# @@protoc_insertion_point(module_scope) diff --git a/spotifycontroller.py b/spotifycontroller.py index 7591d69..8017a60 100644 --- a/spotifycontroller.py +++ b/spotifycontroller.py @@ -1,11 +1,11 @@ from flask import Flask, render_template, request, url_for, redirect, send_from_directory -import time import requests from urllib.parse import urlencode -import webbrowser import base64 -import json import os +import _canvas as SpotifyCanvas +import time +import syncedlyrics app = Flask(__name__) @@ -88,6 +88,7 @@ def callback(): @app.route('/appdata') def appdata(): global access_token + stime = time.time() user_headers = { "Authorization": "Bearer " + access_token, "Content-Type": "application/json" @@ -96,9 +97,12 @@ def appdata(): currently_playing = requests.get("https://api.spotify.com/v1/me/player/currently-playing", headers=user_headers) if currently_playing.content: if currently_playing.json()["is_playing"] is True and currently_playing.json()["item"]["id"] == request.args.get('id'): - return { 'progress_ms': currently_playing.json()["progress_ms"] + print("QUICK" + str(time.time() - stime)) + return { 'progress_ms': currently_playing.json()["progress_ms"], } - elif currently_playing.json()["is_playing"] is True and currently_playing.json()["item"]["id"] != request.args.get('id'): + elif currently_playing.json()["is_playing"] is True and currently_playing.json()["item"]["id"] != request.args.get('id'): + + print("FULL" + str(time.time() - stime)) return { 'id': currently_playing.json()["item"]["id"], 'name': currently_playing.json()["item"]["name"], 'artist': currently_playing.json()["item"]["artists"][0]["name"], @@ -107,20 +111,19 @@ def appdata(): '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] + 'is_liked': requests.get("https://api.spotify.com/v1/me/tracks/contains?ids=" + currently_playing.json()["item"]["id"], headers=user_headers).json()[0], + 'canvas': False, + 'fetchlyrics': 'fetch' } elif currently_playing.json()["is_playing"] is False: - return { 'name': "Not Playing", - 'is_playing': False + return { 'is_playing': False } else: return { 'name': "Error", 'artist': "Error" } else: - return { 'name': "Not Playing", - 'image': "https://cdn.psychologytoday.com/sites/default/files/styles/article-inline-half-caption/public/field_blog_entry_images/2022-02/pause.png", - 'is_playing': False + return { 'is_playing': False } @app.route('/control', methods=['POST']) @@ -157,7 +160,26 @@ def control(): def icons(filename): return send_from_directory('icons', filename) - +@app.route('/canvas') +def canvas(): + print("CANVAS") + id = request.args.get('id') + try: + return { 'canvas_url': SpotifyCanvas.get_canvas_for_track(SpotifyCanvas.get_access_token(), id) } + except AttributeError: + return { 'canvas_url': None } + +@app.route('/lyrics') +def lyrics(): + name = request.args.get('name') + artist = request.args.get('artist') + if name and artist is not None: + full_lyrics = syncedlyrics.search("[" + name + "] [" + artist + "]") + else: + return { 'lyrics': '' } + if full_lyrics is None: + return { 'lyrics': "no lyrics" } + return { 'lyrics': full_lyrics } if __name__ == '__main__': app.run(port=8888, debug=True) \ No newline at end of file diff --git a/templates/webapp.html b/templates/webapp.html index 775743a..e099787 100644 --- a/templates/webapp.html +++ b/templates/webapp.html @@ -21,49 +21,72 @@ } .song-text { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - font-size: 20px; + font-size: 25px; font-weight: bold; text-align: center; - padding-left: 20px; - padding-right: 20px; + margin-bottom: 5px; } .artist-text { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - font-size: 15px; + font-size: 20px; text-align: center; - padding-left: 20px; - padding-right: 20px; + margin-top: 5px; } .middle { + flex: 1; text-align: center; display: inline-block; vertical-align: top; - padding-left: 20px; - padding-right: 20px; + } + .right { + height: 100%; + display: inline-block; + width: 33%; + } + .lyrics { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-size: 32px; + text-align: center; + } + .buttons { + display: initial; + padding-top: 10%; + } + .total { + display: flex; + align-items: center; } - Song Image -
-

{{ data['name'] }}

-

{{ data['artist'] }}

- -

- - - - -

-
-
-
+
+ Song Image + +
+

{{ data['name'] }}

+

{{ data['artist'] }}

+ +

+ + + + +

+
+
+

{{ data['lyric'] }}

+
+
+
+
+
+