Files
media-analyzer/def/architecture/architecture_diagram.svg
2025-08-25 04:03:28 -03:00

490 lines
28 KiB
XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.42.4 (0)
-->
<!-- Title: MediaAnalyzer Pages: 1 -->
<svg width="1589pt" height="735pt"
viewBox="0.00 0.00 1589.00 735.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 731)">
<title>MediaAnalyzer</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-731 1585,-731 1585,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_legend</title>
<polygon fill="lightgray" stroke="lightgray" points="8,-573 8,-719 108,-719 108,-573 8,-573"/>
<text text-anchor="middle" x="58" y="-705.4" font-family="Arial" font-size="12.00">Legend</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_sources</title>
<polygon fill="#e3f2fd" stroke="#e3f2fd" points="116,-646 116,-719 358,-719 358,-646 116,-646"/>
<text text-anchor="middle" x="237" y="-705.4" font-family="Arial" font-size="12.00">Video Sources</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_adapters</title>
<polygon fill="#f3e5f5" stroke="#f3e5f5" points="366,-573 366,-719 648,-719 648,-573 366,-573"/>
<text text-anchor="middle" x="507" y="-705.4" font-family="Arial" font-size="12.00">Source Adapters Pattern</text>
</g>
<g id="clust4" class="cluster">
<title>cluster_core</title>
<polygon fill="#fff3e0" stroke="#fff3e0" points="463,-364.5 463,-554 653,-554 653,-364.5 463,-364.5"/>
<text text-anchor="middle" x="558" y="-540.4" font-family="Arial" font-size="12.00">Core Platform</text>
</g>
<g id="clust5" class="cluster">
<title>cluster_execution</title>
<polygon fill="#e8f5e8" stroke="#e8f5e8" points="983,-173 983,-340.5 1264,-340.5 1264,-173 983,-173"/>
<text text-anchor="middle" x="1123.5" y="-326.9" font-family="Arial" font-size="12.00">Execution Strategies Pattern</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_workers</title>
<polygon fill="#e3f2fd" stroke="#e3f2fd" points="426,-265 426,-343 849,-343 849,-265 426,-265"/>
<text text-anchor="middle" x="637.5" y="-329.4" font-family="Arial" font-size="12.00">Celery Workers (Queue Segregation)</text>
</g>
<g id="clust7" class="cluster">
<title>cluster_ai_adapters</title>
<polygon fill="#fce4ec" stroke="#fce4ec" points="1272,-100 1272,-246 1573,-246 1573,-100 1272,-100"/>
<text text-anchor="middle" x="1422.5" y="-232.4" font-family="Arial" font-size="12.00">Analysis Adapters Pattern</text>
</g>
<g id="clust8" class="cluster">
<title>cluster_storage</title>
<polygon fill="#f1f8e9" stroke="#f1f8e9" points="661,-364.5 661,-437.5 849,-437.5 849,-364.5 661,-364.5"/>
<text text-anchor="middle" x="755" y="-423.9" font-family="Arial" font-size="12.00">Media Storage</text>
</g>
<g id="clust9" class="cluster">
<title>cluster_frontend</title>
<polygon fill="#e8eaf6" stroke="#e8eaf6" points="857,-267.5 857,-440 975,-440 975,-267.5 857,-267.5"/>
<text text-anchor="middle" x="916" y="-426.4" font-family="Arial" font-size="12.00">Frontend</text>
</g>
<g id="clust10" class="cluster">
<title>cluster_cloud</title>
<polygon fill="#e0f2f1" stroke="#e0f2f1" points="1146,-8 1146,-81 1373,-81 1373,-8 1146,-8"/>
<text text-anchor="middle" x="1259.5" y="-67.4" font-family="Arial" font-size="12.00">GCP Services</text>
</g>
<!-- implemented -->
<g id="node1" class="node">
<title>implemented</title>
<polygon fill="#d4edda" stroke="transparent" points="100,-690 16,-690 16,-654 100,-654 100,-690"/>
<text text-anchor="middle" x="58" y="-669.5" font-family="Arial" font-size="10.00">✓ Implemented</text>
</g>
<!-- planned -->
<g id="node2" class="node">
<title>planned</title>
<polygon fill="#fff3cd" stroke="transparent" points="88.5,-617 27.5,-617 27.5,-581 88.5,-581 88.5,-617"/>
<text text-anchor="middle" x="58" y="-596.5" font-family="Arial" font-size="10.00">○ Planned</text>
</g>
<!-- implemented&#45;&gt;planned -->
<!-- webcam -->
<g id="node3" class="node">
<title>webcam</title>
<polygon fill="#d4edda" stroke="black" points="265.5,-690 210.5,-690 210.5,-654 265.5,-654 265.5,-690"/>
<text text-anchor="middle" x="238" y="-669.5" font-family="Arial" font-size="10.00">Webcam</text>
</g>
<!-- webcam_adapter -->
<g id="node7" class="node">
<title>webcam_adapter</title>
<polygon fill="#d4edda" stroke="black" points="555.5,-617 466.5,-617 466.5,-581 555.5,-581 555.5,-617"/>
<text text-anchor="middle" x="511" y="-596.5" font-family="Arial" font-size="10.00">WebcamAdapter</text>
</g>
<!-- webcam&#45;&gt;webcam_adapter -->
<g id="edge2" class="edge">
<title>webcam&#45;&gt;webcam_adapter</title>
<path fill="none" stroke="#2e7d32" d="M259.82,-654C264.61,-650.92 269.81,-648.05 275,-646 348.21,-617.09 375.16,-634.32 456.19,-616.91"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="457.37,-620.23 466.35,-614.61 455.83,-613.41 457.37,-620.23"/>
</g>
<!-- rtmp -->
<g id="node4" class="node">
<title>rtmp</title>
<polygon fill="#d4edda" stroke="black" points="192,-690 124,-690 124,-654 192,-654 192,-690"/>
<text text-anchor="middle" x="158" y="-669.5" font-family="Arial" font-size="10.00">RTMP/OBS</text>
</g>
<!-- rtmp_adapter -->
<g id="node8" class="node">
<title>rtmp_adapter</title>
<polygon fill="#d4edda" stroke="black" points="448,-617 374,-617 374,-581 448,-581 448,-617"/>
<text text-anchor="middle" x="411" y="-596.5" font-family="Arial" font-size="10.00">RtmpAdapter</text>
</g>
<!-- rtmp&#45;&gt;rtmp_adapter -->
<g id="edge3" class="edge">
<title>rtmp&#45;&gt;rtmp_adapter</title>
<path fill="none" stroke="#2e7d32" d="M184.66,-653.8C189.93,-650.89 195.53,-648.13 201,-646 254.94,-625.01 320.58,-612.57 363.96,-606.02"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="364.56,-609.47 373.95,-604.56 363.55,-602.55 364.56,-609.47"/>
</g>
<!-- files -->
<g id="node5" class="node">
<title>files</title>
<polygon fill="#fff3cd" stroke="black" points="350,-690 284,-690 284,-654 350,-654 350,-690"/>
<text text-anchor="middle" x="317" y="-669.5" font-family="Arial" font-size="10.00">File Upload</text>
</g>
<!-- file_adapter -->
<g id="node9" class="node">
<title>file_adapter</title>
<polygon fill="#fff3cd" stroke="black" points="640,-617 574,-617 574,-581 640,-581 640,-617"/>
<text text-anchor="middle" x="607" y="-596.5" font-family="Arial" font-size="10.00">FileAdapter</text>
</g>
<!-- files&#45;&gt;file_adapter -->
<g id="edge18" class="edge">
<title>files&#45;&gt;file_adapter</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M344.36,-653.75C350.02,-650.77 356.07,-648 362,-646 447.95,-617.05 476.34,-640.28 564,-617 564.1,-616.97 564.2,-616.95 564.31,-616.92"/>
<polygon fill="#f57f17" stroke="#f57f17" points="565.39,-620.25 573.92,-613.96 563.34,-613.55 565.39,-620.25"/>
</g>
<!-- base_adapter -->
<g id="node6" class="node">
<title>base_adapter</title>
<polygon fill="#e1bee7" stroke="black" points="563,-690 459,-690 459,-654 563,-654 563,-690"/>
<text text-anchor="middle" x="511" y="-675" font-family="Arial" font-size="10.00">BaseSourceAdapter</text>
<text text-anchor="middle" x="511" y="-664" font-family="Arial" font-size="10.00">(Abstract)</text>
</g>
<!-- base_adapter&#45;&gt;webcam_adapter -->
<g id="edge28" class="edge">
<title>base_adapter&#45;&gt;webcam_adapter</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M511,-653.81C511,-645.79 511,-636.05 511,-627.07"/>
<polygon fill="gray" stroke="gray" points="514.5,-627.03 511,-617.03 507.5,-627.03 514.5,-627.03"/>
</g>
<!-- base_adapter&#45;&gt;rtmp_adapter -->
<g id="edge29" class="edge">
<title>base_adapter&#45;&gt;rtmp_adapter</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M487.05,-653.99C474.03,-644.75 457.75,-633.19 443.59,-623.14"/>
<polygon fill="gray" stroke="gray" points="445.28,-620.05 435.1,-617.11 441.23,-625.76 445.28,-620.05"/>
</g>
<!-- base_adapter&#45;&gt;file_adapter -->
<g id="edge30" class="edge">
<title>base_adapter&#45;&gt;file_adapter</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M534.24,-653.81C546.74,-644.57 562.32,-633.04 575.86,-623.03"/>
<polygon fill="gray" stroke="gray" points="578.02,-625.79 583.98,-617.03 573.85,-620.16 578.02,-625.79"/>
</g>
<!-- django -->
<g id="node10" class="node">
<title>django</title>
<polygon fill="#d4edda" stroke="black" points="544.5,-525 477.5,-525 477.5,-484 544.5,-484 544.5,-525"/>
<text text-anchor="middle" x="511" y="-513" font-family="Arial" font-size="10.00">Django API</text>
<text text-anchor="middle" x="511" y="-502" font-family="Arial" font-size="10.00">+ Channels</text>
<text text-anchor="middle" x="511" y="-491" font-family="Arial" font-size="10.00">:8000</text>
</g>
<!-- webcam_adapter&#45;&gt;django -->
<g id="edge4" class="edge">
<title>webcam_adapter&#45;&gt;django</title>
<path fill="none" stroke="#2e7d32" d="M511,-580.6C511,-567.88 511,-550.32 511,-535.37"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="514.5,-535.06 511,-525.06 507.5,-535.06 514.5,-535.06"/>
</g>
<!-- rtmp_adapter&#45;&gt;django -->
<g id="edge5" class="edge">
<title>rtmp_adapter&#45;&gt;django</title>
<path fill="none" stroke="#2e7d32" d="M429.81,-580.6C444.63,-566.9 465.53,-547.56 482.41,-531.94"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="484.89,-534.42 489.85,-525.06 480.13,-529.28 484.89,-534.42"/>
</g>
<!-- file_adapter&#45;&gt;django -->
<g id="edge19" class="edge">
<title>file_adapter&#45;&gt;django</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M584.95,-580.95C575.17,-573.07 563.71,-563.4 554,-554 547.11,-547.33 540.05,-539.73 533.69,-532.56"/>
<polygon fill="#f57f17" stroke="#f57f17" points="536.31,-530.24 527.1,-525 531.03,-534.84 536.31,-530.24"/>
</g>
<!-- postgres -->
<g id="node11" class="node">
<title>postgres</title>
<polygon fill="#d4edda" stroke="black" points="541,-408.5 471,-408.5 471,-372.5 541,-372.5 541,-408.5"/>
<text text-anchor="middle" x="506" y="-393.5" font-family="Arial" font-size="10.00">PostgreSQL</text>
<text text-anchor="middle" x="506" y="-382.5" font-family="Arial" font-size="10.00">Database</text>
</g>
<!-- django&#45;&gt;postgres -->
<g id="edge6" class="edge">
<title>django&#45;&gt;postgres</title>
<path fill="none" stroke="#2e7d32" d="M510.13,-483.99C509.32,-465.93 508.12,-438.89 507.21,-418.71"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="510.71,-418.42 506.76,-408.59 503.71,-418.73 510.71,-418.42"/>
</g>
<!-- redis -->
<g id="node12" class="node">
<title>redis</title>
<polygon fill="#d4edda" stroke="black" points="645,-408.5 559,-408.5 559,-372.5 645,-372.5 645,-408.5"/>
<text text-anchor="middle" x="602" y="-393.5" font-family="Arial" font-size="10.00">Redis</text>
<text text-anchor="middle" x="602" y="-382.5" font-family="Arial" font-size="10.00">Cache &amp; Broker</text>
</g>
<!-- django&#45;&gt;redis -->
<g id="edge7" class="edge">
<title>django&#45;&gt;redis</title>
<path fill="none" stroke="#2e7d32" d="M526.85,-483.99C542.07,-465.26 565.12,-436.89 581.68,-416.51"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="584.53,-418.55 588.12,-408.59 579.09,-414.14 584.53,-418.55"/>
</g>
<!-- local_storage -->
<g id="node26" class="node">
<title>local_storage</title>
<polygon fill="#d4edda" stroke="black" points="747,-408.5 669,-408.5 669,-372.5 747,-372.5 747,-408.5"/>
<text text-anchor="middle" x="708" y="-393.5" font-family="Arial" font-size="10.00">Local Files</text>
<text text-anchor="middle" x="708" y="-382.5" font-family="Arial" font-size="10.00">(nginx&#45;served)</text>
</g>
<!-- django&#45;&gt;local_storage -->
<g id="edge8" class="edge">
<title>django&#45;&gt;local_storage</title>
<path fill="none" stroke="#2e7d32" d="M544.77,-488.1C547.89,-486.71 551,-485.32 554,-484 599.56,-463.94 615.42,-467.36 657,-440 667.16,-433.31 677.13,-424.5 685.52,-416.21"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="688.25,-418.43 692.74,-408.84 683.24,-413.53 688.25,-418.43"/>
</g>
<!-- gcs_storage -->
<g id="node27" class="node">
<title>gcs_storage</title>
<polygon fill="#d4edda" stroke="black" points="841,-408.5 765,-408.5 765,-372.5 841,-372.5 841,-408.5"/>
<text text-anchor="middle" x="803" y="-393.5" font-family="Arial" font-size="10.00">Google Cloud</text>
<text text-anchor="middle" x="803" y="-382.5" font-family="Arial" font-size="10.00">Storage</text>
</g>
<!-- django&#45;&gt;gcs_storage -->
<g id="edge9" class="edge">
<title>django&#45;&gt;gcs_storage</title>
<path fill="none" stroke="#2e7d32" d="M544.53,-487.48C547.7,-486.21 550.89,-485.03 554,-484 641.23,-455.13 675.72,-484.7 756,-440 766.51,-434.15 776.14,-425.2 783.92,-416.57"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="786.68,-418.72 790.5,-408.84 781.35,-414.18 786.68,-418.72"/>
</g>
<!-- angular -->
<g id="node28" class="node">
<title>angular</title>
<polygon fill="#d4edda" stroke="black" points="951,-411 865,-411 865,-370 951,-370 951,-411"/>
<text text-anchor="middle" x="908" y="-399" font-family="Arial" font-size="10.00">Angular 17 SPA</text>
<text text-anchor="middle" x="908" y="-388" font-family="Arial" font-size="10.00">+ WebSocket</text>
<text text-anchor="middle" x="908" y="-377" font-family="Arial" font-size="10.00">:4200</text>
</g>
<!-- django&#45;&gt;angular -->
<g id="edge15" class="edge">
<title>django&#45;&gt;angular</title>
<path fill="none" stroke="#2e7d32" d="M544.9,-486.94C547.93,-485.82 550.99,-484.82 554,-484 627.31,-464.06 648.93,-477.71 724,-466 763.01,-459.91 772.27,-455.69 811,-448 829.64,-444.3 835.93,-448.36 853,-440 863.97,-434.63 874.43,-426.48 883.24,-418.38"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="885.76,-420.81 890.52,-411.35 880.89,-415.78 885.76,-420.81"/>
<text text-anchor="middle" x="831.5" y="-459.6" font-family="Arial" font-size="8.00">WebSocket</text>
<text text-anchor="middle" x="831.5" y="-450.6" font-family="Arial" font-size="8.00">API</text>
</g>
<!-- logo_worker -->
<g id="node18" class="node">
<title>logo_worker</title>
<polygon fill="#d4edda" stroke="black" points="840.5,-314 757.5,-314 757.5,-273 840.5,-273 840.5,-314"/>
<text text-anchor="middle" x="799" y="-302" font-family="Arial" font-size="10.00">Logo Detection</text>
<text text-anchor="middle" x="799" y="-291" font-family="Arial" font-size="10.00">Worker</text>
<text text-anchor="middle" x="799" y="-280" font-family="Arial" font-size="10.00">(logo_queue)</text>
</g>
<!-- redis&#45;&gt;logo_worker -->
<g id="edge10" class="edge">
<title>redis&#45;&gt;logo_worker</title>
<path fill="none" stroke="#2e7d32" d="M636.79,-372.44C643.41,-369.56 650.35,-366.76 657,-364.5 696.75,-350.97 712.15,-363.13 749,-343 758.87,-337.61 768.17,-329.73 776.01,-321.89"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="779,-323.82 783.33,-314.15 773.91,-319.01 779,-323.82"/>
</g>
<!-- visual_worker -->
<g id="node19" class="node">
<title>visual_worker</title>
<polygon fill="#fff3cd" stroke="black" points="739.5,-314 648.5,-314 648.5,-273 739.5,-273 739.5,-314"/>
<text text-anchor="middle" x="694" y="-302" font-family="Arial" font-size="10.00">Visual Properties</text>
<text text-anchor="middle" x="694" y="-291" font-family="Arial" font-size="10.00">Worker</text>
<text text-anchor="middle" x="694" y="-280" font-family="Arial" font-size="10.00">(visual_queue)</text>
</g>
<!-- redis&#45;&gt;visual_worker -->
<g id="edge20" class="edge">
<title>redis&#45;&gt;visual_worker</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M618.87,-372.08C632.52,-357.99 651.96,-337.91 667.65,-321.71"/>
<polygon fill="#f57f17" stroke="#f57f17" points="670.57,-323.73 675.01,-314.11 665.54,-318.86 670.57,-323.73"/>
</g>
<!-- audio_worker -->
<g id="node20" class="node">
<title>audio_worker</title>
<polygon fill="#fff3cd" stroke="black" points="521.5,-314 434.5,-314 434.5,-273 521.5,-273 521.5,-314"/>
<text text-anchor="middle" x="478" y="-302" font-family="Arial" font-size="10.00">Audio Transcript</text>
<text text-anchor="middle" x="478" y="-291" font-family="Arial" font-size="10.00">Worker</text>
<text text-anchor="middle" x="478" y="-280" font-family="Arial" font-size="10.00">(audio_queue)</text>
</g>
<!-- redis&#45;&gt;audio_worker -->
<g id="edge21" class="edge">
<title>redis&#45;&gt;audio_worker</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M573.63,-372.4C560.34,-364.02 544.5,-353.49 531,-343 522.43,-336.34 513.56,-328.61 505.59,-321.3"/>
<polygon fill="#f57f17" stroke="#f57f17" points="507.67,-318.45 497.96,-314.19 502.89,-323.57 507.67,-318.45"/>
</g>
<!-- text_worker -->
<g id="node21" class="node">
<title>text_worker</title>
<polygon fill="#fff3cd" stroke="black" points="630,-314 540,-314 540,-273 630,-273 630,-314"/>
<text text-anchor="middle" x="585" y="-302" font-family="Arial" font-size="10.00">Text Recognition</text>
<text text-anchor="middle" x="585" y="-291" font-family="Arial" font-size="10.00">Worker</text>
<text text-anchor="middle" x="585" y="-280" font-family="Arial" font-size="10.00">(text_queue)</text>
</g>
<!-- redis&#45;&gt;text_worker -->
<g id="edge22" class="edge">
<title>redis&#45;&gt;text_worker</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M598.88,-372.08C596.5,-358.76 593.16,-340.08 590.35,-324.39"/>
<polygon fill="#f57f17" stroke="#f57f17" points="593.72,-323.34 588.51,-314.11 586.83,-324.57 593.72,-323.34"/>
</g>
<!-- nginx -->
<g id="node13" class="node">
<title>nginx</title>
<polygon fill="#d4edda" stroke="black" points="645,-525 563,-525 563,-484 645,-484 645,-525"/>
<text text-anchor="middle" x="604" y="-513" font-family="Arial" font-size="10.00">NGINX</text>
<text text-anchor="middle" x="604" y="-502" font-family="Arial" font-size="10.00">Reverse Proxy</text>
<text text-anchor="middle" x="604" y="-491" font-family="Arial" font-size="10.00">:80</text>
</g>
<!-- nginx&#45;&gt;angular -->
<g id="edge17" class="edge">
<title>nginx&#45;&gt;angular</title>
<path fill="none" stroke="#2e7d32" d="M645.14,-501.31C707.29,-497.34 821.55,-487.34 856,-466 873.37,-455.24 886.48,-436.29 895.19,-420.19"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="898.39,-421.63 899.81,-411.13 892.15,-418.45 898.39,-421.63"/>
</g>
<!-- base_strategy -->
<g id="node14" class="node">
<title>base_strategy</title>
<polygon fill="#c8e6c9" stroke="black" points="1183.5,-311.5 1064.5,-311.5 1064.5,-275.5 1183.5,-275.5 1183.5,-311.5"/>
<text text-anchor="middle" x="1124" y="-296.5" font-family="Arial" font-size="10.00">BaseExecutionStrategy</text>
<text text-anchor="middle" x="1124" y="-285.5" font-family="Arial" font-size="10.00">(Abstract)</text>
</g>
<!-- local_strategy -->
<g id="node15" class="node">
<title>local_strategy</title>
<polygon fill="#d4edda" stroke="black" points="1255.5,-217 1178.5,-217 1178.5,-181 1255.5,-181 1255.5,-217"/>
<text text-anchor="middle" x="1217" y="-196.5" font-family="Arial" font-size="10.00">LocalStrategy</text>
</g>
<!-- base_strategy&#45;&gt;local_strategy -->
<g id="edge31" class="edge">
<title>base_strategy&#45;&gt;local_strategy</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1141.49,-275.1C1155.87,-260.8 1176.41,-240.37 1192.44,-224.43"/>
<polygon fill="gray" stroke="gray" points="1195.28,-226.54 1199.9,-217.01 1190.34,-221.58 1195.28,-226.54"/>
</g>
<!-- lan_strategy -->
<g id="node16" class="node">
<title>lan_strategy</title>
<polygon fill="#fff3cd" stroke="black" points="1160.5,-217 1087.5,-217 1087.5,-181 1160.5,-181 1160.5,-217"/>
<text text-anchor="middle" x="1124" y="-196.5" font-family="Arial" font-size="10.00">LANStrategy</text>
</g>
<!-- base_strategy&#45;&gt;lan_strategy -->
<g id="edge32" class="edge">
<title>base_strategy&#45;&gt;lan_strategy</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1124,-275.1C1124,-261.58 1124,-242.58 1124,-227.07"/>
<polygon fill="gray" stroke="gray" points="1127.5,-227.01 1124,-217.01 1120.5,-227.01 1127.5,-227.01"/>
</g>
<!-- cloud_strategy -->
<g id="node17" class="node">
<title>cloud_strategy</title>
<polygon fill="#fff3cd" stroke="black" points="1069.5,-217 990.5,-217 990.5,-181 1069.5,-181 1069.5,-217"/>
<text text-anchor="middle" x="1030" y="-196.5" font-family="Arial" font-size="10.00">CloudStrategy</text>
</g>
<!-- base_strategy&#45;&gt;cloud_strategy -->
<g id="edge33" class="edge">
<title>base_strategy&#45;&gt;cloud_strategy</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1106.32,-275.1C1091.79,-260.8 1071.02,-240.37 1054.82,-224.43"/>
<polygon fill="gray" stroke="gray" points="1056.87,-221.53 1047.28,-217.01 1051.96,-226.52 1056.87,-221.53"/>
</g>
<!-- clip_adapter -->
<g id="node23" class="node">
<title>clip_adapter</title>
<polygon fill="#d4edda" stroke="black" points="1565,-144 1493,-144 1493,-108 1565,-108 1565,-144"/>
<text text-anchor="middle" x="1529" y="-129" font-family="Arial" font-size="10.00">CLIPAdapter</text>
<text text-anchor="middle" x="1529" y="-118" font-family="Arial" font-size="10.00">(Local)</text>
</g>
<!-- local_strategy&#45;&gt;clip_adapter -->
<g id="edge12" class="edge">
<title>local_strategy&#45;&gt;clip_adapter</title>
<path fill="none" stroke="#2e7d32" d="M1247.91,-180.89C1254.39,-177.86 1261.3,-175.03 1268,-173 1357.7,-145.78 1388.13,-166.29 1483.16,-143.84"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="1484,-147.24 1492.88,-141.46 1482.33,-140.44 1484,-147.24"/>
</g>
<!-- gcp_vision -->
<g id="node24" class="node">
<title>gcp_vision</title>
<polygon fill="#d4edda" stroke="black" points="1474.5,-144 1375.5,-144 1375.5,-108 1474.5,-108 1474.5,-144"/>
<text text-anchor="middle" x="1425" y="-129" font-family="Arial" font-size="10.00">GCPVisionAdapter</text>
<text text-anchor="middle" x="1425" y="-118" font-family="Arial" font-size="10.00">(Cloud)</text>
</g>
<!-- local_strategy&#45;&gt;gcp_vision -->
<g id="edge13" class="edge">
<title>local_strategy&#45;&gt;gcp_vision</title>
<path fill="none" stroke="#2e7d32" d="M1250.51,-180.96C1256.3,-178.19 1262.29,-175.44 1268,-173 1307.45,-156.15 1321.06,-156.16 1365.67,-144.29"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="1366.62,-147.66 1375.35,-141.66 1364.79,-140.9 1366.62,-147.66"/>
</g>
<!-- yolo_adapter -->
<g id="node25" class="node">
<title>yolo_adapter</title>
<polygon fill="#fff3cd" stroke="black" points="1357.5,-144 1280.5,-144 1280.5,-108 1357.5,-108 1357.5,-144"/>
<text text-anchor="middle" x="1319" y="-129" font-family="Arial" font-size="10.00">YOLOAdapter</text>
<text text-anchor="middle" x="1319" y="-118" font-family="Arial" font-size="10.00">(Planned)</text>
</g>
<!-- lan_strategy&#45;&gt;yolo_adapter -->
<g id="edge27" class="edge">
<title>lan_strategy&#45;&gt;yolo_adapter</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1153.27,-180.98C1158.74,-178.12 1164.47,-175.33 1170,-173 1202.73,-159.22 1241.04,-147.48 1270.61,-139.31"/>
<polygon fill="#f57f17" stroke="#f57f17" points="1271.56,-142.68 1280.29,-136.68 1269.72,-135.93 1271.56,-142.68"/>
</g>
<!-- speech_api -->
<g id="node31" class="node">
<title>speech_api</title>
<polygon fill="#fff3cd" stroke="black" points="1255.5,-52 1154.5,-52 1154.5,-16 1255.5,-16 1255.5,-52"/>
<text text-anchor="middle" x="1205" y="-37" font-family="Arial" font-size="10.00">Speech&#45;to&#45;Text API</text>
<text text-anchor="middle" x="1205" y="-26" font-family="Arial" font-size="10.00">(Audio Transcript)</text>
</g>
<!-- cloud_strategy&#45;&gt;speech_api -->
<g id="edge26" class="edge">
<title>cloud_strategy&#45;&gt;speech_api</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1048.4,-180.86C1079.64,-151.76 1143.17,-92.59 1178.97,-59.25"/>
<polygon fill="#f57f17" stroke="#f57f17" points="1181.65,-61.53 1186.59,-52.15 1176.88,-56.41 1181.65,-61.53"/>
</g>
<!-- logo_worker&#45;&gt;local_strategy -->
<g id="edge11" class="edge">
<title>logo_worker&#45;&gt;local_strategy</title>
<path fill="none" stroke="#2e7d32" d="M832.13,-272.96C838.84,-269.78 845.99,-266.9 853,-265 921.1,-246.49 1105.48,-274.6 1170,-246 1180.6,-241.3 1190.19,-233.09 1197.89,-224.9"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="1200.72,-226.99 1204.68,-217.16 1195.46,-222.37 1200.72,-226.99"/>
</g>
<!-- visual_worker&#45;&gt;lan_strategy -->
<g id="edge23" class="edge">
<title>visual_worker&#45;&gt;lan_strategy</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M727.77,-272.95C734.61,-269.77 741.88,-266.9 749,-265 819.98,-246.08 1012.14,-276.42 1079,-246 1089.33,-241.3 1098.54,-233.09 1105.9,-224.9"/>
<polygon fill="#f57f17" stroke="#f57f17" points="1108.64,-227.08 1112.37,-217.16 1103.27,-222.59 1108.64,-227.08"/>
</g>
<!-- audio_worker&#45;&gt;cloud_strategy -->
<g id="edge24" class="edge">
<title>audio_worker&#45;&gt;cloud_strategy</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M511.46,-272.93C517.79,-269.87 524.47,-267.05 531,-265 689.92,-215.13 888.89,-203.49 980.12,-200.8"/>
<polygon fill="#f57f17" stroke="#f57f17" points="980.28,-204.29 990.18,-200.52 980.09,-197.29 980.28,-204.29"/>
</g>
<!-- text_worker&#45;&gt;cloud_strategy -->
<g id="edge25" class="edge">
<title>text_worker&#45;&gt;cloud_strategy</title>
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M620.44,-272.86C626.82,-269.89 633.5,-267.11 640,-265 758.46,-226.48 904.94,-209.91 980.19,-203.53"/>
<polygon fill="#f57f17" stroke="#f57f17" points="980.65,-207 990.33,-202.69 980.08,-200.02 980.65,-207"/>
</g>
<!-- base_ai -->
<g id="node22" class="node">
<title>base_ai</title>
<polygon fill="#f8bbd9" stroke="black" points="1471,-217 1379,-217 1379,-181 1471,-181 1471,-217"/>
<text text-anchor="middle" x="1425" y="-202" font-family="Arial" font-size="10.00">DetectionAdapter</text>
<text text-anchor="middle" x="1425" y="-191" font-family="Arial" font-size="10.00">(Abstract)</text>
</g>
<!-- base_ai&#45;&gt;clip_adapter -->
<g id="edge34" class="edge">
<title>base_ai&#45;&gt;clip_adapter</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1449.91,-180.99C1463.57,-171.67 1480.7,-159.97 1495.52,-149.86"/>
<polygon fill="gray" stroke="gray" points="1497.65,-152.64 1503.93,-144.11 1493.7,-146.86 1497.65,-152.64"/>
</g>
<!-- base_ai&#45;&gt;gcp_vision -->
<g id="edge35" class="edge">
<title>base_ai&#45;&gt;gcp_vision</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1425,-180.81C1425,-172.79 1425,-163.05 1425,-154.07"/>
<polygon fill="gray" stroke="gray" points="1428.5,-154.03 1425,-144.03 1421.5,-154.03 1428.5,-154.03"/>
</g>
<!-- base_ai&#45;&gt;yolo_adapter -->
<g id="edge36" class="edge">
<title>base_ai&#45;&gt;yolo_adapter</title>
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1399.61,-180.99C1385.68,-171.67 1368.23,-159.97 1353.13,-149.86"/>
<polygon fill="gray" stroke="gray" points="1354.81,-146.77 1344.55,-144.11 1350.91,-152.59 1354.81,-146.77"/>
</g>
<!-- vision_api -->
<g id="node30" class="node">
<title>vision_api</title>
<polygon fill="#d4edda" stroke="black" points="1364.5,-52 1273.5,-52 1273.5,-16 1364.5,-16 1364.5,-52"/>
<text text-anchor="middle" x="1319" y="-37" font-family="Arial" font-size="10.00">Cloud Vision API</text>
<text text-anchor="middle" x="1319" y="-26" font-family="Arial" font-size="10.00">(Logo Detection)</text>
</g>
<!-- gcp_vision&#45;&gt;vision_api -->
<g id="edge14" class="edge">
<title>gcp_vision&#45;&gt;vision_api</title>
<path fill="none" stroke="#2e7d32" d="M1404.56,-107.65C1388.28,-93.83 1365.32,-74.33 1347.28,-59.01"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="1349.26,-56.1 1339.38,-52.3 1344.73,-61.44 1349.26,-56.1"/>
</g>
<!-- hls_player -->
<g id="node29" class="node">
<title>hls_player</title>
<polygon fill="#d4edda" stroke="black" points="966.5,-311.5 865.5,-311.5 865.5,-275.5 966.5,-275.5 966.5,-311.5"/>
<text text-anchor="middle" x="916" y="-296.5" font-family="Arial" font-size="10.00">HLS.js Player</text>
<text text-anchor="middle" x="916" y="-285.5" font-family="Arial" font-size="10.00">+ Canvas Overlays</text>
</g>
<!-- angular&#45;&gt;hls_player -->
<g id="edge16" class="edge">
<title>angular&#45;&gt;hls_player</title>
<path fill="none" stroke="#2e7d32" d="M909.66,-369.82C910.83,-355.86 912.42,-336.96 913.72,-321.59"/>
<polygon fill="#2e7d32" stroke="#2e7d32" points="917.21,-321.87 914.56,-311.61 910.23,-321.28 917.21,-321.87"/>
</g>
</g>
</svg>