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.
- {gitcast-1.0.26/gitcast.egg-info → gitcast-1.0.27}/PKG-INFO +1 -1
- {gitcast-1.0.26 → gitcast-1.0.27}/cli/gitcast.py +110 -48
- {gitcast-1.0.26 → gitcast-1.0.27}/core/ocr.py +23 -9
- {gitcast-1.0.26 → gitcast-1.0.27/gitcast.egg-info}/PKG-INFO +1 -1
- {gitcast-1.0.26 → gitcast-1.0.27}/setup.py +1 -1
- {gitcast-1.0.26 → gitcast-1.0.27}/.env.example +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/LICENSE +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/MANIFEST.in +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/README.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ai/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ai/formatter.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ai/generator.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ai/prompts.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/analytics.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/auth.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/auth_middleware.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/auth_routes.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/monitoring.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/payload.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/ratelimit.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/routes.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/server.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/api/validators.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/favicon.ico +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/assets/icon.png +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/cli/.env.example +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/cli/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/config/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/config/settings.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/capture.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/cloud_client.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/codebase_reader.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/framing.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/hotkey.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/log_stream.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/screenshot_session.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/security.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/tray.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/core/trigger.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/api-keys.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/article-generator.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/capture-flow.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/changelog.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/contributing.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/dashboard.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/insights.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/introduction.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/post-formats.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/providers.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/pypi-errors.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/quickstart.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/SOURCES.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/entry_points.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/requires.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/gitcast.egg-info/top_level.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/publisher/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/publisher/clipboard.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/publisher/twitter.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/pyproject.toml +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/requirements.txt +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/setup.cfg +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/cleanup.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/data/prompts.json +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/engagement.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/insights.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/key_manager.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/logger.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/metrics.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/sprint.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/streak.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/supabase_client.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/storage/tone_memory.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_keys.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_ocr_fix.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/tests/test_validation.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ui/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ui/popup.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/ui/review.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/web/__init__.py +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/web/index.html +0 -0
- {gitcast-1.0.26 → gitcast-1.0.27}/web/landing.html +0 -0
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
176
|
+
if attempt == 0:
|
|
177
|
+
print("> [Cloud] Server waking up, "
|
|
178
|
+
"please wait...")
|
|
179
|
+
time.sleep(8)
|
|
127
180
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
|
9
|
+
# ── Tesseract path ────────────────────────────────────────────────────────────
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 ────────────────────────────────────────────────────────────────
|
|
@@ -50,7 +50,7 @@ if os.path.exists(requirements_path):
|
|
|
50
50
|
|
|
51
51
|
setup(
|
|
52
52
|
name="gitcast",
|
|
53
|
-
version="1.0.
|
|
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
|
|
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
|