jukebox working(ish)

This commit is contained in:
Ben Arc 2021-10-12 18:54:40 +01:00
parent a33b24d6dc
commit cace0892f2
8 changed files with 149 additions and 177 deletions

View file

@ -1,22 +1,13 @@
from typing import List, Optional
from . import db
from .models import Jukebox, JukeboxPayment
from .models import Jukebox, JukeboxPayment, CreateJukeLinkData
from lnbits.helpers import urlsafe_short_hash
async def create_jukebox(
inkey: str,
user: str,
wallet: str,
title: str,
price: int,
sp_user: str,
sp_secret: str,
sp_access_token: Optional[str] = "",
sp_refresh_token: Optional[str] = "",
sp_device: Optional[str] = "",
sp_playlists: Optional[str] = "",
data: CreateJukeLinkData,
inkey: Optional[str] = "",
) -> Jukebox:
juke_id = urlsafe_short_hash()
result = await db.execute(
@ -26,16 +17,16 @@ async def create_jukebox(
""",
(
juke_id,
user,
title,
wallet,
sp_user,
sp_secret,
sp_access_token,
sp_refresh_token,
sp_device,
sp_playlists,
int(price),
data.user,
data.title,
data.wallet,
data.sp_user,
data.sp_secret,
data.sp_access_token,
data.sp_refresh_token,
data.sp_device,
data.sp_playlists,
data.price,
0,
),
)
@ -44,11 +35,15 @@ async def create_jukebox(
return jukebox
async def update_jukebox(juke_id: str, **kwargs) -> Optional[Jukebox]:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
)
async def update_jukebox(
data: CreateJukeLinkData, juke_id: Optional[str] = ""
) -> Optional[Jukebox]:
q = ", ".join([f"{field[0]} = ?" for field in data])
items = [f"{field[1]}" for field in data]
items.append(juke_id)
print(q)
print(items)
await db.execute(f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (items))
row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
return Jukebox(**row) if row else None
@ -66,10 +61,13 @@ async def get_jukebox_by_user(user: str) -> Optional[Jukebox]:
async def get_jukeboxs(user: str) -> List[Jukebox]:
rows = await db.fetchall("SELECT * FROM jukebox.jukebox WHERE user = ?", (user,))
for row in rows:
if row.sp_playlists == "":
if row.sp_playlists == None:
print("cunt")
await delete_jukebox(row.id)
rows = await db.fetchall("SELECT * FROM jukebox.jukebox WHERE user = ?", (user,))
return [Jukebox.from_row(row) for row in rows]
return [Jukebox(**row) for row in rows]
async def delete_jukebox(juke_id: str):

View file

@ -2,6 +2,8 @@ from typing import NamedTuple
from sqlite3 import Row
from fastapi.param_functions import Query
from pydantic.main import BaseModel
from pydantic import BaseModel
from typing import Optional
class CreateJukeLinkData(BaseModel):
@ -17,32 +19,24 @@ class CreateJukeLinkData(BaseModel):
price: str = Query(None)
class Jukebox(NamedTuple):
id: str
user: str
title: str
wallet: str
inkey: str
sp_user: str
sp_secret: str
sp_access_token: str
sp_refresh_token: str
sp_device: str
sp_playlists: str
price: int
profit: int
@classmethod
def from_row(cls, row: Row) -> "Jukebox":
return cls(**dict(row))
class Jukebox(BaseModel):
id: Optional[str]
user: Optional[str]
title: Optional[str]
wallet: Optional[str]
inkey: Optional[str]
sp_user: Optional[str]
sp_secret: Optional[str]
sp_access_token: Optional[str]
sp_refresh_token: Optional[str]
sp_device: Optional[str]
sp_playlists: Optional[str]
price: Optional[int]
profit: Optional[int]
class JukeboxPayment(NamedTuple):
class JukeboxPayment(BaseModel):
payment_hash: str
juke_id: str
song_id: str
paid: bool
@classmethod
def from_row(cls, row: Row) -> "JukeboxPayment":
return cls(**dict(row))

View file

@ -3,17 +3,25 @@
Vue.component(VueQrcode.name, VueQrcode)
var mapJukebox = obj => {
obj._data = _.clone(obj)
obj.sp_id = obj.id
obj.device = obj.sp_device.split('-')[0]
playlists = obj.sp_playlists.split(',')
var i
playlistsar = []
for (i = 0; i < playlists.length; i++) {
playlistsar.push(playlists[i].split('-')[0])
if(obj.sp_device){
obj._data = _.clone(obj)
obj.sp_id = obj._data.id
obj.device = obj._data.sp_device.split('-')[0]
playlists = obj._data.sp_playlists.split(',')
var i
playlistsar = []
for (i = 0; i < playlists.length; i++) {
playlistsar.push(playlists[i].split('-')[0])
}
obj.playlist = playlistsar.join()
console.log(obj)
return obj
}
obj.playlist = playlistsar.join()
return obj
else {
return
}
}
new Vue({
@ -79,13 +87,14 @@ new Vue({
var link = _.findWhere(this.JukeboxLinks, {id: linkId})
this.qrCodeDialog.data = _.clone(link)
console.log(this.qrCodeDialog.data)
this.qrCodeDialog.data.url =
window.location.protocol + '//' + window.location.host
this.qrCodeDialog.show = true
},
getJukeboxes() {
self = this
LNbits.api
.request(
'GET',
@ -93,10 +102,11 @@ new Vue({
self.g.user.wallets[0].adminkey
)
.then(function (response) {
self.JukeboxLinks = response.data.map(mapJukebox)
})
.catch(err => {
LNbits.utils.notifyApiError(err)
self.JukeboxLinks = response.data.map(function (obj) {
return mapJukebox(obj)
})
console.log(self.JukeboxLinks)
})
},
deleteJukebox(juke_id) {
@ -125,7 +135,6 @@ new Vue({
self = this
var link = _.findWhere(self.JukeboxLinks, {id: linkId})
self.jukeboxDialog.data = _.clone(link._data)
console.log(this.jukeboxDialog.data.sp_access_token)
self.refreshDevices()
self.refreshPlaylists()
@ -145,7 +154,7 @@ new Vue({
submitSpotifyKeys() {
self = this
self.jukeboxDialog.data.user = self.g.user.id
LNbits.api
.request(
'POST',
@ -193,9 +202,6 @@ new Vue({
if (self.jukeboxDialog.data.sp_access_token) {
self.refreshPlaylists()
self.refreshDevices()
console.log('this.devices')
console.log(self.devices)
console.log('this.devices')
setTimeout(function () {
if (self.devices.length < 1 || self.playlists.length < 1) {
self.$q.notify({
@ -259,16 +265,14 @@ new Vue({
},
updateDB() {
self = this
console.log(self.jukeboxDialog.data)
LNbits.api
.request(
'PUT',
'/jukebox/api/v1/jukebox/' + this.jukeboxDialog.data.sp_id,
'/jukebox/api/v1/jukebox/' + self.jukeboxDialog.data.sp_id,
self.g.user.wallets[0].adminkey,
self.jukeboxDialog.data
)
.then(function (response) {
console.log(response.data)
if (
self.jukeboxDialog.data.sp_playlists &&
self.jukeboxDialog.data.sp_devices
@ -307,7 +311,6 @@ new Vue({
responseObj.items[i].name + '-' + responseObj.items[i].id
)
}
console.log(self.playlists)
}
},
refreshPlaylists() {
@ -372,13 +375,6 @@ new Vue({
},
callAuthorizationApi(body) {
self = this
console.log(
btoa(
self.jukeboxDialog.data.sp_user +
':' +
self.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')
@ -403,7 +399,6 @@ new Vue({
}
},
created() {
console.log(this.g.user.wallets[0])
var getJukeboxes = this.getJukeboxes
getJukeboxes()
this.selectedWallet = this.g.user.wallets[0]

View file

@ -1,14 +0,0 @@
/* globals Quasar, Vue, _, VueQrcode, windowMixin, LNbits, LOCALE */
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
mixins: [windowMixin],
data() {
return {}
},
computed: {},
methods: {},
created() {}
})

View file

@ -1,4 +1,4 @@
{% extends "public.html" %} {% block page %} {% raw %}
{% extends "public.html" %} {% block page %}
<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">
@ -9,10 +9,12 @@
<img style="width: 100px" :src="currentPlay.image" />
</div>
<div class="col-8">
{% raw %}
<strong style="font-size: 20px">{{ currentPlay.name }}</strong
><br />
<strong style="font-size: 15px">{{ currentPlay.artist }}</strong>
</div>
{% endraw %}
</div>
</q-card-section>
</q-card>
@ -46,7 +48,7 @@
>
<q-item-section>
<q-item-label>
{{ item.name }} - ({{ item.artist }})
{% raw %} {{ item.name }} - ({{ item.artist }}){% endraw %}
</q-item-label>
</q-item-section>
</q-item>
@ -54,49 +56,48 @@
</q-virtual-scroll>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="receive.dialogues.first" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section class="q-pa-none">
<div class="row">
<div class="col-4">
<img style="width: 100px" :src="receive.image" />
</div>
<div class="col-8">
<strong style="font-size: 20px">{{ receive.name }}</strong><br />
<strong style="font-size: 15px">{{ receive.artist }}</strong>
<q-dialog v-model="receive.dialogues.first" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section class="q-pa-none">
<div class="row">
<div class="col-4">
<img style="width: 100px" :src="receive.image" />
</div>
<div class="col-8">
{% raw %}
<strong style="font-size: 20px">{{ receive.name }}</strong><br />
<strong style="font-size: 15px">{{ receive.artist }}</strong>
</div>
</div>
</q-card-section>
<br />
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="getInvoice(receive.id)"
>Play for {% endraw %}{{ price }}sats
</q-btn>
</div>
</q-card-section>
<br />
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="getInvoice(receive.id)"
>Play for {% endraw %}{{ price }}{% raw %} sats
</q-btn>
</div>
</q-card>
</q-dialog>
<q-dialog v-model="receive.dialogues.second" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
<qrcode
:value="'lightning:' + receive.paymentReq"
:options="{width: 800}"
class="rounded-borders"
></qrcode>
</q-responsive>
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="copyText(receive.paymentReq)"
>Copy invoice</q-btn
>
</div>
</q-card>
</q-dialog>
</q-card>
</q-dialog>
<q-dialog v-model="receive.dialogues.second" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
<qrcode
:value="'lightning:' + receive.paymentReq"
:options="{width: 800}"
class="rounded-borders"
></qrcode>
</q-responsive>
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="copyText(receive.paymentReq)"
>Copy invoice</q-btn
>
</div>
</q-card>
</q-dialog>
</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>
{% endblock %} {% block scripts %}
<script>
Vue.component(VueQrcode.name, VueQrcode)

View file

@ -31,15 +31,13 @@ async def connect_to_jukebox(request: Request, juke_id):
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Jukebox does not exist."
)
deviceCheck = await api_get_jukebox_device_check(juke_id)
devices = json.loads(deviceCheck[0].text)
deviceConnected = False
devices = await api_get_jukebox_device_check(juke_id)
for device in devices["devices"]:
if device["id"] == jukebox.sp_device.split("-")[1]:
deviceConnected = True
if deviceConnected:
return jukebox_renderer().TemplateResponse(
"jukebox/display.html",
"jukebox/jukebox.html",
{
"request": request,
"playlists": jukebox.sp_playlists.split(","),

View file

@ -38,11 +38,10 @@ async def api_get_jukeboxs(
):
wallet_user = wallet.wallet.user
jukeboxs = [jukebox.dict() for jukebox in await get_jukeboxs(wallet_user)]
print(jukeboxs)
try:
return [
{**jukebox.dict(), "jukebox": jukebox.jukebox(req)}
for jukebox in await get_jukeboxs(wallet_user)
]
return jukeboxs
except:
raise HTTPException(
@ -54,7 +53,7 @@ async def api_get_jukeboxs(
##################SPOTIFY AUTH#####################
@jukebox_ext.get("/api/v1/jukebox/spotify/cb/<juke_id>")
@jukebox_ext.get("/api/v1/jukebox/spotify/cb/{juke_id}", response_class=HTMLResponse)
async def api_check_credentials_callbac(
juke_id: str = Query(None),
code: str = Query(None),
@ -69,19 +68,12 @@ async def api_check_credentials_callbac(
except:
raise HTTPException(detail="No Jukebox", status_code=HTTPStatus.FORBIDDEN)
if code:
sp_code = code
jukebox = await update_jukebox(
juke_id=juke_id, sp_secret=jukebox.sp_secret, sp_access_token=sp_code
)
jukebox.sp_access_token = code
jukebox = await update_jukebox(jukebox, juke_id=juke_id)
if access_token:
sp_access_token = access_token
sp_refresh_token = refresh_token
jukebox = await update_jukebox(
juke_id=juke_id,
sp_secret=jukebox.sp_secret,
sp_access_token=sp_access_token,
sp_refresh_token=sp_refresh_token,
)
jukebox.sp_access_token = access_token
jukebox.sp_refresh_token = refresh_token
jukebox = await update_jukebox(jukebox, juke_id=juke_id)
return "<h1>Success!</h1><h2>You can close this window</h2>"
@ -103,11 +95,10 @@ async def api_create_update_jukebox(
wallet: WalletTypeInfo = Depends(get_key_type),
):
if juke_id:
jukebox = await update_jukebox(juke_id=juke_id, inkey=g.wallet.inkey, **g.data)
jukebox = await update_jukebox(data, juke_id=juke_id)
else:
jukebox = await create_jukebox(inkey=g.wallet.inkey, **g.data)
return jukebox.dict()
jukebox = await create_jukebox(data, inkey=wallet.wallet.inkey)
return jukebox
@jukebox_ext.delete("/api/v1/jukebox/{juke_id}")
@ -117,7 +108,7 @@ async def api_delete_item(
):
await delete_jukebox(juke_id)
try:
return [{**jukebox.dict()} for jukebox in await get_jukeboxs(g.wallet.user)]
return [{**jukebox} for jukebox in await get_jukeboxs(wallet.wallet.user)]
except:
raise HTTPException(
status_code=HTTPStatus.NO_CONTENT,
@ -212,9 +203,8 @@ async def api_get_token(juke_id=None):
if "access_token" not in r.json():
return False
else:
await update_jukebox(
juke_id=juke_id, sp_access_token=r.json()["access_token"]
)
jukebox.sp_access_token = r.json()["access_token"]
await update_jukebox(jukebox, juke_id=juke_id)
except:
something = None
return True
@ -241,9 +231,8 @@ async def api_get_jukebox_device_check(
timeout=40,
headers={"Authorization": "Bearer " + jukebox.sp_access_token},
)
if rDevice.status_code == 204 or rDevice.status_code == 200:
return rDevice
return json.loads(rDevice.text)
elif rDevice.status_code == 401 or rDevice.status_code == 403:
token = await api_get_token(juke_id)
if token == False:
@ -275,14 +264,14 @@ async def api_get_jukebox_invoice(
):
try:
jukebox = await get_jukebox(juke_id)
print(jukebox)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No jukebox",
)
try:
deviceCheck = await api_get_jukebox_device_check(juke_id)
devices = json.loads(deviceCheck[0].text)
devices = await api_get_jukebox_device_check(juke_id)
deviceConnected = False
for device in devices["devices"]:
if device["id"] == jukebox.sp_device.split("-")[1]:

View file

@ -133,6 +133,15 @@
</div>
</q-card>
</q-dialog>
<q-dialog v-model="complete.show" position="top">
<q-icon
name="check_circle"
transition-show="fade"
class="text-green"
style="font-size: 20em"
></q-icon
></q-dialog>
</q-page>
</q-page-container>
{% endblock %} {% block styles %}
@ -172,6 +181,9 @@
},
urlDialog: {
show: false
},
complete: {
show: false
}
}
},
@ -234,11 +246,10 @@
dialog.dismissMsg()
dialog.show = false
self.$q.notify({
type: 'positive',
message: self.fsat + ' sat received!',
icon: null
})
self.complete.show = true
setTimeout(function () {
self.complete.show = false
}, 5000)
}
})
}, 3000)