mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-03-03 17:37:06 +01:00
Merge branch 'master' into feature/readthemeenv
This commit is contained in:
commit
2cd6877645
13 changed files with 169 additions and 125 deletions
|
@ -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
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ __pycache__
|
|||
*$py.class
|
||||
.mypy_cache
|
||||
.vscode
|
||||
*-lock.json
|
||||
|
||||
*.egg
|
||||
*.egg-info
|
||||
|
|
|
@ -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"\
|
||||

|
||||
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\
|
||||

|
||||
- follow the steps to get your Spotify App and get the client ID and secret key\
|
||||

|
||||
- paste the codes in the form\
|
||||

|
||||
- copy the _Redirect URL_ presented on the form\
|
||||

|
||||
- on Spotify click the "EDIT SETTINGS" button and paste the copied link in the _Redirect URI's_ prompt
|
||||

|
||||
- 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)\
|
||||

|
||||
|
||||
3. After Jukebox is created, click the icon to open the dialog with the shareable QR, open the Jukebox page, etc...\
|
||||

|
||||
4. The users will see the Jukebox page and choose a song from the selected playlist\
|
||||

|
||||
5. After selecting a song they'd like to hear next a dialog will show presenting the music\
|
||||

|
||||
6. After payment, the song will automatically start playing on the device selected or enter the queue if some other music is already playing
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -46,12 +46,6 @@ new Vue({
|
|||
align: 'left',
|
||||
label: 'Price',
|
||||
field: 'price'
|
||||
},
|
||||
{
|
||||
name: 'profit',
|
||||
align: 'left',
|
||||
label: 'Profit',
|
||||
field: 'profit'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
|
|
27
lnbits/extensions/jukebox/tasks.py
Normal file
27
lnbits/extensions/jukebox/tasks.py
Normal 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)
|
|
@ -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)
|
||||
self.$q.notify({
|
||||
message: 'Processing'
|
||||
})
|
||||
})
|
||||
.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>
|
||||
|
|
|
@ -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": <invoice_key>}</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/<pay_id></code
|
||||
><span class="text-blue">GET</span> /lnurlp/api/v1/links/<pay_id></code
|
||||
>
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||
<code>{"X-Api-Key": <invoice_key>}</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": <admin_key>}</code><br />
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
||||
<code>{"description": <string> "amount": <integer>}</code>
|
||||
<code>{"description": <string> "amount": <integer> "max": <integer> "min": <integer> "comment_chars": <integer>}</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":
|
||||
<string>, "amount": <integer>}' -H "Content-type:
|
||||
<string>, "amount": <integer>, "max": <integer>, "min": <integer>, "comment_chars": <integer>}' -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/<pay_id></code
|
||||
/lnurlp/api/v1/links/<pay_id></code
|
||||
>
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
||||
|
@ -111,7 +111,7 @@
|
|||
<q-card-section>
|
||||
<code
|
||||
><span class="text-pink">DELETE</span>
|
||||
/api/v1/links/<pay_id></code
|
||||
/lnurlp/api/v1/links/<pay_id></code
|
||||
>
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -149,17 +149,17 @@
|
|||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<h6 class="text-subtitle1 q-my-none">LNbits Subdomain extension</h6>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
<q-list> {% include "subdomains/_api_docs.html" %} </q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<h6 class="text-subtitle1 q-my-none">LNbits Subdomain extension</h6>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
<q-list> {% include "subdomains/_api_docs.html" %} </q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<q-dialog v-model="domainDialog.show" position="top">
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue