gitcast 1.0.26__tar.gz → 1.0.27__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {gitcast-1.0.26/gitcast.egg-info → gitcast-1.0.27}/PKG-INFO +1 -1
  2. {gitcast-1.0.26 → gitcast-1.0.27}/cli/gitcast.py +110 -48
  3. {gitcast-1.0.26 → gitcast-1.0.27}/core/ocr.py +23 -9
  4. {gitcast-1.0.26 → gitcast-1.0.27/gitcast.egg-info}/PKG-INFO +1 -1
  5. {gitcast-1.0.26 → gitcast-1.0.27}/setup.py +1 -1
  6. {gitcast-1.0.26 → gitcast-1.0.27}/.env.example +0 -0
  7. {gitcast-1.0.26 → gitcast-1.0.27}/LICENSE +0 -0
  8. {gitcast-1.0.26 → gitcast-1.0.27}/MANIFEST.in +0 -0
  9. {gitcast-1.0.26 → gitcast-1.0.27}/README.md +0 -0
  10. {gitcast-1.0.26 → gitcast-1.0.27}/ai/__init__.py +0 -0
  11. {gitcast-1.0.26 → gitcast-1.0.27}/ai/formatter.py +0 -0
  12. {gitcast-1.0.26 → gitcast-1.0.27}/ai/generator.py +0 -0
  13. {gitcast-1.0.26 → gitcast-1.0.27}/ai/prompts.py +0 -0
  14. {gitcast-1.0.26 → gitcast-1.0.27}/ai/viral_patterns.py +0 -0
  15. {gitcast-1.0.26 → gitcast-1.0.27}/api/__init__.py +0 -0
  16. {gitcast-1.0.26 → gitcast-1.0.27}/api/analytics.py +0 -0
  17. {gitcast-1.0.26 → gitcast-1.0.27}/api/auth.py +0 -0
  18. {gitcast-1.0.26 → gitcast-1.0.27}/api/auth_middleware.py +0 -0
  19. {gitcast-1.0.26 → gitcast-1.0.27}/api/auth_routes.py +0 -0
  20. {gitcast-1.0.26 → gitcast-1.0.27}/api/monitoring.py +0 -0
  21. {gitcast-1.0.26 → gitcast-1.0.27}/api/payload.py +0 -0
  22. {gitcast-1.0.26 → gitcast-1.0.27}/api/ratelimit.py +0 -0
  23. {gitcast-1.0.26 → gitcast-1.0.27}/api/routes.py +0 -0
  24. {gitcast-1.0.26 → gitcast-1.0.27}/api/server.py +0 -0
  25. {gitcast-1.0.26 → gitcast-1.0.27}/api/validators.py +0 -0
  26. {gitcast-1.0.26 → gitcast-1.0.27}/assets/__init__.py +0 -0
  27. {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-16x16.png +0 -0
  28. {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-32x32.png +0 -0
  29. {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-64x64.png +0 -0
  30. {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon.ico +0 -0
  31. {gitcast-1.0.26 → gitcast-1.0.27}/assets/icon.png +0 -0
  32. {gitcast-1.0.26 → gitcast-1.0.27}/cli/.env.example +0 -0
  33. {gitcast-1.0.26 → gitcast-1.0.27}/cli/__init__.py +0 -0
  34. {gitcast-1.0.26 → gitcast-1.0.27}/config/__init__.py +0 -0
  35. {gitcast-1.0.26 → gitcast-1.0.27}/config/settings.py +0 -0
  36. {gitcast-1.0.26 → gitcast-1.0.27}/core/__init__.py +0 -0
  37. {gitcast-1.0.26 → gitcast-1.0.27}/core/capture.py +0 -0
  38. {gitcast-1.0.26 → gitcast-1.0.27}/core/cloud_client.py +0 -0
  39. {gitcast-1.0.26 → gitcast-1.0.27}/core/codebase_reader.py +0 -0
  40. {gitcast-1.0.26 → gitcast-1.0.27}/core/framing.py +0 -0
  41. {gitcast-1.0.26 → gitcast-1.0.27}/core/hotkey.py +0 -0
  42. {gitcast-1.0.26 → gitcast-1.0.27}/core/log_stream.py +0 -0
  43. {gitcast-1.0.26 → gitcast-1.0.27}/core/screenshot_session.py +0 -0
  44. {gitcast-1.0.26 → gitcast-1.0.27}/core/security.py +0 -0
  45. {gitcast-1.0.26 → gitcast-1.0.27}/core/tray.py +0 -0
  46. {gitcast-1.0.26 → gitcast-1.0.27}/core/trigger.py +0 -0
  47. {gitcast-1.0.26 → gitcast-1.0.27}/docs/api-keys.md +0 -0
  48. {gitcast-1.0.26 → gitcast-1.0.27}/docs/article-generator.md +0 -0
  49. {gitcast-1.0.26 → gitcast-1.0.27}/docs/capture-flow.md +0 -0
  50. {gitcast-1.0.26 → gitcast-1.0.27}/docs/changelog.md +0 -0
  51. {gitcast-1.0.26 → gitcast-1.0.27}/docs/contributing.md +0 -0
  52. {gitcast-1.0.26 → gitcast-1.0.27}/docs/dashboard.md +0 -0
  53. {gitcast-1.0.26 → gitcast-1.0.27}/docs/insights.md +0 -0
  54. {gitcast-1.0.26 → gitcast-1.0.27}/docs/introduction.md +0 -0
  55. {gitcast-1.0.26 → gitcast-1.0.27}/docs/post-formats.md +0 -0
  56. {gitcast-1.0.26 → gitcast-1.0.27}/docs/providers.md +0 -0
  57. {gitcast-1.0.26 → gitcast-1.0.27}/docs/pypi-errors.md +0 -0
  58. {gitcast-1.0.26 → gitcast-1.0.27}/docs/quickstart.md +0 -0
  59. {gitcast-1.0.26 → gitcast-1.0.27}/docs/sprint-mode.md +0 -0
  60. {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/SOURCES.txt +0 -0
  61. {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/dependency_links.txt +0 -0
  62. {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/entry_points.txt +0 -0
  63. {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/requires.txt +0 -0
  64. {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/top_level.txt +0 -0
  65. {gitcast-1.0.26 → gitcast-1.0.27}/publisher/__init__.py +0 -0
  66. {gitcast-1.0.26 → gitcast-1.0.27}/publisher/clipboard.py +0 -0
  67. {gitcast-1.0.26 → gitcast-1.0.27}/publisher/twitter.py +0 -0
  68. {gitcast-1.0.26 → gitcast-1.0.27}/pyproject.toml +0 -0
  69. {gitcast-1.0.26 → gitcast-1.0.27}/requirements.txt +0 -0
  70. {gitcast-1.0.26 → gitcast-1.0.27}/setup.cfg +0 -0
  71. {gitcast-1.0.26 → gitcast-1.0.27}/storage/__init__.py +0 -0
  72. {gitcast-1.0.26 → gitcast-1.0.27}/storage/cleanup.py +0 -0
  73. {gitcast-1.0.26 → gitcast-1.0.27}/storage/data/prompts.json +0 -0
  74. {gitcast-1.0.26 → gitcast-1.0.27}/storage/engagement.py +0 -0
  75. {gitcast-1.0.26 → gitcast-1.0.27}/storage/insights.py +0 -0
  76. {gitcast-1.0.26 → gitcast-1.0.27}/storage/key_manager.py +0 -0
  77. {gitcast-1.0.26 → gitcast-1.0.27}/storage/logger.py +0 -0
  78. {gitcast-1.0.26 → gitcast-1.0.27}/storage/metrics.py +0 -0
  79. {gitcast-1.0.26 → gitcast-1.0.27}/storage/sprint.py +0 -0
  80. {gitcast-1.0.26 → gitcast-1.0.27}/storage/streak.py +0 -0
  81. {gitcast-1.0.26 → gitcast-1.0.27}/storage/supabase_client.py +0 -0
  82. {gitcast-1.0.26 → gitcast-1.0.27}/storage/tone_memory.py +0 -0
  83. {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_keys.py +0 -0
  84. {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_ocr_fix.py +0 -0
  85. {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_validation.py +0 -0
  86. {gitcast-1.0.26 → gitcast-1.0.27}/ui/__init__.py +0 -0
  87. {gitcast-1.0.26 → gitcast-1.0.27}/ui/popup.py +0 -0
  88. {gitcast-1.0.26 → gitcast-1.0.27}/ui/review.py +0 -0
  89. {gitcast-1.0.26 → gitcast-1.0.27}/web/__init__.py +0 -0
  90. {gitcast-1.0.26 → gitcast-1.0.27}/web/index.html +0 -0
  91. {gitcast-1.0.26 → gitcast-1.0.27}/web/landing.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.26
3
+ Version: 1.0.27
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -2,9 +2,16 @@ import sys
2
2
  import subprocess
3
3
  import httpx
4
4
  import webbrowser
5
+ import os
6
+ import signal
5
7
 
6
8
  _browser_opened = False
7
- VERSION = "1.0.26"
9
+ VERSION = "1.0.27"
10
+
11
+
12
+ def handle_exit(sig=None, frame=None):
13
+ print("\n> [Gitcast] shutting down... bye.")
14
+ os._exit(0)
8
15
 
9
16
 
10
17
  def setup():
@@ -65,6 +72,41 @@ Get a free Groq key in 2 minutes:
65
72
 
66
73
 
67
74
  def check_tesseract() -> bool:
75
+ import subprocess
76
+ import shutil
77
+ import os
78
+
79
+ # Method 1: check system PATH
80
+ tesseract_path = shutil.which("tesseract")
81
+ if tesseract_path:
82
+ print(f"> [OK] Tesseract OCR found")
83
+ return True
84
+
85
+ # Method 2: check common Windows install paths
86
+ windows_paths = [
87
+ r"C:\Program Files\Tesseract-OCR\tesseract.exe",
88
+ r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",
89
+ r"C:\Users\{}\AppData\Local\Tesseract-OCR\tesseract.exe".format(
90
+ os.environ.get("USERNAME", "")),
91
+ ]
92
+ for path in windows_paths:
93
+ if os.path.exists(path):
94
+ # found it — add to PATH so pytesseract
95
+ # can use it
96
+ tesseract_dir = os.path.dirname(path)
97
+ os.environ["PATH"] = (
98
+ tesseract_dir + os.pathsep +
99
+ os.environ.get("PATH", ""))
100
+ # also tell pytesseract directly
101
+ try:
102
+ import pytesseract
103
+ pytesseract.pytesseract.tesseract_cmd = path
104
+ except ImportError:
105
+ pass
106
+ print(f"> [OK] Tesseract OCR found")
107
+ return True
108
+
109
+ # Method 3: try running it directly
68
110
  try:
69
111
  result = subprocess.run(
70
112
  ["tesseract", "--version"],
@@ -75,65 +117,73 @@ def check_tesseract() -> bool:
75
117
  if result.returncode == 0:
76
118
  print("> [OK] Tesseract OCR found")
77
119
  return True
78
- except (FileNotFoundError, subprocess.TimeoutExpired):
120
+ except Exception:
79
121
  pass
80
122
 
123
+ # genuinely not found — show install instructions
81
124
  print("""
82
125
  > [!!] TESSERACT OCR NOT INSTALLED
83
-
84
- Required for reading your screen locally.
85
- Your code never leaves your machine without
86
- your permission - OCR runs on-device.
87
-
88
- Install for your OS:
89
-
90
- WINDOWS:
91
- Download installer:
92
- github.com/UB-Mannheim/tesseract/wiki
93
- Run it -> check "Add to PATH" -> restart terminal
94
-
95
- MAC:
96
- brew install tesseract
97
-
98
- LINUX:
99
- sudo apt install tesseract-ocr
100
-
101
- Gitcast will still run. Screenshot text
102
- extraction will be limited until Tesseract
103
- is installed.
104
- """)
126
+ >
127
+ > Required for reading your screen locally.
128
+ > Your code never leaves your machine without
129
+ > your permission - OCR runs on-device.
130
+ >
131
+ > WINDOWS:
132
+ > Download: github.com/UB-Mannheim/tesseract/wiki
133
+ > Run installer
134
+ > Check "Add to PATH" during install
135
+ > Restart terminal then run gitcast again
136
+ >
137
+ > MAC:
138
+ > brew install tesseract
139
+ >
140
+ > LINUX:
141
+ > sudo apt install tesseract-ocr
142
+ >
143
+ > Gitcast will still run. Posts will generate
144
+ > but screenshot text extraction is limited.
145
+ """)
105
146
  return False
106
147
 
107
148
 
108
149
  def check_server():
109
150
  from core.cloud_client import check_server_health
151
+ import time
110
152
 
111
153
  print("> [Cloud] Connecting to Gitcast server...")
112
- health = check_server_health()
113
-
114
- if health.get("status") == "unreachable":
115
- print("> [!!] Cannot reach Gitcast server")
116
- print("> Check your internet connection")
117
- print("> Server: https://gitcast-api.onrender.com")
118
- return False
119
154
 
120
- if health.get("status") == "ok":
121
- providers = health.get("providers", {})
122
- active = [key for key, value in providers.items() if value]
123
- print("> [OK] Connected to Gitcast server")
124
- print(f"> [OK] AI providers: {', '.join(active)}")
155
+ # Render free tier sleeps after inactivity
156
+ # retry up to 3 times with increasing delays
157
+ for attempt in range(3):
158
+ health = check_server_health()
159
+
160
+ if health.get("status") == "ok":
161
+ providers = health.get("providers", {})
162
+ active = [k for k, v in providers.items()
163
+ if v]
164
+ print(f"> [OK] Connected to Gitcast server")
165
+ print(f"> [OK] Providers: "
166
+ f"{', '.join(active)}")
167
+ from config.settings import BYOK_KEY
168
+ if BYOK_KEY:
169
+ print("> [OK] Using your own API key")
170
+ else:
171
+ print("> [OK] Using Gitcast shared key")
172
+ print("> Add your own for unlimited:")
173
+ print("> gitcast --setup")
174
+ return True
125
175
 
126
- from config.settings import BYOK_KEY
176
+ if attempt == 0:
177
+ print("> [Cloud] Server waking up, "
178
+ "please wait...")
179
+ time.sleep(8)
127
180
 
128
- if BYOK_KEY:
129
- print("> [OK] Using your own API key")
130
- else:
131
- print("> [OK] Using Gitcast shared key")
132
- print("> Add your own key anytime:")
133
- print("> gitcast --setup or [BYOK] in dashboard")
134
- return True
135
-
136
- return True
181
+ # after retries still failed
182
+ print("> [!!] Cannot reach Gitcast server")
183
+ print("> Posts may not generate")
184
+ print("> Check: "
185
+ "https://gitcast-api.onrender.com/health")
186
+ return False
137
187
 
138
188
  def main():
139
189
  if "--setup" in sys.argv:
@@ -148,8 +198,9 @@ def main():
148
198
  print(f"> GITCAST v{VERSION}")
149
199
  print("> git diff -> published post.")
150
200
  print(">")
201
+ print("> // Press Ctrl+C to quit\n")
202
+
151
203
  check_tesseract()
152
- check_server()
153
204
 
154
205
  # start FastAPI server in background thread
155
206
  import threading
@@ -184,9 +235,20 @@ def main():
184
235
  print("> [OK] browser opened")
185
236
  print("> Press Ctrl+Shift+P to capture")
186
237
 
238
+ check_server()
239
+
187
240
  from core.tray import run_tray
188
241
  from core.trigger import on_trigger
189
- run_tray(trigger_callback=on_trigger)
242
+
243
+ # register BEFORE starting tray:
244
+ signal.signal(signal.SIGINT, handle_exit)
245
+ signal.signal(signal.SIGTERM, handle_exit)
246
+
247
+ # wrap run_tray in try/except:
248
+ try:
249
+ run_tray(trigger_callback=on_trigger)
250
+ except KeyboardInterrupt:
251
+ handle_exit()
190
252
  return
191
253
 
192
254
  command = sys.argv[1]
@@ -1,21 +1,35 @@
1
1
  import os
2
+ import shutil
2
3
  import pytesseract
3
4
  from PIL import Image
4
5
  from pathlib import Path
5
6
  from config.settings import get_ocr_threshold
6
7
  from core.log_stream import stream_log
7
8
 
8
- # ── Tesseract path (Windows) ──────────────────────────────────────────────────
9
+ # ── Tesseract path ────────────────────────────────────────────────────────────
9
10
 
10
- # explicitly set the tesseract path for Windows
11
- # if installed elsewhere, update this path
12
- TESSERACT_PATH = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
11
+ def _find_and_set_tesseract():
12
+ """Find Tesseract and configure pytesseract."""
13
+ # check PATH first
14
+ path = shutil.which("tesseract")
15
+ if path:
16
+ pytesseract.pytesseract.tesseract_cmd = path
17
+ return True
13
18
 
14
- if Path(TESSERACT_PATH).exists():
15
- pytesseract.pytesseract.tesseract_cmd = TESSERACT_PATH
16
- else:
17
- # fallback — rely on PATH
18
- stream_log("OCR", "WARN", "Tesseract not found at default path. Relying on PATH.")
19
+ # check common Windows locations
20
+ windows_paths = [
21
+ r"C:\Program Files\Tesseract-OCR\tesseract.exe",
22
+ r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",
23
+ ]
24
+ for p in windows_paths:
25
+ if os.path.exists(p):
26
+ pytesseract.pytesseract.tesseract_cmd = p
27
+ return True
28
+
29
+ return False
30
+
31
+ # call once at module level:
32
+ _find_and_set_tesseract()
19
33
 
20
34
 
21
35
  # ── OCR runner ────────────────────────────────────────────────────────────────
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.26
3
+ Version: 1.0.27
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -50,7 +50,7 @@ if os.path.exists(requirements_path):
50
50
 
51
51
  setup(
52
52
  name="gitcast",
53
- version="1.0.26",
53
+ version="1.0.27",
54
54
  description="git diff → published post. under 60 seconds.",
55
55
  long_description=long_description,
56
56
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes