Path: blob/master/docker/platerec_installer/platerec_installer.py
1093 views
import argparse1import base642import logging3import os4import re5import sys6import time78import dash9import dash_bootstrap_components as dbc10import installer_helpers as helpers11from dash import Input, Output, State, clientside_callback, dcc, html12from dash.exceptions import PreventUpdate1314SHARE_LINK = "https://guides.platerecognizer.com/docs/stream/manual-install#step-2"15STREAM_PLAN_LINK = "https://app.platerecognizer.com/accounts/plan/#stream/?utm_source=installer&utm_medium=app"16SDK_PLAN_LINK = "https://app.platerecognizer.com/accounts/plan/#sdk/?utm_source=installer&utm_medium=app"17STREAM_DOCS_LINK = "https://guides.platerecognizer.com/docs/stream/configuration"18STREAM_IMAGE = "platerecognizer/alpr-stream"19SDK_IMAGE = "platerecognizer/alpr"20STREAM = "stream"21SNAPSHOT = "snapshot"22USER_DATA = "/user-data/"23NONE = {"display": "none"}24BLOCK = {"display": "block"}25FLEX = {"display": "flex"}26WIDTH = "91.5%"27DISPLAY_CARD = {"display": "block", "width": WIDTH}28CONSOLE_WELCOME = """29############################################30# Thank you for choosing Plate Recognizer! #31############################################3233- To continue open http://localhost:8050/ in your web browser. If your browser is34on a separate device, replace "localhost" by the host's IP.3536- When you are done with the installation, you can close this window.3738############################################"""394041def get_splash_screen():42return html.Div(43[44html.Div(45[46html.H2("Please choose a product:", className="splash-header"),47dbc.Button("Stream", size="lg", id="button-choose-stream"),48dbc.Button(49"Snapshot",50size="lg",51id="button-choose-snapshot",52class_name="ms-3",53),54],55className="splash",56)57],58className="background",59)606162def get_refresh(product):63docker_links = {64"Windows": "https://platerecognizer.com/docker/#install-SDK",65"Linux": "https://docs.docker.com/install/",66"Mac OS": "https://hub.docker.com/editions/community/docker-ce-desktop-mac/",67}68docker_link = docker_links.get(helpers.get_os())69docker_info = [70"Do you have Docker? If so, please run it now. "71"If not, then please go here to install Docker on your machine: ",72html.A(docker_link, href=docker_link, target="_blank"),73]74permission_error_info = [75"Got a 'permission denied error' while trying to connect to the Docker daemon. "76"Does the user running the installer able to execute Docker commands?"77]78if helpers.get_os() == "Windows":79docker_info += [80". If using the legacy Hyper-V backend and not WSL2, "81"Make sure to check the box (next to C) for ",82html.A("Resource File Sharing", href=SHARE_LINK, target="_blank"),83" and the click “Apply & Restart”.",84]85return dbc.Row(86[87dbc.Label(88docker_info,89id=f"info-docker-{product}",90html_for=f"refresh-docker-{product}",91style=BLOCK,92width=7,93),94dbc.Label(95permission_error_info,96id=f"permissions-docker-{product}",97html_for=f"refresh-docker-{product}",98style=NONE,99width=7,100),101dcc.Loading(102type="circle", children=html.Div(id=f"loading-refresh-{product}")103),104dbc.Col(105dbc.Button(106"Refresh", color="secondary", id=f"refresh-docker-{product}"107),108width=4,109),110],111style=BLOCK,112id=f"refresh-{product}",113)114115116def get_update(product):117return dbc.Row(118[119dbc.Col(120[121dbc.Button(122"Update",123color="secondary",124id=f"update-image-{product}",125style={"width": "100%"},126),127html.Span(128"Updated", id=f"span-update-{product}", className="align-middle"129),130],131width=2,132),133dbc.Label(134"Update the Docker image.",135html_for=f"update-image-{product}",136className="col-auto align-self-center",137),138dcc.Loading(139type="circle",140children=html.Div(id=f"loading-update-{product}"),141parent_className="col-auto",142),143],144style=NONE,145id=f"update-{product}",146class_name="mb-3",147)148149150def get_uninstall(product):151return dbc.Row(152[153dbc.Col(154[155dbc.Button(156"Uninstall",157color="danger",158id=f"uninstall-image-{product}",159style={"width": "100%"},160),161html.Span(162"",163id=f"span-uninstall-{product}",164className="align-middle",165style={"color": "red"},166),167dbc.Modal(168[169dbc.ModalHeader("Uninstall"),170dbc.ModalBody(171"Are you sure you want to uninstall an image?"172),173dbc.ModalFooter(174[175dbc.Button(176"OK",177color="danger",178id=f"ok-uninstall-{product}",179),180dbc.Button(181"Cancel", id=f"cancel-uninstall-{product}"182),183]184),185],186id=f"modal-uninstall-{product}",187centered=True,188),189],190width=2,191),192dbc.Label(193"Remove the Docker image and mark the product as uninstalled.",194html_for=f"uninstall-image-{product}",195className="col-auto align-self-center",196),197dcc.Loading(198type="circle",199children=html.Div(id=f"loading-uninstall-{product}"),200parent_className="col-auto",201),202],203style=NONE,204id=f"uninstall-{product}",205class_name="mb-3",206)207208209def get_token(product):210link = STREAM_PLAN_LINK if product == "stream" else SDK_PLAN_LINK211return dbc.Row(212[213dbc.Label(214[215"Please enter your Plate Recognizer ",216html.A("API Token", href=link, target="_blank"),217":",218],219html_for=f"input-token-{product}",220width=7,221),222dbc.Col(223dbc.Input(224type="text",225id=f"input-token-{product}",226placeholder="Token",227persistence=True,228),229width=4,230),231],232class_name="mb-3",233)234235236def get_license_key(product):237link = STREAM_PLAN_LINK if product == "stream" else SDK_PLAN_LINK238return dbc.Row(239[240dbc.Label(241[242"Please enter your ",243html.A(244f"{product.capitalize()} License Key",245href=link,246target="_blank",247),248":",249],250html_for=f"input-key-{product}",251width=7,252),253dbc.Col(254dbc.Input(255type="text",256id=f"input-key-{product}",257placeholder="License Key",258persistence=True,259),260width=4,261),262],263class_name="mb-3",264)265266267def get_directory(product):268return dbc.Row(269[270dbc.Label(271f"Path to your {product.capitalize()} installation directory:",272html_for=f"input-home-{product}",273width=7,274),275dbc.Col(276dbc.Input(277value=helpers.get_home(),278type="text",279id=f"input-home-{product}",280placeholder="Path to directory",281persistence=True,282),283width=4,284),285],286class_name="mb-3",287)288289290def get_boot(product):291return dbc.Row(292[293dbc.Label(294[295f"Do you want {product.capitalize()} to automatically start on system startup?"296],297html_for=f"check-boot-{product}",298width=7,299),300dbc.Col(301dbc.Checkbox(id=f"check-boot-{product}", className="align-bottom"),302width=4,303),304],305class_name="mb-3",306)307308309def get_local_config():310return dbc.Row(311[312dbc.Label(313["Do you want to modify your stream configuration locally?"],314html_for="check-config-local",315width=7,316),317dbc.Col(318dbc.Checkbox(id="check-config-local", className="align-bottom"), width=4319),320],321class_name="mb-3",322)323324325def get_port(product):326return dbc.Row(327[328dbc.Label(329["Set the container port (default is 8080):"],330html_for=f"input-port-{product}",331width=7,332),333dbc.Col(334dbc.Input(335type="text",336id=f"input-port-{product}",337value="8080",338placeholder="Port",339persistence=True,340),341width=4,342),343],344class_name="mb-3",345)346347348def get_hardware_dropdown(product):349return dbc.Row(350[351dbc.Label(352"Docker image to use:", html_for=f"dropdown-hardware-{product}", width=7353),354dbc.Col(355dcc.Dropdown(356options=[357{"label": "Intel CPU", "value": "platerecognizer/alpr:latest"},358{359"label": "Raspberry",360"value": "platerecognizer/alpr-raspberry-pi:latest",361},362{363"label": "GPU (Nvidia Only)",364"value": "platerecognizer/alpr-gpu:latest",365},366{367"label": "Jetson Nano",368"value": "platerecognizer/alpr-jetson:latest",369},370{371"label": "ZCU104",372"value": "platerecognizer/alpr-zcu104:latest",373},374{"label": "Thailand", "value": "platerecognizer/alpr:thailand"},375],376value="platerecognizer/alpr:latest",377clearable=False,378id=f"dropdown-hardware-{product}",379style={"borderRadius": "0"},380persistence=True,381),382width=4,383),384],385class_name="mb-3",386)387388389def get_video_checkbox(product):390return dbc.Row(391[392dbc.Label(393[f"Use {product.capitalize()} on a local video file."],394html_for=f"check-video-{product}",395id=f"label-video-{product}",396width=7,397),398dbc.Col(399dbc.Checkbox(400id=f"check-video-{product}",401className="align-bottom",402persistence=True,403),404width=4,405),406],407class_name="mb-3",408)409410411def get_video_picker(product):412return dbc.Row(413[414dbc.Label(415[416f"Select a video file. If it is not inside your {product.capitalize()} folder, we will copy it there. Big files (~400Mb) may slow down your system."417],418html_for=f"pickup-video-{product}",419id=f"label-pickup-{product}",420width=7,421),422dcc.Loading(423type="circle", children=html.Div(id=f"loading-upload-{product}")424),425dbc.Col(426[427dcc.Upload(428[429dbc.Button("Upload File"),430html.Span(431"", id=f"span-videopath-{product}", className="me-2"432),433],434id=f"pickup-video-{product}",435accept="video/*",436)437],438width=4,439),440],441id=f"pickup-{product}",442class_name="mb-3",443)444445446def get_config_label(product):447return html.P(448[449"Edit your Stream configuration file. See the ",450html.A("documentation", href=STREAM_DOCS_LINK, target="_blank"),451" for details.",452],453id=f"label-config-{product}",454)455456457def get_config_body(product):458return dbc.Textarea(459size="sm",460class_name="mb-3",461id=f"area-config-{product}",462style={"width": WIDTH, "height": "300px"},463)464465466def get_status(product):467return html.P(468children="", style={"color": "red"}, className="mb-0", id=f"p-status-{product}"469)470471472def get_config_status():473return html.P(474children="", style={"color": "red"}, className="mb-0", id="p-status-config"475)476477478def get_success_card(product):479sdk_endpoint = ""480if product == SNAPSHOT:481sdk_endpoint = [482html.P(483" To use the SDK endpoint call: ",484className="card-title mt-3 mb-0",485style={"display": "inline-block"},486),487html.Code(id="curl-snapshot"),488]489return dbc.CardBody(490[491html.P(492f"You can now start {product.capitalize()}. Open a terminal and type the command below. You can save this command for future use.",493className="card-title",494),495html.Code(className="card-text d-block", id=f"command-{product}"),496html.Div(497[498html.Button(499"copy to clipboard",500id=f"copy-{product}",501**{"data-clipboard-target": f"#command-{product}"},502className="btn btn-sm btn-warning",503style={"borderRadius": "15px"},504),505html.Span(506id=f"copy-status-{product}",507className="ms-2",508style={"fontSize": "13px", "color": "green"},509),510],511className="mt-3",512),513html.P(sdk_endpoint, className="my-0"),514]515)516517518def get_continue(product):519return dbc.Row(520[521dbc.Col(522[523dbc.Button(524"Show Docker Command",525color="primary",526id=f"button-submit-{product}",527style={"width": "100%"},528)529],530width=2,531),532dbc.Label(533"Confirm settings and show docker command.",534html_for=f"button-submit-{product}",535className="col-auto align-self-center",536),537],538class_name="mt-3 mb-3",539)540541542def get_loading_submit(product):543return dcc.Loading(type="circle", children=html.Div(id=f"loading-submit-{product}"))544545546def get_confirm(product):547return (548dcc.ConfirmDialogProvider(549children=html.Button("Click Me"),550id="danger-danger-provider",551message="Danger danger! Are you sure you want to continue?",552),553)554555556def edit_config():557return dbc.Row(558[559dbc.Col(560[561dbc.Button(562"Configure",563color="success",564id="button-stream-config",565style={"width": "100%"},566)567],568width=2,569),570dbc.Label(571"Edit configurations for your Stream license",572html_for="button-stream-config",573className="col-auto align-self-center",574),575],576class_name="mt-3 mb-3",577)578579580############581# Dash App #582############583584app = dash.Dash(585__name__,586title="Plate Recognizer Installer",587assets_folder=helpers.resource_path("assets"),588external_stylesheets=[dbc.themes.YETI],589external_scripts=[590"https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"591],592)593594app.layout = dbc.Container(595[596get_splash_screen(),597html.H2(children="Plate Recognizer Installer", className="text-center my-3"),598dbc.Tabs(599[600dbc.Tab(601[602dbc.Form([get_refresh(STREAM)], class_name="mt-3"),603dbc.Form(604[605get_token(STREAM),606get_license_key(STREAM),607get_directory(STREAM),608get_boot(STREAM),609get_local_config(),610get_video_checkbox(STREAM),611get_video_picker(STREAM),612],613style=NONE,614class_name="mt-3",615id=f"form-{STREAM}",616),617html.Div(618[619get_config_label(STREAM),620get_config_body(STREAM),621get_config_status(),622get_status(STREAM),623dbc.Card(624[get_success_card(STREAM)],625id=f"card-{STREAM}",626className="mt-3",627style=NONE,628),629get_loading_submit(STREAM),630],631id=f"footer-{STREAM}",632className="mt-3",633style=NONE,634),635dbc.Form(636[637edit_config(),638get_continue(STREAM),639get_update(STREAM),640get_uninstall(STREAM),641],642style=NONE,643id=f"form-update-{STREAM}",644),645],646label=STREAM.capitalize(),647tab_id=STREAM,648className="offset-md-1 stream-tab",649),650dbc.Tab(651[652dbc.Form([get_refresh(SNAPSHOT)], class_name="mt-3"),653dbc.Form(654[655get_token(SNAPSHOT),656get_license_key(SNAPSHOT),657get_boot(SNAPSHOT),658get_port(SNAPSHOT),659get_hardware_dropdown(SNAPSHOT),660],661style=NONE,662class_name="mt-3",663id=f"form-{SNAPSHOT}",664),665html.Div(666[667get_status(SNAPSHOT),668dbc.Card(669[get_success_card(SNAPSHOT)],670id=f"card-{SNAPSHOT}",671className="mt-3",672style=NONE,673),674get_loading_submit(SNAPSHOT),675],676id=f"footer-{SNAPSHOT}",677className="mt-3",678style=NONE,679),680dbc.Form(681[682get_continue(SNAPSHOT),683get_update(SNAPSHOT),684get_uninstall(SNAPSHOT),685],686style=NONE,687id=f"form-update-{SNAPSHOT}",688),689],690label=SNAPSHOT.capitalize(),691tab_id=SNAPSHOT,692className="offset-md-1 snapshot-tab",693),694],695id="tabs",696active_tab=STREAM,697style={"width": "84%"},698class_name="offset-md-1 justify-content-center",699),700]701)702703704@app.callback(705[706Output("refresh-stream", "style"),707Output("update-stream", "style"),708Output("form-stream", "style"),709Output("form-update-stream", "style"),710Output("footer-stream", "style"),711Output("loading-refresh-stream", "children"),712Output("info-docker-stream", "style"),713Output("permissions-docker-stream", "style"),714],715[716Input("refresh-docker-stream", "n_clicks"),717Input("ok-uninstall-stream", "n_clicks"),718],719)720def refresh_docker_stream(n_clicks, uninstall):721try:722docker_is_ok = helpers.verify_docker_install()723except helpers.DockerPermissionError:724return FLEX, NONE, NONE, NONE, NONE, None, NONE, BLOCK725if docker_is_ok:726if (727dash.callback_context.triggered[0]["prop_id"]728== "ok-uninstall-stream.n_clicks"729):730time.sleep(2)731return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE732if helpers.get_image(STREAM_IMAGE):733return NONE, FLEX, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE734else:735return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE736else:737return FLEX, NONE, NONE, NONE, NONE, None, BLOCK, NONE738739740@app.callback(741[742Output("refresh-snapshot", "style"),743Output("update-snapshot", "style"),744Output("form-snapshot", "style"),745Output("form-update-snapshot", "style"),746Output("footer-snapshot", "style"),747Output("loading-refresh-snapshot", "children"),748Output("info-docker-snapshot", "style"),749Output("permissions-docker-snapshot", "style"),750],751[752Input("refresh-docker-snapshot", "n_clicks"),753Input("dropdown-hardware-snapshot", "value"),754Input("ok-uninstall-snapshot", "n_clicks"),755],756)757def refresh_docker_snapshot(n_clicks, hardware, uninstall):758try:759docker_is_ok = helpers.verify_docker_install()760except helpers.DockerPermissionError:761return FLEX, NONE, NONE, NONE, NONE, None, NONE, BLOCK762if docker_is_ok:763if (764dash.callback_context.triggered[0]["prop_id"]765== "ok-uninstall-snapshot.n_clicks"766):767time.sleep(2)768return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE769if helpers.get_image(hardware):770return NONE, FLEX, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE771else:772return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE773else:774return FLEX, NONE, NONE, NONE, NONE, None, BLOCK, NONE775776777@app.callback(778[Output("pickup-stream", "style")], [Input("check-video-stream", "value")]779)780def select_video(checked):781if checked:782return [FLEX]783else:784return [NONE]785786787@app.callback(788[789Output("pickup-video-stream", "style"),790Output("label-pickup-stream", "style"),791Output("check-video-stream", "style"),792Output("label-video-stream", "style"),793Output("area-config-stream", "style"),794Output("label-config-stream", "style"),795],796[Input("check-config-local", "value")],797)798def local_config(checked):799if checked:800config = {"display": "block", "width": WIDTH, "height": "300px"}801return [FLEX, FLEX, FLEX, FLEX, config, FLEX]802else:803return [NONE, NONE, NONE, NONE, NONE, NONE]804805806@app.callback(807[808Output("span-videopath-stream", "children"),809Output("loading-upload-stream", "children"),810],811[812Input("pickup-video-stream", "contents"),813State("pickup-video-stream", "filename"),814State("input-home-stream", "value"),815],816)817def set_videopath(content, name, path):818if content and name and path:819return [name, None]820elif name and path:821return ["File error", None]822else:823raise PreventUpdate824825826@app.callback(827[828Output("update-image-stream", "disabled"),829Output("span-update-stream", "style"),830Output("loading-update-stream", "children"),831],832[Input("update-image-stream", "n_clicks"), Input("tabs", "active_tab")],833)834def update_image_stream(n_clicks, tab):835if dash.callback_context.triggered[0]["prop_id"] == "update-image-stream.n_clicks":836helpers.pull_docker(STREAM_IMAGE)837return False, {"display": "inline", "color": "green"}, None838return False, NONE, None839840841@app.callback(842[843Output("update-image-snapshot", "disabled"),844Output("span-update-snapshot", "style"),845Output("loading-update-snapshot", "children"),846],847[848Input("update-image-snapshot", "n_clicks"),849Input("tabs", "active_tab"),850Input("dropdown-hardware-snapshot", "value"),851],852)853def update_image_snapshot(n_clicks, tab, hardware):854if (855dash.callback_context.triggered[0]["prop_id"]856== "update-image-snapshot.n_clicks"857):858helpers.pull_docker(hardware)859return False, {"display": "inline", "color": "green"}, None860return False, NONE, None861862863@app.callback(864Output("modal-uninstall-stream", "is_open"),865[866Input("uninstall-image-stream", "n_clicks"),867Input("ok-uninstall-stream", "n_clicks"),868Input("cancel-uninstall-stream", "n_clicks"),869],870[State("modal-uninstall-stream", "is_open")],871)872def toggle_modal_stream(n1, n2, n3, is_open):873if n1 or n2 or n3:874return not is_open875return is_open876877878@app.callback(879Output("modal-uninstall-snapshot", "is_open"),880[881Input("uninstall-image-snapshot", "n_clicks"),882Input("ok-uninstall-snapshot", "n_clicks"),883Input("cancel-uninstall-snapshot", "n_clicks"),884],885[State("modal-uninstall-snapshot", "is_open")],886)887def toggle_modal_snapshot(n1, n2, n3, is_open):888if n1 or n2 or n3:889return not is_open890return is_open891892893@app.callback(894[Output("uninstall-stream", "style")], [Input("ok-uninstall-stream", "n_clicks")]895)896def uninstall_button_stream(n_clicks):897if dash.callback_context.triggered[0]["prop_id"] == "ok-uninstall-stream.n_clicks":898time.sleep(2)899return [NONE]900if not helpers.verify_docker_install():901return [NONE]902if helpers.get_image(STREAM_IMAGE):903return [FLEX]904else:905return [NONE]906907908@app.callback(909[Output("uninstall-snapshot", "style")],910[911Input("ok-uninstall-snapshot", "n_clicks"),912Input("dropdown-hardware-snapshot", "value"),913],914)915def uninstall_button_snapshot(n_clicks, hardware):916if (917dash.callback_context.triggered[0]["prop_id"]918== "ok-uninstall-snapshot.n_clicks"919):920time.sleep(2)921return [NONE]922if not helpers.verify_docker_install():923return [NONE]924if helpers.get_image(hardware):925return [FLEX]926else:927return [NONE]928929930clientside_callback(931"""932function(n_clicks) {933if (n_clicks > 0) {934const key = document.getElementById("input-key-stream").value;935if (key) {936const url = "https://app.platerecognizer.com/stream-config/" + key;937window.open(url, "_blank");938} else {939const statusConfig = document.getElementById("p-status-config");940statusConfig.innerHTML = "License key is required.";941}942}943}944""",945[Output("button-stream-config", "style"), Output("p-status-config", "children")],946[Input("button-stream-config", "n_clicks"), State("input-key-stream", "value")],947prevent_initial_call=True,948)949950951@app.callback(952[953Output("loading-uninstall-stream", "children"),954Output("span-uninstall-stream", "children"),955],956[957Input("ok-uninstall-stream", "n_clicks"),958State("input-token-stream", "value"),959State("input-key-stream", "value"),960],961)962def uninstall_stream(n_clicks, token, key):963if dash.callback_context.triggered[0]["prop_id"] == "ok-uninstall-stream.n_clicks":964if not helpers.get_image(STREAM_IMAGE):965return [None, "Image already uninstalled."]966helpers.stop_container(STREAM_IMAGE)967return helpers.uninstall_docker_image(STREAM_IMAGE)968raise PreventUpdate969970971@app.callback(972[973Output("loading-uninstall-snapshot", "children"),974Output("span-uninstall-snapshot", "children"),975],976[977Input("ok-uninstall-snapshot", "n_clicks"),978Input("dropdown-hardware-snapshot", "value"),979State("input-token-snapshot", "value"),980State("input-key-snapshot", "value"),981],982)983def uninstall_snapshot(n_clicks, hardware, token, key):984if (985dash.callback_context.triggered[0]["prop_id"]986== "dropdown-hardware-snapshot.value"987):988return [None, ""]989if (990dash.callback_context.triggered[0]["prop_id"]991== "ok-uninstall-snapshot.n_clicks"992):993if not helpers.get_image(hardware):994return [None, "Image already uninstalled."]995verification = helpers.verify_token(token, key, product=SNAPSHOT)996if verification[0]:997helpers.stop_container(hardware)998cmd = f"docker run --rm -t -v license:/license -e TOKEN={token} -e LICENSE_KEY={key} -e UNINSTALL=1 {hardware}"999os.system(cmd)1000return helpers.uninstall_docker_image(hardware)1001else:1002return [None, verification[1]]1003raise PreventUpdate100410051006@app.callback(1007Output("area-config-stream", "value"),1008[1009Input("input-home-stream", "value"),1010Input("pickup-video-stream", "filename"),1011State("check-video-stream", "value"),1012],1013)1014def change_path(home, videofile, videocheck):1015config = helpers.read_config(home)1016if videofile and videocheck: # replace url with a path to video1017url = re.search("url = (.*)\n", config).group(1)1018config = re.sub(url, f"{USER_DATA}{videofile}", config)1019return config102010211022@app.callback(1023[1024Output("p-status-stream", "children"),1025Output("card-stream", "style"),1026Output("command-stream", "children"),1027Output("loading-submit-stream", "children"),1028],1029[1030Input("area-config-stream", "value"),1031Input("button-submit-stream", "n_clicks"),1032Input("input-token-stream", "value"),1033Input("input-key-stream", "value"),1034Input("input-home-stream", "value"),1035Input("check-boot-stream", "value"),1036State("pickup-video-stream", "contents"),1037State("pickup-video-stream", "filename"),1038State("check-video-stream", "value"),1039],1040)1041def submit_stream(1042config, n_clicks, token, key, home, boot, videocontent, videofile, videocheck1043):1044if dash.callback_context.triggered[0]["prop_id"] == "button-submit-stream.n_clicks":1045is_valid, error = helpers.verify_token(token, key, product="stream")1046if is_valid:1047write_result, error = helpers.write_config(home, config)1048if not write_result:1049return error, NONE, "", None1050if videocontent and videofile and videocheck:1051content_type, content_string = videocontent.split(",")1052decoded = base64.b64decode(content_string)1053with open(os.path.join(home, videofile), "wb") as video:1054video.write(decoded)1055user_info = ""1056nvidia = ""1057image_tag = ""1058autoboot = "--rm"1059if helpers.get_os() != "Windows":1060user_info = "--user `id -u`:`id -g`"1061if os.path.exists("/etc/nv_tegra_release"):1062nvidia = "--runtime nvidia --privileged --group-add video"1063image_tag = ":jetson"1064if boot:1065autoboot = "--restart unless-stopped"1066if not helpers.get_image(STREAM_IMAGE):1067helpers.pull_docker(STREAM_IMAGE)1068command = (1069f"docker run {autoboot} -t "1070f"{nvidia} --name stream "1071f'-v "{home}:{USER_DATA}" '1072f"{user_info} "1073f"-e LICENSE_KEY={key} "1074f"-e TOKEN={token} "1075f"{STREAM_IMAGE}{image_tag}"1076)1077return "", DISPLAY_CARD, command, None1078else:1079return error, NONE, "", None1080else:1081return "", NONE, "", None108210831084@app.callback(1085[1086Output("p-status-snapshot", "children"),1087Output("card-snapshot", "style"),1088Output("command-snapshot", "children"),1089Output("curl-snapshot", "children"),1090Output("loading-submit-snapshot", "children"),1091],1092[1093Input("button-submit-snapshot", "n_clicks"),1094Input("input-token-snapshot", "value"),1095Input("input-key-snapshot", "value"),1096Input("check-boot-snapshot", "value"),1097Input("input-port-snapshot", "value"),1098Input("dropdown-hardware-snapshot", "value"),1099],1100)1101def submit_snapshot(n_clicks, token, key, boot, port, hardware):1102if (1103dash.callback_context.triggered[0]["prop_id"]1104== "button-submit-snapshot.n_clicks"1105):1106is_valid, error = helpers.verify_token(token, key, product="snapshot")1107if is_valid:1108autoboot = "--restart unless-stopped" if boot else "--rm"1109if not helpers.is_valid_port(port):1110return "Wrong port", NONE, "", None, None1111if not helpers.get_image(hardware):1112helpers.pull_docker(hardware)1113gpus = "--gpus all" if "gpu" in hardware else ""1114nvidia = "--runtime nvidia" if "jetson" in hardware else ""1115command = (1116f"docker run {gpus} {nvidia} {autoboot} "1117f"-t -p {port}:8080 "1118f"-v license:/license "1119f"-e LICENSE_KEY={key} "1120f"-e TOKEN={token} "1121f"{hardware}"1122)1123curl = f' curl -F "upload=@my_file.jpg" http://localhost:{port}/v1/plate-reader/'1124return "", DISPLAY_CARD, command, curl, None1125else:1126return error, NONE, "", None, None1127else:1128return "", NONE, "", None, None112911301131@app.callback(1132Output("copy-status-stream", "children"),1133[Input("copy-stream", "n_clicks"), Input("button-submit-stream", "n_clicks")],1134)1135def copy_to_clipboard_stream(n_clicks, n_clicks_submit):1136if dash.callback_context.triggered[0]["prop_id"] == "copy-stream.n_clicks":1137return "Item copied to clipboard."1138else:1139return ""114011411142@app.callback(1143Output("copy-status-snapshot", "children"),1144[Input("copy-snapshot", "n_clicks"), Input("button-submit-snapshot", "n_clicks")],1145)1146def copy_to_clipboard_snapshot(n_clicks, n_clicks_submit):1147if dash.callback_context.triggered[0]["prop_id"] == "copy-snapshot.n_clicks":1148return "Item copied to clipboard."1149else:1150return ""115111521153def parse_arguments():1154parser = argparse.ArgumentParser(description="")1155parser.add_argument("--debug", action="store_true")1156return parser.parse_args()115711581159if __name__ == "__main__":1160args = parse_arguments()1161print(CONSOLE_WELCOME)1162if args.debug:1163app.run_server(debug=True, host="0.0.0.0")1164else:1165helpers.launch_browser("http://127.0.0.1:8050/")11661167# Update log levels1168app.logger.setLevel(logging.ERROR)1169log = logging.getLogger("werkzeug")1170log.setLevel(logging.ERROR)1171cli = sys.modules["flask.cli"]1172cli.show_server_banner = lambda *_: None # type: ignore11731174# Start server1175app.run_server(1176debug=False, host="0.0.0.0", dev_tools_silence_routes_logging=True1177)117811791180