THEZYZSTUDIO commited on
Commit
6994006
·
verified ·
1 Parent(s): 09fa4af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -40
app.py CHANGED
@@ -556,61 +556,146 @@ def _get_mouse_pos(display: str) -> tuple:
556
  return 0, 0
557
 
558
 
559
- def capture_with_grid(display: str, scale: float = 0.65, quality: int = 72,
560
- force_mx: int | None = None,
561
- force_my: int | None = None) -> dict:
562
- """يلتقط الشاشة من display محدد ويضيف Grid overlay."""
563
- from PIL import Image, ImageDraw
564
-
565
- img, ow, oh = _capture_raw(display)
566
- if img is None:
567
- return {"data": "", "width": 1920, "height": 1080, "mouse_x": 0, "mouse_y": 0}
568
 
569
- mx, my = (force_mx, force_my) if force_mx is not None else _get_mouse_pos(display)
570
 
571
- sw = int(ow * scale)
572
- sh = int(oh * scale)
573
- grid_img = img.resize((sw, sh), Image.LANCZOS)
574
- draw = ImageDraw.Draw(grid_img, "RGBA")
 
 
 
 
 
 
 
 
 
575
 
576
- step_x = int(100 * sw / ow)
577
- step_y = int(100 * sh / oh)
578
- gc = (255, 255, 255, 40)
579
- lc = (0, 255, 180, 210)
 
580
 
581
- x_sc, x_r = step_x, 100
 
582
  while x_sc < sw:
583
- draw.line([(x_sc, 0), (x_sc, sh)], fill=gc, width=1)
584
- draw.rectangle([(x_sc+1, 2), (x_sc+34, 15)], fill=(0, 0, 0, 170))
585
- draw.text((x_sc+2, 3), str(x_r), fill=lc)
586
- x_sc += step_x; x_r += 100
587
-
588
- y_sc, y_r = step_y, 100
 
 
 
 
 
589
  while y_sc < sh:
590
- draw.line([(0, y_sc), (sw, y_sc)], fill=gc, width=1)
591
- draw.rectangle([(2, y_sc+1), (38, y_sc+14)], fill=(0, 0, 0, 170))
592
- draw.text((3, y_sc+2), str(y_r), fill=lc)
593
- y_sc += step_y; y_r += 100
 
 
 
 
 
 
 
 
 
 
 
 
594
 
595
  msx = int(mx * sw / ow)
596
  msy = int(my * sh / oh)
597
- r = 11
598
- draw.ellipse([(msx-r, msy-r), (msx+r, msy+r)], outline=(255, 50, 50, 240), width=2)
599
- draw.line([(msx-18, msy), (msx+18, msy)], fill=(255, 50, 50, 200), width=1)
600
- draw.line([(msx, msy-18), (msx, msy+18)], fill=(255, 50, 50, 200), width=1)
601
 
602
- final = grid_img.convert("RGB")
603
  draw2 = ImageDraw.Draw(final)
604
  draw2.rectangle([(0, 0), (sw, 20)], fill=(0, 0, 0))
605
- draw2.text((4, 3), f"SCREEN {ow}x{oh} | MOUSE:({mx},{my}) | GRID=100px | DSP:{display}", fill=(0, 220, 160))
606
  draw2.rectangle([(0, sh-20), (sw, sh)], fill=(0, 0, 0))
607
- draw2.text((4, sh-17), "CLICK COORDS = numbers on grid lines (real screen pixels)", fill=(180, 180, 70))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
- buf = io.BytesIO()
610
- final.save(buf, format="JPEG", quality=quality, optimize=True)
611
- data = base64.b64encode(buf.getvalue()).decode()
 
 
 
612
 
613
- return {"data": data, "width": ow, "height": oh, "mouse_x": mx, "mouse_y": my}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
 
615
 
616
  def _frame_hash(data: str) -> str:
@@ -925,6 +1010,9 @@ async def handle_action(ws: WebSocket, msg: dict, sess: dict):
925
  await send({
926
  "type": "screenshot",
927
  "data": result["data"],
 
 
 
928
  "ts": int(time.time() * 1000),
929
  "auto": True, "label": label,
930
  "screen_width": result["width"],
@@ -944,6 +1032,9 @@ async def handle_action(ws: WebSocket, msg: dict, sess: dict):
944
  await send({
945
  "type": "screenshot",
946
  "data": result2["data"],
 
 
 
947
  "ts": int(time.time() * 1000),
948
  "auto": True, "label": f"{label} (delayed)",
949
  "screen_width": result2["width"],
@@ -963,6 +1054,9 @@ async def handle_action(ws: WebSocket, msg: dict, sess: dict):
963
  await send({
964
  "type": "screenshot",
965
  "data": result["data"],
 
 
 
966
  "ts": int(time.time() * 1000),
967
  "auto": False, "label": label,
968
  "screen_width": result["width"],
@@ -1282,6 +1376,9 @@ async def websocket_endpoint(
1282
  await ws.send_text(json.dumps({
1283
  "type": "screenshot",
1284
  "data": result["data"],
 
 
 
1285
  "ts": int(time.time() * 1000),
1286
  "label": "Initial screen",
1287
  "screen_width": result["width"],
@@ -1326,6 +1423,9 @@ async def rest_screenshot(user_id: str = "anonymous"):
1326
  result = await asyncio.to_thread(capture_with_grid, display, 0.7, 75)
1327
  return JSONResponse({
1328
  "image": result["data"],
 
 
 
1329
  "ts": int(time.time() * 1000),
1330
  "screen_width": result["width"],
1331
  "screen_height": result["height"],
 
556
  return 0, 0
557
 
558
 
559
+ def _draw_mouse_marker(draw, msx, msy, color=(255, 50, 50, 240)):
560
+ """يرسم علامة الماوس (دائرة + خطوط تقاطع) في نقطة محددة على صورة."""
561
+ r = 11
562
+ line_color = (color[0], color[1], color[2], 200)
563
+ draw.ellipse([(msx-r, msy-r), (msx+r, msy+r)], outline=color, width=2)
564
+ draw.line([(msx-18, msy), (msx+18, msy)], fill=line_color, width=1)
565
+ draw.line([(msx, msy-18), (msx, msy+18)], fill=line_color, width=1)
 
 
566
 
 
567
 
568
+ def _render_grid_variant(base_img, ow, oh, sw, sh, mx, my, display,
569
+ grid_step: int, line_color: tuple, label_every: int,
570
+ major_only_labels: bool = True) -> "Image.Image":
571
+ """
572
+ يرسم نسخة شبكة إحداثيات واحدة فوق نسخة من الصورة الأساسية.
573
+ grid_step → المسافة بالبكسل الحقيقي بين كل خط شبكة.
574
+ line_color → لون الخطوط والأرقام (RGBA)، مثل أخضر/سماوي أو أحمر.
575
+ label_every → كل كم بكسل حقيقي تُكتب فيه تسمية إحداثي (x,y) كاملة عند التقاطعات.
576
+ major_only_labels → إذا True، الأرقام على الحواف تظهر فقط عند خطوط "رئيسية" (مضاعفات label_every).
577
+ """
578
+ from PIL import Image, ImageDraw
579
+ img = base_img.copy()
580
+ draw = ImageDraw.Draw(img, "RGBA")
581
 
582
+ step_x = max(1, int(grid_step * sw / ow))
583
+ step_y = max(1, int(grid_step * sh / oh))
584
+ minor_color = (line_color[0], line_color[1], line_color[2], 35)
585
+ major_color = (line_color[0], line_color[1], line_color[2], 110)
586
+ text_color = line_color
587
 
588
+ x_sc, x_r = step_x, grid_step
589
+ x_majors = []
590
  while x_sc < sw:
591
+ is_major = (x_r % label_every == 0)
592
+ draw.line([(x_sc, 0), (x_sc, sh)], fill=(major_color if is_major else minor_color), width=1)
593
+ if is_major or not major_only_labels:
594
+ draw.rectangle([(x_sc+1, 2), (x_sc+34, 15)], fill=(0, 0, 0, 175))
595
+ draw.text((x_sc+2, 3), str(x_r), fill=text_color)
596
+ if is_major:
597
+ x_majors.append((x_sc, x_r))
598
+ x_sc += step_x; x_r += grid_step
599
+
600
+ y_sc, y_r = step_y, grid_step
601
+ y_majors = []
602
  while y_sc < sh:
603
+ is_major = (y_r % label_every == 0)
604
+ draw.line([(0, y_sc), (sw, y_sc)], fill=(major_color if is_major else minor_color), width=1)
605
+ if is_major or not major_only_labels:
606
+ draw.rectangle([(2, y_sc+1), (38, y_sc+14)], fill=(0, 0, 0, 175))
607
+ draw.text((3, y_sc+2), str(y_r), fill=text_color)
608
+ if is_major:
609
+ y_majors.append((y_sc, y_r))
610
+ y_sc += step_y; y_r += grid_step
611
+
612
+ # تسمية إحداثي (x,y) كاملة عند كل تقاطع "رئيسي" — تضمن رقماً حقيقياً قريباً من كل منطقة
613
+ for (xs, xr) in x_majors:
614
+ for (ys, yr) in y_majors:
615
+ label = f"{xr},{yr}"
616
+ tw = 6 * len(label) + 4
617
+ draw.rectangle([(xs+2, ys+2), (xs+2+tw, ys+13)], fill=(0, 0, 0, 150))
618
+ draw.text((xs+4, ys+2), label, fill=text_color)
619
 
620
  msx = int(mx * sw / ow)
621
  msy = int(my * sh / oh)
622
+ _draw_mouse_marker(draw, msx, msy, color=(line_color[0], line_color[1], line_color[2], 240))
 
 
 
623
 
624
+ final = img.convert("RGB")
625
  draw2 = ImageDraw.Draw(final)
626
  draw2.rectangle([(0, 0), (sw, 20)], fill=(0, 0, 0))
627
+ draw2.text((4, 3), f"SCREEN {ow}x{oh} | MOUSE:({mx},{my}) | GRID={grid_step}px | DSP:{display}", fill=(0, 220, 160))
628
  draw2.rectangle([(0, sh-20), (sw, sh)], fill=(0, 0, 0))
629
+ draw2.text((4, sh-17), "CLICKCOORDS = numbers at every intersection (real screen pixels)", fill=(180, 180, 70))
630
+ return final
631
+
632
+
633
+ def capture_with_grid(display: str, scale: float = 0.65, quality: int = 72,
634
+ force_mx: int | None = None,
635
+ force_my: int | None = None,
636
+ grid_step: int = 50) -> dict:
637
+ """
638
+ يلتقط الشاشة من display محدد ويُنتج 4 نسخ من نفس اللقطة بالضبط (نفس اللحظة):
639
+ - "data" → النسخة العادية (نظيفة تماماً) + علامة الماوس فقط، بدون أي Grid.
640
+ هذه فقط تُعرض للمستخدم في الواجهة كـ "الشاشة الحقيقية".
641
+ - "data_grid" → شبكة إحداثيات عادية (كل 50px)، أخضر/سماوي — للذكاء الاصطناعي.
642
+ - "data_grid2" → شبكة إحداثيات أدق (كل 25px)، نفس الألوان — للذكاء الاصطناعي.
643
+ - "data_grid3" → شبكة إحداثيات فائقة الدقة (كل 10px تقريباً)، باللون الأحمر — للذكاء الاصطناعي.
644
+ الثلاث نسخ الأخيرة كلها من نفس اللقطة الأصلية بالضبط (نفس اللحظة، لا التقاط متكرر)
645
+ لضمان تطابقها تماماً ولتفادي أي تأخير أو اختلاف بينها.
646
+ """
647
+ from PIL import Image, ImageDraw
648
 
649
+ img, ow, oh = _capture_raw(display)
650
+ if img is None:
651
+ return {"data": "", "data_grid": "", "data_grid2": "", "data_grid3": "",
652
+ "width": 1920, "height": 1080, "mouse_x": 0, "mouse_y": 0}
653
+
654
+ mx, my = (force_mx, force_my) if force_mx is not None else _get_mouse_pos(display)
655
 
656
+ sw = int(ow * scale)
657
+ sh = int(oh * scale)
658
+ base_img = img.resize((sw, sh), Image.LANCZOS)
659
+ msx = int(mx * sw / ow)
660
+ msy = int(my * sh / oh)
661
+
662
+ # ── النسخة العادية النظيفة (بدون أي شبكة) — فقط علامة الماوس ─────────
663
+ clean_img = base_img.copy()
664
+ draw_clean = ImageDraw.Draw(clean_img, "RGBA")
665
+ _draw_mouse_marker(draw_clean, msx, msy)
666
+ clean_final = clean_img.convert("RGB")
667
+ buf_clean = io.BytesIO()
668
+ clean_final.save(buf_clean, format="JPEG", quality=quality, optimize=True)
669
+ data_clean = base64.b64encode(buf_clean.getvalue()).decode()
670
+
671
+ # ── نسخة شبكة عادية: كل 50px، أخضر/سماوي ────────────────────────────
672
+ grid1 = _render_grid_variant(base_img, ow, oh, sw, sh, mx, my, display,
673
+ grid_step=grid_step, line_color=(0, 255, 180, 235),
674
+ label_every=100)
675
+ buf1 = io.BytesIO(); grid1.save(buf1, format="JPEG", quality=quality, optimize=True)
676
+ data_grid1 = base64.b64encode(buf1.getvalue()).decode()
677
+
678
+ # ── نسخة شبكة أدق: كل 25px، نفس الألوان ──────────────────────────────
679
+ grid2 = _render_grid_variant(base_img, ow, oh, sw, sh, mx, my, display,
680
+ grid_step=25, line_color=(0, 255, 180, 235),
681
+ label_every=100)
682
+ buf2 = io.BytesIO(); grid2.save(buf2, format="JPEG", quality=quality, optimize=True)
683
+ data_grid2 = base64.b64encode(buf2.getvalue()).decode()
684
+
685
+ # ── نسخة شبكة فائقة الدقة: كل 10px تقريباً، أحمر بالكامل ────────────
686
+ grid3 = _render_grid_variant(base_img, ow, oh, sw, sh, mx, my, display,
687
+ grid_step=10, line_color=(255, 60, 60, 235),
688
+ label_every=50, major_only_labels=True)
689
+ buf3 = io.BytesIO(); grid3.save(buf3, format="JPEG", quality=max(quality, 78), optimize=True)
690
+ data_grid3 = base64.b64encode(buf3.getvalue()).decode()
691
+
692
+ return {
693
+ "data": data_clean, # النسخة العادية النظيفة (تُعرض للمستخدم فقط)
694
+ "data_grid": data_grid1, # شبكة عادية 50px (أخضر/سماوي)
695
+ "data_grid2": data_grid2, # شبكة أدق 25px (أخضر/سماوي)
696
+ "data_grid3": data_grid3, # شبكة فائقة الدقة 10px (أحمر)
697
+ "width": ow, "height": oh, "mouse_x": mx, "mouse_y": my,
698
+ }
699
 
700
 
701
  def _frame_hash(data: str) -> str:
 
1010
  await send({
1011
  "type": "screenshot",
1012
  "data": result["data"],
1013
+ "data_grid": result.get("data_grid", ""),
1014
+ "data_grid2": result.get("data_grid2", ""),
1015
+ "data_grid3": result.get("data_grid3", ""),
1016
  "ts": int(time.time() * 1000),
1017
  "auto": True, "label": label,
1018
  "screen_width": result["width"],
 
1032
  await send({
1033
  "type": "screenshot",
1034
  "data": result2["data"],
1035
+ "data_grid": result2.get("data_grid", ""),
1036
+ "data_grid2": result2.get("data_grid2", ""),
1037
+ "data_grid3": result2.get("data_grid3", ""),
1038
  "ts": int(time.time() * 1000),
1039
  "auto": True, "label": f"{label} (delayed)",
1040
  "screen_width": result2["width"],
 
1054
  await send({
1055
  "type": "screenshot",
1056
  "data": result["data"],
1057
+ "data_grid": result.get("data_grid", ""),
1058
+ "data_grid2": result.get("data_grid2", ""),
1059
+ "data_grid3": result.get("data_grid3", ""),
1060
  "ts": int(time.time() * 1000),
1061
  "auto": False, "label": label,
1062
  "screen_width": result["width"],
 
1376
  await ws.send_text(json.dumps({
1377
  "type": "screenshot",
1378
  "data": result["data"],
1379
+ "data_grid": result.get("data_grid", ""),
1380
+ "data_grid2": result.get("data_grid2", ""),
1381
+ "data_grid3": result.get("data_grid3", ""),
1382
  "ts": int(time.time() * 1000),
1383
  "label": "Initial screen",
1384
  "screen_width": result["width"],
 
1423
  result = await asyncio.to_thread(capture_with_grid, display, 0.7, 75)
1424
  return JSONResponse({
1425
  "image": result["data"],
1426
+ "image_grid": result.get("data_grid", ""),
1427
+ "image_grid2": result.get("data_grid2", ""),
1428
+ "image_grid3": result.get("data_grid3", ""),
1429
  "ts": int(time.time() * 1000),
1430
  "screen_width": result["width"],
1431
  "screen_height": result["height"],