mirror of
https://github.com/lnbits/lnbits.git
synced 2025-09-19 20:14:59 +02:00
Added jukebox page
This commit is contained in:
@@ -332,6 +332,11 @@ new Vue({
|
||||
},
|
||||
callAuthorizationApi(body) {
|
||||
self = this
|
||||
console.log(btoa(
|
||||
this.jukeboxDialog.data.sp_user +
|
||||
':' +
|
||||
this.jukeboxDialog.data.sp_secret
|
||||
))
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open('POST', 'https://accounts.spotify.com/api/token', true)
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
|
@@ -1,24 +1,104 @@
|
||||
{% extends "print.html" %} {% block page %} {% raw %}
|
||||
<div class="row justify-center">
|
||||
<div v-for="item in items" class="q-my-sm q-mx-lg">
|
||||
<div class="text-center q-ma-none q-mb-sm">{{ item.name }}</div>
|
||||
<qrcode :value="item.lnurl" :options="{margin: 0, width: 250}"></qrcode>
|
||||
<div class="text-center q-ma-none q-mt-sm">{{ item.price }}</div>
|
||||
{% extends "public.html" %} {% block page %} {% raw %}
|
||||
<div class="row q-col-gutter-md justify-center">
|
||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4">
|
||||
<q-card class="q-pa-lg">
|
||||
<q-card-section class="q-pa-none">
|
||||
<p style="font-size: 22px">Currently playing</p>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<img style="width: 100px" :src="currentPlaylist[0].image" />
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<strong style="font-size: 20px"
|
||||
>{{ currentPlaylist[0].name }}</strong
|
||||
><br />
|
||||
<strong style="font-size: 15px"
|
||||
>{{ currentPlaylist[0].artist }}</strong
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<q-card class="q-mt-lg">
|
||||
<q-card-section>
|
||||
<p style="font-size: 22px">Pick a song</p>
|
||||
<q-select
|
||||
outlined
|
||||
v-model="model"
|
||||
:options="playlists"
|
||||
label="playlists"
|
||||
></q-select>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
<q-virtual-scroll
|
||||
style="max-height: 300px"
|
||||
:items="currentPlaylist"
|
||||
separator
|
||||
>
|
||||
<template v-slot="{ item, index }">
|
||||
<q-item :key="index" dense clickable v-ripple>
|
||||
<q-item-section>
|
||||
<q-item-label>
|
||||
{{ item.name }} - ({{ item.artist }})
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-virtual-scroll>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4 q-gutter-y-md">
|
||||
<q-card class="q-pa-lg">
|
||||
<q-card-section class="q-pa-none">
|
||||
<p style="font-size: 21px">Queued</p>
|
||||
<br />
|
||||
<q-list bordered separator>
|
||||
<q-item
|
||||
bordered
|
||||
v-for="song in queued"
|
||||
:key="song.id"
|
||||
clickable
|
||||
v-ripple
|
||||
>
|
||||
<q-item-section prepend>
|
||||
<img style="width: 50px" :src="song.image" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>{{ song.name }} ({{ song.artist }})</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
{% endraw %} {% endblock %} {% block scripts %}
|
||||
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||
<style></style>
|
||||
<script>
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
created: function () {
|
||||
window.print()
|
||||
},
|
||||
data: function () {
|
||||
mixins: [windowMixin],
|
||||
data() {
|
||||
return {
|
||||
items: JSON.parse('{{items | tojson}}')
|
||||
currentPlaylist: JSON.parse('{{ firstPlaylist[0] | tojson }}'),
|
||||
playlists: JSON.parse('{{ playlists | tojson }}'),
|
||||
heavyList: [],
|
||||
queued: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
created() {
|
||||
this.queued[0] = this.currentPlaylist[2]
|
||||
this.queued[1] = this.currentPlaylist[5]
|
||||
this.queued[2] = this.currentPlaylist[6]
|
||||
this.queued[3] = this.currentPlaylist[7]
|
||||
console.log(this.currentPlaylist)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@@ -10,6 +10,9 @@ from lnbits.core.crud import get_standalone_payment
|
||||
from . import jukebox_ext
|
||||
from .crud import get_jukebox
|
||||
from urllib.parse import unquote
|
||||
from .views_api import (
|
||||
api_get_jukebox_songs,
|
||||
)
|
||||
|
||||
|
||||
@jukebox_ext.route("/")
|
||||
@@ -22,5 +25,15 @@ async def index():
|
||||
@jukebox_ext.route("/<juke_id>")
|
||||
async def print_qr_codes(juke_id):
|
||||
jukebox = await get_jukebox(juke_id)
|
||||
if not jukebox:
|
||||
return "error"
|
||||
firstPlaylist = await api_get_jukebox_songs(
|
||||
juke_id, jukebox.sp_playlists.split(",")[0].split("-")[1]
|
||||
)
|
||||
print(firstPlaylist)
|
||||
|
||||
return await render_template("jukebox/jukebox.html", jukebox=jukebox)
|
||||
return await render_template(
|
||||
"jukebox/jukebox.html",
|
||||
playlists=jukebox.sp_playlists.split(","),
|
||||
firstPlaylist=firstPlaylist,
|
||||
)
|
||||
|
@@ -109,42 +109,49 @@ async def api_delete_item(juke_id):
|
||||
################JUKEBOX ENDPOINTS##################
|
||||
|
||||
|
||||
@jukebox_ext.route("/api/v1/jukebox/jb/<sp_id>", methods=["GET"])
|
||||
async def api_get_jukebox_songs(sp_id):
|
||||
@jukebox_ext.route("/api/v1/jukebox/jb/<sp_id>/<sp_playlist>", methods=["GET"])
|
||||
async def api_get_jukebox_songs(sp_id, sp_playlist):
|
||||
jukebox = await get_jukebox(sp_id)
|
||||
print(jukebox.sp_playlists.split(",")[0].split("-")[1])
|
||||
tracks = []
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
r = await client.get(
|
||||
"https://api.spotify.com/v1/playlists/"
|
||||
+ jukebox.sp_playlists.split(",")[0].split("-")[1]
|
||||
+ "/tracks",
|
||||
"https://api.spotify.com/v1/playlists/" + sp_playlist + "/tracks",
|
||||
timeout=40,
|
||||
headers={"Authorization": "Bearer " + jukebox.sp_access_token},
|
||||
)
|
||||
if r.json()["error"]["status"] == 401:
|
||||
token = await api_get_token(sp_id)
|
||||
if token['error'] == 'invalid_client':
|
||||
print("invalid")
|
||||
return ""
|
||||
else:
|
||||
return await api_get_jukebox_songs(sp_id)
|
||||
print(r.json()["items"])
|
||||
resp = r.json()["items"][0]
|
||||
print("id: " + resp["track"]["id"])
|
||||
print("name: " + resp["track"]["name"])
|
||||
print("album: " + resp["track"]["album"]["name"])
|
||||
print("artist: " + resp["track"]["artists"][0]["name"])
|
||||
print("image: " + resp["track"]["album"]["images"][0])
|
||||
if "items" not in r.json():
|
||||
if r.json()["error"]["status"] == 401:
|
||||
token = await api_get_token(sp_id)
|
||||
if token == False:
|
||||
print("invalid")
|
||||
return False
|
||||
else:
|
||||
return await api_get_jukebox_songs(sp_id, sp_playlist)
|
||||
for item in r.json()["items"]:
|
||||
tracks.append(
|
||||
{
|
||||
"id": item["track"]["id"],
|
||||
"name": item["track"]["name"],
|
||||
"album": item["track"]["album"]["name"],
|
||||
"artist": item["track"]["artists"][0]["name"],
|
||||
"image": item["track"]["album"]["images"][0]["url"],
|
||||
}
|
||||
)
|
||||
except AssertionError:
|
||||
something = None
|
||||
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
||||
print(jsonify(tracks))
|
||||
return tracks, HTTPStatus.OK
|
||||
|
||||
|
||||
@jukebox_ext.route("/api/v1/jukebox/jb/<sp_id>", methods=["GET"])
|
||||
async def api_get_token(sp_id):
|
||||
jukebox = await get_jukebox(sp_id)
|
||||
print(jukebox.sp_playlists.split(",")[0].split("-")[1])
|
||||
print(
|
||||
"Authorization: Bearer "
|
||||
+ base64.b64encode(
|
||||
str(jukebox.sp_user + ":" + jukebox.sp_secret).encode("ascii")
|
||||
).decode("ascii")
|
||||
)
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
r = await client.post(
|
||||
@@ -156,24 +163,20 @@ async def api_get_token(sp_id):
|
||||
"client_id": jukebox.sp_user,
|
||||
},
|
||||
headers={
|
||||
"Authorization": "Bearer "
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Authorization": "Basic "
|
||||
+ base64.b64encode(
|
||||
(jukebox.sp_user + ":" + jukebox.sp_refresh_token).encode(
|
||||
"utf-8"
|
||||
)
|
||||
str(jukebox.sp_user + ":" + jukebox.sp_secret).encode("ascii")
|
||||
).decode("ascii"),
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
)
|
||||
print(r)
|
||||
print(r.json())
|
||||
if r.json()['error'] == 'invalid_client':
|
||||
return r.json()
|
||||
#await update_jukebox(
|
||||
# juke_id=sp_id,
|
||||
# sp_access_token=r.json()["access_token"],
|
||||
# sp_refresh_token=r.json()["refresh_token"],
|
||||
#)
|
||||
if "access_token" not in r.json():
|
||||
return False
|
||||
else:
|
||||
await update_jukebox(
|
||||
juke_id=sp_id, sp_access_token=r.json()["access_token"]
|
||||
)
|
||||
except AssertionError:
|
||||
something = None
|
||||
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
||||
return True
|
||||
|
Reference in New Issue
Block a user