def init_app(self, app): ws_route = app.config.get('TURBO_WEBSOCKET_ROUTE', '/turbo-stream') if ws_route: self.sock = Sock() @self.sock.route(ws_route) def turbo_stream(ws): user_id = self.user_id_callback() if user_id not in self.clients: self.clients[user_id] = [] self.clients[user_id].append(ws) try: while True: ws.receive(timeout=10) except ConnectionClosed: self.clients[user_id].remove(ws) if not self.clients[user_id]: del self.clients[user_id] self.sock.init_app(app) app.context_processor(self.context_processor)
def _setup_single_view_dispatcher_route( blueprint: Blueprint, options: Options, constructor: RootComponentConstructor) -> None: sock = Sock(blueprint) def model_stream(ws: WebSocket, path: str = "") -> None: def send(value: Any) -> None: ws.send(json.dumps(value)) def recv() -> LayoutEvent: return LayoutEvent(**json.loads(ws.receive())) _dispatch_in_thread(ws, path, constructor(), send, recv) sock.route("/_api/stream", endpoint="without_path")(model_stream) sock.route("/<path:path>/_api/stream", endpoint="with_path")(model_stream)
class Turbo: def __init__(self, app=None): self.user_id_callback = self.default_user_id self.sock = None self.clients = {} if app: self.init_app(app) def init_app(self, app): ws_route = app.config.get('TURBO_WEBSOCKET_ROUTE', '/turbo-stream') if ws_route: self.sock = Sock() @self.sock.route(ws_route) def turbo_stream(ws): user_id = self.user_id_callback() if user_id not in self.clients: self.clients[user_id] = [] self.clients[user_id].append(ws) try: while True: ws.receive(timeout=10) except ConnectionClosed: self.clients[user_id].remove(ws) if not self.clients[user_id]: del self.clients[user_id] self.sock.init_app(app) app.context_processor(self.context_processor) def user_id(self, f): """Configure an application-specific user id generator, to allow the application to push updates over WebSocket to individual clients. Example: @turbo.user_id def get_user_id(): return current_user.id """ self.user_id_callback = f return f def default_user_id(self): """Default user id generator. An application-specific function can be configured with the `@user_id` decorator.""" return uuid.uuid4().hex def turbo(self, version=_VER, url=None): """Add turbo.js to the template. Add `{{ turbo() }}` in the `<head>` section of your main template. """ if url is None: url = f'{_CDN}/pin/{_PKG}@{version}/min/{_PKG}.js' ws_route = current_app.config.get('TURBO_WEBSOCKET_ROUTE', '/turbo-stream') if ws_route: return Markup(f'''<script type="module"> import * as Turbo from "{url}"; Turbo.connectStreamSource(new WebSocket(`ws://${{location.host}}/{ws_route}`)); </script>''') else: return Markup(f'<script type="module" src="{url}"></script>') def context_processor(self): return {'turbo': self.turbo} def requested_frame(self): """Returns the target frame the client expects, or `None`.""" return request.headers.get('Turbo-Frame') def can_stream(self): """Returns `True` if the client accepts turbo stream reponses.""" stream_mimetype = 'text/vnd.turbo-stream.html' best = request.accept_mimetypes.best_match( [stream_mimetype, 'text/html']) return best == stream_mimetype def can_push(self, to=None): """Returns `True` if the client accepts turbo stream updates over WebSocket. :param to: the id of the client. If not given then the answer is `True` if there is at least one client listening to updates over WebSocket. """ if to is None: return self.clients != {} return to in self.clients def _make_stream(self, action, content, target): return (f'<turbo-stream action="{action}" target="{target}">' f'<template>{content}</template></turbo-stream>') def append(self, content, target): """Create an append stream. :param content: the HTML content to include in the stream. :param target: the target ID for this change. """ return self._make_stream('append', content, target) def prepend(self, content, target): """Create a prepend stream. :param content: the HTML content to include in the stream. :param target: the target ID for this change. """ return self._make_stream('prepend', content, target) def replace(self, content, target): """Create a replace stream. :param content: the HTML content to include in the stream. :param target: the target ID for this change. """ return self._make_stream('replace', content, target) def update(self, content, target): """Create an update stream. :param content: the HTML content to include in the stream. :param target: the target ID for this change. """ return self._make_stream('update', content, target) def remove(self, target): """Create a remove stream. :param target: the target ID for this change. """ return self._make_stream('remove', '', target) def stream(self, stream): """Create a turbo stream response. :param stream: one or a list of streamed responses generated by the `append()`, `prepend()`, `replace()`, `update()` and `remove()` methods. """ return current_app.response_class( stream, mimetype='text/vnd.turbo-stream.html') def push(self, stream, to=None): """Push a turbo stream update over WebSocket to one or more clients. :param stream: one or a list of stream updates generated by the `append()`, `prepend()`, `replace()`, `update()` and `remove()` methods. :param to: the id of the target client. Set to `None` to send to all connected clients, or to a list of ids to target multiple clients. """ if to in self.clients: to = [to] elif to is None: to = self.clients.keys() for recipient in to: for ws in self.clients[recipient]: ws.send(stream)
import image_utils import json from PIL import Image import traceback import legolize import palette import base64 from io import BytesIO import simple_websocket logger = utils.init_log() app = Flask(__name__) CORS(app) HOST = os.environ['HOST'] DEBUG = os.environ.get('DEBUG', 'False') sock = Sock(app) pal = palette.Palette() logger.info(f"palette loaded of {len(pal.colors)} colors") @app.route('/upload/<size>', methods=['POST']) def upload(size): uid = str(uuid.uuid4()) input_name = utils.input_name(uid) file = request.files['file'] file.save(input_name)
logging.basicConfig(level=logging.DEBUG, filename='log.txt', filemode='w', format='%(asctime)s - %(levelname)s - %(message)s', datefmt="%Y-%m-%d %H:%M:%S") console = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', "%Y-%m-%d %H:%M:%S") console.setFormatter(formatter) if args.logging_level == 'info': console.setLevel(logging.INFO) elif args.logging_level == 'debug': console.setLevel(logging.DEBUG) logging.getLogger().addHandler(console) #------------------------------------------------------------------------- # Flask server configuration #------------------------------------------------------------------------- logging.debug('creating the api flask object') api = Flask(__name__) sock = Sock(api) CORS(api) # CORS BS that we likely don't need to worry about' #------------------------------------------------------------------------- # LED Matrix configuration # Only load these items if we are not in debug mode #------------------------------------------------------------------------- if not args.debug: logging.debug('setting up the led matrix options') from rgbmatrix import RGBMatrix, RGBMatrixOptions sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..')) options = RGBMatrixOptions() # Configuration for the matrix. Refer to the rpi-rgb-led-matrix python binding docs for the meanings of each option options = RGBMatrixOptions()