iyougame commited on
Commit
a0006e9
·
verified ·
1 Parent(s): 15f1285

Sync from GitHub: 8be01a61a2247c26411c1cd49f4aaf0985690108

Browse files
Files changed (2) hide show
  1. app.py +46 -57
  2. test_auth.py +26 -30
app.py CHANGED
@@ -11,13 +11,12 @@ from urllib.parse import urlparse, unquote
11
  import google.auth.exceptions
12
 
13
  # ---------------------------------------------------------
14
- # 0. 配置日志 (Debug Level)
15
  # ---------------------------------------------------------
16
- # 开启 HTTP 调试日志
17
- http.client.HTTPConnection.debuglevel = 1
18
 
19
- # 配置 Python Logging
20
- logging.basicConfig(level=logging.DEBUG)
21
  logger = logging.getLogger(__name__)
22
 
23
  # ---------------------------------------------------------
@@ -32,12 +31,7 @@ def get_drive_service():
32
  refresh_token = os.environ.get("G_REFRESH_TOKEN")
33
 
34
  if not all([client_id, client_secret, refresh_token]):
35
- logger.error("❌ 环境变量缺失")
36
- raise EnvironmentError("❌ 缺少必要的 OAuth 环境变量 (G_CLIENT_ID, G_CLIENT_SECRET, G_REFRESH_TOKEN)")
37
-
38
- logger.info("🔑 正在构建凭据对象...")
39
- logger.debug(f"Client ID: {client_id[:5]}...")
40
- logger.debug(f"Refresh Token: {refresh_token[:5]}...")
41
 
42
  creds = Credentials(
43
  token=None,
@@ -69,11 +63,12 @@ class StreamingUploadFile(io.IOBase):
69
  raise
70
 
71
  def seek(self, offset, whence=io.SEEK_SET):
 
72
  if whence == io.SEEK_SET and offset == self.position:
73
  return self.position
74
  if whence == io.SEEK_CUR and offset == 0:
75
  return self.position
76
- logger.warning(f"⚠️ 尝试 Seek 到不支持的位置: {offset}, 当前: {self.position}")
77
  return self.position
78
 
79
  def tell(self):
@@ -94,21 +89,13 @@ def process_upload(file_url, progress=gr.Progress()):
94
  return "❌ 错误: 请输入有效的 URL"
95
 
96
  try:
97
- # --- 🔍 验证 Token 有效性 ---
98
- logger.info("🔍 开始上传前验证 Token...")
99
  try:
100
  service = get_drive_service()
101
- # 尝试做一个轻量级请求来验证 Token
102
- service.about().get(fields="user").execute()
103
- logger.info("✅ Token 验证通过!")
104
- except google.auth.exceptions.RefreshError as re:
105
- logger.error(f"❌ Token 刷新失败 (无效或过期): {re}")
106
- return f"❌ **鉴权失败**: Refresh Token 无效或已过期。\n详情: {re}\n请重新生成 Token。"
107
  except Exception as e:
108
- logger.error(f"❌ Token 验证时发生未知错误: {e}")
109
- return f"❌ **鉴权错误**: 无法连接 Google Drive API。\n详情: {e}"
110
 
111
- # --- 🚀 开始下载 ---
112
  progress(0, desc="🚀 初始化连接...")
113
  logger.info(f"📥 开始下载 URL: {file_url}")
114
 
@@ -117,21 +104,15 @@ def process_upload(file_url, progress=gr.Progress()):
117
 
118
  filename = get_filename_from_response(response, file_url)
119
  filesize = int(response.headers.get('Content-Length', 0))
120
-
121
  msg_size = f"{filesize / 1024 / 1024:.2f} MB" if filesize > 0 else "未知大小"
122
- progress(0.1, desc=f"📥 准备传输: {filename} ({msg_size})")
123
- logger.info(f"📄 文件名: {filename}, 大小: {msg_size}")
124
 
125
- # --- ☁️ 准备上传 ---
126
  folder_id = os.environ.get("GDRIVE_FOLDER_ID")
127
  file_metadata = {'name': filename}
128
- if folder_id:
129
- # 验证文件夹 ID 是否为空字符串
130
- if folder_id.strip():
131
- file_metadata['parents'] = [folder_id]
132
- logger.info(f"📂 目标文件夹 ID: {folder_id}")
133
- else:
134
- logger.warning("⚠️ GDRIVE_FOLDER_ID 为空,将上传到根目录")
135
 
136
  stream_wrapper = StreamingUploadFile(response)
137
 
@@ -142,8 +123,7 @@ def process_upload(file_url, progress=gr.Progress()):
142
  chunksize=10 * 1024 * 1024
143
  )
144
 
145
- progress(0.2, desc="☁️ 正在流式上传到 Google Drive...")
146
- logger.info("🚀 发起 create 请求...")
147
 
148
  request = service.files().create(
149
  body=file_metadata,
@@ -151,53 +131,62 @@ def process_upload(file_url, progress=gr.Progress()):
151
  fields='id, webContentLink, webViewLink'
152
  )
153
 
154
- # --- 🔥 执行上传 ---
155
  file = None
156
- response = None
157
- while response is None:
158
- status, response = request.next_chunk()
159
  if status:
160
  progress_percent = int(status.progress() * 100)
161
- # progress(0.2 + (0.7 * status.progress()), desc=f"☁️ 上传中: {progress_percent}%")
162
- logger.debug(f"⏳ 上传进度: {progress_percent}%")
163
 
164
- file = response
165
  file_id = file.get('id')
166
  logger.info(f"✅ 上传完成,File ID: {file_id}")
167
 
168
- progress(0.9, desc="🔓 正在设置公开权限...")
169
-
170
- service.permissions().create(
171
- fileId=file_id,
172
- body={'role': 'reader', 'type': 'anyone'}
173
- ).execute()
 
 
 
 
 
 
 
 
 
 
174
 
175
- web_link = file.get('webContentLink', file.get('webViewLink'))
176
  return f"""✅ **转存成功!**
177
 
178
  **文件名**: {filename}
179
- **文件ID**: {file_id}
180
- **下载链接**: [点击下载]({web_link})
181
  """
182
 
183
  except BrokenPipeError:
184
- logger.error("❌ BrokenPipeError: 连接被 Google 意外关闭。")
185
- return "❌ **上传中断**: 连接被 Google 拒绝。通常是因为 Token 无效、配额超限或网络不稳。请检查 Logs 获取详细 HTTP 响应。"
186
  except Exception as e:
187
- logger.error(f"❌ 全局异常捕获: {str(e)}", exc_info=True)
188
  return f"❌ **发生错误**: {str(e)}"
189
 
190
  # ---------------------------------------------------------
191
  # 3. 构建界面
192
  # ---------------------------------------------------------
193
  with gr.Blocks(title="URL to Drive Saver") as demo:
194
- gr.Markdown("# 🚀 URL to Google Drive Saver (Debug Mode)")
195
 
196
  with gr.Row():
197
  url_input = gr.Textbox(label="文件 URL", placeholder="https://example.com/video.mp4")
198
  submit_btn = gr.Button("开始转存", variant="primary")
199
 
200
- output_markdown = gr.Markdown(label="状态日志")
201
 
202
  submit_btn.click(
203
  fn=process_upload,
 
11
  import google.auth.exceptions
12
 
13
  # ---------------------------------------------------------
14
+ # 0. 配置日志
15
  # ---------------------------------------------------------
16
+ # 关闭过于详细的 HTTP 调试日志,以免刷屏
17
+ # http.client.HTTPConnection.debuglevel = 1
18
 
19
+ logging.basicConfig(level=logging.INFO)
 
20
  logger = logging.getLogger(__name__)
21
 
22
  # ---------------------------------------------------------
 
31
  refresh_token = os.environ.get("G_REFRESH_TOKEN")
32
 
33
  if not all([client_id, client_secret, refresh_token]):
34
+ raise EnvironmentError("❌ 缺少必要的 OAuth 环境变量")
 
 
 
 
 
35
 
36
  creds = Credentials(
37
  token=None,
 
63
  raise
64
 
65
  def seek(self, offset, whence=io.SEEK_SET):
66
+ # Google Drive Upload 可能会尝试 seek(0) 来获取大小或重试
67
  if whence == io.SEEK_SET and offset == self.position:
68
  return self.position
69
  if whence == io.SEEK_CUR and offset == 0:
70
  return self.position
71
+ # 忽略不支持的 seek 操作,通常不影响流式上传
72
  return self.position
73
 
74
  def tell(self):
 
89
  return "❌ 错误: 请输入有效的 URL"
90
 
91
  try:
92
+ # --- 1. 鉴权 ---
 
93
  try:
94
  service = get_drive_service()
 
 
 
 
 
 
95
  except Exception as e:
96
+ return f"❌ **鉴权错误**: {str(e)}"
 
97
 
98
+ # --- 2. 下载 ---
99
  progress(0, desc="🚀 初始化连接...")
100
  logger.info(f"📥 开始下载 URL: {file_url}")
101
 
 
104
 
105
  filename = get_filename_from_response(response, file_url)
106
  filesize = int(response.headers.get('Content-Length', 0))
 
107
  msg_size = f"{filesize / 1024 / 1024:.2f} MB" if filesize > 0 else "未知大小"
108
+
109
+ progress(0.1, desc=f"📥 准备: {filename} ({msg_size})")
110
 
111
+ # --- 3. 上传配置 ---
112
  folder_id = os.environ.get("GDRIVE_FOLDER_ID")
113
  file_metadata = {'name': filename}
114
+ if folder_id and folder_id.strip():
115
+ file_metadata['parents'] = [folder_id]
 
 
 
 
 
116
 
117
  stream_wrapper = StreamingUploadFile(response)
118
 
 
123
  chunksize=10 * 1024 * 1024
124
  )
125
 
126
+ progress(0.2, desc="☁️ 正在流式上传...")
 
127
 
128
  request = service.files().create(
129
  body=file_metadata,
 
131
  fields='id, webContentLink, webViewLink'
132
  )
133
 
134
+ # --- 4. 执行上传 ---
135
  file = None
136
+ response_upload = None
137
+ while response_upload is None:
138
+ status, response_upload = request.next_chunk()
139
  if status:
140
  progress_percent = int(status.progress() * 100)
141
+ # 可以在日志里看进度,不需要频繁打扰前端
142
+ # logger.debug(f"⏳ 上传进度: {progress_percent}%")
143
 
144
+ file = response_upload
145
  file_id = file.get('id')
146
  logger.info(f"✅ 上传完成,File ID: {file_id}")
147
 
148
+ # --- 5. 权限设置 (容错处理) ---
149
+ link_status = "🔒 私有文件 (仅自己可见)"
150
+ web_link = f"https://drive.google.com/file/d/{file_id}/view"
151
+
152
+ try:
153
+ progress(0.9, desc="🔓 尝试设置公开权限...")
154
+ service.permissions().create(
155
+ fileId=file_id,
156
+ body={'role': 'reader', 'type': 'anyone'}
157
+ ).execute()
158
+ link_status = "🌍 公开链接"
159
+ # 获取直链
160
+ web_link = file.get('webContentLink', web_link)
161
+ except Exception as perm_err:
162
+ logger.warning(f"⚠️ 无法设置为公开权限 (可能是 Google 安全策略限制): {perm_err}")
163
+ link_status = "🔒 私有文件 (Google 拒绝了公开分享,请去网盘查看)"
164
 
 
165
  return f"""✅ **转存成功!**
166
 
167
  **文件名**: {filename}
168
+ **状态**: {link_status}
169
+ **文件链接**: [点击打开 Google Drive]({web_link})
170
  """
171
 
172
  except BrokenPipeError:
173
+ logger.error("❌ BrokenPipeError")
174
+ return "❌ **上传中断**: 连接被 Google 拒绝。请检查网络或 Token。"
175
  except Exception as e:
176
+ logger.error(f"❌ 错误: {str(e)}", exc_info=True)
177
  return f"❌ **发生错误**: {str(e)}"
178
 
179
  # ---------------------------------------------------------
180
  # 3. 构建界面
181
  # ---------------------------------------------------------
182
  with gr.Blocks(title="URL to Drive Saver") as demo:
183
+ gr.Markdown("# 🚀 URL to Google Drive Saver")
184
 
185
  with gr.Row():
186
  url_input = gr.Textbox(label="文件 URL", placeholder="https://example.com/video.mp4")
187
  submit_btn = gr.Button("开始转存", variant="primary")
188
 
189
+ output_markdown = gr.Markdown(label="结果")
190
 
191
  submit_btn.click(
192
  fn=process_upload,
test_auth.py CHANGED
@@ -2,28 +2,26 @@ import os
2
  import logging
3
  from google.oauth2.credentials import Credentials
4
  from googleapiclient.discovery import build
5
- import http.client
6
 
7
- # 开启调试日志
8
- http.client.HTTPConnection.debuglevel = 1
9
  logging.basicConfig(level=logging.DEBUG)
10
  logger = logging.getLogger(__name__)
11
 
12
  def test_auth_only():
13
- print("\n" + "="*50)
14
- print("🛠️ 正在执行纯鉴权测试 (Test Auth Only)")
15
  print("="*50)
16
 
17
  client_id = os.environ.get("G_CLIENT_ID")
18
  client_secret = os.environ.get("G_CLIENT_SECRET")
19
  refresh_token = os.environ.get("G_REFRESH_TOKEN")
20
 
21
- print(f"Client ID: {client_id[:10]}... (Len: {len(str(client_id))})")
22
- print(f"Client Secret: {client_secret[:5]}... (Len: {len(str(client_secret))})")
23
- print(f"Refresh Token: {refresh_token[:10]}... (Len: {len(str(refresh_token))})")
24
 
25
  if not all([client_id, client_secret, refresh_token]):
26
- print("❌ 错误: 环境变量缺失")
27
  return
28
 
29
  try:
@@ -35,33 +33,31 @@ def test_auth_only():
35
  client_secret=client_secret
36
  )
37
 
38
- # 强制刷新 Token,这是最直接的验证方式
39
- print("\n🔄 正在尝试刷新 Access Token...")
40
- from google.auth.transport.requests import Request
41
- creds.refresh(Request())
42
- print(f"✅ Token 刷新成功! 新 Access Token: {creds.token[:10]}...")
43
-
44
- # 构建 Service 并调用简单的 API
45
- print("\n📡 正在连接 Google Drive API...")
46
  service = build("drive", "v3", credentials=creds)
47
 
48
- print("👤 正在获取用户信息 (about.get)...")
49
- about = service.about().get(fields="user").execute()
50
- user_info = about.get('user', {})
 
 
51
 
52
  print("\n" + "="*50)
53
- print(f"✅ 鉴权完美通过!")
54
- print(f"👋 用户名: {user_info.get('displayName')}")
55
- print(f"📧 邮箱: {user_info.get('emailAddress')}")
56
  print("="*50)
57
-
58
- except Exception as e:
59
- print("\n" + "="*50)
60
- print(f" 鉴权测试失败!")
61
- print(f"错误信息: {e}")
62
  print("="*50)
63
- import traceback
64
- traceback.print_exc()
 
 
 
 
 
 
65
 
66
  if __name__ == "__main__":
67
  test_auth_only()
 
2
  import logging
3
  from google.oauth2.credentials import Credentials
4
  from googleapiclient.discovery import build
5
+ import google.auth.exceptions
6
 
7
+ # 配置日志
 
8
  logging.basicConfig(level=logging.DEBUG)
9
  logger = logging.getLogger(__name__)
10
 
11
  def test_auth_only():
12
+ print("="*50)
13
+ print("🚀 Google Drive 鉴权独立测试")
14
  print("="*50)
15
 
16
  client_id = os.environ.get("G_CLIENT_ID")
17
  client_secret = os.environ.get("G_CLIENT_SECRET")
18
  refresh_token = os.environ.get("G_REFRESH_TOKEN")
19
 
20
+ print(f"Client ID (前5位): {client_id[:5] if client_id else 'MISSING'}")
21
+ print(f"Refresh Token (前5位): {refresh_token[:5] if refresh_token else 'MISSING'}")
 
22
 
23
  if not all([client_id, client_secret, refresh_token]):
24
+ print("❌ 环境变量缺失,无法测试。")
25
  return
26
 
27
  try:
 
33
  client_secret=client_secret
34
  )
35
 
36
+ print("\n⏳ 正在构建 Drive 服务并刷新 Token...")
 
 
 
 
 
 
 
37
  service = build("drive", "v3", credentials=creds)
38
 
39
+ print("🔍 正在请求用户信息 (about.get)...")
40
+ about = service.about().get(fields="user,storageQuota").execute()
41
+
42
+ user = about.get('user', {})
43
+ quota = about.get('storageQuota', {})
44
 
45
  print("\n" + "="*50)
46
+ print("✅ 鉴权成功连接正常!")
 
 
47
  print("="*50)
48
+ print(f"👤 用户名: {user.get('displayName')}")
49
+ print(f"📧 邮箱: {user.get('emailAddress')}")
50
+ print(f"💾 已用空间: {int(quota.get('usage', 0)) / 1024 / 1024 / 1024:.2f} GB")
51
+ print(f"☁️ 总空间: {int(quota.get('limit', 0)) / 1024 / 1024 / 1024:.2f} GB")
 
52
  print("="*50)
53
+
54
+ except google.auth.exceptions.RefreshError as e:
55
+ print("\n❌ 鉴权失败: Refresh Token 无效或过期")
56
+ print(f"详细错误: {e}")
57
+ print("👉 即使您觉得是新的,也请重新生成。Google 可能因为 IP 变动暂时封锁了旧 Token。")
58
+ except Exception as e:
59
+ print("\n❌ 发生未知错误")
60
+ print(f"详细错误: {e}")
61
 
62
  if __name__ == "__main__":
63
  test_auth_only()