Shinhati2023 commited on
Commit
b3bb0a5
Β·
verified Β·
1 Parent(s): 9078f7d

Create run_upload.py

Browse files
Files changed (1) hide show
  1. run_upload.py +244 -0
run_upload.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+ import time
4
+ import re
5
+
6
+ # --- Configuration: Read from Environment Variables (Hugging Face Secrets) ---
7
+ # Environment variables MUST be set in the Hugging Face Space Settings
8
+ API_KEY = os.environ.get("CIVITAI_API_KEY")
9
+ MODEL_HASH = os.environ.get("MODEL_FILE_HASH")
10
+ EXTERNAL_MODEL_URL = os.environ.get("EXTERNAL_MODEL_URL")
11
+
12
+ # Base Civitai API URL
13
+ BASE_URL = "https://civitai.com/api/v1"
14
+
15
+ # Headers for authorized requests (set dynamically inside main_upload_flow)
16
+ HEADERS = {}
17
+
18
+ # We extract the filename from the URL, prioritizing the 'filename=' attachment
19
+ # which gives a cleaner name than relying on the URL path.
20
+ match = re.search(r'filename\=\"([^\"]+)\"', EXTERNAL_MODEL_URL or "")
21
+ LOCAL_FILENAME = match.group(1) if match else "uploaded_model_file.safetensors"
22
+
23
+ # --- CORE FUNCTIONS (Unchanged from previous version, adapted to use global variables) ---
24
+
25
+ def get_model_id_by_hash(file_hash):
26
+ """
27
+ Looks up the Model Version by its SHA256 hash using the public Civitai API.
28
+ If found, it returns the numeric Model ID needed to update the model.
29
+ """
30
+ print(f"0. Searching for existing Model ID using hash: {file_hash[:10]}...")
31
+ url = f"{BASE_URL}/model-versions/by-hash/{file_hash}"
32
+
33
+ try:
34
+ response = requests.get(url, headers=HEADERS)
35
+ response.raise_for_status()
36
+
37
+ result = response.json()
38
+ model_id = result.get('modelId')
39
+
40
+ if model_id:
41
+ print(f" βœ… Hash match found! Model ID: {model_id}, Model Version: {result.get('id')}")
42
+ return model_id
43
+ else:
44
+ print(" ❌ Hash lookup failed to find a matching model version.")
45
+ return None
46
+
47
+ except requests.exceptions.HTTPError as e:
48
+ if e.response.status_code == 404:
49
+ print(f" ❌ Hash lookup failed: Model version not found.")
50
+ else:
51
+ print(f" ❌ Error during hash lookup ({e.response.status_code}): {e}")
52
+ return None
53
+ except requests.exceptions.RequestException as e:
54
+ print(f" ❌ Network/Request Error during hash lookup: {e}")
55
+ return None
56
+
57
+
58
+ def create_model_version(model_id, version_data):
59
+ """
60
+ Step 1: Creates a new model version entry on Civitai and gets an upload reference.
61
+
62
+ NOTE: This remains a conceptual step as the exact upload initiation endpoint
63
+ for users is not part of the public documentation.
64
+ """
65
+ print(f"\n1. Attempting to create new version for Model ID: {model_id}...")
66
+
67
+ url = f"{BASE_URL}/models/{model_id}/versions"
68
+
69
+ try:
70
+ response = requests.post(url, headers=HEADERS, json=version_data)
71
+ response.raise_for_status()
72
+
73
+ result = response.json()
74
+ print(" βœ… Version creation request sent successfully.")
75
+
76
+ new_version_id = result.get('id')
77
+ if not new_version_id:
78
+ raise Exception("API did not return a new version ID.")
79
+
80
+ print(f" New Model Version ID created: {new_version_id}")
81
+ return new_version_id
82
+
83
+ except requests.exceptions.RequestException as e:
84
+ print(f" ❌ Error creating model version: {e}")
85
+ print(f" Response Body: {response.text if 'response' in locals() else 'N/A'}")
86
+ return None
87
+
88
+ def download_file_from_link(url, filename):
89
+ """
90
+ Step 2: Downloads the model file from the external link to the local system.
91
+ """
92
+ print(f"\n2. Downloading file from external link (this may take a long time): {filename}")
93
+ try:
94
+ with requests.get(url, stream=True) as r:
95
+ r.raise_for_status()
96
+ total_size = int(r.headers.get('content-length', 0))
97
+ block_size = 1024 * 1024 # 1 Megabyte
98
+ downloaded = 0
99
+
100
+ with open(filename, 'wb') as f:
101
+ for data in r.iter_content(block_size):
102
+ downloaded += len(data)
103
+ f.write(data)
104
+ # Simple progress indicator
105
+ if total_size > 0:
106
+ progress = downloaded / total_size * 100
107
+ print(f" {downloaded / (1024*1024):.2f} MB / {total_size / (1024*1024):.2f} MB ({progress:.1f}%)", end='\r')
108
+ print(f"\n βœ… Download complete. File saved as: {filename}")
109
+ return True
110
+ except requests.exceptions.RequestException as e:
111
+ print(f" ❌ Error downloading file. The signed URL may have expired: {e}")
112
+ return False
113
+
114
+ def get_presigned_upload_url(model_version_id, filename):
115
+ """
116
+ Step 3: Requests a secure, time-limited upload URL from Civitai.
117
+ """
118
+ print("\n3. Requesting presigned upload URL from Civitai...")
119
+
120
+ url = f"{BASE_URL}/model-versions/{model_version_id}/files/upload-url"
121
+
122
+ payload = {
123
+ "filename": filename,
124
+ "filesize": os.path.getsize(filename),
125
+ "filetype": "Checkpoint",
126
+ "mimetype": "application/octet-stream"
127
+ }
128
+
129
+ try:
130
+ response = requests.post(url, headers=HEADERS, json=payload)
131
+ response.raise_for_status()
132
+
133
+ signed_url = response.json().get('uploadUrl')
134
+ if not signed_url:
135
+ raise Exception("API did not return a signed upload URL.")
136
+
137
+ print(" βœ… Received secure upload URL.")
138
+ return signed_url
139
+
140
+ except requests.exceptions.RequestException as e:
141
+ print(f" ❌ Error requesting signed URL: {e}")
142
+ print(f" Response Body: {response.text if 'response' in locals() else 'N/A'}")
143
+ return None
144
+
145
+ def stream_upload_file(local_path, signed_url):
146
+ """
147
+ Step 4: Uploads the local file directly to the presigned cloud storage URL.
148
+ """
149
+ print("\n4. Streaming file content to the secure upload URL...")
150
+
151
+ try:
152
+ file_size = os.path.getsize(local_path)
153
+
154
+ with open(local_path, 'rb') as f:
155
+ upload_headers = {
156
+ 'Content-Length': str(file_size),
157
+ 'Content-Type': 'application/octet-stream'
158
+ }
159
+
160
+ response = requests.put(signed_url, data=f, headers=upload_headers)
161
+ response.raise_for_status()
162
+
163
+ print(" βœ… File upload successful.")
164
+ return True
165
+
166
+ except requests.exceptions.RequestException as e:
167
+ print(f" ❌ Error during file streaming upload: {e}")
168
+ print(f" Response Status: {response.status_code if 'response' in locals() else 'N/A'}")
169
+ return False
170
+
171
+
172
+ def main_upload_flow():
173
+ """
174
+ Executes the full simulated API upload flow, dynamically getting the model ID.
175
+ """
176
+ global HEADERS
177
+
178
+ # Check for required environment variables
179
+ if not all([API_KEY, MODEL_HASH, EXTERNAL_MODEL_URL]):
180
+ print("--- CONFIGURATION ERROR ---")
181
+ if not API_KEY: print("CIVITAI_API_KEY secret is missing.")
182
+ if not MODEL_HASH: print("MODEL_FILE_HASH secret is missing.")
183
+ if not EXTERNAL_MODEL_URL: print("EXTERNAL_MODEL_URL secret is missing.")
184
+ print("Please set these as Secrets in your Hugging Face Space settings.")
185
+ return
186
+
187
+ # Set up headers now that API_KEY is confirmed
188
+ HEADERS.update({
189
+ "Authorization": f"Bearer {API_KEY}",
190
+ "Content-Type": "application/json"
191
+ })
192
+
193
+ print("--- Civitai API Link Upload Workflow Initiated ---")
194
+
195
+ # A. Get the numeric MODEL_ID using the provided hash
196
+ model_id = get_model_id_by_hash(MODEL_HASH)
197
+
198
+ if not model_id:
199
+ print("\nFlow terminated: Could not find existing model via hash.")
200
+ return
201
+
202
+ # 1. Define Model Version Metadata
203
+ version_metadata = {
204
+ "name": "v_HF_auto_upload", # Placeholder version name
205
+ "description": "Programmatic upload via Hugging Face Space using external link and hash lookup.",
206
+ "baseModel": "SDXL",
207
+ "trainedWords": ["dreamlookai_sdxl", "ukj_style", "hf_upload"],
208
+ }
209
+
210
+ # B. Create the Model Version Entry
211
+ new_version_id = create_model_version(model_id, version_metadata)
212
+ if not new_version_id:
213
+ return
214
+
215
+ # C. Download the file from the external link
216
+ if not download_file_from_link(EXTERNAL_MODEL_URL, LOCAL_FILENAME):
217
+ return
218
+
219
+ # D. Get the secure URL for upload
220
+ signed_url = get_presigned_upload_url(new_version_id, LOCAL_FILENAME)
221
+ if not signed_url:
222
+ return
223
+
224
+ # E. Stream the downloaded file to the secure URL
225
+ if not stream_upload_file(LOCAL_FILENAME, signed_url):
226
+ return
227
+
228
+ # F. Cleanup (Remove the downloaded file from the Hugging Face Space)
229
+ try:
230
+ os.remove(LOCAL_FILENAME)
231
+ print(f"\n5. Cleanup complete. Model uploaded and local file '{LOCAL_FILENAME}' removed.")
232
+ except OSError as e:
233
+ print(f"\n5. Cleanup failed: Could not remove local file '{LOCAL_FILENAME}': {e}")
234
+
235
+ print("\n**********************************************************************************")
236
+ print(f"** SUCCESS: Programmatic file transfer complete for Model Version ID: {new_version_id} **")
237
+ print("** Next Action: Go to the Civitai web UI to verify the file and publish the version. **")
238
+ print("**********************************************************************************")
239
+
240
+
241
+ if __name__ == "__main__":
242
+ main_upload_flow()
243
+
244
+