documentation and system diagrams

This commit is contained in:
buenosairesam
2025-08-25 04:03:28 -03:00
parent 1cd999f825
commit cdab5191e4
10 changed files with 1154 additions and 68 deletions

View File

@@ -0,0 +1,489 @@
<?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>

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,153 @@
+-------+
| OBS | RTMP stream
+---+---+ --------------+
| |
v |
+-----+------+ |
| nginx-rtmp |-- HLS ───+ |
| (RTMP/HLS) | | |
+-----+------+ | |
| | |
HLS on /media | |
| | |
v | |
+----------+-----------+ | |
| Host “media/” dir |<----+ |
+----------+-----------+ |
| File watcher |
v |
+----------+-----------+ |
| Django Backend | |
| (REST + ASGI + WS) +<--WebSocket--+
+---+------------------+ |
| | |
| +-------+ HTTP/API |
| v |
| +-------+------+ |
| | PostgreSQL | |
| +-------+------+ |
| |
| Celery tasks |
v |
+------+--------+ queue +------+--------+
| celery-logo |<-----------| celery-default |
+---------------+ +---------------+
| ^
| |
+----> Redis (broker) <------+
Frontend:
+-----------+
| Angular |
| (4200) |
+-----+-----+
|
| HTTP/API (/api/…)
|
+-----v-----+
| Nginx |
| (SSP) |
+-----------+
Legend:
• OBS pushes RTMP into nginx-rtmp, which writes HLS (.m3u8/.ts) into the shared media/ folder.
• Djangos file-watcher polls media/ for new segments and enqueues Celery tasks.
• Django Backend serves REST API, WebSockets (via Channels → Redis), and hosts analysis logic.
• Celery Workers consume tasks from Redis:
celery-logo for logo detection
celery-default (or others) for visual analysis, etc.
• PostgreSQL holds application & analysis metadata.
• Angular (dev or static via Nginx) calls the Django API and listens on WebSockets.
• Nginx (frontend) proxies /api and /ws to Django and serves the Angular SPA.
This layout keeps streaming, AI-processing, data storage, and UI layers cleanly separated.
codex
Below is an updated architecture sketch—with the local webcam path and AI executionstrategy layer added. Each box is a service or component, and arrows show the main flows:
+--------+
| OBS | RTMP stream
+---+----+ --------+
| |
v |
+-------+------+ |
| nginx-rtmp |-- HLS →| (writes .m3u8/.ts to host media/)
| (RTMP/HLS) | |
+-------+------+ |
| |
+------------+---------------+-------------+
| HLS |
v |
+------+-------+ |
| Host “media/”|<-----------------------------------+
| directory | (mount in backend & nginx-rtmp)
+------+-------+
|
| (poll .ts files)
v
+------+-------+
| File Watcher | → enqueues Celery tasks
+------+-------+
|
v
+------+-------+
| Django | REST API & WebSocket (ASGI)
| Backend |
| - RTMP/Webcam|
| Adapters |
+------+-------+
| +--------------------------------+
| WebSocket | |
+---------->+ Frontend Nginx (SPA + proxy) |
| | Serves Angular app on 80 |
| | Proxies /api → Django |
v | Proxies /ws → Django |
+------+-------+ | Proxies /streaming → nginx-rtmp
| PostgreSQL | +--------------------------------+
+------+-------+
|
v
+------+-------+ +------------------------+
| Redis |<-----------+ Celery Workers |
| (broker) | tasks | Worker queues: |
+------+-------+ | logo_detection, |
| | visual_analysis, |
| | default… |
+------------------→ | Uses AnalysisEngine |
| with Execution |
| Strategies: |
| • local (in-worker) |
| • remote LAN (via |
| worker_host) |
| • cloud (via API) |
+------------------------+
+--------+
| Webcam | local device
+---+----+
|
| via Djangos Webcam
|SourceAdapter (ffmpeg)
v
[ Django Backend ]
|
v
+------+-------+
| Host “media/”|
+--------------+
Key points:
• OBS → nginx-rtmp → host “media/” → Django file-watcher → Celery tasks
• Local Webcam → Django backend (WebcamSourceAdapter with ffmpeg) → host “media/” → same pipeline
• Django Backend (REST + ASGI/WebSocket) ↔ Angular (served by Frontend Nginx)
• Celery Workers pick up tasks from Redis, run AnalysisEngine → can execute locally, offload to remote LAN workers, or call cloud APIs
• PostgreSQL stores streams, queue, and analysis results
This should give a clear birds-eye of how data and control flow through your streaming+AI stack.

172
def/architecture/convo Normal file
View File

@@ -0,0 +1,172 @@
> generate a diagram of the architecture to include in readme, just the diagram output in /def. ask question if needed, suggest options which technology to use to generate them I need a graphviz like
diagram of how services connect and some of design decisions on the code, like adapter for sources, and strategy for execution (local, lan, cloud), how the │
│ queue is organized in different analysis task, media storage, that could be media_root server by nginx or some cloud storage
● I'll create an architecture diagram showing the service connections and design patterns. Let me ask a few questions to make it accurate:
Questions:
1. Current media storage: Are you currently using local nginx-served files or cloud storage?
2. Queue organization: Do you have separate queues like logo_queue, visual_queue, etc., or task routing by queue names?
3. GCP services: Which specific GCP services are you integrating (Cloud Vision, Cloud Storage, others)?
Diagram tool options:
1. Graphviz/DOT - Clean, professional, version-controllable
2. PlantUML - Good for architectural diagrams, supports multiple output formats
3. Mermaid - GitHub-friendly, can render directly in markdown
4. Python + matplotlib/networkx - Programmatic, customizable
My recommendation: Graphviz/DOT because it:
- Creates clean, professional diagrams
- Is text-based (version control friendly)
- Excellent for showing service connections and data flow
- Industry standard for technical architecture
Should I proceed with Graphviz showing:
- Service containers (Django, Celery workers, Redis, PostgreSQL, nginx)
- Design patterns (Source Adapters, Execution Strategies)
- Queue segregation by analysis type
- Media storage options (local vs cloud)
- WebSocket connections for real-time updates
What's your preference for the tool and any specific details to include/exclude?
> even if not ready the idea is to include planed features, maybe use a different style or color to signal this, also maybe use something like svg so easily updetable for when they are done, 2. same thing
now is only logo detection, need default as base,3, GCS and Vision for now, extra.. about tool options can you give links for demos that are reprensetative of each to check the general feel. But I think
graphviz is the de facto, for super flexible and programable, not at all the usual WYSIWYG generic look which I'd like to avoid
● Perfect! Here are representative demos to see the visual style:
Tool Options with Demos:
1. Graphviz/DOT - https://graphviz.org/gallery/
- Clean, programmatic, no WYSIWYG look
- Example: https://graphviz.org/Gallery/directed/cluster.html
2. PlantUML - https://real-world-plantuml.com/
- Architecture: https://plantuml.com/c4-plantuml
3. Mermaid - https://mermaid.live/
- Architecture: https://mermaid.live/edit#pako:eNptkk1PwzAMhv-K5QtIq6vCx9ZlqiAmEEKIwwSInBqaOG2mxqlWM9Gf6nO6hB3dN0_8vI8f2ccEJqFABKNQzFklUMsK3CrRkrOQF2h8Lk2nPXRt67DpeQHe90QKFNYzb8e4qFXt-Rc
4. Python + networkx - https://networkx.org/documentation/stable/auto_examples/index.html
Graphviz is definitely the best choice for your needs - professional, flexible, no generic look, SVG output, version-controllable.
For the diagram, I'll include:
✅ Current (solid lines/colors):
- Django API, Celery logo workers, Redis, PostgreSQL
- Webcam/RTMP source adapters
- Local execution strategy
- GCS + Vision API integration
🔄 Planned (dashed lines/lighter colors):
- Additional analysis workers (visual, audio, text)
- LAN/Cloud execution strategies
- Advanced queue routing
- GPU effects pipeline
Media storage: I'll show both nginx-served local and GCS options with a switch.
Should I create the Graphviz DOT file now? It'll generate a clean SVG that you can easily update as features are implemented.
> yes if you need to generate many files, use a folder inside /def
● I'll create the architecture diagram using Graphviz. Let me create a folder and generate the files:
● Bash(mkdir -p /home/mariano/wdir/snk/def/architecture)
 (No content)
● Write(~/wdir/snk/def/architecture/system_architecture.dot)
 Wrote 175 lines to ../def/architecture/system_architecture.dot
digraph MediaAnalyzer {
// Graph settings
rankdir=TB;
bgcolor="white";
fontname="Arial";
fontsize=12;
// Node defaults
node [fontname="Arial", fontsize=10, shape=box, style=filled];
edge [fontname="Arial", fontsize=8];
… +165 lines (ctrl+r to expand)
● Bash(cd /home/mariano/wdir/snk/def/architecture && dot -Tsvg system_architecture.dot -o architecture_diagram.svg)
 (No content)
● Write(README.md)
 User rejected write to README.md
1 + # Architecture Diagram Generation
2 +
3 + ## Files
4 + - `system_architecture.dot` - Graphviz source file
5 + - `architecture_diagram.svg` - Generated SVG diagram
6 +
7 + ## To Update Diagram
8 +
9 + ```bash
10 + cd def/architecture
11 + dot -Tsvg system_architecture.dot -o architecture_diagram.svg
12 + ```
13 +
14 + ## Design Patterns Shown
15 +
16 + 1. **Source Adapters** - Abstract different video input sources
17 + 2. **Execution Strategies** - Local vs distributed processing
18 + 3. **Queue Segregation** - Separate workers for different analysis types
19 + 4. **Analysis Adapters** - Pluggable AI models
20 +
21 + ## Color Coding
22 +
23 + - **Green (✓)** - Currently implemented
24 + - **Yellow (○)** - Planned features
25 + - **Dotted lines** - Inheritance/abstraction
26 + - **Dashed lines** - Planned connections
27 +
28 + ## Update Process
29 +
30 + When implementing new features:
31 + 1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
32 + 2. Change edge style from `dashed` to `solid`
33 + 3. Regenerate SVG
34 \ No newline at end of file
> dont step on the actual README !! in any case name it something like diagram md
● Write(diagram.md)
 Wrote 33 lines to diagram.md
# Architecture Diagram Generation
## Files
- `system_architecture.dot` - Graphviz source file
- `architecture_diagram.svg` - Generated SVG diagram
## To Update Diagram
```bash
cd def/architecture
… +23 lines (ctrl+r to expand)
● Perfect! The architecture diagram is now generated at:
- Source: /def/architecture/system_architecture.dot
- Generated SVG: /def/architecture/architecture_diagram.svg
- Instructions: /def/architecture/diagram.md
The diagram shows:
✅ Current implementation (green, solid lines) - Logo detection with CLIP/GCP Vision🔄 Planned features (yellow, dashed lines) - Additional analysis types, LAN/Cloud strategies📐 Design patterns - Source
adapters, execution strategies, queue segregation
Now you can update the README to reference def/architecture/architecture_diagram.svg for the architecture diagram!
> can we make it less wide without making square smallers, so it looks better in the main github
page, I think that the workers could be aligned verticaly to make a clearer separation with the
"core", media and frontend, also a bit more contrast in the colors, not much, especialy grouping
background squares
● Update(system_architecture.dot)
 Updated system_architecture.dot with 2 additions
4 bgcolor="white";
5 fontname="Arial";
6 fontsize=12;
7 + ratio=fill;
8 + size="12,16!";
9
10 // Node defaults
11 node [fontname="Arial", fontsize=10, shape=box, style=filled];
 5-hour limit reached ∙ resets 1pm
/upgrade to increase your usage limit.

View File

@@ -0,0 +1,33 @@
# Architecture Diagram Generation
## Files
- `system_architecture.dot` - Graphviz source file
- `architecture_diagram.svg` - Generated SVG diagram
## To Update Diagram
```bash
cd def/architecture
dot -Tsvg system_architecture.dot -o architecture_diagram.svg
```
## Design Patterns Shown
1. **Source Adapters** - Abstract different video input sources
2. **Execution Strategies** - Local vs distributed processing
3. **Queue Segregation** - Separate workers for different analysis types
4. **Analysis Adapters** - Pluggable AI models
## Color Coding
- **Green (✓)** - Currently implemented
- **Yellow (○)** - Planned features
- **Dotted lines** - Inheritance/abstraction
- **Dashed lines** - Planned connections
## Update Process
When implementing new features:
1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
2. Change edge style from `dashed` to `solid`
3. Regenerate SVG

View File

@@ -0,0 +1,177 @@
digraph MediaAnalyzer {
// Graph settings
rankdir=TB;
bgcolor="white";
fontname="Arial";
fontsize=12;
ratio=fill;
size="12,16!";
// Node defaults
node [fontname="Arial", fontsize=10, shape=box, style=filled];
edge [fontname="Arial", fontsize=8];
// Color scheme
subgraph cluster_legend {
label="Legend";
style=filled;
color=lightgray;
node [shape=plaintext, style=filled];
implemented [label="✓ Implemented", fillcolor="#d4edda", color="#155724"];
planned [label="○ Planned", fillcolor="#fff3cd", color="#856404"];
implemented -> planned [style=invis];
}
// Input Sources
subgraph cluster_sources {
label="Video Sources";
style=filled;
color="#e3f2fd";
webcam [label="Webcam", fillcolor="#d4edda"];
rtmp [label="RTMP/OBS", fillcolor="#d4edda"];
files [label="File Upload", fillcolor="#fff3cd"];
}
// Source Adapters (Design Pattern)
subgraph cluster_adapters {
label="Source Adapters Pattern";
style=filled;
color="#f3e5f5";
base_adapter [label="BaseSourceAdapter\n(Abstract)", fillcolor="#e1bee7"];
webcam_adapter [label="WebcamAdapter", fillcolor="#d4edda"];
rtmp_adapter [label="RtmpAdapter", fillcolor="#d4edda"];
file_adapter [label="FileAdapter", fillcolor="#fff3cd"];
}
// Core Services
subgraph cluster_core {
label="Core Platform";
style=filled;
color="#fff3e0";
django [label="Django API\n+ Channels\n:8000", fillcolor="#d4edda"];
postgres [label="PostgreSQL\nDatabase", fillcolor="#d4edda"];
redis [label="Redis\nCache & Broker", fillcolor="#d4edda"];
nginx [label="NGINX\nReverse Proxy\n:80", fillcolor="#d4edda"];
}
// Execution Strategies (Design Pattern)
subgraph cluster_execution {
label="Execution Strategies Pattern";
style=filled;
color="#e8f5e8";
base_strategy [label="BaseExecutionStrategy\n(Abstract)", fillcolor="#c8e6c9"];
local_strategy [label="LocalStrategy", fillcolor="#d4edda"];
lan_strategy [label="LANStrategy", fillcolor="#fff3cd"];
cloud_strategy [label="CloudStrategy", fillcolor="#fff3cd"];
}
// Analysis Workers (Queue Segregation)
subgraph cluster_workers {
label="Celery Workers (Queue Segregation)";
style=filled;
color="#e3f2fd";
logo_worker [label="Logo Detection\nWorker\n(logo_queue)", fillcolor="#d4edda"];
visual_worker [label="Visual Properties\nWorker\n(visual_queue)", fillcolor="#fff3cd"];
audio_worker [label="Audio Transcript\nWorker\n(audio_queue)", fillcolor="#fff3cd"];
text_worker [label="Text Recognition\nWorker\n(text_queue)", fillcolor="#fff3cd"];
}
// AI Adapters (Design Pattern)
subgraph cluster_ai_adapters {
label="Analysis Adapters Pattern";
style=filled;
color="#fce4ec";
base_ai [label="DetectionAdapter\n(Abstract)", fillcolor="#f8bbd9"];
clip_adapter [label="CLIPAdapter\n(Local)", fillcolor="#d4edda"];
gcp_vision [label="GCPVisionAdapter\n(Cloud)", fillcolor="#d4edda"];
yolo_adapter [label="YOLOAdapter\n(Planned)", fillcolor="#fff3cd"];
}
// Storage Options
subgraph cluster_storage {
label="Media Storage";
style=filled;
color="#f1f8e9";
local_storage [label="Local Files\n(nginx-served)", fillcolor="#d4edda"];
gcs_storage [label="Google Cloud\nStorage", fillcolor="#d4edda"];
}
// Frontend
subgraph cluster_frontend {
label="Frontend";
style=filled;
color="#e8eaf6";
angular [label="Angular 17 SPA\n+ WebSocket\n:4200", fillcolor="#d4edda"];
hls_player [label="HLS.js Player\n+ Canvas Overlays", fillcolor="#d4edda"];
}
// Cloud Services
subgraph cluster_cloud {
label="GCP Services";
style=filled;
color="#e0f2f1";
vision_api [label="Cloud Vision API\n(Logo Detection)", fillcolor="#d4edda"];
speech_api [label="Speech-to-Text API\n(Audio Transcript)", fillcolor="#fff3cd"];
}
// Connections - Current Implementation (solid)
webcam -> webcam_adapter [color="#2e7d32"];
rtmp -> rtmp_adapter [color="#2e7d32"];
webcam_adapter -> django [color="#2e7d32"];
rtmp_adapter -> django [color="#2e7d32"];
django -> postgres [color="#2e7d32"];
django -> redis [color="#2e7d32"];
django -> local_storage [color="#2e7d32"];
django -> gcs_storage [color="#2e7d32"];
redis -> logo_worker [color="#2e7d32"];
logo_worker -> local_strategy [color="#2e7d32"];
local_strategy -> clip_adapter [color="#2e7d32"];
local_strategy -> gcp_vision [color="#2e7d32"];
gcp_vision -> vision_api [color="#2e7d32"];
django -> angular [label="WebSocket\nAPI", color="#2e7d32"];
angular -> hls_player [color="#2e7d32"];
nginx -> angular [color="#2e7d32"];
// Planned Connections (dashed)
files -> file_adapter [color="#f57f17", style=dashed];
file_adapter -> django [color="#f57f17", style=dashed];
redis -> visual_worker [color="#f57f17", style=dashed];
redis -> audio_worker [color="#f57f17", style=dashed];
redis -> text_worker [color="#f57f17", style=dashed];
visual_worker -> lan_strategy [color="#f57f17", style=dashed];
audio_worker -> cloud_strategy [color="#f57f17", style=dashed];
text_worker -> cloud_strategy [color="#f57f17", style=dashed];
cloud_strategy -> speech_api [color="#f57f17", style=dashed];
lan_strategy -> yolo_adapter [color="#f57f17", style=dashed];
// Inheritance relationships (dotted)
base_adapter -> webcam_adapter [style=dotted, color=gray];
base_adapter -> rtmp_adapter [style=dotted, color=gray];
base_adapter -> file_adapter [style=dotted, color=gray];
base_strategy -> local_strategy [style=dotted, color=gray];
base_strategy -> lan_strategy [style=dotted, color=gray];
base_strategy -> cloud_strategy [style=dotted, color=gray];
base_ai -> clip_adapter [style=dotted, color=gray];
base_ai -> gcp_vision [style=dotted, color=gray];
base_ai -> yolo_adapter [style=dotted, color=gray];
}