proper docs folder
This commit is contained in:
645
docs/architecture/architecture_diagram.svg
Normal file
645
docs/architecture/architecture_diagram.svg
Normal file
@@ -0,0 +1,645 @@
|
||||
<?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="1979pt" height="2160pt"
|
||||
viewBox="0.00 0.00 1979.34 2160.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 2165.46)">
|
||||
<title>MediaAnalyzer</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-2165.46 1984,-2165.46 1984,4 -4,4"/>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_legend</title>
|
||||
<polygon fill="lightgray" stroke="lightgray" points="8.47,-1739.35 8.47,-2138.17 249.75,-2138.17 249.75,-1739.35 8.47,-1739.35"/>
|
||||
<text text-anchor="middle" x="129.11" y="-2124.57" font-family="Arial" font-size="12.00">Legend</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_sources</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="258.21,-1925.66 258.21,-2138.17 522.78,-2138.17 522.78,-1925.66 258.21,-1925.66"/>
|
||||
<text text-anchor="middle" x="390.5" y="-2124.57" font-family="Arial" font-size="12.00">Video Sources</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_adapters</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="531.25,-1739.35 531.25,-2138.17 837.08,-2138.17 837.08,-1739.35 531.25,-1739.35"/>
|
||||
<text text-anchor="middle" x="684.16" y="-2124.57" font-family="Arial" font-size="12.00">Source Adapters Pattern</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_core</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="641.3,-1158.6 641.3,-1689.87 846.61,-1689.87 846.61,-1158.6 641.3,-1158.6"/>
|
||||
<text text-anchor="middle" x="743.96" y="-1676.27" font-family="Arial" font-size="12.00">Core Platform</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_execution</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1665.7,-570.57 1665.7,-1052.35 1971.53,-1052.35 1971.53,-570.57 1665.7,-570.57"/>
|
||||
<text text-anchor="middle" x="1818.62" y="-1038.75" font-family="Arial" font-size="12.00">Execution Strategies Pattern</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_event_sources</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1200.06,-839.84 1200.06,-1371.11 1657.23,-1371.11 1657.23,-839.84 1200.06,-839.84"/>
|
||||
<text text-anchor="middle" x="1428.65" y="-1357.51" font-family="Arial" font-size="12.00">Event Source Pattern</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_events</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="1164.08,-23.29 1164.08,-783.07 1321.76,-783.07 1321.76,-23.29 1164.08,-23.29"/>
|
||||
<text text-anchor="middle" x="1242.92" y="-769.47" font-family="Arial" font-size="12.00">Event-Driven Processing</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_workers</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="386.26,-832.56 386.26,-1059.62 846.61,-1059.62 846.61,-832.56 386.26,-832.56"/>
|
||||
<text text-anchor="middle" x="616.44" y="-1046.02" font-family="Arial" font-size="12.00">Celery Workers</text>
|
||||
</g>
|
||||
<g id="clust9" class="cluster">
|
||||
<title>cluster_ai_adapters</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1330.23,-334.77 1330.23,-783.07 1657.23,-783.07 1657.23,-334.77 1330.23,-334.77"/>
|
||||
<text text-anchor="middle" x="1493.73" y="-769.47" font-family="Arial" font-size="12.00">Analysis Adapters Pattern</text>
|
||||
</g>
|
||||
<g id="clust10" class="cluster">
|
||||
<title>cluster_storage</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="988.41,-1158.6 988.41,-1371.11 1191.6,-1371.11 1191.6,-1158.6 988.41,-1158.6"/>
|
||||
<text text-anchor="middle" x="1090.01" y="-1357.51" font-family="Arial" font-size="12.00">Media Storage</text>
|
||||
</g>
|
||||
<g id="clust11" class="cluster">
|
||||
<title>cluster_frontend</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="855.07,-839.84 855.07,-1378.38 979.95,-1378.38 979.95,-839.84 855.07,-839.84"/>
|
||||
<text text-anchor="middle" x="917.51" y="-1364.78" font-family="Arial" font-size="12.00">Frontend</text>
|
||||
</g>
|
||||
<g id="clust12" class="cluster">
|
||||
<title>cluster_cloud</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="1569.4,-23.29 1569.4,-235.8 1813.85,-235.8 1813.85,-23.29 1569.4,-23.29"/>
|
||||
<text text-anchor="middle" x="1691.62" y="-222.2" font-family="Arial" font-size="12.00">GCP Services</text>
|
||||
</g>
|
||||
<!-- implemented -->
|
||||
<g id="node1" class="node">
|
||||
<title>implemented</title>
|
||||
<polygon fill="#28a745" stroke="transparent" points="239.18,-2019.29 150.18,-2019.29 150.18,-1983.29 239.18,-1983.29 239.18,-2019.29"/>
|
||||
<text text-anchor="middle" x="194.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">✅ Implemented</text>
|
||||
</g>
|
||||
<!-- planned -->
|
||||
<g id="node2" class="node">
|
||||
<title>planned</title>
|
||||
<polygon fill="#ffc107" stroke="transparent" points="239.68,-1833.29 171.68,-1833.29 171.68,-1797.29 239.68,-1797.29 239.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="205.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">⏳ Planned</text>
|
||||
</g>
|
||||
<!-- implemented->planned -->
|
||||
<!-- code_pattern -->
|
||||
<g id="node3" class="node">
|
||||
<title>code_pattern</title>
|
||||
<polygon fill="#8e24aa" stroke="transparent" points="121.68,-2019.29 31.68,-2019.29 31.68,-1983.29 121.68,-1983.29 121.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="76.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">📐 Code Pattern</text>
|
||||
</g>
|
||||
<!-- service_arch -->
|
||||
<g id="node4" class="node">
|
||||
<title>service_arch</title>
|
||||
<polygon fill="#1976d2" stroke="transparent" points="142.18,-1833.29 21.18,-1833.29 21.18,-1797.29 142.18,-1797.29 142.18,-1833.29"/>
|
||||
<text text-anchor="middle" x="81.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">⚡ Service/Architecture</text>
|
||||
</g>
|
||||
<!-- code_pattern->service_arch -->
|
||||
<!-- webcam -->
|
||||
<g id="node5" class="node">
|
||||
<title>webcam</title>
|
||||
<polygon fill="#28a745" stroke="black" points="324.18,-2019.29 269.18,-2019.29 269.18,-1983.29 324.18,-1983.29 324.18,-2019.29"/>
|
||||
<text text-anchor="middle" x="296.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">Webcam</text>
|
||||
</g>
|
||||
<!-- webcam_adapter -->
|
||||
<g id="node9" class="node">
|
||||
<title>webcam_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="631.18,-1833.29 542.18,-1833.29 542.18,-1797.29 631.18,-1797.29 631.18,-1833.29"/>
|
||||
<text text-anchor="middle" x="586.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">WebcamAdapter</text>
|
||||
</g>
|
||||
<!-- webcam->webcam_adapter -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>webcam->webcam_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M318.21,-1983.19C344.52,-1962.61 390.72,-1927.49 432.68,-1900.79 469.09,-1877.62 512.31,-1854.24 543.69,-1837.97"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="545.33,-1841.06 552.61,-1833.36 542.12,-1834.83 545.33,-1841.06"/>
|
||||
</g>
|
||||
<!-- rtmp -->
|
||||
<g id="node6" class="node">
|
||||
<title>rtmp</title>
|
||||
<polygon fill="#28a745" stroke="black" points="512.68,-2019.29 444.68,-2019.29 444.68,-1983.29 512.68,-1983.29 512.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="478.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">RTMP/OBS</text>
|
||||
</g>
|
||||
<!-- rtmp_adapter -->
|
||||
<g id="node10" class="node">
|
||||
<title>rtmp_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="826.68,-1833.29 752.68,-1833.29 752.68,-1797.29 826.68,-1797.29 826.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="789.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">RtmpAdapter</text>
|
||||
</g>
|
||||
<!-- rtmp->rtmp_adapter -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>rtmp->rtmp_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M507.66,-1983.14C564.14,-1949.73 688.93,-1875.9 751.84,-1838.67"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="753.84,-1841.56 760.66,-1833.46 750.27,-1835.54 753.84,-1841.56"/>
|
||||
</g>
|
||||
<!-- files -->
|
||||
<g id="node7" class="node">
|
||||
<title>files</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="416.68,-2019.29 350.68,-2019.29 350.68,-1983.29 416.68,-1983.29 416.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="383.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#212529">File Upload</text>
|
||||
</g>
|
||||
<!-- file_adapter -->
|
||||
<g id="node11" class="node">
|
||||
<title>file_adapter</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="724.68,-1833.29 658.68,-1833.29 658.68,-1797.29 724.68,-1797.29 724.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="691.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">FileAdapter</text>
|
||||
</g>
|
||||
<!-- files->file_adapter -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>files->file_adapter</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M406.89,-1983.22C435.24,-1962.66 484.94,-1927.58 529.68,-1900.79 569.19,-1877.13 615.98,-1853.13 649.1,-1836.79"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="651.05,-1839.73 658.48,-1832.18 647.96,-1833.44 651.05,-1839.73"/>
|
||||
</g>
|
||||
<!-- base_adapter -->
|
||||
<g id="node8" class="node">
|
||||
<title>base_adapter</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="743.68,-2019.29 639.68,-2019.29 639.68,-1983.29 743.68,-1983.29 743.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="691.68" y="-2004.29" font-family="Arial" font-size="10.00">BaseSourceAdapter</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1993.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- base_adapter->webcam_adapter -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>base_adapter->webcam_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M681.8,-1982.98C663.31,-1950.57 623.36,-1880.56 601.58,-1842.39"/>
|
||||
<polygon fill="gray" stroke="gray" points="604.45,-1840.37 596.46,-1833.42 598.37,-1843.84 604.45,-1840.37"/>
|
||||
</g>
|
||||
<!-- base_adapter->rtmp_adapter -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>base_adapter->rtmp_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M700.9,-1982.98C718.16,-1950.57 755.45,-1880.56 775.78,-1842.39"/>
|
||||
<polygon fill="gray" stroke="gray" points="778.95,-1843.89 780.56,-1833.42 772.77,-1840.6 778.95,-1843.89"/>
|
||||
</g>
|
||||
<!-- base_adapter->file_adapter -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>base_adapter->file_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M691.68,-1982.98C691.68,-1950.98 691.68,-1882.33 691.68,-1843.86"/>
|
||||
<polygon fill="gray" stroke="gray" points="695.18,-1843.42 691.68,-1833.42 688.18,-1843.42 695.18,-1843.42"/>
|
||||
</g>
|
||||
<!-- django -->
|
||||
<g id="node12" class="node">
|
||||
<title>django</title>
|
||||
<polygon fill="#28a745" stroke="black" points="725.18,-1565.79 658.18,-1565.79 658.18,-1524.79 725.18,-1524.79 725.18,-1565.79"/>
|
||||
<text text-anchor="middle" x="691.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">Django API</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ Channels</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:8000</text>
|
||||
</g>
|
||||
<!-- webcam_adapter->django -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>webcam_adapter->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M593.41,-1797.12C611,-1752.21 658.51,-1630.94 680.38,-1575.12"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="683.64,-1576.4 684.03,-1565.81 677.13,-1573.84 683.64,-1576.4"/>
|
||||
</g>
|
||||
<!-- rtmp_adapter->django -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>rtmp_adapter->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M783.4,-1797.12C767.02,-1752.31 722.82,-1631.45 702.35,-1575.47"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="705.54,-1574 698.82,-1565.81 698.97,-1576.4 705.54,-1574"/>
|
||||
</g>
|
||||
<!-- file_adapter->django -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>file_adapter->django</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M691.68,-1797.12C691.68,-1752.4 691.68,-1631.95 691.68,-1575.82"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="695.18,-1575.81 691.68,-1565.81 688.18,-1575.81 695.18,-1575.81"/>
|
||||
</g>
|
||||
<!-- postgres -->
|
||||
<g id="node13" class="node">
|
||||
<title>postgres</title>
|
||||
<polygon fill="#28a745" stroke="black" points="835.68,-1252.29 765.68,-1252.29 765.68,-1216.29 835.68,-1216.29 835.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="800.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">PostgreSQL</text>
|
||||
<text text-anchor="middle" x="800.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Database</text>
|
||||
</g>
|
||||
<!-- django->postgres -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>django->postgres</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M698.67,-1524.49C717.54,-1470.97 769.61,-1323.37 791.15,-1262.29"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="794.55,-1263.19 794.58,-1252.6 787.95,-1260.86 794.55,-1263.19"/>
|
||||
</g>
|
||||
<!-- redis -->
|
||||
<g id="node14" class="node">
|
||||
<title>redis</title>
|
||||
<polygon fill="#28a745" stroke="black" points="738.68,-1252.29 652.68,-1252.29 652.68,-1216.29 738.68,-1216.29 738.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="695.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis</text>
|
||||
<text text-anchor="middle" x="695.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Cache & Broker</text>
|
||||
</g>
|
||||
<!-- django->redis -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>django->redis</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M691.94,-1524.49C692.63,-1471.08 694.53,-1323.98 695.33,-1262.67"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="698.83,-1262.64 695.46,-1252.6 691.83,-1262.55 698.83,-1262.64"/>
|
||||
</g>
|
||||
<!-- local_storage -->
|
||||
<g id="node35" class="node">
|
||||
<title>local_storage</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1076.68,-1252.29 998.68,-1252.29 998.68,-1216.29 1076.68,-1216.29 1076.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1037.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Local Files</text>
|
||||
<text text-anchor="middle" x="1037.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">(nginx-served)</text>
|
||||
</g>
|
||||
<!-- django->local_storage -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>django->local_storage</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M716.06,-1524.54C742.04,-1503.33 784.24,-1468.48 819.68,-1437.29 890.85,-1374.64 972.16,-1297.86 1012.4,-1259.51"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1015.06,-1261.8 1019.88,-1252.36 1010.23,-1256.74 1015.06,-1261.8"/>
|
||||
</g>
|
||||
<!-- gcs_storage -->
|
||||
<g id="node36" class="node">
|
||||
<title>gcs_storage</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1180.68,-1252.29 1104.68,-1252.29 1104.68,-1216.29 1180.68,-1216.29 1180.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1142.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#212529">Google Cloud</text>
|
||||
<text text-anchor="middle" x="1142.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#212529">Storage</text>
|
||||
</g>
|
||||
<!-- django->gcs_storage -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>django->gcs_storage</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M724.82,-1524.68C794.21,-1483.15 959.01,-1382.07 1087.68,-1283.79 1097.54,-1276.26 1107.86,-1267.43 1116.87,-1259.39"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1119.33,-1261.88 1124.4,-1252.57 1114.64,-1256.69 1119.33,-1261.88"/>
|
||||
</g>
|
||||
<!-- angular -->
|
||||
<g id="node37" class="node">
|
||||
<title>angular</title>
|
||||
<polygon fill="#28a745" stroke="black" points="951.68,-1254.79 865.68,-1254.79 865.68,-1213.79 951.68,-1213.79 951.68,-1254.79"/>
|
||||
<text text-anchor="middle" x="908.68" y="-1242.79" font-family="Arial" font-size="10.00" fill="#ffffff">Angular 17 SPA</text>
|
||||
<text text-anchor="middle" x="908.68" y="-1231.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ WebSocket</text>
|
||||
<text text-anchor="middle" x="908.68" y="-1220.79" font-family="Arial" font-size="10.00" fill="#ffffff">:4200</text>
|
||||
</g>
|
||||
<!-- django->angular -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>django->angular</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M705.59,-1524.49C742.94,-1471.29 845.59,-1325.13 888.94,-1263.4"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="892.01,-1265.12 894.89,-1254.92 886.28,-1261.1 892.01,-1265.12"/>
|
||||
<text text-anchor="middle" x="794.18" y="-1430.89" font-family="Arial" font-size="8.00">WebSocket</text>
|
||||
<text text-anchor="middle" x="794.18" y="-1421.89" font-family="Arial" font-size="8.00">API</text>
|
||||
</g>
|
||||
<!-- logo_worker -->
|
||||
<g id="node27" class="node">
|
||||
<title>logo_worker</title>
|
||||
<polygon fill="#28a745" stroke="black" points="480.18,-935.79 397.18,-935.79 397.18,-894.79 480.18,-894.79 480.18,-935.79"/>
|
||||
<text text-anchor="middle" x="438.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#ffffff">Logo Detection</text>
|
||||
<text text-anchor="middle" x="438.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#ffffff">Worker</text>
|
||||
<text text-anchor="middle" x="438.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#ffffff">(logo_queue)</text>
|
||||
</g>
|
||||
<!-- redis->logo_worker -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>redis->logo_worker</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M681.86,-1216.25C639.76,-1164.31 512.77,-1007.67 461.01,-943.83"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="463.52,-941.37 454.5,-935.8 458.08,-945.78 463.52,-941.37"/>
|
||||
</g>
|
||||
<!-- visual_worker -->
|
||||
<g id="node28" class="node">
|
||||
<title>visual_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="600.18,-935.79 509.18,-935.79 509.18,-894.79 600.18,-894.79 600.18,-935.79"/>
|
||||
<text text-anchor="middle" x="554.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Visual Properties</text>
|
||||
<text text-anchor="middle" x="554.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="554.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(visual_queue)</text>
|
||||
</g>
|
||||
<!-- redis->visual_worker -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>redis->visual_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M688.1,-1216.25C665.14,-1164.63 596.18,-1009.58 567.46,-945.01"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="570.62,-943.52 563.36,-935.8 564.23,-946.36 570.62,-943.52"/>
|
||||
</g>
|
||||
<!-- audio_worker -->
|
||||
<g id="node29" class="node">
|
||||
<title>audio_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="835.18,-935.79 748.18,-935.79 748.18,-894.79 835.18,-894.79 835.18,-935.79"/>
|
||||
<text text-anchor="middle" x="791.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Audio Transcript</text>
|
||||
<text text-anchor="middle" x="791.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="791.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(audio_queue)</text>
|
||||
</g>
|
||||
<!-- redis->audio_worker -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>redis->audio_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M700.84,-1216.25C716.44,-1164.73 763.23,-1010.22 782.86,-945.41"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="786.22,-946.39 785.77,-935.8 779.52,-944.36 786.22,-946.39"/>
|
||||
</g>
|
||||
<!-- text_worker -->
|
||||
<g id="node30" class="node">
|
||||
<title>text_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="718.68,-935.79 628.68,-935.79 628.68,-894.79 718.68,-894.79 718.68,-935.79"/>
|
||||
<text text-anchor="middle" x="673.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Text Recognition</text>
|
||||
<text text-anchor="middle" x="673.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="673.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(text_queue)</text>
|
||||
</g>
|
||||
<!-- redis->text_worker -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>redis->text_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M694.5,-1216.25C690.93,-1164.84 680.24,-1010.86 675.73,-945.81"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="679.22,-945.54 675.04,-935.8 672.24,-946.02 679.22,-945.54"/>
|
||||
</g>
|
||||
<!-- nginx -->
|
||||
<g id="node15" class="node">
|
||||
<title>nginx</title>
|
||||
<polygon fill="#28a745" stroke="black" points="835.68,-1565.79 753.68,-1565.79 753.68,-1524.79 835.68,-1524.79 835.68,-1565.79"/>
|
||||
<text text-anchor="middle" x="794.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">NGINX</text>
|
||||
<text text-anchor="middle" x="794.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">Reverse Proxy</text>
|
||||
<text text-anchor="middle" x="794.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:80</text>
|
||||
</g>
|
||||
<!-- nginx->angular -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>nginx->angular</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M801.99,-1524.49C821.49,-1471.62 874.87,-1326.93 897.89,-1264.55"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="901.26,-1265.52 901.44,-1254.92 894.69,-1263.09 901.26,-1265.52"/>
|
||||
</g>
|
||||
<!-- base_strategy -->
|
||||
<g id="node16" class="node">
|
||||
<title>base_strategy</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1877.18,-933.29 1758.18,-933.29 1758.18,-897.29 1877.18,-897.29 1877.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1817.68" y="-918.29" font-family="Arial" font-size="10.00">BaseExecutionStrategy</text>
|
||||
<text text-anchor="middle" x="1817.68" y="-907.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- local_strategy -->
|
||||
<g id="node17" class="node">
|
||||
<title>local_strategy</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1754.18,-664.29 1677.18,-664.29 1677.18,-628.29 1754.18,-628.29 1754.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1715.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#ffffff">LocalStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->local_strategy -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>base_strategy->local_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1811.15,-897.18C1793.84,-851.89 1746.75,-728.61 1725.87,-673.97"/>
|
||||
<polygon fill="gray" stroke="gray" points="1729.13,-672.69 1722.29,-664.6 1722.59,-675.19 1729.13,-672.69"/>
|
||||
</g>
|
||||
<!-- lan_strategy -->
|
||||
<g id="node18" class="node">
|
||||
<title>lan_strategy</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1854.18,-664.29 1781.18,-664.29 1781.18,-628.29 1854.18,-628.29 1854.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1817.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">LANStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->lan_strategy -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>base_strategy->lan_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1817.68,-897.18C1817.68,-852.08 1817.68,-729.63 1817.68,-674.65"/>
|
||||
<polygon fill="gray" stroke="gray" points="1821.18,-674.6 1817.68,-664.6 1814.18,-674.6 1821.18,-674.6"/>
|
||||
</g>
|
||||
<!-- cloud_strategy -->
|
||||
<g id="node19" class="node">
|
||||
<title>cloud_strategy</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1961.18,-664.29 1882.18,-664.29 1882.18,-628.29 1961.18,-628.29 1961.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1921.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">CloudStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->cloud_strategy -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>base_strategy->cloud_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1824.35,-897.18C1841.99,-851.89 1890.01,-728.61 1911.29,-673.97"/>
|
||||
<polygon fill="gray" stroke="gray" points="1914.57,-675.19 1914.94,-664.6 1908.05,-672.65 1914.57,-675.19"/>
|
||||
</g>
|
||||
<!-- clip_adapter -->
|
||||
<g id="node32" class="node">
|
||||
<title>clip_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1412.68,-428.29 1340.68,-428.29 1340.68,-392.29 1412.68,-392.29 1412.68,-428.29"/>
|
||||
<text text-anchor="middle" x="1376.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">CLIPAdapter</text>
|
||||
<text text-anchor="middle" x="1376.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local)</text>
|
||||
</g>
|
||||
<!-- local_strategy->clip_adapter -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>local_strategy->clip_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1691.03,-628.27C1631.12,-586.92 1477.85,-481.12 1409.93,-434.24"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1411.92,-431.36 1401.7,-428.56 1407.94,-437.12 1411.92,-431.36"/>
|
||||
</g>
|
||||
<!-- gcp_vision -->
|
||||
<g id="node33" class="node">
|
||||
<title>gcp_vision</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1540.18,-428.29 1441.18,-428.29 1441.18,-392.29 1540.18,-392.29 1540.18,-428.29"/>
|
||||
<text text-anchor="middle" x="1490.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">GCPVisionAdapter</text>
|
||||
<text text-anchor="middle" x="1490.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Cloud)</text>
|
||||
</g>
|
||||
<!-- local_strategy->gcp_vision -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>local_strategy->gcp_vision</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1702.78,-628.22C1682.83,-602.22 1642.81,-551.56 1604.68,-512.29 1577.5,-484.29 1543.76,-455 1520.07,-435.22"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1522.07,-432.33 1512.13,-428.64 1517.6,-437.71 1522.07,-432.33"/>
|
||||
</g>
|
||||
<!-- yolo_adapter -->
|
||||
<g id="node34" class="node">
|
||||
<title>yolo_adapter</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1646.18,-428.29 1569.18,-428.29 1569.18,-392.29 1646.18,-392.29 1646.18,-428.29"/>
|
||||
<text text-anchor="middle" x="1607.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">YOLOAdapter</text>
|
||||
<text text-anchor="middle" x="1607.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Planned)</text>
|
||||
</g>
|
||||
<!-- lan_strategy->yolo_adapter -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>lan_strategy->yolo_adapter</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1802.24,-628.08C1765.53,-587.17 1672.91,-483.97 1630.06,-436.23"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1632.43,-433.62 1623.15,-428.52 1627.22,-438.3 1632.43,-433.62"/>
|
||||
</g>
|
||||
<!-- speech_api -->
|
||||
<g id="node40" class="node">
|
||||
<title>speech_api</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1802.18,-117.29 1701.18,-117.29 1701.18,-81.29 1802.18,-81.29 1802.18,-117.29"/>
|
||||
<text text-anchor="middle" x="1751.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Speech-to-Text API</text>
|
||||
<text text-anchor="middle" x="1751.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Audio Transcript)</text>
|
||||
</g>
|
||||
<!-- cloud_strategy->speech_api -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>cloud_strategy->speech_api</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1916.31,-628.08C1891.75,-549.34 1790.02,-223.21 1760.04,-127.07"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1763.37,-126.01 1757.05,-117.5 1756.69,-128.09 1763.37,-126.01"/>
|
||||
</g>
|
||||
<!-- base_event_source -->
|
||||
<g id="node20" class="node">
|
||||
<title>base_event_source</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1490.68,-1252.29 1378.68,-1252.29 1378.68,-1216.29 1490.68,-1216.29 1490.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1434.68" y="-1237.29" font-family="Arial" font-size="10.00">SegmentEventSource</text>
|
||||
<text text-anchor="middle" x="1434.68" y="-1226.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- file_watcher_source -->
|
||||
<g id="node21" class="node">
|
||||
<title>file_watcher_source</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1338.18,-933.29 1213.18,-933.29 1213.18,-897.29 1338.18,-897.29 1338.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1275.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">FileWatcherEventSource</text>
|
||||
<text text-anchor="middle" x="1275.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local/LAN)</text>
|
||||
</g>
|
||||
<!-- base_event_source->file_watcher_source -->
|
||||
<g id="edge47" class="edge">
|
||||
<title>base_event_source->file_watcher_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1426.13,-1216.25C1399.93,-1164 1320.58,-1005.81 1288.93,-942.7"/>
|
||||
<polygon fill="gray" stroke="gray" points="1291.88,-940.79 1284.27,-933.42 1285.63,-943.92 1291.88,-940.79"/>
|
||||
</g>
|
||||
<!-- cloud_storage_source -->
|
||||
<g id="node22" class="node">
|
||||
<title>cloud_storage_source</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1500.68,-933.29 1368.68,-933.29 1368.68,-897.29 1500.68,-897.29 1500.68,-933.29"/>
|
||||
<text text-anchor="middle" x="1434.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">CloudStorageEventSource</text>
|
||||
<text text-anchor="middle" x="1434.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(GCS Pub/Sub)</text>
|
||||
</g>
|
||||
<!-- base_event_source->cloud_storage_source -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>base_event_source->cloud_storage_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1434.68,-1216.25C1434.68,-1164.21 1434.68,-1007.11 1434.68,-943.48"/>
|
||||
<polygon fill="gray" stroke="gray" points="1438.18,-943.42 1434.68,-933.42 1431.18,-943.42 1438.18,-943.42"/>
|
||||
</g>
|
||||
<!-- webhook_source -->
|
||||
<g id="node23" class="node">
|
||||
<title>webhook_source</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1645.18,-933.29 1532.18,-933.29 1532.18,-897.29 1645.18,-897.29 1645.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1588.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">WebhookEventSource</text>
|
||||
<text text-anchor="middle" x="1588.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(External)</text>
|
||||
</g>
|
||||
<!-- base_event_source->webhook_source -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>base_event_source->webhook_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1442.96,-1216.25C1468.34,-1164 1545.19,-1005.81 1575.85,-942.7"/>
|
||||
<polygon fill="gray" stroke="gray" points="1579.14,-943.94 1580.36,-933.42 1572.84,-940.88 1579.14,-943.94"/>
|
||||
</g>
|
||||
<!-- event_source_manager -->
|
||||
<g id="node24" class="node">
|
||||
<title>event_source_manager</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1309.68,-664.29 1199.68,-664.29 1199.68,-628.29 1309.68,-628.29 1309.68,-664.29"/>
|
||||
<text text-anchor="middle" x="1254.68" y="-649.29" font-family="Arial" font-size="10.00" fill="#ffffff">EventSourceManager</text>
|
||||
<text text-anchor="middle" x="1254.68" y="-638.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Environment-based)</text>
|
||||
</g>
|
||||
<!-- file_watcher_source->event_source_manager -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>file_watcher_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1274.34,-897.18C1270.79,-852.08 1261.16,-729.63 1256.83,-674.65"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1260.32,-674.29 1256.04,-664.6 1253.34,-674.84 1260.32,-674.29"/>
|
||||
</g>
|
||||
<!-- cloud_storage_source->event_source_manager -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>cloud_storage_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1423.15,-897.18C1392.42,-851.61 1308.47,-727.07 1271.99,-672.96"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1274.84,-670.93 1266.35,-664.6 1269.04,-674.85 1274.84,-670.93"/>
|
||||
</g>
|
||||
<!-- webhook_source->event_source_manager -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>webhook_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1567.28,-897.18C1509.57,-851.04 1350.61,-723.97 1284.32,-670.98"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1286.33,-668.11 1276.34,-664.6 1281.96,-673.58 1286.33,-668.11"/>
|
||||
</g>
|
||||
<!-- redis_events -->
|
||||
<g id="node25" class="node">
|
||||
<title>redis_events</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1299.68,-428.29 1197.68,-428.29 1197.68,-392.29 1299.68,-392.29 1299.68,-428.29"/>
|
||||
<text text-anchor="middle" x="1248.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis Event Queue</text>
|
||||
<text text-anchor="middle" x="1248.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(segment_events)</text>
|
||||
</g>
|
||||
<!-- event_source_manager->redis_events -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>event_source_manager->redis_events</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1254.24,-628.08C1253.21,-587.85 1250.63,-487.37 1249.38,-438.64"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1252.88,-438.43 1249.12,-428.52 1245.88,-438.61 1252.88,-438.43"/>
|
||||
<text text-anchor="middle" x="1268.68" y="-523.89" font-family="Arial" font-size="8.00">publishes</text>
|
||||
<text text-anchor="middle" x="1268.68" y="-514.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- event_processor -->
|
||||
<g id="node26" class="node">
|
||||
<title>event_processor</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1282.68,-117.29 1188.68,-117.29 1188.68,-81.29 1282.68,-81.29 1282.68,-117.29"/>
|
||||
<text text-anchor="middle" x="1235.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#ffffff">Event Processor</text>
|
||||
<text text-anchor="middle" x="1235.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#ffffff">(triggers analysis)</text>
|
||||
</g>
|
||||
<!-- redis_events->event_processor -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>redis_events->event_processor</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1247.96,-392.24C1245.82,-341.27 1239.45,-189.79 1236.83,-127.5"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1240.31,-127.13 1236.4,-117.29 1233.32,-127.43 1240.31,-127.13"/>
|
||||
<text text-anchor="middle" x="1262.18" y="-287.89" font-family="Arial" font-size="8.00">consumes</text>
|
||||
<text text-anchor="middle" x="1262.18" y="-278.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- event_processor->logo_worker -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>event_processor->logo_worker</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1219.03,-117.29C1118.31,-220.17 590.46,-759.27 465.31,-887.09"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="462.48,-884.98 457.99,-894.57 467.48,-889.87 462.48,-884.98"/>
|
||||
<text text-anchor="middle" x="844.68" y="-523.89" font-family="Arial" font-size="8.00">triggers</text>
|
||||
<text text-anchor="middle" x="844.68" y="-514.89" font-family="Arial" font-size="8.00">analysis</text>
|
||||
</g>
|
||||
<!-- logo_worker->django -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>logo_worker->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M446.55,-935.81C483.06,-1026.45 636.1,-1406.32 679.91,-1515.08"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="676.71,-1516.49 683.69,-1524.46 683.2,-1513.87 676.71,-1516.49"/>
|
||||
<text text-anchor="middle" x="599.68" y="-1236.89" font-family="Arial" font-size="8.00">analysis</text>
|
||||
<text text-anchor="middle" x="599.68" y="-1227.89" font-family="Arial" font-size="8.00">results</text>
|
||||
</g>
|
||||
<!-- logo_worker->local_strategy -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>logo_worker->local_strategy</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M476.74,-894.69C483.88,-891.64 491.4,-888.82 498.68,-886.79 1003.74,-745.56 1195.76,-932.06 1662.68,-693.29 1673.48,-687.77 1683.89,-679.59 1692.59,-671.65"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1695.24,-673.95 1700.05,-664.51 1690.41,-668.89 1695.24,-673.95"/>
|
||||
</g>
|
||||
<!-- visual_worker->lan_strategy -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>visual_worker->lan_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M594.34,-894.72C601.93,-891.62 609.94,-888.79 617.68,-886.79 975.57,-794.36 1084.97,-889.51 1445.68,-808.79 1593.23,-775.77 1635.63,-770.42 1765.68,-693.29 1775.84,-687.26 1785.84,-679.11 1794.32,-671.34"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1796.8,-673.81 1801.63,-664.39 1791.97,-668.74 1796.8,-673.81"/>
|
||||
</g>
|
||||
<!-- audio_worker->cloud_strategy -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>audio_worker->cloud_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M828.42,-894.66C835.33,-891.61 842.61,-888.81 849.68,-886.79 1144.76,-802.58 1236.56,-877.23 1535.68,-808.79 1687.16,-774.13 1730.23,-769.44 1865.68,-693.29 1876.54,-687.18 1887.37,-678.92 1896.58,-671.08"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.01,-673.6 1904.21,-664.37 1894.39,-668.35 1899.01,-673.6"/>
|
||||
</g>
|
||||
<!-- text_worker->cloud_strategy -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>text_worker->cloud_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M714,-894.79C721.72,-891.68 729.84,-888.83 737.68,-886.79 1065.47,-801.63 1163.94,-876.92 1495.68,-808.79 1664.43,-774.13 1714.1,-775.14 1865.68,-693.29 1876.64,-687.37 1887.5,-679.15 1896.7,-671.29"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.14,-673.8 1904.31,-664.55 1894.5,-668.56 1899.14,-673.8"/>
|
||||
</g>
|
||||
<!-- base_ai -->
|
||||
<g id="node31" class="node">
|
||||
<title>base_ai</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1536.68,-664.29 1444.68,-664.29 1444.68,-628.29 1536.68,-628.29 1536.68,-664.29"/>
|
||||
<text text-anchor="middle" x="1490.68" y="-649.29" font-family="Arial" font-size="10.00">DetectionAdapter</text>
|
||||
<text text-anchor="middle" x="1490.68" y="-638.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- base_ai->clip_adapter -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>base_ai->clip_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1482.3,-628.08C1462.58,-587.6 1413.13,-486.1 1389.56,-437.72"/>
|
||||
<polygon fill="gray" stroke="gray" points="1392.6,-435.98 1385.08,-428.52 1386.31,-439.04 1392.6,-435.98"/>
|
||||
</g>
|
||||
<!-- base_ai->gcp_vision -->
|
||||
<g id="edge45" class="edge">
|
||||
<title>base_ai->gcp_vision</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1490.68,-628.08C1490.68,-587.85 1490.68,-487.37 1490.68,-438.64"/>
|
||||
<polygon fill="gray" stroke="gray" points="1494.18,-438.52 1490.68,-428.52 1487.18,-438.52 1494.18,-438.52"/>
|
||||
</g>
|
||||
<!-- base_ai->yolo_adapter -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>base_ai->yolo_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1499.28,-628.08C1519.53,-587.6 1570.28,-486.1 1594.46,-437.72"/>
|
||||
<polygon fill="gray" stroke="gray" points="1597.72,-439.03 1599.07,-428.52 1591.46,-435.9 1597.72,-439.03"/>
|
||||
</g>
|
||||
<!-- vision_api -->
|
||||
<g id="node39" class="node">
|
||||
<title>vision_api</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1672.18,-117.29 1581.18,-117.29 1581.18,-81.29 1672.18,-81.29 1672.18,-117.29"/>
|
||||
<text text-anchor="middle" x="1626.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Cloud Vision API</text>
|
||||
<text text-anchor="middle" x="1626.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Logo Detection)</text>
|
||||
</g>
|
||||
<!-- gcp_vision->vision_api -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>gcp_vision->vision_api</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1498.19,-392.24C1520.71,-341.06 1587.84,-188.54 1615.04,-126.73"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1618.37,-127.85 1619.2,-117.29 1611.97,-125.03 1618.37,-127.85"/>
|
||||
</g>
|
||||
<!-- local_storage->file_watcher_source -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>local_storage->file_watcher_source</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1045.51,-1216.18C1057.76,-1190.12 1082.96,-1139.4 1110.68,-1100.29 1154.5,-1038.45 1216.41,-974.35 1250.58,-940.54"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1253.16,-942.92 1257.83,-933.41 1248.25,-937.92 1253.16,-942.92"/>
|
||||
<text text-anchor="middle" x="1137.18" y="-1111.89" font-family="Arial" font-size="8.00">monitors</text>
|
||||
<text text-anchor="middle" x="1137.18" y="-1102.89" font-family="Arial" font-size="8.00">HLS segments</text>
|
||||
</g>
|
||||
<!-- gcs_storage->cloud_storage_source -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>gcs_storage->cloud_storage_source</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1158.38,-1216.25C1207,-1163.46 1355.23,-1002.54 1412.12,-940.78"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1414.7,-943.14 1418.9,-933.42 1409.56,-938.4 1414.7,-943.14"/>
|
||||
<text text-anchor="middle" x="1275.68" y="-1111.89" font-family="Arial" font-size="8.00">storage</text>
|
||||
<text text-anchor="middle" x="1275.68" y="-1102.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- hls_player -->
|
||||
<g id="node38" class="node">
|
||||
<title>hls_player</title>
|
||||
<polygon fill="#28a745" stroke="black" points="968.18,-933.29 867.18,-933.29 867.18,-897.29 968.18,-897.29 968.18,-933.29"/>
|
||||
<text text-anchor="middle" x="917.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">HLS.js Player</text>
|
||||
<text text-anchor="middle" x="917.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">+ Canvas Overlays</text>
|
||||
</g>
|
||||
<!-- angular->hls_player -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>angular->hls_player</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M909.24,-1213.46C910.79,-1158.86 915.13,-1006.23 916.91,-943.62"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="920.41,-943.45 917.2,-933.35 913.41,-943.25 920.41,-943.45"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 40 KiB |
153
docs/architecture/asciidiagram.md
Normal file
153
docs/architecture/asciidiagram.md
Normal 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.
|
||||
• Django’s 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 execution‐strategy 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 Django’s 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 bird’s-eye of how data and control flow through your streaming+AI stack.
|
||||
172
docs/architecture/convo
Normal file
172
docs/architecture/convo
Normal 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.
|
||||
33
docs/architecture/diagram.md
Normal file
33
docs/architecture/diagram.md
Normal 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
|
||||
224
docs/architecture/system_architecture.dot
Normal file
224
docs/architecture/system_architecture.dot
Normal file
@@ -0,0 +1,224 @@
|
||||
digraph MediaAnalyzer {
|
||||
// Graph settings
|
||||
rankdir=TB;
|
||||
bgcolor="white";
|
||||
fontname="Arial";
|
||||
fontsize=12;
|
||||
ratio=fill;
|
||||
size="27.5,30!";
|
||||
ranksep=0.3;
|
||||
nodesep=0.3;
|
||||
|
||||
// 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="#28a745", color="#ffffff", fontcolor="#ffffff"];
|
||||
planned [label="⏳ Planned", fillcolor="#ffc107", color="#212529", fontcolor="#212529"];
|
||||
code_pattern [label="📐 Code Pattern", fillcolor="#8e24aa", color="#ffffff", fontcolor="#ffffff"];
|
||||
service_arch [label="⚡ Service/Architecture", fillcolor="#1976d2", color="#ffffff", fontcolor="#ffffff"];
|
||||
|
||||
implemented -> planned [style=invis];
|
||||
code_pattern -> service_arch [style=invis];
|
||||
}
|
||||
|
||||
// Input Sources
|
||||
subgraph cluster_sources {
|
||||
label="Video Sources";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
webcam [label="Webcam", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
rtmp [label="RTMP/OBS", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
files [label="File Upload", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Source Adapters (Design Pattern)
|
||||
subgraph cluster_adapters {
|
||||
label="Source Adapters Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_adapter [label="BaseSourceAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
||||
webcam_adapter [label="WebcamAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
rtmp_adapter [label="RtmpAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
file_adapter [label="FileAdapter", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Core Services
|
||||
subgraph cluster_core {
|
||||
label="Core Platform";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
django [label="Django API\n+ Channels\n:8000", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
postgres [label="PostgreSQL\nDatabase", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
redis [label="Redis\nCache & Broker", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
nginx [label="NGINX\nReverse Proxy\n:80", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Execution Strategies (Design Pattern)
|
||||
subgraph cluster_execution {
|
||||
label="Execution Strategies Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_strategy [label="BaseExecutionStrategy\n(Abstract)", fillcolor="#ce93d8"];
|
||||
local_strategy [label="LocalStrategy", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
lan_strategy [label="LANStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
cloud_strategy [label="CloudStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Event Source Pattern (NEW)
|
||||
subgraph cluster_event_sources {
|
||||
label="Event Source Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_event_source [label="SegmentEventSource\n(Abstract)", fillcolor="#ce93d8"];
|
||||
file_watcher_source [label="FileWatcherEventSource\n(Local/LAN)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
cloud_storage_source [label="CloudStorageEventSource\n(GCS Pub/Sub)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
webhook_source [label="WebhookEventSource\n(External)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Event-Driven Processing Pipeline
|
||||
subgraph cluster_events {
|
||||
label="Event-Driven Processing";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
event_source_manager [label="EventSourceManager\n(Environment-based)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
redis_events [label="Redis Event Queue\n(segment_events)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
event_processor [label="Event Processor\n(triggers analysis)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Analysis Workers
|
||||
subgraph cluster_workers {
|
||||
label="Celery Workers";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
logo_worker [label="Logo Detection\nWorker\n(logo_queue)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
visual_worker [label="Visual Properties\nWorker\n(visual_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
audio_worker [label="Audio Transcript\nWorker\n(audio_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
text_worker [label="Text Recognition\nWorker\n(text_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// AI Adapters (Design Pattern)
|
||||
subgraph cluster_ai_adapters {
|
||||
label="Analysis Adapters Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_ai [label="DetectionAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
||||
clip_adapter [label="CLIPAdapter\n(Local)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
gcp_vision [label="GCPVisionAdapter\n(Cloud)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
yolo_adapter [label="YOLOAdapter\n(Planned)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Storage Options
|
||||
subgraph cluster_storage {
|
||||
label="Media Storage";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
local_storage [label="Local Files\n(nginx-served)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
gcs_storage [label="Google Cloud\nStorage", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Frontend
|
||||
subgraph cluster_frontend {
|
||||
label="Frontend";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
angular [label="Angular 17 SPA\n+ WebSocket\n:4200", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
hls_player [label="HLS.js Player\n+ Canvas Overlays", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Cloud Services
|
||||
subgraph cluster_cloud {
|
||||
label="GCP Services";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
vision_api [label="Cloud Vision API\n(Logo Detection)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
speech_api [label="Speech-to-Text API\n(Audio Transcript)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// 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"];
|
||||
|
||||
// Event source pattern connections
|
||||
local_storage -> file_watcher_source [label="monitors\nHLS segments", color="#2e7d32"];
|
||||
gcs_storage -> cloud_storage_source [label="storage\nevents", color="#2e7d32"];
|
||||
file_watcher_source -> event_source_manager [color="#2e7d32"];
|
||||
cloud_storage_source -> event_source_manager [color="#2e7d32"];
|
||||
webhook_source -> event_source_manager [color="#2e7d32"];
|
||||
|
||||
// Event-driven processing flow
|
||||
event_source_manager -> redis_events [label="publishes\nevents", color="#2e7d32"];
|
||||
redis_events -> event_processor [label="consumes\nevents", color="#2e7d32"];
|
||||
event_processor -> logo_worker [label="triggers\nanalysis", 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"];
|
||||
|
||||
// WebSocket real-time updates
|
||||
logo_worker -> django [label="analysis\nresults", 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];
|
||||
|
||||
// Event source inheritance (NEW)
|
||||
base_event_source -> file_watcher_source [style=dotted, color=gray];
|
||||
base_event_source -> cloud_storage_source [style=dotted, color=gray];
|
||||
base_event_source -> webhook_source [style=dotted, color=gray];
|
||||
}
|
||||
128
docs/index.html
Normal file
128
docs/index.html
Normal file
@@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Media Analyzer - Documentation</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Media Analyzer</h1>
|
||||
<p class="subtitle">Real-Time Video Analysis Platform</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="graph-section" id="system-architecture">
|
||||
<div class="graph-header-row">
|
||||
<h2>System Architecture</h2>
|
||||
<a href="architecture/architecture_diagram.svg" class="view-btn" target="_blank">View Full</a>
|
||||
</div>
|
||||
<a href="architecture/architecture_diagram.svg" class="graph-preview" target="_blank">
|
||||
<img src="architecture/architecture_diagram.svg" alt="System Architecture Diagram">
|
||||
</a>
|
||||
<div class="graph-details">
|
||||
<p>Complete system overview showing video ingestion, AI processing pipeline, and real-time dashboard components.</p>
|
||||
<h4>Components</h4>
|
||||
<ul>
|
||||
<li><strong>Video Ingestion</strong>: RTMP streams from OBS, FFmpeg HLS conversion</li>
|
||||
<li><strong>AI Processing</strong>: CLIP/YOLO for logo detection and scene analysis</li>
|
||||
<li><strong>Real-time Communication</strong>: Django Channels WebSocket for live updates</li>
|
||||
<li><strong>Frontend</strong>: Angular 17+ with HLS.js video player and Canvas overlays</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="findings-section">
|
||||
<h2>Key Features</h2>
|
||||
<div class="findings-grid">
|
||||
<article class="finding-card">
|
||||
<h3>Video Streaming</h3>
|
||||
<ul>
|
||||
<li>RTMP ingestion from OBS Studio</li>
|
||||
<li>FFmpeg HLS conversion</li>
|
||||
<li>Event-driven segment detection</li>
|
||||
<li>WebSocket-powered live updates</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="finding-card">
|
||||
<h3>AI Analysis</h3>
|
||||
<ul>
|
||||
<li>Logo/brand detection (CLIP)</li>
|
||||
<li>Object detection (YOLO)</li>
|
||||
<li>Real-time vs batch processing modes</li>
|
||||
<li>Switchable local/cloud backends</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="finding-card">
|
||||
<h3>Infrastructure</h3>
|
||||
<ul>
|
||||
<li>Docker containerized services</li>
|
||||
<li>Kubernetes orchestration</li>
|
||||
<li>GCP integration (Storage, Vision)</li>
|
||||
<li>Celery task queue with Redis</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tech-section">
|
||||
<h2>Technology Stack</h2>
|
||||
<div class="tech-grid">
|
||||
<div class="tech-column">
|
||||
<h3>Backend</h3>
|
||||
<ul>
|
||||
<li>Django + Channels</li>
|
||||
<li>Django REST Framework</li>
|
||||
<li>PostgreSQL</li>
|
||||
<li>Celery + Redis</li>
|
||||
<li>FFmpeg</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tech-column">
|
||||
<h3>AI/ML</h3>
|
||||
<ul>
|
||||
<li>OpenCV</li>
|
||||
<li>CLIP (scene analysis)</li>
|
||||
<li>YOLO (object detection)</li>
|
||||
<li>Hugging Face Transformers</li>
|
||||
<li>GCP Vision API</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tech-column">
|
||||
<h3>Frontend</h3>
|
||||
<ul>
|
||||
<li>Angular 17+</li>
|
||||
<li>HLS.js video player</li>
|
||||
<li>Canvas overlays</li>
|
||||
<li>WebSocket client</li>
|
||||
<li>Standalone components</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="findings-section">
|
||||
<h2>Architecture Goals</h2>
|
||||
<div class="findings-grid">
|
||||
<article class="finding-card">
|
||||
<h3>Event-Driven Design</h3>
|
||||
<p>File system watchers detect new HLS segments, triggering AI processing and real-time WebSocket notifications.</p>
|
||||
</article>
|
||||
<article class="finding-card">
|
||||
<h3>Scalable Processing</h3>
|
||||
<p>Celery workers handle AI tasks with configurable queues for real-time vs batch processing modes.</p>
|
||||
</article>
|
||||
<article class="finding-card">
|
||||
<h3>Cloud-Native</h3>
|
||||
<p>Kubernetes manifests for local (KIND) and production (GKE) deployment with easy environment switching.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Media Analyzer - Real-Time Video Analysis Platform</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
360
docs/styles.css
Normal file
360
docs/styles.css
Normal file
@@ -0,0 +1,360 @@
|
||||
/* Reset and base */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-bg: #0f0f0f;
|
||||
--color-surface: #1a1a1a;
|
||||
--color-surface-hover: #252525;
|
||||
--color-border: #333;
|
||||
--color-text: #e0e0e0;
|
||||
--color-text-muted: #888;
|
||||
--color-accent: #4A90D9;
|
||||
--color-accent-light: #6BA3E0;
|
||||
--radius: 8px;
|
||||
--shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
background: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
header .subtitle {
|
||||
margin: 0.5rem 0 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* Card Grid */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 700px) and (max-width: 999px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Graph Cards */
|
||||
.card {
|
||||
display: block;
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow);
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.card-preview {
|
||||
aspect-ratio: 4/3;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-content h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.card-content p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
/* Findings Grid */
|
||||
.findings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.findings-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.finding-card {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.finding-card h3 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.finding-card p,
|
||||
.finding-card ul {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.finding-card ul {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.finding-card li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.finding-card code {
|
||||
background: #2a2a2a;
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.finding-card pre {
|
||||
background: var(--color-bg);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Tech Stack Grid */
|
||||
.tech-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
.tech-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.tech-column {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.tech-column h3 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.tech-column ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tech-column li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
/* Graph Section */
|
||||
.graph-section {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.graph-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.graph-header-row h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
background: var(--color-accent);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
background: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.graph-section .graph-preview {
|
||||
display: block;
|
||||
background: #fff;
|
||||
max-height: 400px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.graph-section .graph-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
object-position: top left;
|
||||
}
|
||||
|
||||
.graph-details {
|
||||
padding: 1.5rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.graph-details p {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.graph-details h4 {
|
||||
margin: 1.5rem 0 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.graph-details h4:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.graph-details ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.graph-details li {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.graph-details code {
|
||||
background: #2a2a2a;
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
background: var(--color-surface);
|
||||
border-top: 1px solid var(--color-border);
|
||||
padding: 1.5rem 2rem;
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 600px) {
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-grid,
|
||||
.findings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user