Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
|
@@ -3367,14 +3367,25 @@ class GlossarionWeb:
|
|
| 3367 |
|
| 3368 |
# AuthGPT login button (visible for authgpt/* models)
|
| 3369 |
_initial_model = self.get_config_value('model', 'authgpt/gpt-5.2').lower()
|
|
|
|
|
|
|
| 3370 |
authgpt_login_btn = gr.Button(
|
| 3371 |
-
"π ChatGPT Login",
|
| 3372 |
variant="secondary",
|
| 3373 |
-
visible=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3374 |
)
|
| 3375 |
authgpt_login_status = gr.Textbox(
|
| 3376 |
label="", interactive=False, visible=False,
|
| 3377 |
-
max_lines=
|
| 3378 |
)
|
| 3379 |
|
| 3380 |
# Use all profiles without filtering
|
|
@@ -3980,18 +3991,20 @@ class GlossarionWeb:
|
|
| 3980 |
|
| 3981 |
# --- Model change handlers: toggle API key & AuthGPT login ---
|
| 3982 |
def _on_model_change(model):
|
| 3983 |
-
"""Return visibility updates for API key
|
| 3984 |
hide_key = _model_needs_no_api_key(model or '')
|
| 3985 |
is_authgpt = (model or '').lower().startswith('authgpt/')
|
|
|
|
| 3986 |
return (
|
| 3987 |
-
gr.update(visible=not hide_key),
|
| 3988 |
-
gr.update(visible=is_authgpt),
|
|
|
|
| 3989 |
)
|
| 3990 |
|
| 3991 |
epub_model.change(
|
| 3992 |
fn=_on_model_change,
|
| 3993 |
inputs=[epub_model],
|
| 3994 |
-
outputs=[epub_api_key, authgpt_login_btn]
|
| 3995 |
)
|
| 3996 |
manga_model.change(
|
| 3997 |
fn=lambda m: gr.update(visible=not _model_needs_no_api_key(m or '')),
|
|
@@ -4000,32 +4013,68 @@ class GlossarionWeb:
|
|
| 4000 |
)
|
| 4001 |
|
| 4002 |
# --- AuthGPT Login handler ---
|
| 4003 |
-
def _authgpt_login():
|
| 4004 |
-
"""Run OAuth flow for ChatGPT subscription login.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4005 |
try:
|
| 4006 |
-
|
|
|
|
| 4007 |
store = get_default_store()
|
| 4008 |
|
|
|
|
|
|
|
| 4009 |
# If already logged in, show status
|
| 4010 |
-
if store.has_tokens:
|
| 4011 |
info = store.account_info
|
| 4012 |
email = info.get('email', '')
|
| 4013 |
plan = info.get('plan_type', '')
|
| 4014 |
return gr.update(value=f"β
Already logged in ({email or plan or 'active'})", visible=True)
|
| 4015 |
|
| 4016 |
-
|
| 4017 |
-
|
| 4018 |
-
|
| 4019 |
-
|
| 4020 |
-
|
| 4021 |
-
|
| 4022 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4023 |
except Exception as e:
|
| 4024 |
return gr.update(value=f"β Login failed: {e}", visible=True)
|
| 4025 |
|
| 4026 |
authgpt_login_btn.click(
|
| 4027 |
fn=_authgpt_login,
|
| 4028 |
-
inputs=[],
|
| 4029 |
outputs=[authgpt_login_status],
|
| 4030 |
concurrency_limit=None # OAuth flow blocks for up to 300s waiting for browser callback
|
| 4031 |
)
|
|
|
|
| 3367 |
|
| 3368 |
# AuthGPT login button (visible for authgpt/* models)
|
| 3369 |
_initial_model = self.get_config_value('model', 'authgpt/gpt-5.2').lower()
|
| 3370 |
+
_is_hf_spaces = os.getenv('SPACE_ID') is not None or os.getenv('HF_SPACES') == 'true'
|
| 3371 |
+
_show_authgpt = _initial_model.startswith('authgpt/')
|
| 3372 |
authgpt_login_btn = gr.Button(
|
| 3373 |
+
"π ChatGPT Login" if not _is_hf_spaces else "π Save Token",
|
| 3374 |
variant="secondary",
|
| 3375 |
+
visible=_show_authgpt
|
| 3376 |
+
)
|
| 3377 |
+
# On HF Spaces, show a textbox for users to paste their refresh token
|
| 3378 |
+
authgpt_token_input = gr.Textbox(
|
| 3379 |
+
label="π AuthGPT Refresh Token",
|
| 3380 |
+
type="password",
|
| 3381 |
+
placeholder="Paste refresh_token from ~/.glossarion/authgpt_tokens.json",
|
| 3382 |
+
visible=_is_hf_spaces and _show_authgpt,
|
| 3383 |
+
info="Browser login unavailable on HF Spaces. Paste your refresh token here.",
|
| 3384 |
+
interactive=True
|
| 3385 |
)
|
| 3386 |
authgpt_login_status = gr.Textbox(
|
| 3387 |
label="", interactive=False, visible=False,
|
| 3388 |
+
max_lines=2
|
| 3389 |
)
|
| 3390 |
|
| 3391 |
# Use all profiles without filtering
|
|
|
|
| 3991 |
|
| 3992 |
# --- Model change handlers: toggle API key & AuthGPT login ---
|
| 3993 |
def _on_model_change(model):
|
| 3994 |
+
"""Return visibility updates for API key, AuthGPT login button, and token input."""
|
| 3995 |
hide_key = _model_needs_no_api_key(model or '')
|
| 3996 |
is_authgpt = (model or '').lower().startswith('authgpt/')
|
| 3997 |
+
_hf = os.getenv('SPACE_ID') is not None or os.getenv('HF_SPACES') == 'true'
|
| 3998 |
return (
|
| 3999 |
+
gr.update(visible=not hide_key), # api_key
|
| 4000 |
+
gr.update(visible=is_authgpt), # authgpt_login_btn
|
| 4001 |
+
gr.update(visible=is_authgpt and _hf), # authgpt_token_input
|
| 4002 |
)
|
| 4003 |
|
| 4004 |
epub_model.change(
|
| 4005 |
fn=_on_model_change,
|
| 4006 |
inputs=[epub_model],
|
| 4007 |
+
outputs=[epub_api_key, authgpt_login_btn, authgpt_token_input]
|
| 4008 |
)
|
| 4009 |
manga_model.change(
|
| 4010 |
fn=lambda m: gr.update(visible=not _model_needs_no_api_key(m or '')),
|
|
|
|
| 4013 |
)
|
| 4014 |
|
| 4015 |
# --- AuthGPT Login handler ---
|
| 4016 |
+
def _authgpt_login(pasted_token):
|
| 4017 |
+
"""Run OAuth flow for ChatGPT subscription login.
|
| 4018 |
+
|
| 4019 |
+
On Hugging Face Spaces (headless), accepts a pasted refresh token
|
| 4020 |
+
instead of opening a browser for the OAuth flow.
|
| 4021 |
+
"""
|
| 4022 |
try:
|
| 4023 |
+
import time
|
| 4024 |
+
from authgpt_auth import get_default_store, run_oauth_flow, refresh_access_token
|
| 4025 |
store = get_default_store()
|
| 4026 |
|
| 4027 |
+
_hf = os.getenv('SPACE_ID') is not None or os.getenv('HF_SPACES') == 'true'
|
| 4028 |
+
|
| 4029 |
# If already logged in, show status
|
| 4030 |
+
if store.has_tokens and not pasted_token:
|
| 4031 |
info = store.account_info
|
| 4032 |
email = info.get('email', '')
|
| 4033 |
plan = info.get('plan_type', '')
|
| 4034 |
return gr.update(value=f"β
Already logged in ({email or plan or 'active'})", visible=True)
|
| 4035 |
|
| 4036 |
+
if _hf:
|
| 4037 |
+
# --- HF Spaces: use pasted token ---
|
| 4038 |
+
token_str = (pasted_token or '').strip()
|
| 4039 |
+
if not token_str:
|
| 4040 |
+
return gr.update(
|
| 4041 |
+
value="β Paste your refresh_token from ~/.glossarion/authgpt_tokens.json above, then click Save Token.",
|
| 4042 |
+
visible=True
|
| 4043 |
+
)
|
| 4044 |
+
|
| 4045 |
+
# Try to use the pasted value as a refresh token
|
| 4046 |
+
try:
|
| 4047 |
+
refreshed = refresh_access_token(token_str)
|
| 4048 |
+
store.save_tokens(refreshed)
|
| 4049 |
+
info = store.account_info
|
| 4050 |
+
email = info.get('email', '')
|
| 4051 |
+
plan = info.get('plan_type', '')
|
| 4052 |
+
return gr.update(value=f"β
Logged in via token ({email or plan or 'success'})", visible=True)
|
| 4053 |
+
except Exception as ref_exc:
|
| 4054 |
+
# Maybe it's a raw access token instead?
|
| 4055 |
+
manual_tokens = {
|
| 4056 |
+
"access_token": token_str,
|
| 4057 |
+
"expires_at": time.time() + 3600,
|
| 4058 |
+
}
|
| 4059 |
+
store.save_tokens(manual_tokens)
|
| 4060 |
+
return gr.update(
|
| 4061 |
+
value=f"β οΈ Saved as access token (refresh failed: {ref_exc}). May expire in ~1h.",
|
| 4062 |
+
visible=True
|
| 4063 |
+
)
|
| 4064 |
+
else:
|
| 4065 |
+
# --- Local/desktop: run browser OAuth flow ---
|
| 4066 |
+
tokens = run_oauth_flow()
|
| 4067 |
+
store.save_tokens(tokens)
|
| 4068 |
+
info = store.account_info
|
| 4069 |
+
email = info.get('email', '')
|
| 4070 |
+
plan = info.get('plan_type', '')
|
| 4071 |
+
return gr.update(value=f"β
Logged in ({email or plan or 'success'})", visible=True)
|
| 4072 |
except Exception as e:
|
| 4073 |
return gr.update(value=f"β Login failed: {e}", visible=True)
|
| 4074 |
|
| 4075 |
authgpt_login_btn.click(
|
| 4076 |
fn=_authgpt_login,
|
| 4077 |
+
inputs=[authgpt_token_input],
|
| 4078 |
outputs=[authgpt_login_status],
|
| 4079 |
concurrency_limit=None # OAuth flow blocks for up to 300s waiting for browser callback
|
| 4080 |
)
|