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