diff --git a/.env.example b/.env.example index 795bb6357..a9faad0b6 100644 --- a/.env.example +++ b/.env.example @@ -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 diff --git a/.gitignore b/.gitignore index ca3fcd001..79e10fb8c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__ *$py.class .mypy_cache .vscode +*-lock.json *.egg *.egg-info diff --git a/lnbits/extensions/jukebox/README.md b/lnbits/extensions/jukebox/README.md index b92e7ea6f..c761db448 100644 --- a/lnbits/extensions/jukebox/README.md +++ b/lnbits/extensions/jukebox/README.md @@ -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 diff --git a/lnbits/extensions/jukebox/__init__.py b/lnbits/extensions/jukebox/__init__.py index b6ec402f7..076ae4d9d 100644 --- a/lnbits/extensions/jukebox/__init__.py +++ b/lnbits/extensions/jukebox/__init__.py @@ -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)) diff --git a/lnbits/extensions/jukebox/static/js/index.js b/lnbits/extensions/jukebox/static/js/index.js index 61142c549..fc382d711 100644 --- a/lnbits/extensions/jukebox/static/js/index.js +++ b/lnbits/extensions/jukebox/static/js/index.js @@ -46,12 +46,6 @@ new Vue({ align: 'left', label: 'Price', field: 'price' - }, - { - name: 'profit', - align: 'left', - label: 'Profit', - field: 'profit' } ], pagination: { diff --git a/lnbits/extensions/jukebox/tasks.py b/lnbits/extensions/jukebox/tasks.py new file mode 100644 index 000000000..7c902937a --- /dev/null +++ b/lnbits/extensions/jukebox/tasks.py @@ -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) diff --git a/lnbits/extensions/jukebox/templates/jukebox/jukebox.html b/lnbits/extensions/jukebox/templates/jukebox/jukebox.html index 1883773bf..ff8a4488a 100644 --- a/lnbits/extensions/jukebox/templates/jukebox/jukebox.html +++ b/lnbits/extensions/jukebox/templates/jukebox/jukebox.html @@ -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() } }) diff --git a/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html b/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html index 68cea8f8a..d47ab1f1c 100644 --- a/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html +++ b/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html @@ -7,7 +7,7 @@ - GET /api/v1/links + GET /lnurlp/api/v1/links
Headers
{"X-Api-Key": <invoice_key>}
Body (application/json)
@@ -27,7 +27,7 @@ GET /api/v1/links/<pay_id>GET /lnurlp/api/v1/links/<pay_id>
Headers
{"X-Api-Key": <invoice_key>}
@@ -52,11 +52,11 @@ > - POST /api/v1/links + POST /lnurlp/api/v1/links
Headers
{"X-Api-Key": <admin_key>}
Body (application/json)
- {"description": <string> "amount": <integer>} + {"description": <string> "amount": <integer> "max": <integer> "min": <integer> "comment_chars": <integer>}
Returns 201 CREATED (application/json)
@@ -64,7 +64,7 @@
Curl example
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 }}"
@@ -80,7 +80,7 @@ PUT - /api/v1/links/<pay_id>
Headers
{"X-Api-Key": <admin_key>}
@@ -111,7 +111,7 @@ DELETE - /api/v1/links/<pay_id>
Headers
{"X-Api-Key": <admin_key>}
diff --git a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html index e78ae4ac4..b839c641d 100644 --- a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html +++ b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html @@ -11,8 +11,11 @@

Charge people for using your subdomain name...
- Are you the owner of cool-domain.com and want to sell - cool-subdomain.cool-domain.com + + More details
Created by, Kris -

- - -
LNbits Subdomain extension
-
- - - {% include "subdomains/_api_docs.html" %} - -
-
+ +
+ + +
LNbits Subdomain extension
+
+ + + {% include "subdomains/_api_docs.html" %} + +
diff --git a/lnbits/static/js/base.js b/lnbits/static/js/base.js index 45b2c8917..8a4bbd345 100644 --- a/lnbits/static/js/base.js +++ b/lnbits/static/js/base.js @@ -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 () { diff --git a/lnbits/static/scss/base.scss b/lnbits/static/scss/base.scss index 5498ddfb4..32718f79e 100644 --- a/lnbits/static/scss/base.scss +++ b/lnbits/static/scss/base.scss @@ -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; diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html index 1f0fb845f..3aa7767ef 100644 --- a/lnbits/templates/base.html +++ b/lnbits/templates/base.html @@ -21,7 +21,7 @@ - + {% 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" - >flamingo - - monochrome + elSalvador + quasar + >flamingo