diff --git a/lnbits/extensions/livestream/lnurl.py b/lnbits/extensions/livestream/lnurl.py index 63523f339..e3e1b1be7 100644 --- a/lnbits/extensions/livestream/lnurl.py +++ b/lnbits/extensions/livestream/lnurl.py @@ -1,12 +1,9 @@ -import hashlib import math from http import HTTPStatus -from os import name -from fastapi.exceptions import HTTPException -from fastapi.params import Query +from fastapi import HTTPException, Query, Request from lnurl import LnurlErrorResponse, LnurlPayActionResponse, LnurlPayResponse -from starlette.requests import Request # type: ignore +from lnurl.models import ClearnetUrl, LightningInvoice, MilliSatoshi from lnbits.core.services import create_invoice @@ -29,9 +26,12 @@ async def lnurl_livestream(ls_id, request: Request): ) resp = LnurlPayResponse( - callback=request.url_for("livestream.lnurl_callback", track_id=track.id), - min_sendable=track.min_sendable, - max_sendable=track.max_sendable, + callback=ClearnetUrl( + request.url_for("livestream.lnurl_callback", track_id=track.id), + scheme="https", + ), + minSendable=MilliSatoshi(track.min_sendable), + maxSendable=MilliSatoshi(track.max_sendable), metadata=await track.lnurlpay_metadata(), ) @@ -48,9 +48,12 @@ async def lnurl_track(track_id, request: Request): raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Track not found.") resp = LnurlPayResponse( - callback=request.url_for("livestream.lnurl_callback", track_id=track.id), - min_sendable=track.min_sendable, - max_sendable=track.max_sendable, + callback=ClearnetUrl( + request.url_for("livestream.lnurl_callback", track_id=track.id), + scheme="https", + ), + minSendable=MilliSatoshi(track.min_sendable), + maxSendable=MilliSatoshi(track.max_sendable), metadata=await track.lnurlpay_metadata(), ) @@ -85,6 +88,7 @@ async def lnurl_callback( ).dict() ls = await get_livestream_by_track(track_id) + assert ls extra_amount = amount_received - int(amount_received * (100 - ls.fee_pct) / 100) @@ -101,13 +105,14 @@ async def lnurl_callback( }, ) + assert track.price_msat if amount_received < track.price_msat: success_action = None else: success_action = track.success_action(payment_hash, request=request) resp = LnurlPayActionResponse( - pr=payment_request, success_action=success_action, routes=[] + pr=LightningInvoice(payment_request), successAction=success_action, routes=[] ) return resp.dict() diff --git a/lnbits/extensions/livestream/models.py b/lnbits/extensions/livestream/models.py index 0034f4a79..5d617da99 100644 --- a/lnbits/extensions/livestream/models.py +++ b/lnbits/extensions/livestream/models.py @@ -1,13 +1,12 @@ import json from typing import Optional -from fastapi import Query +from fastapi import Query, Request from lnurl import Lnurl -from lnurl import encode as lnurl_encode # type: ignore -from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore -from lnurl.types import LnurlPayMetadata # type: ignore +from lnurl import encode as lnurl_encode +from lnurl.models import ClearnetUrl, Max144Str, UrlAction +from lnurl.types import LnurlPayMetadata from pydantic import BaseModel -from starlette.requests import Request class CreateTrack(BaseModel): @@ -32,7 +31,7 @@ class Livestream(BaseModel): class Track(BaseModel): id: int download_url: Optional[str] - price_msat: Optional[int] + price_msat: int = 0 name: str producer: int @@ -71,7 +70,7 @@ class Track(BaseModel): def success_action( self, payment_hash: str, request: Request - ) -> Optional[LnurlPaySuccessAction]: + ) -> Optional[UrlAction]: if not self.download_url: return None @@ -79,7 +78,8 @@ class Track(BaseModel): url_with_query = f"{url}?p={payment_hash}" return UrlAction( - url=url_with_query, description=f"Download the track {self.name}!" + url=ClearnetUrl(url_with_query, scheme="https"), + description=Max144Str(f"Download the track {self.name}!"), ) diff --git a/lnbits/extensions/livestream/views.py b/lnbits/extensions/livestream/views.py index 97f803a31..ca12f16bd 100644 --- a/lnbits/extensions/livestream/views.py +++ b/lnbits/extensions/livestream/views.py @@ -1,20 +1,16 @@ from http import HTTPStatus -from fastapi.param_functions import Depends -from fastapi.params import Query -from starlette.exceptions import HTTPException -from starlette.requests import Request +from fastapi import Depends, HTTPException, Query, Request +from starlette.datastructures import URL from starlette.responses import HTMLResponse, RedirectResponse from lnbits.core.crud import get_wallet_payment -from lnbits.core.models import Payment, User +from lnbits.core.models import User from lnbits.decorators import check_user_exists from . import livestream_ext, livestream_renderer from .crud import get_livestream_by_track, get_track -# from mmap import MAP_DENYWRITE - @livestream_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): @@ -28,12 +24,18 @@ async def track_redirect_download(track_id, p: str = Query(...)): payment_hash = p track = await get_track(track_id) ls = await get_livestream_by_track(track_id) - payment: Payment = await get_wallet_payment(ls.wallet, payment_hash) + assert ls + payment = await get_wallet_payment(ls.wallet, payment_hash) if not payment: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, - detail=f"Couldn't find the payment {payment_hash} or track {track.id}.", + detail=f"Couldn't find the payment {payment_hash}.", + ) + if not track: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Couldn't find the track {track_id}.", ) if payment.pending: @@ -41,4 +43,6 @@ async def track_redirect_download(track_id, p: str = Query(...)): status_code=HTTPStatus.PAYMENT_REQUIRED, detail=f"Payment {payment_hash} wasn't received yet. Please try again in a minute.", ) - return RedirectResponse(url=track.download_url) + + assert track.download_url + return RedirectResponse(url=URL(track.download_url)) diff --git a/lnbits/extensions/livestream/views_api.py b/lnbits/extensions/livestream/views_api.py index 0c169a71f..63a017428 100644 --- a/lnbits/extensions/livestream/views_api.py +++ b/lnbits/extensions/livestream/views_api.py @@ -1,9 +1,7 @@ from http import HTTPStatus -from fastapi.param_functions import Depends +from fastapi import Depends, HTTPException, Request from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl -from starlette.exceptions import HTTPException -from starlette.requests import Request # type: ignore from lnbits.decorators import WalletTypeInfo, get_key_type from lnbits.extensions.livestream.models import CreateTrack @@ -27,6 +25,7 @@ async def api_livestream_from_wallet( req: Request, g: WalletTypeInfo = Depends(get_key_type) ): ls = await get_or_create_livestream_by_wallet(g.wallet.id) + assert ls tracks = await get_tracks(ls.id) producers = await get_producers(ls.id) @@ -55,17 +54,17 @@ async def api_update_track(track_id, g: WalletTypeInfo = Depends(get_key_type)): id = int(track_id) except ValueError: id = 0 - if id <= 0: - id = None ls = await get_or_create_livestream_by_wallet(g.wallet.id) - await update_current_track(ls.id, id) + assert ls + await update_current_track(ls.id, None if id <= 0 else id) return "", HTTPStatus.NO_CONTENT @livestream_ext.put("/api/v1/livestream/fee/{fee_pct}") async def api_update_fee(fee_pct, g: WalletTypeInfo = Depends(get_key_type)): ls = await get_or_create_livestream_by_wallet(g.wallet.id) + assert ls await update_livestream_fee(ls.id, int(fee_pct)) return "", HTTPStatus.NO_CONTENT @@ -76,9 +75,10 @@ async def api_add_track( data: CreateTrack, id=None, g: WalletTypeInfo = Depends(get_key_type) ): ls = await get_or_create_livestream_by_wallet(g.wallet.id) + assert ls if data.producer_id: - p_id = data.producer_id + p_id = int(data.producer_id) elif data.producer_name: p_id = await add_producer(ls.id, data.producer_name) else: @@ -96,5 +96,6 @@ async def api_add_track( @livestream_ext.delete("/api/v1/livestream/tracks/{track_id}") async def api_delete_track(track_id, g: WalletTypeInfo = Depends(get_key_type)): ls = await get_or_create_livestream_by_wallet(g.wallet.id) + assert ls await delete_track_from_livestream(ls.id, track_id) return "", HTTPStatus.NO_CONTENT