Can connect to spotify ccount
This commit is contained in:
parent
dc10a0f52b
commit
b594281f83
7 changed files with 137 additions and 61 deletions
|
|
@ -11,15 +11,16 @@ async def create_jukebox(
|
||||||
price: int,
|
price: int,
|
||||||
sp_user: str,
|
sp_user: str,
|
||||||
sp_secret: str,
|
sp_secret: str,
|
||||||
sp_token: Optional[str] = "",
|
sp_access_token: Optional[str] = "",
|
||||||
|
sp_refresh_token: Optional[str] = "",
|
||||||
sp_device: Optional[str] = "",
|
sp_device: Optional[str] = "",
|
||||||
sp_playlists: Optional[str] = "",
|
sp_playlists: Optional[str] = "",
|
||||||
) -> Jukebox:
|
) -> Jukebox:
|
||||||
juke_id = urlsafe_short_hash()
|
juke_id = urlsafe_short_hash()
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO jukebox (id, title, wallet, sp_user, sp_secret, sp_token, sp_device, sp_playlists, price)
|
INSERT INTO jukebox (id, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
juke_id,
|
juke_id,
|
||||||
|
|
@ -27,7 +28,8 @@ async def create_jukebox(
|
||||||
wallet,
|
wallet,
|
||||||
sp_user,
|
sp_user,
|
||||||
sp_secret,
|
sp_secret,
|
||||||
sp_token,
|
sp_access_token,
|
||||||
|
sp_refresh_token,
|
||||||
sp_device,
|
sp_device,
|
||||||
sp_playlists,
|
sp_playlists,
|
||||||
int(price),
|
int(price),
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ async def m001_initial(db):
|
||||||
wallet TEXT,
|
wallet TEXT,
|
||||||
sp_user TEXT NOT NULL,
|
sp_user TEXT NOT NULL,
|
||||||
sp_secret TEXT NOT NULL,
|
sp_secret TEXT NOT NULL,
|
||||||
sp_token TEXT,
|
sp_access_token TEXT,
|
||||||
|
sp_refresh_token TEXT,
|
||||||
sp_device TEXT,
|
sp_device TEXT,
|
||||||
sp_playlists TEXT,
|
sp_playlists TEXT,
|
||||||
price INTEGER
|
price INTEGER
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ class Jukebox(NamedTuple):
|
||||||
wallet: str
|
wallet: str
|
||||||
sp_user: str
|
sp_user: str
|
||||||
sp_secret: str
|
sp_secret: str
|
||||||
sp_token: str
|
sp_access_token: str
|
||||||
|
sp_refresh_token: str
|
||||||
sp_device: str
|
sp_device: str
|
||||||
sp_playlists: str
|
sp_playlists: str
|
||||||
price: int
|
price: int
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ new Vue({
|
||||||
return {
|
return {
|
||||||
isPwd: true,
|
isPwd: true,
|
||||||
tokenFetched: true,
|
tokenFetched: true,
|
||||||
device: [],
|
devices: [],
|
||||||
jukebox: {},
|
jukebox: {},
|
||||||
playlists: [],
|
playlists: [],
|
||||||
step: 1,
|
step: 1,
|
||||||
locationcbPath: "",
|
locationcbPath: "",
|
||||||
|
locationcb: "",
|
||||||
jukeboxDialog: {
|
jukeboxDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
data: {}
|
data: {}
|
||||||
|
|
@ -55,22 +56,26 @@ new Vue({
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if(response.data){
|
if(response.data){
|
||||||
var timerId = setInterval(function(){
|
var timerId = setInterval(function(){
|
||||||
|
console.log(response.data)
|
||||||
if(!self.jukeboxDialog.data.sp_user){
|
if(!self.jukeboxDialog.data.sp_user){
|
||||||
clearInterval(timerId);
|
clearInterval(timerId);
|
||||||
}
|
}
|
||||||
|
self.jukeboxDialog.data.sp_id = response.data.id
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', '/jukebox/api/v1/jukebox/spotify/' + self.jukeboxDialog.data.sp_user, self.g.user.wallets[0].inkey)
|
.request('GET', '/jukebox/api/v1/jukebox/spotify/' + self.jukeboxDialog.data.sp_id, self.g.user.wallets[0].inkey)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if(response.data.sp_token){
|
if(response.data.sp_access_token){
|
||||||
console.log(response.data.sp_token)
|
self.jukeboxDialog.data.sp_access_token = response.data.sp_access_token
|
||||||
|
|
||||||
self.step = 3
|
self.step = 3
|
||||||
clearInterval(timerId);
|
self.fetchAccessToken()
|
||||||
self.refreshPlaylists()
|
|
||||||
self.$q.notify({
|
clearInterval(timerId)
|
||||||
message: 'Success! App is now linked!',
|
|
||||||
timeout: 3000
|
// self.refreshPlaylists(response.data.sp_token)
|
||||||
})
|
// self.$q.notify({
|
||||||
|
// message: 'Success! App is now linked!',
|
||||||
|
// timeout: 3000
|
||||||
|
// })
|
||||||
//set devices, playlists
|
//set devices, playlists
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -86,13 +91,12 @@ new Vue({
|
||||||
},
|
},
|
||||||
requestAuthorization(){
|
requestAuthorization(){
|
||||||
self = this
|
self = this
|
||||||
let url = 'https://accounts.spotify.com/authorize'
|
var url = 'https://accounts.spotify.com/authorize'
|
||||||
url += '?scope=user-modify-playback-state%20user-read-playback-position'
|
url += '?client_id=' + self.jukeboxDialog.data.sp_user
|
||||||
url += '%20user-library-read%20streaming%20user-read-playback-state'
|
url += '&response_type=code'
|
||||||
url += '%20user-read-recently-played%20playlist-read-private&response_type=code'
|
url += '&redirect_uri=' + encodeURI(self.locationcbPath + self.jukeboxDialog.data.sp_user)
|
||||||
url += '&redirect_uri=' + encodeURIComponent(self.locationcbPath) + self.jukeboxDialog.data.sp_user
|
url += "&show_dialog=true"
|
||||||
url += '&client_id=' + self.jukeboxDialog.data.sp_user
|
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 += '&show_dialog=true'
|
|
||||||
console.log(url)
|
console.log(url)
|
||||||
window.open(url)
|
window.open(url)
|
||||||
},
|
},
|
||||||
|
|
@ -105,47 +109,96 @@ new Vue({
|
||||||
let item = this.jukebox.items.find(item => item.id === itemId)
|
let item = this.jukebox.items.find(item => item.id === itemId)
|
||||||
this.jukeboxDialog.data = item
|
this.jukeboxDialog.data = item
|
||||||
},
|
},
|
||||||
|
createJukebox(){
|
||||||
|
self = this
|
||||||
|
|
||||||
callApi(method, url, body, callback){
|
LNbits.api.request(
|
||||||
|
'PUT',
|
||||||
|
'/jukebox/api/v1/jukebox/' + this.jukeboxDialog.data.sp_id,
|
||||||
|
self.g.user.wallets[0].adminkey,
|
||||||
|
self.jukeboxDialog.data
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
LNbits.utils.notifyApiError(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
playlistApi(method, url, body){
|
||||||
|
self = this
|
||||||
let xhr = new XMLHttpRequest()
|
let xhr = new XMLHttpRequest()
|
||||||
xhr.open(method, url, true)
|
xhr.open(method, url, true)
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||||
xhr.setRequestHeader('Authorization', 'Bearer ' + self.jukeboxDialog.data.sp_token)
|
xhr.setRequestHeader('Authorization', 'Bearer ' + this.jukeboxDialog.data.sp_access_token)
|
||||||
xhr.send(body)
|
xhr.send(body)
|
||||||
xhr.onload = callback
|
xhr.onload = function() {
|
||||||
|
let responseObj = JSON.parse(xhr.response)
|
||||||
|
console.log(responseObj.items)
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < responseObj.items.length; i++) {
|
||||||
|
self.playlists.push(responseObj.items[i].name + "-" + responseObj.items[i].id)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
refreshPlaylists(){
|
refreshPlaylists(){
|
||||||
console.log("sdfvasdv")
|
self = this
|
||||||
callApi( "GET", "https://api.spotify.com/v1/me/playlists", null, handlePlaylistsResponse )
|
self.playlistApi( "GET", "https://api.spotify.com/v1/me/playlists", null)
|
||||||
},
|
},
|
||||||
handlePlaylistsResponse(){
|
deviceApi(method, url, body){
|
||||||
console.log("data")
|
self = this
|
||||||
if ( this.status == 200 ){
|
let xhr = new XMLHttpRequest()
|
||||||
var data = JSON.parse(this.responseText)
|
xhr.open(method, url, true)
|
||||||
console.log(data)
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||||
}
|
xhr.setRequestHeader('Authorization', 'Bearer ' + this.jukeboxDialog.data.sp_access_token)
|
||||||
else if ( this.status == 401 ){
|
xhr.send(body)
|
||||||
refreshAccessToken()
|
xhr.onload = function() {
|
||||||
}
|
let responseObj = xhr.response
|
||||||
else {
|
alert(responseObj)
|
||||||
console.log(this.responseText)
|
var i;
|
||||||
alert(this.responseText)
|
for (i = 0; i < responseObj.items.length; i++) {
|
||||||
|
self.devices.push(responseObj.items[i].name + "-" + responseObj.items[i].id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
refreshDevices(){
|
||||||
|
self = this
|
||||||
|
self.deviceApi( "GET", "https://api.spotify.com/v1/me/player/devices", null)
|
||||||
|
},
|
||||||
|
fetchAccessToken( ){
|
||||||
|
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)
|
||||||
|
this.callAuthorizationApi(body)
|
||||||
|
},
|
||||||
refreshAccessToken(){
|
refreshAccessToken(){
|
||||||
refresh_token = localStorage.getItem("refresh_token")
|
self = this
|
||||||
let body = "grant_type=refresh_token"
|
let body = "grant_type=refresh_token"
|
||||||
body += "&refresh_token=" + self.jukeboxDialog.data.sp_token
|
body += "&refresh_token=" + self.jukeboxDialog.data.sp_refresh_token
|
||||||
body += "&client_id=" + self.jukeboxDialog.data.sp_user
|
body += "&client_id=" + self.jukeboxDialog.data.sp_user
|
||||||
callAuthorizationApi(body)
|
this.callAuthorizationApi(body)
|
||||||
},
|
},
|
||||||
callAuthorizationApi(body){
|
callAuthorizationApi(body){
|
||||||
|
self = this
|
||||||
let xhr = new XMLHttpRequest()
|
let xhr = new XMLHttpRequest()
|
||||||
xhr.open("POST", 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')
|
||||||
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(self.jukeboxDialog.data.sp_user + ":" + self.jukeboxDialog.data.sp_secret))
|
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(this.jukeboxDialog.data.sp_user + ":" + this.jukeboxDialog.data.sp_secret))
|
||||||
xhr.send(body)
|
xhr.send(body)
|
||||||
xhr.onload = handleAuthorizationResponse
|
console.log(('Authorization', 'Basic ' + btoa(this.jukeboxDialog.data.sp_user + ":" + this.jukeboxDialog.data.sp_secret)))
|
||||||
|
xhr.onload = function() {
|
||||||
|
let responseObj = JSON.parse(xhr.response)
|
||||||
|
alert(responseObj.access_token)
|
||||||
|
alert(responseObj.refresh_token)
|
||||||
|
self.jukeboxDialog.data.sp_access_token = responseObj.access_token
|
||||||
|
self.jukeboxDialog.data.sp_refresh_token = responseObj.refresh_token
|
||||||
|
console.log(self.jukeboxDialog.data)
|
||||||
|
self.refreshPlaylists()
|
||||||
|
self.refreshDevices()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -156,5 +209,7 @@ new Vue({
|
||||||
window.location.host,
|
window.location.host,
|
||||||
'/jukebox/api/v1/jukebox/spotify/cb/'
|
'/jukebox/api/v1/jukebox/spotify/cb/'
|
||||||
].join(''))
|
].join(''))
|
||||||
|
console.log(this.locationcbPath)
|
||||||
|
this.locationcb = this.locationcbPath
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@
|
||||||
>here</a
|
>here</a
|
||||||
>. <br />
|
>. <br />
|
||||||
In the app go to edit-settings, set the redirect URI to this link
|
In the app go to edit-settings, set the redirect URI to this link
|
||||||
(replacing the CLIENT-ID with your own) {% raw %}{{ locationcbPath
|
(replacing the CLIENT-ID with your own) {% raw %}{{ locationcb
|
||||||
}}CLIENT-ID{% endraw %}
|
}}CLIENT-ID{% endraw %}
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
|
|
@ -238,13 +238,14 @@
|
||||||
dense
|
dense
|
||||||
emit-value
|
emit-value
|
||||||
v-model="jukeboxDialog.data.sp_device"
|
v-model="jukeboxDialog.data.sp_device"
|
||||||
:options="device"
|
:options="devices"
|
||||||
label="Device jukebox will play to"
|
label="Device jukebox will play to"
|
||||||
></q-select>
|
></q-select>
|
||||||
<q-select
|
<q-select
|
||||||
class="q-pb-md"
|
class="q-pb-md"
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
|
multiple
|
||||||
emit-value
|
emit-value
|
||||||
v-model="jukeboxDialog.data.sp_playlists"
|
v-model="jukeboxDialog.data.sp_playlists"
|
||||||
:options="playlists"
|
:options="playlists"
|
||||||
|
|
@ -252,7 +253,9 @@
|
||||||
></q-select>
|
></q-select>
|
||||||
<div class="row q-mt-md">
|
<div class="row q-mt-md">
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<q-btn color="green-7" @click="step = 2">Create Jukebox</q-btn>
|
<q-btn color="green-7" @click="createJukebox"
|
||||||
|
>Create Jukebox</q-btn
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ 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
|
||||||
|
|
||||||
@jukebox_ext.route("/")
|
@jukebox_ext.route("/")
|
||||||
@validate_uuids(["usr"], required=True)
|
@validate_uuids(["usr"], required=True)
|
||||||
|
|
@ -22,4 +22,4 @@ async def index():
|
||||||
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)
|
||||||
|
|
||||||
return await render_template("jukebox/jukebox.html", jukebox=jukebox)
|
return await render_template("jukebox/jukebox.html", jukebox=jukebox)
|
||||||
|
|
@ -25,18 +25,30 @@ async def api_get_jukeboxs():
|
||||||
##################SPOTIFY AUTH#####################
|
##################SPOTIFY AUTH#####################
|
||||||
|
|
||||||
|
|
||||||
@jukebox_ext.route("/api/v1/jukebox/spotify/cb/<sp_user>/", methods=["GET"])
|
@jukebox_ext.route("/api/v1/jukebox/spotify/cb/<sp_user>", methods=["GET"])
|
||||||
async def api_check_credentials_callbac(sp_user):
|
async def api_check_credentials_callbac(sp_user):
|
||||||
|
sp_code = ""
|
||||||
|
sp_access_token = ""
|
||||||
|
sp_refresh_token = ""
|
||||||
|
print(request.args)
|
||||||
jukebox = await get_jukebox_by_user(sp_user)
|
jukebox = await get_jukebox_by_user(sp_user)
|
||||||
jukebox = await update_jukebox(
|
if request.args.get('code'):
|
||||||
sp_user=sp_user, sp_secret=jukebox.sp_secret, sp_token=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
|
||||||
|
)
|
||||||
|
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, sp_secret=jukebox.sp_secret, sp_access_token=sp_access_token, sp_refresh_token=sp_refresh_token
|
||||||
|
)
|
||||||
return "<h1>Success!</h1><h2>You can close this window</h2>"
|
return "<h1>Success!</h1><h2>You can close this window</h2>"
|
||||||
|
|
||||||
@jukebox_ext.route("/api/v1/jukebox/spotify/<sp_user>", methods=["GET"])
|
@jukebox_ext.route("/api/v1/jukebox/spotify/<sp_id>", methods=["GET"])
|
||||||
@api_check_wallet_key("invoice")
|
@api_check_wallet_key("invoice")
|
||||||
async def api_check_credentials_check(sp_user):
|
async def api_check_credentials_check(sp_id):
|
||||||
jukebox = await get_jukebox_by_user(sp_user)
|
jukebox = await get_jukebox(sp_id)
|
||||||
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -49,14 +61,16 @@ async def api_check_credentials_check(sp_user):
|
||||||
"wallet": {"type": "string", "empty": False, "required": True},
|
"wallet": {"type": "string", "empty": False, "required": True},
|
||||||
"sp_user": {"type": "string", "empty": False, "required": True},
|
"sp_user": {"type": "string", "empty": False, "required": True},
|
||||||
"sp_secret": {"type": "string", "required": True},
|
"sp_secret": {"type": "string", "required": True},
|
||||||
"sp_token": {"type": "string", "required": False},
|
"sp_access_token": {"type": "string", "required": False},
|
||||||
|
"sp_refresh_token": {"type": "string", "required": False},
|
||||||
"sp_device": {"type": "string", "required": False},
|
"sp_device": {"type": "string", "required": False},
|
||||||
"sp_playlists": {"type": "string", "required": False},
|
"sp_playlists": {"type": "string", "required": False},
|
||||||
"price": {"type": "string", "required": True},
|
"price": {"type": "string", "required": True},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
async def api_create_update_jukebox(item_id=None):
|
async def api_create_update_jukebox(item_id=None):
|
||||||
print(g.data)
|
if item_id:
|
||||||
|
jukebox = await update_jukebox(**g.data)
|
||||||
jukebox = await create_jukebox(**g.data)
|
jukebox = await create_jukebox(**g.data)
|
||||||
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue