Added update jukebox

This commit is contained in:
Ben Arc 2021-05-03 23:22:40 +01:00
parent 245a819f19
commit be234c349f
6 changed files with 260 additions and 155 deletions

View file

@ -20,8 +20,8 @@ async def create_jukebox(
juke_id = urlsafe_short_hash()
result = await db.execute(
"""
INSERT INTO jukebox (id, user, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO jukebox (id, user, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price, profit)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
juke_id,
@ -35,6 +35,7 @@ async def create_jukebox(
sp_device,
sp_playlists,
int(price),
0,
),
)
jukebox = await get_jukebox(juke_id)
@ -42,17 +43,17 @@ async def create_jukebox(
return jukebox
async def update_jukebox(id: str, **kwargs) -> Optional[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 SET {q} WHERE id = ?", (*kwargs.values(), id)
f"UPDATE jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
)
row = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (id,))
row = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (juke_id,))
return Jukebox(**row) if row else None
async def get_jukebox(id: str) -> Optional[Jukebox]:
row = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (id,))
async def get_jukebox(juke_id: str) -> Optional[Jukebox]:
row = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (juke_id,))
return Jukebox(**row) if row else None
@ -60,18 +61,20 @@ async def get_jukebox_by_user(user: str) -> Optional[Jukebox]:
row = await db.fetchone("SELECT * FROM jukebox WHERE sp_user = ?", (user,))
return Jukebox(**row) if row else None
async def get_jukeboxs(user: str) -> List[Jukebox]:
rows = await db.fetchall("SELECT * FROM jukebox WHERE user = ?", (user,))
for row in rows:
if not row.sp_playlists:
if row.sp_playlists == "":
await delete_jukebox(row.id)
rows.remove(row)
rows = await db.fetchall("SELECT * FROM jukebox WHERE user = ?", (user,))
return [Jukebox.from_row(row) for row in rows]
async def delete_jukebox(id: str):
async def delete_jukebox(juke_id: str):
await db.execute(
"""
DELETE FROM jukebox WHERE id = ?
""",
(id),
(juke_id),
)

View file

@ -15,7 +15,8 @@ async def m001_initial(db):
sp_refresh_token TEXT,
sp_device TEXT,
sp_playlists TEXT,
price INTEGER
price INTEGER,
profit INTEGER
);
"""
)

View file

@ -19,6 +19,7 @@ class Jukebox(NamedTuple):
sp_device: str
sp_playlists: str
price: int
profit: int
@classmethod
def from_row(cls, row: Row) -> "Jukebox":

View file

@ -4,13 +4,13 @@ Vue.component(VueQrcode.name, VueQrcode)
var mapJukebox = obj => {
obj._data = _.clone(obj)
obj.device = obj.sp_device.split("-")[0]
playlists = obj.sp_playlists.split(",")
var i;
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])
playlistsar.push(playlists[i].split('-')[0])
}
obj.playlist = playlistsar.join()
return obj
@ -47,6 +47,12 @@ new Vue({
label: 'Price',
field: 'price'
},
{
name: 'profit',
align: 'left',
label: 'Profit',
field: 'profit'
}
],
pagination: {
rowsPerPage: 10
@ -60,8 +66,8 @@ new Vue({
playlists: [],
JukeboxLinks: [],
step: 1,
locationcbPath: "",
locationcb: "",
locationcbPath: '',
locationcb: '',
jukeboxDialog: {
show: false,
data: {}
@ -69,90 +75,132 @@ new Vue({
spotifyDialog: false
}
},
computed: {
},
computed: {},
methods: {
getJukeboxes(){
getJukeboxes() {
self = this
LNbits.api
.request('GET', '/jukebox/api/v1/jukebox', self.g.user.wallets[0].inkey)
.then(function (response) {
self.JukeboxLinks = response.data.map(mapJukebox)
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
},
deleteJukebox(juke_id){
self = this
LNbits.api
.request('DELETE', '/jukebox/api/v1/jukebox/' + juke_id, self.g.user.wallets[0].adminkey)
.then(function (response) {
self.JukeboxLinks = _.reject(self.JukeboxLinks, function (obj) {
return obj.id === juke_id
.request('GET', '/jukebox/api/v1/jukebox', self.g.user.wallets[0].inkey)
.then(function (response) {
self.JukeboxLinks = response.data.map(mapJukebox)
})
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
},
deleteJukebox(juke_id) {
self = this
LNbits.utils
.confirmDialog('Are you sure you want to delete this Jukebox?')
.onOk(function () {
LNbits.api
.request(
'DELETE',
'/jukebox/api/v1/jukebox/' + juke_id,
self.g.user.wallets[0].adminkey
)
.then(function (response) {
self.JukeboxLinks = _.reject(self.JukeboxLinks, function (obj) {
return obj.id === juke_id
})
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
})
},
updateJukebox: function (linkId) {
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()
self.step = 4
self.jukeboxDialog.data.sp_device = []
self.jukeboxDialog.data.sp_playlists = []
self.jukeboxDialog.data.sp_id = self.jukeboxDialog.data.id
self.jukeboxDialog.show = true
},
closeFormDialog() {
this.jukeboxDialog.data = {}
this.jukeboxDialog.show = false
this.step = 1
},
submitSpotify() {
submitSpotifyKeys() {
self = this
self.jukeboxDialog.data.user = self.g.user.id
self.requestAuthorization()
this.$q.notify({
spinner: true,
message: 'Fetching token',
timeout: 4000
})
LNbits.api.request(
LNbits.api
.request(
'POST',
'/jukebox/api/v1/jukebox/',
self.g.user.wallets[0].adminkey,
self.jukeboxDialog.data
)
.then(response => {
if(response.data){
var timerId = setInterval(function(){
if(!self.jukeboxDialog.data.sp_user){
clearInterval(timerId);
}
if (response.data) {
self.jukeboxDialog.data.sp_id = response.data.id
LNbits.api
.request('GET', '/jukebox/api/v1/jukebox/spotify/' + self.jukeboxDialog.data.sp_id, self.g.user.wallets[0].inkey)
.then(response => {
if(response.data.sp_access_token){
self.jukeboxDialog.data.sp_access_token = response.data.sp_access_token
self.step = 3
self.fetchAccessToken()
clearInterval(timerId)
}
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
}, 3000)
}
self.step = 3
}
})
.catch(err => {
LNbits.utils.notifyApiError(err)
LNbits.utils.notifyApiError(err)
})
},
requestAuthorization(){
authAccess() {
self = this
self.requestAuthorization()
self.$q.notify({
spinner: true,
message: 'Fetching token',
timeout: 4000
})
self.getSpotifyTokens()
},
getSpotifyTokens() {
self = this
var timerId = setInterval(function () {
if (!self.jukeboxDialog.data.sp_user) {
clearInterval(timerId)
}
LNbits.api
.request(
'GET',
'/jukebox/api/v1/jukebox/spotify/' + self.jukeboxDialog.data.sp_id,
self.g.user.wallets[0].inkey
)
.then(response => {
if (response.data.sp_access_token) {
self.fetchAccessToken(response.data.sp_access_token)
if (self.jukeboxDialog.data.sp_access_token) {
self.refreshPlaylists()
self.refreshDevices()
self.step = 4
clearInterval(timerId)
}
}
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
}, 5000)
},
requestAuthorization() {
self = this
var url = 'https://accounts.spotify.com/authorize'
url += '?client_id=' + self.jukeboxDialog.data.sp_user
url += '&response_type=code'
url += '&redirect_uri=' + encodeURI(self.locationcbPath) + self.jukeboxDialog.data.sp_user
url += "&show_dialog=true"
url += '&scope=user-read-private user-read-email user-modify-playback-state user-read-playback-position user-library-read streaming user-read-playback-state user-read-recently-played playlist-read-private'
url +=
'&redirect_uri=' +
encodeURI(self.locationcbPath + self.jukeboxDialog.data.sp_id)
url += '&show_dialog=true'
url +=
'&scope=user-read-private user-read-email user-modify-playback-state user-read-playback-position user-library-read streaming user-read-playback-state user-read-recently-played playlist-read-private'
window.open(url)
},
@ -160,15 +208,12 @@ new Vue({
this.jukeboxDialog.show = true
this.jukeboxDialog.data = {}
},
openUpdateDialog(itemId) {
this.jukeboxDialog.show = true
let item = this.jukebox.items.find(item => item.id === itemId)
this.jukeboxDialog.data = item
},
createJukebox(){
createJukebox() {
self = this
console.log(this.jukeboxDialog.data)
this.jukeboxDialog.data.sp_playlists = this.jukeboxDialog.data.sp_playlists.join()
LNbits.api.request(
LNbits.api
.request(
'PUT',
'/jukebox/api/v1/jukebox/' + this.jukeboxDialog.data.sp_id,
self.g.user.wallets[0].adminkey,
@ -179,90 +224,118 @@ new Vue({
self.jukeboxDialog.show = false
})
},
playlistApi(method, url, body){
playlistApi(method, url, body) {
self = this
let xhr = new XMLHttpRequest()
xhr.open(method, url, true)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.setRequestHeader('Authorization', 'Bearer ' + this.jukeboxDialog.data.sp_access_token)
xhr.setRequestHeader(
'Authorization',
'Bearer ' + this.jukeboxDialog.data.sp_access_token
)
xhr.send(body)
xhr.onload = function() {
xhr.onload = function () {
let responseObj = JSON.parse(xhr.response)
self.jukeboxDialog.data.playlists = null
self.playlists = []
var i;
self.jukeboxDialog.data.playlists = []
var i
for (i = 0; i < responseObj.items.length; i++) {
self.playlists.push(responseObj.items[i].name + "-" + responseObj.items[i].id)
self.playlists.push(
responseObj.items[i].name + '-' + responseObj.items[i].id
)
}
}
},
refreshPlaylists(){
refreshPlaylists() {
self = this
self.playlistApi( "GET", "https://api.spotify.com/v1/me/playlists", null)
self.playlistApi('GET', 'https://api.spotify.com/v1/me/playlists', null)
},
deviceApi(method, url, body){
deviceApi(method, url, body) {
self = this
let xhr = new XMLHttpRequest()
xhr.open(method, url, true)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.setRequestHeader('Authorization', 'Bearer ' + this.jukeboxDialog.data.sp_access_token)
xhr.setRequestHeader(
'Authorization',
'Bearer ' + this.jukeboxDialog.data.sp_access_token
)
xhr.send(body)
xhr.onload = function() {
xhr.onload = function () {
let responseObj = JSON.parse(xhr.response)
self.devices = []
var i;
for (i = 0; i < responseObj.devices.length; i++) {
self.devices.push(responseObj.devices[i].name + "-" + responseObj.devices[i].id)
self.jukeboxDialog.data.devices = []
self.devices = []
var i
for (i = 0; i < responseObj.devices.length; i++) {
self.devices.push(
responseObj.devices[i].name + '-' + responseObj.devices[i].id
)
}
}
},
refreshDevices(){
refreshDevices() {
self = this
self.deviceApi( "GET", "https://api.spotify.com/v1/me/player/devices", null)
self.deviceApi(
'GET',
'https://api.spotify.com/v1/me/player/devices',
null
)
},
fetchAccessToken( ){
fetchAccessToken(code) {
self = this
let body = "grant_type=authorization_code"
body += "&code=" + self.jukeboxDialog.data.sp_access_token
body += '&redirect_uri=' + encodeURI(self.locationcbPath) + self.jukeboxDialog.data.sp_user
let body = 'grant_type=authorization_code'
body += '&code=' + code
body +=
'&redirect_uri=' +
encodeURI(self.locationcbPath + self.jukeboxDialog.data.sp_id)
this.callAuthorizationApi(body)
},
refreshAccessToken(){
refreshAccessToken() {
self = this
let body = "grant_type=refresh_token"
body += "&refresh_token=" + self.jukeboxDialog.data.sp_refresh_token
body += "&client_id=" + self.jukeboxDialog.data.sp_user
let body = 'grant_type=refresh_token'
body += '&refresh_token=' + self.jukeboxDialog.data.sp_refresh_token
body += '&client_id=' + self.jukeboxDialog.data.sp_user
this.callAuthorizationApi(body)
},
callAuthorizationApi(body){
callAuthorizationApi(body) {
self = this
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('Authorization', 'Basic ' + btoa(this.jukeboxDialog.data.sp_user + ":" + this.jukeboxDialog.data.sp_secret))
xhr.setRequestHeader(
'Authorization',
'Basic ' +
btoa(
this.jukeboxDialog.data.sp_user +
':' +
this.jukeboxDialog.data.sp_secret
)
)
xhr.send(body)
xhr.onload = function() {
xhr.onload = function () {
let responseObj = JSON.parse(xhr.response)
self.jukeboxDialog.data.sp_access_token = responseObj.access_token
self.jukeboxDialog.data.sp_refresh_token = responseObj.refresh_token
self.refreshPlaylists()
self.refreshDevices()
if (responseObj.access_token) {
self.jukeboxDialog.data.sp_access_token = responseObj.access_token
self.jukeboxDialog.data.sp_refresh_token = responseObj.refresh_token
console.log(responseObj)
}
}
},
}
},
created() {
var getJukeboxes = this.getJukeboxes
getJukeboxes()
this.selectedWallet = this.g.user.wallets[0]
this.locationcbPath = String([
window.location.protocol,
'//',
window.location.host,
'/jukebox/api/v1/jukebox/spotify/cb/'
].join(''))
this.locationcbPath = String(
[
window.location.protocol,
'//',
window.location.host,
'/jukebox/api/v1/jukebox/spotify/cb/'
].join('')
)
this.locationcb = this.locationcbPath
}
})

View file

@ -58,6 +58,14 @@
</q-btn>
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="updateJukebox(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
<q-btn
flat
dense
@ -169,10 +177,7 @@
target="_blank"
href="https://developer.spotify.com/dashboard/applications"
>here</a
>. <br />
In the app go to edit-settings, set the redirect URI to this link
(replacing the CLIENT-ID with your own) {% raw %}{{ locationcb
}}CLIENT-ID{% endraw %}
>.
<q-input
filled
class="q-pb-md q-pt-md"
@ -202,11 +207,43 @@
<q-btn
v-if="jukeboxDialog.data.sp_secret != null && jukeboxDialog.data.sp_user != null && tokenFetched"
color="green-7"
@click="submitSpotify"
>Get token</q-btn
@click="submitSpotifyKeys"
>Submit keys</q-btn
>
<q-btn v-else color="green-7" disable color="green-7"
>Get token</q-btn
>Submit keys</q-btn
>
</div>
<div class="col-8">
<q-btn
color="green-7"
class="float-right"
@click="closeFormDialog"
>Cancel</q-btn
>
</div>
</div>
<br />
</q-step>
<q-step :name="3" title="Add Redirect URI" icon="link" :done="step > 3">
In the app go to edit-settings, set the redirect URI to this link
<br /><small
>{% raw %}{{ locationcb }}{{ jukeboxDialog.data.sp_id }}{% endraw
%}</small
>
<div class="row q-mt-md">
<div class="col-4">
<q-btn
v-if="jukeboxDialog.data.sp_secret != null && jukeboxDialog.data.sp_user != null && tokenFetched"
color="green-7"
@click="authAccess"
>Authorise access</q-btn
>
<q-btn v-else color="green-7" disable color="green-7"
>Authorise access</q-btn
>
</div>
<div class="col-8">
@ -223,11 +260,11 @@
</q-step>
<q-step
:name="3"
:name="4"
title="Select playlists"
icon="queue_music"
active-color="green-8"
:done="step > 3"
:done="step > 4"
>
<q-select
class="q-pb-md q-pt-md"

View file

@ -22,12 +22,7 @@ async def api_get_jukeboxs():
try:
return (
jsonify(
[
{
**jukebox._asdict()
}
for jukebox in await get_jukeboxs(g.wallet.user)
]
[{**jukebox._asdict()} for jukebox in await get_jukeboxs(g.wallet.user)]
),
HTTPStatus.OK,
)
@ -38,23 +33,23 @@ async def api_get_jukeboxs():
##################SPOTIFY AUTH#####################
@jukebox_ext.route("/api/v1/jukebox/spotify/cb/<sp_user>", methods=["GET"])
async def api_check_credentials_callbac(sp_user):
@jukebox_ext.route("/api/v1/jukebox/spotify/cb/<juke_id>", methods=["GET"])
async def api_check_credentials_callbac(juke_id):
print(request.args)
sp_code = ""
sp_access_token = ""
sp_refresh_token = ""
print(request.args)
jukebox = await get_jukebox_by_user(sp_user)
jukebox = await get_jukebox(juke_id)
if request.args.get("code"):
sp_code = request.args.get("code")
jukebox = await update_jukebox(
sp_user=sp_user, sp_secret=jukebox.sp_secret, sp_access_token=sp_code
juke_id=juke_id, sp_secret=jukebox.sp_secret, sp_access_token=sp_code
)
if request.args.get("access_token"):
sp_access_token = request.args.get("access_token")
sp_refresh_token = request.args.get("refresh_token")
jukebox = await update_jukebox(
sp_user=sp_user,
juke_id=juke_id,
sp_secret=jukebox.sp_secret,
sp_access_token=sp_access_token,
sp_refresh_token=sp_refresh_token,
@ -70,7 +65,7 @@ async def api_check_credentials_check(sp_id):
@jukebox_ext.route("/api/v1/jukebox/", methods=["POST"])
@jukebox_ext.route("/api/v1/jukebox/<item_id>", methods=["PUT"])
@jukebox_ext.route("/api/v1/jukebox/<juke_id>", methods=["PUT"])
@api_check_wallet_key("admin")
@api_validate_post_request(
schema={
@ -86,9 +81,9 @@ async def api_check_credentials_check(sp_id):
"price": {"type": "string", "required": True},
}
)
async def api_create_update_jukebox(item_id=None):
if item_id:
jukebox = await update_jukebox(item_id, **g.data)
async def api_create_update_jukebox(juke_id=None):
if juke_id:
jukebox = await update_jukebox(juke_id=juke_id, **g.data)
else:
jukebox = await create_jukebox(**g.data)
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
@ -101,12 +96,7 @@ async def api_delete_item(juke_id):
try:
return (
jsonify(
[
{
**jukebox._asdict()
}
for jukebox in await get_jukeboxs(g.wallet.user)
]
[{**jukebox._asdict()} for jukebox in await get_jukeboxs(g.wallet.user)]
),
HTTPStatus.OK,
)