Merge branch 'master' into feature/readthemeenv

This commit is contained in:
Tiago vasconcelos 2021-07-01 12:05:40 +01:00
commit 2cd6877645
13 changed files with 169 additions and 125 deletions

View file

@ -16,7 +16,7 @@ LNBITS_SERVICE_FEE="0.0"
# Change theme
LNBITS_SITE_TITLE=LNbits
# Choose from mint, flamingo, quasar, autumn, monochrome
LNBITS_THEME_OPTIONS="mint, flamingo, quasar, autumn, monochrome"
LNBITS_THEME_OPTIONS="mint, flamingo, quasar, autumn, monochrome, salvador"
# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, LndWallet (gRPC),
# LndRestWallet, CLightningWallet, LNbitsWallet, SparkWallet

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ __pycache__
*$py.class
.mypy_cache
.vscode
*-lock.json
*.egg
*.egg-info

View file

@ -1,5 +1,36 @@
# Jukebox
To use this extension you need a Spotify client ID and client secret. You get these by creating an app in the Spotify developers dashboard here https://developer.spotify.com/dashboard/applications
## An actual Jukebox where users pay sats to play their favourite music from your playlists
Select the playlists you want people to be able to pay for, share the frontend page, profit :)
**Note:** To use this extension you need a Premium Spotify subscription.
## Usage
1. Click on "ADD SPOTIFY JUKEBOX"\
![add jukebox](https://i.imgur.com/NdVoKXd.png)
2. Follow the steps required on the form\
- give your jukebox a name
- select a wallet to receive payment
- define the price a user must pay to select a song\
![pick wallet price](https://i.imgur.com/4bJ8mb9.png)
- follow the steps to get your Spotify App and get the client ID and secret key\
![spotify keys](https://i.imgur.com/w2EzFtB.png)
- paste the codes in the form\
![api keys](https://i.imgur.com/6b9xauo.png)
- copy the _Redirect URL_ presented on the form\
![redirect url](https://i.imgur.com/GMzl0lG.png)
- on Spotify click the "EDIT SETTINGS" button and paste the copied link in the _Redirect URI's_ prompt
![spotify app setting](https://i.imgur.com/vb0x4Tl.png)
- back on LNBits, click "AUTORIZE ACCESS" and "Agree" on the page that will open
- choose on which device the LNBits Jukebox extensions will stream to, you may have to be logged in in order to select the device (browser, smartphone app, etc...)
- and select what playlist will be available for users to choose songs (you need to have already playlist on Spotify)\
![select playlists](https://i.imgur.com/g4dbtED.png)
3. After Jukebox is created, click the icon to open the dialog with the shareable QR, open the Jukebox page, etc...\
![shareable jukebox](https://i.imgur.com/EAh9PI0.png)
4. The users will see the Jukebox page and choose a song from the selected playlist\
![select song](https://i.imgur.com/YYjeQAs.png)
5. After selecting a song they'd like to hear next a dialog will show presenting the music\
![play for sats](https://i.imgur.com/eEHl3o8.png)
6. After payment, the song will automatically start playing on the device selected or enter the queue if some other music is already playing

View file

@ -10,3 +10,8 @@ jukebox_ext: Blueprint = Blueprint(
from .views_api import * # noqa
from .views import * # noqa
from .tasks import register_listeners
from lnbits.tasks import record_async
jukebox_ext.record(record_async(register_listeners))

View file

@ -46,12 +46,6 @@ new Vue({
align: 'left',
label: 'Price',
field: 'price'
},
{
name: 'profit',
align: 'left',
label: 'Profit',
field: 'profit'
}
],
pagination: {

View file

@ -0,0 +1,27 @@
import json
import trio # type: ignore
from lnbits.core.models import Payment
from lnbits.core.crud import create_payment
from lnbits.core import db as core_db
from lnbits.tasks import register_invoice_listener, internal_invoice_paid
from lnbits.helpers import urlsafe_short_hash
from .crud import get_jukebox, update_jukebox_payment
async def register_listeners():
invoice_paid_chan_send, invoice_paid_chan_recv = trio.open_memory_channel(2)
register_invoice_listener(invoice_paid_chan_send)
await wait_for_paid_invoices(invoice_paid_chan_recv)
async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel):
async for payment in invoice_paid_chan:
await on_invoice_paid(payment)
async def on_invoice_paid(payment: Payment) -> None:
if "jukebox" != payment.extra.get("tag"):
# not a jukebox invoice
return
await update_jukebox_payment(payment.payment_hash, paid=True)

View file

@ -134,14 +134,6 @@
}
},
methods: {
cancelPayment: function () {
this.paymentReq = null
clearInterval(this.paymentDialog.checker)
if (this.paymentDialog.dismissMsg) {
this.paymentDialog.dismissMsg()
}
},
closeReceiveDialog() {},
payForSong(song_id, name, artist, image) {
self = this
self.receive.name = name
@ -150,6 +142,49 @@
self.receive.id = song_id
self.receive.dialogues.first = true
},
startPaymentNotifier() {
this.cancelListener()
this.cancelListener = LNbits.events.onInvoicePaid(
this.selectedWallet,
payment => {
this.paid = true
this.receive.dialogues.first = false
this.receive.dialogues.second = false
LNbits.api
.request(
'GET',
'/jukebox/api/v1/jukebox/jb/invoicep/' +
this.receive.id +
'/{{ juke_id }}/' +
this.receive.paymentHash
)
.then(response1 => {
if (response1.data[2] == this.receive.id) {
setTimeout(() => {
this.getCurrent()
}, 500)
this.$q.notify({
color: 'green',
message:
'Success! "' +
this.receive.name +
'" will be played soon',
timeout: 3000
})
this.paid = false
response1 = []
}
})
.catch(err => {
LNbits.utils.notifyApiError(err)
self.paid = false
response1 = []
})
}
)
},
getInvoice(song_id) {
self = this
LNbits.api
@ -165,51 +200,9 @@
self.receive.paymentHash = response.data[0][0]
self.receive.dialogues.second = true
var paymentChecker = setInterval(function () {
if (!self.paid) {
self.checkInvoice(self.receive.paymentHash, '{{ juke_id }}')
}
if (self.paid) {
clearInterval(paymentChecker)
self.paid = true
self.receive.dialogues.first = false
self.receive.dialogues.second = false
self.$q.notify({
message: 'Processing'
})
LNbits.api
.request(
'GET',
'/jukebox/api/v1/jukebox/jb/invoicep/' +
song_id +
'/{{ juke_id }}/' +
self.receive.paymentHash
)
.then(function (response1) {
if (response1.data[2] == song_id) {
setTimeout(function () {
self.getCurrent()
}, 500)
self.$q.notify({
color: 'green',
message:
'Success! "' +
self.receive.name +
'" will be played soon',
timeout: 3000
})
self.paid = false
response1 = []
}
})
.catch(err => {
LNbits.utils.notifyApiError(err)
self.paid = false
response1 = []
})
}
}, 3000)
})
.catch(err => {
self.$q.notify({
@ -221,24 +214,6 @@
})
})
},
checkInvoice(juke_id, paymentHash) {
var self = this
LNbits.api
.request(
'GET',
'/jukebox/api/v1/jukebox/jb/checkinvoice/' +
juke_id +
'/' +
paymentHash,
'filla'
)
.then(function (response) {
self.paid = response.data.paid
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getCurrent() {
LNbits.api
.request('GET', '/jukebox/api/v1/jukebox/jb/currently/{{juke_id}}')
@ -273,7 +248,8 @@
created() {
this.getCurrent()
this.playlists = JSON.parse('{{ playlists | tojson }}')
this.selectedWallet.inkey = '{{ inkey }}'
this.startPaymentNotifier()
self = this
LNbits.api
.request(
@ -289,8 +265,6 @@
.catch(err => {
LNbits.utils.notifyApiError(err)
})
// this.startPaymentNotifier()
}
})
</script>

View file

@ -7,7 +7,7 @@
<q-expansion-item group="api" dense expand-separator label="List pay links">
<q-card>
<q-card-section>
<code><span class="text-blue">GET</span> /api/v1/links</code>
<code><span class="text-blue">GET</span> /lnurlp/api/v1/links</code>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;invoice_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
@ -27,7 +27,7 @@
<q-card>
<q-card-section>
<code
><span class="text-blue">GET</span> /api/v1/links/&lt;pay_id&gt;</code
><span class="text-blue">GET</span> /lnurlp/api/v1/links/&lt;pay_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;invoice_key&gt;}</code><br />
@ -52,11 +52,11 @@
>
<q-card>
<q-card-section>
<code><span class="text-green">POST</span> /api/v1/links</code>
<code><span class="text-green">POST</span> /lnurlp/api/v1/links</code>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
<code>{"description": &lt;string&gt; "amount": &lt;integer&gt;}</code>
<code>{"description": &lt;string&gt; "amount": &lt;integer&gt; "max": &lt;integer&gt; "min": &lt;integer&gt; "comment_chars": &lt;integer&gt;}</code>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 201 CREATED (application/json)
</h5>
@ -64,7 +64,7 @@
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
<code
>curl -X POST {{ request.url_root }}api/v1/links -d '{"description":
&lt;string&gt;, "amount": &lt;integer&gt;}' -H "Content-type:
&lt;string&gt;, "amount": &lt;integer&gt;, "max": &lt;integer&gt;, "min": &lt;integer&gt;, "comment_chars": &lt;integer&gt;}' -H "Content-type:
application/json" -H "X-Api-Key: {{ g.user.wallets[0].adminkey }}"
</code>
</q-card-section>
@ -80,7 +80,7 @@
<q-card-section>
<code
><span class="text-green">PUT</span>
/api/v1/links/&lt;pay_id&gt;</code
/lnurlp/api/v1/links/&lt;pay_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />
@ -111,7 +111,7 @@
<q-card-section>
<code
><span class="text-pink">DELETE</span>
/api/v1/links/&lt;pay_id&gt;</code
/lnurlp/api/v1/links/&lt;pay_id&gt;</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;admin_key&gt;}</code><br />

View file

@ -11,8 +11,11 @@
</h5>
<p>
Charge people for using your subdomain name...<br />
Are you the owner of <b>cool-domain.com</b> and want to sell
<i>cool-subdomain</i>.<b>cool-domain.com</b>
<a
href="https://github.com/lnbits/lnbits/tree/master/lnbits/extensions/subdomains"
>More details</a
>
<br />
<small>
Created by, <a href="https://github.com/grmkris">Kris</a></small

View file

@ -149,6 +149,7 @@
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
@ -160,7 +161,6 @@
</q-card-section>
</q-card>
</div>
</div>
<q-dialog v-model="domainDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">

View file

@ -316,8 +316,6 @@ window.windowMixin = {
methods: {
changeColor: function (newValue) {
document.body.setAttribute('data-theme', newValue)
//console.log(document.body.getAttribute('data-theme'))
//console.log(newValue)
this.$q.localStorage.set('lnbits.theme', newValue)
},
toggleDarkMode: function () {

View file

@ -26,9 +26,9 @@ $themes: (
'flamingo': (
primary: #d11d53,
secondary: #db3e6d,
dark: #e75480,
dark: #803a45,
info: #ec7599,
marginal-bg: #e75480,
marginal-bg: #803a45,
marginal-text: rgb(255, 255, 255)
),
'monochrome': (
@ -41,10 +41,6 @@ $themes: (
)
);
[data-theme='quasar'] .q-drawer--dark {
background: #121212 !important;
}
@each $theme, $colors in $themes {
@each $name, $color in $colors {
@if $name == 'dark' {
@ -79,6 +75,21 @@ $themes: (
}
}
}
[data-theme='salvador'] .q-drawer--dark {
background: #242424 !important;
}
[data-theme='salvador'] .q-header {
background: #0f47af !important;
}
[data-theme='flamingo'] .q-drawer--dark {
background: #e75480 !important;
}
[data-theme='flamingo'] .q-header {
background: #e75480 !important;
}
[v-cloak] {
display: none;

View file

@ -21,7 +21,7 @@
</head>
<body data-theme="classic">
<q-layout :color="dark" id="vue" view="hHh lpR lfr" v-cloak>
<q-layout id="vue" view="hHh lpR lfr" v-cloak>
<q-header bordered class="bg-marginal-bg">
<q-toolbar>
{% block drawer_toggle %}
@ -89,31 +89,31 @@
v-if="g.allowedThemes.includes('flamingo')"
dense
flat
@click="changeColor('flamingo')"
icon="format_color_fill"
color="pink-3"
size="md"
><q-tooltip>flamingo</q-tooltip>
</q-btn>
<q-btn
v-if="g.allowedThemes.includes('monochrome')"
dense
flat
@click="changeColor('monochrome')"
icon="format_color_fill"
color="grey"
size="md"
><q-tooltip>monochrome</q-tooltip>
</q-btn>
<q-btn
v-if="g.allowedThemes.includes('monochrome')"
dense
flat
@click="changeColor('salvador')"
icon="format_color_fill"
color="blue-10"
size="md"
><q-tooltip>elSalvador</q-tooltip>
</q-btn>
<q-btn
v-if="g.allowedThemes.includes('quasar')"
dense
flat
@click="changeColor('quasar')"
@click="changeColor('flamingo')"
icon="format_color_fill"
color="blue"
color="pink-3"
size="md"
><q-tooltip>quasar</q-tooltip>
><q-tooltip>flamingo</q-tooltip>
</q-btn>
</div>
</q-btn-dropdown>