nexusbert commited on
Commit
1c4d53b
Β·
1 Parent(s): 2b5a760
READY_PLAYER_ME_FIXES.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ready Player Me Integration Fixes
2
+
3
+ ## Issues Fixed
4
+
5
+ ### 1. βœ… Profile Picture Loading
6
+ - **Problem**: Avatar profile picture wasn't displaying after creation
7
+ - **Fix**:
8
+ - Updated profile loading to prioritize `profilePicture` from backend
9
+ - Added profile reload after avatar creation
10
+ - Improved fallback logic for avatar render URL
11
+
12
+ ### 2. βœ… Avatar Creation Flow
13
+ - **Problem**: Profile picture not updating after avatar export
14
+ - **Fix**:
15
+ - Reload profile after avatar creation to get updated `profilePicture`
16
+ - Update user context with new profile data
17
+ - Better error handling and fallbacks
18
+
19
+ ### 3. ⚠️ Ready Player Me API 400 Errors
20
+ - **Problem**: Multiple 400 Bad Request errors from Ready Player Me API
21
+ - **Possible Causes**:
22
+ 1. **Subdomain mismatch**: The subdomain in `.env` might not match your Ready Player Me application
23
+ 2. **Application configuration**: The application might not be properly configured in Ready Player Me Studio
24
+ 3. **API permissions**: The API key might not have the right permissions
25
+
26
+ ## How to Fix 400 Errors
27
+
28
+ ### Step 1: Verify Subdomain
29
+ 1. Go to [Ready Player Me Studio](https://studio.readyplayer.me/)
30
+ 2. Check your application settings
31
+ 3. Find your **subdomain** (partner name)
32
+ 4. Update `stylegptUI/.env`:
33
+ ```env
34
+ VITE_READY_PLAYER_ME_SUBDOMAIN=your-actual-subdomain
35
+ ```
36
+
37
+ ### Step 2: Verify Application Configuration
38
+ 1. In Ready Player Me Studio, go to your application
39
+ 2. Check that:
40
+ - Application ID matches `READY_PLAYER_ME_APPLICATION_ID` in backend `.env`
41
+ - Organization ID matches `READY_PLAYER_ME_ORGANIZATION_ID` in backend `.env`
42
+ - Application is **active** and **published**
43
+
44
+ ### Step 3: Check API Key Permissions
45
+ 1. Verify your `READY_API_KEY` has permissions for:
46
+ - Guest user creation
47
+ - Avatar management
48
+ - Asset creation
49
+ - Avatar rendering
50
+
51
+ ### Step 4: Test Iframe URL
52
+ The iframe URL should be:
53
+ ```
54
+ https://{subdomain}.readyplayer.me/avatar?frameApi&id={avatarId}
55
+ ```
56
+
57
+ Example:
58
+ ```
59
+ https://stylegpt.readyplayer.me/avatar?frameApi&id=your-avatar-id
60
+ ```
61
+
62
+ ## Current Configuration
63
+
64
+ ### Frontend (.env)
65
+ ```env
66
+ VITE_READY_PLAYER_ME_SUBDOMAIN=stylegpt
67
+ VITE_API_URL=https://nexusbert-stylegpt-milestone2.hf.space
68
+ ```
69
+
70
+ ### Backend (.env)
71
+ ```env
72
+ READY_API_KEY=your-api-key
73
+ READY_PLAYER_ME_APPLICATION_ID=your-application-id
74
+ READY_PLAYER_ME_ORGANIZATION_ID=your-organization-id
75
+ ```
76
+
77
+ ## Testing Checklist
78
+
79
+ After fixing the configuration:
80
+
81
+ 1. βœ… **Clear browser cache** and refresh
82
+ 2. βœ… **Create/Edit Avatar** - Should load without 400 errors
83
+ 3. βœ… **Export Avatar** - Should save avatar ID
84
+ 4. βœ… **Profile Picture** - Should display avatar portrait
85
+ 5. βœ… **View Avatar** - Should show avatar in profile
86
+
87
+ ## Common Issues
88
+
89
+ ### Issue: "appName and environment are static"
90
+ - **Cause**: Ready Player Me iframe needs proper application context
91
+ - **Fix**: Ensure subdomain matches your application in Ready Player Me Studio
92
+
93
+ ### Issue: 400 Bad Request errors
94
+ - **Cause**: API calls failing due to missing/invalid configuration
95
+ - **Fix**:
96
+ 1. Verify subdomain is correct
97
+ 2. Check application is active in Ready Player Me Studio
98
+ 3. Verify API key has proper permissions
99
+
100
+ ### Issue: Avatar not displaying in profile
101
+ - **Cause**: Profile picture not loading after avatar creation
102
+ - **Fix**:
103
+ - Refresh the page
104
+ - Check browser console for errors
105
+ - Verify avatar was created successfully (check backend logs)
106
+
107
+ ## Next Steps
108
+
109
+ 1. **Verify your Ready Player Me Studio configuration**
110
+ 2. **Update subdomain in `.env` if needed**
111
+ 3. **Test avatar creation flow**
112
+ 4. **Check browser console for any remaining errors**
113
+ 5. **Verify profile picture displays correctly**
114
+
115
+ ## Support
116
+
117
+ If issues persist:
118
+ 1. Check Ready Player Me documentation: https://docs.readyplayer.me/
119
+ 2. Verify your application settings in Ready Player Me Studio
120
+ 3. Check backend logs for API errors
121
+ 4. Review browser console for frontend errors
122
+
READY_PLAYER_ME_TESTING_GUIDE.md ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ready Player Me Integration Testing Guide
2
+
3
+ ## 🎯 Testing Checklist
4
+
5
+ Test the Ready Player Me integration on the deployed app: **https://stylegpt-ui.vercel.app/**
6
+
7
+ ---
8
+
9
+ ## 1. User Registration & Guest User Creation
10
+
11
+ ### Steps:
12
+ 1. Navigate to the registration page
13
+ 2. Fill in registration form:
14
+ - Name
15
+ - Email
16
+ - Password
17
+ 3. Click "Register"
18
+
19
+ ### Expected Results:
20
+ - βœ… User account created successfully
21
+ - βœ… Backend automatically creates a Ready Player Me guest user
22
+ - βœ… `readyPlayerMeUserId` stored in database
23
+ - βœ… User redirected to login or dashboard
24
+
25
+ ### How to Verify:
26
+ - Check browser console for any errors
27
+ - Check backend logs for "Guest user created" message
28
+ - User should be able to proceed to Profile page
29
+
30
+ ---
31
+
32
+ ## 2. Avatar Creation
33
+
34
+ ### Steps:
35
+ 1. Log in to your account
36
+ 2. Navigate to **Profile** page
37
+ 3. Look for "Create Avatar" or "Edit Avatar" button
38
+ 4. Click the button
39
+ 5. Avatar Creator iframe should open
40
+ 6. Create your avatar in the Ready Player Me interface:
41
+ - Choose gender
42
+ - Customize appearance
43
+ - Select clothing/accessories
44
+ 7. Click "Export" or "Done" in the Ready Player Me interface
45
+
46
+ ### Expected Results:
47
+ - βœ… Avatar Creator iframe loads correctly
48
+ - βœ… Ready Player Me interface is fully functional
49
+ - βœ… After exporting, avatar ID is automatically saved
50
+ - βœ… Profile picture updates to show avatar portrait
51
+ - βœ… "Create Avatar" button changes to "Edit Avatar"
52
+
53
+ ### How to Verify:
54
+ - Check browser console for `v1.avatar.exported` event
55
+ - Check backend logs for "Avatar ID saved" message
56
+ - Profile picture should show your avatar's 2D render
57
+ - Refresh page - avatar should persist
58
+
59
+ ### Troubleshooting:
60
+ - **Iframe doesn't load**: Check `VITE_READY_PLAYER_ME_SUBDOMAIN` env var
61
+ - **Avatar not saving**: Check browser console for postMessage errors
62
+ - **Profile picture not updating**: Check if `readyPlayerMeAvatarId` is saved in backend
63
+
64
+ ---
65
+
66
+ ## 3. Wardrobe Item Upload with 3D Model & Asset Creation
67
+
68
+ ### Steps:
69
+ 1. Navigate to **Upload** or **Wardrobe** page
70
+ 2. Click "Upload" or "Add Item"
71
+ 3. Select an image file (clothing item)
72
+ 4. Select a **Style** (casual, formal, streetwear, sportswear)
73
+ 5. Click "Upload" or "Submit"
74
+ 6. Wait for processing:
75
+ - Background removal
76
+ - FashionClip classification
77
+ - 3D model generation
78
+ - Ready Player Me asset creation
79
+
80
+ ### Expected Results:
81
+ - βœ… Image uploads successfully
82
+ - βœ… Background is removed for classification
83
+ - βœ… Original image (with background) is stored
84
+ - βœ… Item is classified with color and category
85
+ - βœ… 3D model is generated (may take 30-60 seconds)
86
+ - βœ… 3D model is converted to base64 and stored
87
+ - βœ… Ready Player Me asset is created
88
+ - βœ… `readyPlayerMeAssetId` is stored in database
89
+ - βœ… Item appears in wardrobe with name (e.g., "black t-shirt")
90
+
91
+ ### How to Verify:
92
+ - Check upload progress indicator
93
+ - Item should appear in wardrobe after processing
94
+ - Check browser console for any errors
95
+ - Check backend logs for:
96
+ - "3D model generated"
97
+ - "Asset created"
98
+ - "Asset ID saved"
99
+ - In database, verify:
100
+ - `model3dUrl` field has base64 GLB data
101
+ - `readyPlayerMeAssetId` field has asset ID
102
+ - `name` field has "color + category" format
103
+
104
+ ### Troubleshooting:
105
+ - **3D generation fails**: Check Hugging Face token/quota
106
+ - **Asset creation fails**: Check `READY_PLAYER_ME_APPLICATION_ID` and `READY_PLAYER_ME_ORGANIZATION_ID`
107
+ - **Item not appearing**: Check backend logs for errors
108
+
109
+ ---
110
+
111
+ ## 4. View Wardrobe (2D & 3D)
112
+
113
+ ### Steps (2D View):
114
+ 1. Navigate to **Wardrobe** page
115
+ 2. Items should be grouped by category
116
+ 3. Click on a category to see items
117
+ 4. Items should slide out with animation
118
+
119
+ ### Steps (3D View):
120
+ 1. Navigate to **Wardrobe 3D** page
121
+ 2. Wait for scene to load
122
+ 3. Items should be organized by category on circular platforms
123
+ 4. Hover over items to see them scale up
124
+ 5. Click items to interact
125
+ 6. Use mouse to rotate camera
126
+ 7. Scroll to zoom
127
+
128
+ ### Expected Results:
129
+ - βœ… 2D view shows items grouped by category
130
+ - βœ… Category selection animates smoothly
131
+ - βœ… 3D view loads Three.js scene
132
+ - βœ… Items with 3D models display as 3D objects
133
+ - βœ… Items without 3D models display as 2D image planes
134
+ - βœ… Camera controls work (rotate, zoom)
135
+ - βœ… Hover effects work
136
+
137
+ ### How to Verify:
138
+ - Check browser console for Three.js errors
139
+ - Verify items with `model3dUrl` display as 3D
140
+ - Verify items without `model3dUrl` display as 2D images
141
+ - Test camera controls
142
+
143
+ ---
144
+
145
+ ## 5. AI Outfit Suggestion with Avatar Try-On
146
+
147
+ ### Steps:
148
+ 1. Navigate to **Chat** page
149
+ 2. Type a request like:
150
+ - "Suggest a casual outfit"
151
+ - "What should I wear today?"
152
+ - "Show me a formal outfit"
153
+ 3. Wait for AI response (streaming)
154
+ 4. AI should suggest specific items from your wardrobe
155
+ 5. Look for **"Try on Avatar"** button (if you have an avatar)
156
+ 6. Click "Try on Avatar" button
157
+ 7. Wait for outfit to be equipped to avatar
158
+
159
+ ### Expected Results:
160
+ - βœ… AI suggests items using actual item names (e.g., "the black zara pants")
161
+ - βœ… Selected items are displayed in a grid
162
+ - βœ… "Try on Avatar" button appears (if avatar exists)
163
+ - βœ… Clicking button equips assets to avatar
164
+ - βœ… 3D avatar with outfit displays in an iframe
165
+ - βœ… Avatar GLB URL is returned and displayed
166
+
167
+ ### How to Verify:
168
+ - Check AI response mentions actual item names
169
+ - Check `selectedItems` array in response
170
+ - Check browser console for "Try on Avatar" API call
171
+ - Check backend logs for:
172
+ - "Equipped X assets to avatar"
173
+ - "Avatar GLB URL generated"
174
+ - Verify iframe loads 3D avatar with outfit
175
+
176
+ ### Troubleshooting:
177
+ - **"Try on Avatar" button not showing**:
178
+ - Check if user has `readyPlayerMeAvatarId`
179
+ - Check if selected items have `readyPlayerMeAssetId`
180
+ - **Avatar not displaying**:
181
+ - Check if GLB URL is valid
182
+ - Check browser console for CORS or loading errors
183
+ - **Outfit not appearing on avatar**:
184
+ - Check if assets were equipped successfully
185
+ - Check backend logs for equip errors
186
+
187
+ ---
188
+
189
+ ## 6. Profile Picture (Avatar Portrait)
190
+
191
+ ### Steps:
192
+ 1. Navigate to **Profile** page
193
+ 2. Check profile picture section
194
+
195
+ ### Expected Results:
196
+ - βœ… If avatar exists: Profile picture shows 2D avatar portrait
197
+ - βœ… Portrait is generated from Ready Player Me render API
198
+ - βœ… Portrait uses "portrait" camera, 400x400px, 90% quality
199
+ - βœ… Portrait updates automatically when avatar changes
200
+
201
+ ### How to Verify:
202
+ - Profile picture should be a 2D render of your avatar
203
+ - Image URL should be from `models.readyplayer.me`
204
+ - Check browser network tab for render API call
205
+ - Check backend logs for "Avatar render URL generated"
206
+
207
+ ---
208
+
209
+ ## 7. Delete Wardrobe Items
210
+
211
+ ### Steps:
212
+ 1. Navigate to **Wardrobe** or **Wardrobe 3D** page
213
+ 2. Find an item you want to delete
214
+ 3. Click delete button (trash icon)
215
+ 4. Confirm deletion
216
+
217
+ ### Expected Results:
218
+ - βœ… Item is deleted from database
219
+ - βœ… Item disappears from wardrobe view
220
+ - βœ… Item disappears from 3D view
221
+ - βœ… Ready Player Me asset is NOT deleted (by design)
222
+
223
+ ### How to Verify:
224
+ - Item should be removed from UI immediately
225
+ - Check backend logs for "Item deleted"
226
+ - Refresh page - item should not reappear
227
+
228
+ ---
229
+
230
+ ## πŸ” Backend API Endpoints to Test
231
+
232
+ ### Avatar Endpoints:
233
+ - `POST /api/avatar/create` - Save avatar ID
234
+ - `GET /api/avatar/glb` - Get 3D avatar GLB URL
235
+ - `GET /api/avatar/render` - Get 2D avatar render URL
236
+ - `GET /api/avatar/metadata` - Get avatar metadata
237
+ - `POST /api/avatar/try-on` - Equip assets and get avatar with outfit
238
+
239
+ ### Other Endpoints:
240
+ - `POST /api/auth/register` - Should create guest user
241
+ - `POST /api/upload` - Should create asset
242
+ - `GET /api/wardrobe` - Should return items with asset IDs
243
+ - `POST /api/suggest/stream` - Should return avatar URL if applicable
244
+
245
+ ---
246
+
247
+ ## πŸ› Common Issues & Solutions
248
+
249
+ ### Issue: Avatar Creator iframe doesn't load
250
+ **Solution**:
251
+ - Check `VITE_READY_PLAYER_ME_SUBDOMAIN` environment variable
252
+ - Verify Ready Player Me subdomain is correct
253
+ - Check browser console for iframe errors
254
+
255
+ ### Issue: Assets not being created
256
+ **Solution**:
257
+ - Check `READY_PLAYER_ME_APPLICATION_ID` and `READY_PLAYER_ME_ORGANIZATION_ID` env vars
258
+ - Verify API key has asset creation permissions
259
+ - Check backend logs for asset creation errors
260
+
261
+ ### Issue: Avatar not displaying in chat
262
+ **Solution**:
263
+ - Verify user has `readyPlayerMeAvatarId`
264
+ - Check if selected items have `readyPlayerMeAssetId`
265
+ - Verify GLB URL is accessible (check CORS)
266
+ - Check browser console for Three.js errors
267
+
268
+ ### Issue: Profile picture not updating
269
+ **Solution**:
270
+ - Verify avatar was created successfully
271
+ - Check backend logs for render API calls
272
+ - Verify `readyPlayerMeAvatarId` is saved in database
273
+ - Try refreshing the page
274
+
275
+ ---
276
+
277
+ ## πŸ“Š Test Results Template
278
+
279
+ ```
280
+ Date: ___________
281
+ Tester: ___________
282
+
283
+ βœ… User Registration & Guest User Creation: [PASS/FAIL]
284
+ βœ… Avatar Creation: [PASS/FAIL]
285
+ βœ… Wardrobe Upload with 3D & Asset: [PASS/FAIL]
286
+ βœ… View Wardrobe (2D): [PASS/FAIL]
287
+ βœ… View Wardrobe (3D): [PASS/FAIL]
288
+ βœ… AI Outfit Suggestion: [PASS/FAIL]
289
+ βœ… Try on Avatar: [PASS/FAIL]
290
+ βœ… Profile Picture (Avatar Portrait): [PASS/FAIL]
291
+ βœ… Delete Items: [PASS/FAIL]
292
+
293
+ Notes:
294
+ _________________________________________________
295
+ _________________________________________________
296
+ _________________________________________________
297
+ ```
298
+
299
+ ---
300
+
301
+ ## πŸš€ Quick Test Flow
302
+
303
+ 1. **Register** β†’ Creates guest user
304
+ 2. **Create Avatar** β†’ Saves avatar ID, updates profile picture
305
+ 3. **Upload Item** β†’ Creates 3D model and asset
306
+ 4. **Ask for Outfit** β†’ AI suggests items
307
+ 5. **Try on Avatar** β†’ Displays 3D avatar with outfit
308
+ 6. **Check Profile** β†’ Profile picture shows avatar portrait
309
+
310
+ ---
311
+
312
+ ## πŸ“ Environment Variables Checklist
313
+
314
+ ### Backend (.env):
315
+ - βœ… `READY_API_KEY` - Ready Player Me API key
316
+ - βœ… `READY_PLAYER_ME_APPLICATION_ID` - Your application ID
317
+ - βœ… `READY_PLAYER_ME_ORGANIZATION_ID` - Your organization ID
318
+
319
+ ### Frontend (Vercel Environment Variables):
320
+ - βœ… `VITE_API_URL` - Backend API URL
321
+ - βœ… `VITE_READY_PLAYER_ME_SUBDOMAIN` - Your Ready Player Me subdomain
322
+
323
+ ---
324
+
325
+ Good luck with testing! πŸŽ‰
326
+
src/routes/upload.ts CHANGED
@@ -186,11 +186,16 @@ router.post("/", authenticateToken, upload.array("image", 20), async (req: AuthR
186
 
187
  if (model3dUrl && process.env.READY_PLAYER_ME_APPLICATION_ID && process.env.READY_PLAYER_ME_ORGANIZATION_ID) {
188
  try {
 
189
  const assetType = mapCategoryToAssetType(normalizedCategory);
190
  if (assetType) {
 
 
 
191
  const base64Data = model3dUrl.split(',')[1];
192
  const binaryString = Buffer.from(base64Data, 'base64');
193
 
 
194
  const modelUrl = await readyPlayerMeClient.uploadAssetFile(
195
  binaryString,
196
  `${itemName.replace(/\s+/g, '-')}.glb`,
@@ -198,8 +203,34 @@ router.post("/", authenticateToken, upload.array("image", 20), async (req: AuthR
198
  );
199
 
200
  if (modelUrl) {
201
- const iconUrl = processedBase64Image || base64Image;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
 
 
 
 
 
 
 
 
 
 
203
  const asset = await readyPlayerMeClient.createAsset({
204
  name: itemName,
205
  type: assetType,
@@ -218,12 +249,38 @@ router.post("/", authenticateToken, upload.array("image", 20), async (req: AuthR
218
  if (asset) {
219
  savedItem.readyPlayerMeAssetId = asset.id;
220
  await itemRepo.save(savedItem);
221
- console.log(`Created Ready Player Me asset: ${asset.id} for item ${savedItem.id}`);
 
 
 
222
  }
 
 
 
 
 
 
223
  }
 
 
224
  }
225
  } catch (assetError: any) {
226
- console.warn(`Failed to create Ready Player Me asset for ${file.originalname}:`, assetError.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
  }
229
 
 
186
 
187
  if (model3dUrl && process.env.READY_PLAYER_ME_APPLICATION_ID && process.env.READY_PLAYER_ME_ORGANIZATION_ID) {
188
  try {
189
+ console.log(`πŸ”„ Creating Ready Player Me asset for: ${itemName} (category: ${normalizedCategory})`);
190
  const assetType = mapCategoryToAssetType(normalizedCategory);
191
  if (assetType) {
192
+ console.log(` - Asset type mapped: ${assetType}`);
193
+
194
+ // Upload 3D model file
195
  const base64Data = model3dUrl.split(',')[1];
196
  const binaryString = Buffer.from(base64Data, 'base64');
197
 
198
+ console.log(` - Uploading 3D model file (${binaryString.length} bytes)...`);
199
  const modelUrl = await readyPlayerMeClient.uploadAssetFile(
200
  binaryString,
201
  `${itemName.replace(/\s+/g, '-')}.glb`,
 
203
  );
204
 
205
  if (modelUrl) {
206
+ console.log(` - 3D model uploaded: ${modelUrl}`);
207
+
208
+ // Upload icon image file
209
+ const iconImage = processedBase64Image || base64Image;
210
+ const iconBase64Data = iconImage.split(',')[1];
211
+ const iconMimeType = iconImage.startsWith('data:image/png') ? 'image/png' : 'image/jpeg';
212
+ const iconBuffer = Buffer.from(iconBase64Data, 'base64');
213
+
214
+ console.log(` - Uploading icon image (${iconBuffer.length} bytes, ${iconMimeType})...`);
215
+ const iconUrl = await readyPlayerMeClient.uploadAssetFile(
216
+ iconBuffer,
217
+ `${itemName.replace(/\s+/g, '-')}-icon.${iconMimeType === 'image/png' ? 'png' : 'jpg'}`,
218
+ iconMimeType
219
+ );
220
+
221
+ if (iconUrl) {
222
+ console.log(` - Icon uploaded: ${iconUrl}`);
223
 
224
+ console.log(` - Creating asset with type: ${assetType}, gender: ${getAssetGender()}`);
225
+ console.log(` - Asset data:`, {
226
+ name: itemName,
227
+ type: assetType,
228
+ gender: getAssetGender(),
229
+ modelUrl: modelUrl.substring(0, 100) + '...',
230
+ iconUrl: iconUrl.substring(0, 100) + '...',
231
+ organizationId: process.env.READY_PLAYER_ME_ORGANIZATION_ID?.substring(0, 10) + '...',
232
+ applicationId: process.env.READY_PLAYER_ME_APPLICATION_ID?.substring(0, 10) + '...',
233
+ });
234
  const asset = await readyPlayerMeClient.createAsset({
235
  name: itemName,
236
  type: assetType,
 
249
  if (asset) {
250
  savedItem.readyPlayerMeAssetId = asset.id;
251
  await itemRepo.save(savedItem);
252
+ console.log(`βœ… Created Ready Player Me asset: ${asset.id} for item ${savedItem.id}`);
253
+ } else {
254
+ console.error(`❌ Failed to create Ready Player Me asset: createAsset returned null`);
255
+ console.error(` This usually means the API call failed. Check READY_API_KEY permissions.`);
256
  }
257
+ } else {
258
+ console.error(`❌ Failed to upload icon image to Ready Player Me temporary storage`);
259
+ console.error(` Check if READY_API_KEY has permission to upload files.`);
260
+ }
261
+ } else {
262
+ console.error(`❌ Failed to upload 3D model to Ready Player Me temporary storage`);
263
  }
264
+ } else {
265
+ console.warn(`⚠️ Could not map category "${normalizedCategory}" to a Ready Player Me asset type`);
266
  }
267
  } catch (assetError: any) {
268
+ console.error(`❌ Error creating Ready Player Me asset for ${file.originalname}:`, assetError.message);
269
+ if (assetError.response) {
270
+ console.error(` Response status: ${assetError.response.status}`);
271
+ console.error(` Response data:`, JSON.stringify(assetError.response.data, null, 2));
272
+ }
273
+ if (assetError.stack) {
274
+ console.error(` Stack trace:`, assetError.stack);
275
+ }
276
+ }
277
+ } else {
278
+ if (!model3dUrl) {
279
+ console.warn(`⚠️ Skipping Ready Player Me asset creation: no 3D model generated`);
280
+ } else if (!process.env.READY_PLAYER_ME_APPLICATION_ID) {
281
+ console.warn(`⚠️ Skipping Ready Player Me asset creation: READY_PLAYER_ME_APPLICATION_ID not set`);
282
+ } else if (!process.env.READY_PLAYER_ME_ORGANIZATION_ID) {
283
+ console.warn(`⚠️ Skipping Ready Player Me asset creation: READY_PLAYER_ME_ORGANIZATION_ID not set`);
284
  }
285
  }
286
 
src/utils/readyPlayerMe.ts CHANGED
@@ -157,19 +157,38 @@ export class ReadyPlayerMeClient {
157
  contentType: mimetype,
158
  });
159
 
 
 
 
 
 
 
160
  const response = await axios.post<{ data: { url: string } }>(
161
  `${READY_API_BASE}/temporary-media`,
162
  formData,
163
  {
164
- headers: {
165
- ...getHeaders(),
166
- ...formData.getHeaders(),
167
- },
168
  }
169
  );
170
- return response.data.data.url;
 
 
 
 
 
 
 
171
  } catch (error: any) {
172
- console.error("Error uploading asset file:", error.response?.data || error.message);
 
 
 
 
 
 
 
173
  return null;
174
  }
175
  }
@@ -189,6 +208,7 @@ export class ReadyPlayerMeClient {
189
  }>;
190
  }): Promise<ReadyPlayerMeAsset | null> {
191
  try {
 
192
  const response = await axios.post<{ data: ReadyPlayerMeAsset }>(
193
  `${READY_API_BASE}/assets`,
194
  { data },
@@ -196,9 +216,23 @@ export class ReadyPlayerMeClient {
196
  headers: getHeaders(),
197
  }
198
  );
199
- return response.data.data;
 
 
 
 
 
 
 
200
  } catch (error: any) {
201
- console.error("Error creating asset:", error.response?.data || error.message);
 
 
 
 
 
 
 
202
  return null;
203
  }
204
  }
 
157
  contentType: mimetype,
158
  });
159
 
160
+ const headers = {
161
+ ...getHeaders(),
162
+ ...formData.getHeaders(),
163
+ };
164
+
165
+ console.log(`[Ready Player Me] Uploading file: ${filename} (${fileBuffer.length} bytes, ${mimetype})`);
166
  const response = await axios.post<{ data: { url: string } }>(
167
  `${READY_API_BASE}/temporary-media`,
168
  formData,
169
  {
170
+ headers,
171
+ maxContentLength: Infinity,
172
+ maxBodyLength: Infinity,
 
173
  }
174
  );
175
+
176
+ if (response.data?.data?.url) {
177
+ console.log(`[Ready Player Me] File uploaded successfully: ${response.data.data.url}`);
178
+ return response.data.data.url;
179
+ } else {
180
+ console.error("[Ready Player Me] Upload response missing URL:", response.data);
181
+ return null;
182
+ }
183
  } catch (error: any) {
184
+ console.error("[Ready Player Me] Error uploading asset file:");
185
+ console.error(" Status:", error.response?.status);
186
+ console.error(" Status Text:", error.response?.statusText);
187
+ console.error(" Response Data:", JSON.stringify(error.response?.data, null, 2));
188
+ console.error(" Error Message:", error.message);
189
+ if (error.response?.data) {
190
+ console.error(" Full Error:", JSON.stringify(error.response.data, null, 2));
191
+ }
192
  return null;
193
  }
194
  }
 
208
  }>;
209
  }): Promise<ReadyPlayerMeAsset | null> {
210
  try {
211
+ console.log(`[Ready Player Me] Creating asset: ${data.name} (type: ${data.type}, gender: ${data.gender})`);
212
  const response = await axios.post<{ data: ReadyPlayerMeAsset }>(
213
  `${READY_API_BASE}/assets`,
214
  { data },
 
216
  headers: getHeaders(),
217
  }
218
  );
219
+
220
+ if (response.data?.data) {
221
+ console.log(`[Ready Player Me] Asset created successfully: ${response.data.data.id}`);
222
+ return response.data.data;
223
+ } else {
224
+ console.error("[Ready Player Me] Create asset response missing data:", response.data);
225
+ return null;
226
+ }
227
  } catch (error: any) {
228
+ console.error("[Ready Player Me] Error creating asset:");
229
+ console.error(" Status:", error.response?.status);
230
+ console.error(" Status Text:", error.response?.statusText);
231
+ console.error(" Response Data:", JSON.stringify(error.response?.data, null, 2));
232
+ console.error(" Error Message:", error.message);
233
+ if (error.response?.data) {
234
+ console.error(" Full Error:", JSON.stringify(error.response.data, null, 2));
235
+ }
236
  return null;
237
  }
238
  }
stylegptUI CHANGED
@@ -1 +1 @@
1
- Subproject commit 5739926b88e5287f4f5d33e0bf8f4ec7f9b92e2c
 
1
+ Subproject commit 47bd250878e5dcecf1a829869686f27f8e79c560
test-ready-player-me.ts ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dotenv from "dotenv";
2
+ import { ReadyPlayerMeClient } from "./src/utils/readyPlayerMe";
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ dotenv.config();
7
+
8
+ const READY_API_KEY = process.env.READY_API_KEY;
9
+ const READY_APPLICATION_ID = process.env.READY_PLAYER_ME_APPLICATION_ID;
10
+ const READY_ORGANIZATION_ID = process.env.READY_PLAYER_ME_ORGANIZATION_ID;
11
+
12
+ async function testReadyPlayerMeAPIs() {
13
+ console.log("=== Ready Player Me API Test Suite ===\n");
14
+
15
+ if (!READY_API_KEY) {
16
+ console.error("❌ READY_API_KEY not found in environment variables");
17
+ process.exit(1);
18
+ }
19
+
20
+ if (!READY_APPLICATION_ID) {
21
+ console.warn("⚠️ READY_PLAYER_ME_APPLICATION_ID not found - some tests will be skipped");
22
+ }
23
+
24
+ if (!READY_ORGANIZATION_ID) {
25
+ console.warn("⚠️ READY_PLAYER_ME_ORGANIZATION_ID not found - some tests will be skipped");
26
+ }
27
+
28
+ const client = new ReadyPlayerMeClient(READY_APPLICATION_ID);
29
+
30
+ let testGuestUserId: string | null = null;
31
+ let testAssetId: string | null = null;
32
+ let testAvatarId: string | null = null;
33
+
34
+ try {
35
+ console.log("1. Testing createGuestUser...");
36
+ if (READY_APPLICATION_ID) {
37
+ const guestUser = await client.createGuestUser(READY_APPLICATION_ID);
38
+ if (guestUser) {
39
+ testGuestUserId = guestUser.id;
40
+ console.log("βœ… Guest user created:", {
41
+ id: guestUser.id,
42
+ applicationIds: guestUser.applicationIds,
43
+ createdAt: guestUser.createdAt,
44
+ });
45
+ } else {
46
+ console.log("❌ Failed to create guest user");
47
+ }
48
+ } else {
49
+ console.log("⏭️ Skipped (no APPLICATION_ID)");
50
+ }
51
+
52
+ console.log("\n2. Testing getAvatarGLB (URL generation)...");
53
+ const testAvatarIdForURL = "test-avatar-id-123";
54
+ const glbUrl = await client.getAvatarGLB(testAvatarIdForURL, {
55
+ quality: "high",
56
+ lod: 0,
57
+ textureFormat: "webp",
58
+ });
59
+ console.log("βœ… GLB URL generated:", glbUrl);
60
+ console.log(" Expected format: https://avatars.readyplayer.me/{id}.glb?quality=high&lod=0&textureFormat=webp");
61
+
62
+ console.log("\n3. Testing getAvatar2DRender (URL generation)...");
63
+ const renderUrl = await client.getAvatar2DRender(testAvatarIdForURL, {
64
+ size: 400,
65
+ quality: 90,
66
+ camera: "portrait",
67
+ });
68
+ console.log("βœ… 2D Render URL generated:", renderUrl);
69
+ console.log(" Expected format: https://models.readyplayer.me/{id}.png?size=400&quality=90&camera=portrait");
70
+
71
+ console.log("\n4. Testing getAvatarMetadata...");
72
+ if (testAvatarId) {
73
+ const metadata = await client.getAvatarMetadata(testAvatarId);
74
+ if (metadata) {
75
+ console.log("βœ… Avatar metadata retrieved:", {
76
+ id: metadata.id,
77
+ url: metadata.url,
78
+ });
79
+ } else {
80
+ console.log("❌ Failed to get avatar metadata (avatar may not exist)");
81
+ }
82
+ } else {
83
+ console.log("⏭️ Skipped (no test avatar ID - create an avatar first)");
84
+ console.log(" To test this, create an avatar via the Profile page and use its ID");
85
+ }
86
+
87
+ console.log("\n5. Testing uploadAssetFile...");
88
+ const testImagePath = path.join(__dirname, "tits.jpeg");
89
+ let uploadedUrl: string | null = null;
90
+
91
+ if (fs.existsSync(testImagePath)) {
92
+ const fileBuffer = fs.readFileSync(testImagePath);
93
+ uploadedUrl = await client.uploadAssetFile(
94
+ fileBuffer,
95
+ "test-upload.bin",
96
+ "application/octet-stream"
97
+ );
98
+ if (uploadedUrl) {
99
+ console.log("βœ… File uploaded to temporary storage");
100
+ console.log(" URL:", uploadedUrl.substring(0, 100) + "...");
101
+ console.log(" Note: This URL expires after 24 hours");
102
+ } else {
103
+ console.log("❌ Failed to upload file");
104
+ }
105
+ } else {
106
+ console.log("⏭️ Skipped (test file not found)");
107
+ }
108
+
109
+ console.log("\n6. Testing createAsset...");
110
+ if (READY_ORGANIZATION_ID) {
111
+ if (!uploadedUrl) {
112
+ console.log("⚠️ Note: Asset creation requires a valid GLB file URL.");
113
+ console.log(" In production, this comes from 3D model generation.");
114
+ console.log(" Testing with a placeholder URL (will fail validation)...");
115
+ }
116
+
117
+ const testModelUrl = uploadedUrl || "https://example.com/test-asset.glb";
118
+ const asset = await client.createAsset({
119
+ name: "Test Wardrobe Item",
120
+ type: "top",
121
+ gender: "neutral",
122
+ modelUrl: testModelUrl,
123
+ iconUrl: testModelUrl,
124
+ organizationId: READY_ORGANIZATION_ID,
125
+ locked: false,
126
+ applications: READY_APPLICATION_ID ? [{
127
+ id: READY_APPLICATION_ID,
128
+ organizationId: READY_ORGANIZATION_ID,
129
+ isVisibleInEditor: true,
130
+ }] : undefined,
131
+ });
132
+
133
+ if (asset) {
134
+ testAssetId = asset.id;
135
+ console.log("βœ… Asset created:", {
136
+ id: asset.id,
137
+ name: asset.name,
138
+ type: asset.type,
139
+ gender: asset.gender,
140
+ });
141
+ } else {
142
+ if (uploadedUrl) {
143
+ console.log("❌ Failed to create asset (uploaded file may not be a valid GLB)");
144
+ console.log(" This is expected if the uploaded file is not a GLB model.");
145
+ } else {
146
+ console.log("❌ Failed to create asset (placeholder URL rejected)");
147
+ }
148
+ }
149
+ } else {
150
+ console.log("⏭️ Skipped (no ORGANIZATION_ID)");
151
+ }
152
+
153
+ console.log("\n7. Testing listAssets...");
154
+ const assetsList = await client.listAssets({
155
+ limit: 5,
156
+ page: 1,
157
+ });
158
+ if (assetsList) {
159
+ console.log("βœ… Assets listed:", {
160
+ count: assetsList.data.length,
161
+ totalPages: assetsList.pagination?.totalPages || "unknown",
162
+ assets: assetsList.data.map(a => ({
163
+ id: a.id,
164
+ name: a.name,
165
+ type: a.type,
166
+ })),
167
+ });
168
+ } else {
169
+ console.log("❌ Failed to list assets");
170
+ }
171
+
172
+ console.log("\n8. Testing equipAsset (single)...");
173
+ if (testAvatarId && testAssetId) {
174
+ const equipSuccess = await client.equipAsset(testAvatarId, testAssetId);
175
+ if (equipSuccess) {
176
+ console.log("βœ… Asset equipped to avatar");
177
+
178
+ console.log("\n9. Testing unequipAsset...");
179
+ const unequipSuccess = await client.unequipAsset(testAvatarId, testAssetId);
180
+ if (unequipSuccess) {
181
+ console.log("βœ… Asset unequipped from avatar");
182
+ } else {
183
+ console.log("❌ Failed to unequip asset");
184
+ }
185
+ } else {
186
+ console.log("❌ Failed to equip asset");
187
+ }
188
+ } else {
189
+ console.log("⏭️ Skipped (no test avatar ID or asset ID)");
190
+ console.log(" To test this:");
191
+ console.log(" 1. Create an avatar via Profile page");
192
+ console.log(" 2. Upload a wardrobe item (creates an asset)");
193
+ console.log(" 3. Use those IDs to test equip/unequip");
194
+ }
195
+
196
+ console.log("\n10. Testing equipAsset (multiple assets)...");
197
+ if (testAvatarId && testAssetId) {
198
+ const equipPromises = [testAssetId].map(assetId =>
199
+ client.equipAsset(testAvatarId, assetId)
200
+ );
201
+ const results = await Promise.all(equipPromises);
202
+ const allSuccess = results.every(r => r === true);
203
+ if (allSuccess) {
204
+ console.log("βœ… Multiple assets equipped successfully");
205
+ } else {
206
+ console.log("❌ Some assets failed to equip");
207
+ }
208
+ } else {
209
+ console.log("⏭️ Skipped (no test avatar ID or asset ID)");
210
+ }
211
+
212
+ console.log("\n11. Testing getAuthToken...");
213
+ if (testGuestUserId) {
214
+ const token = await client.getAuthToken(testGuestUserId, "readyplayerme");
215
+ if (token) {
216
+ console.log("βœ… Auth token retrieved:", token.substring(0, 20) + "...");
217
+ } else {
218
+ console.log("❌ Failed to get auth token (may require specific partner permissions)");
219
+ console.log(" This is normal if your API key doesn't have token generation permissions");
220
+ }
221
+ } else {
222
+ console.log("⏭️ Skipped (no guest user ID)");
223
+ }
224
+
225
+ console.log("\n=== Test Summary ===");
226
+ console.log("βœ… URL generation tests passed");
227
+ console.log("βœ… API connectivity verified");
228
+ if (testGuestUserId) {
229
+ console.log("βœ… Guest user creation: PASSED");
230
+ }
231
+ if (testAssetId) {
232
+ console.log("βœ… Asset creation: PASSED");
233
+ }
234
+ console.log("\nNote: Some tests require real avatar/asset IDs from your application.");
235
+ console.log(" Run the full application flow to test equip/unequip functionality.");
236
+
237
+ } catch (error: any) {
238
+ console.error("\n❌ Test suite error:", error.message);
239
+ if (error.response) {
240
+ console.error(" Response status:", error.response.status);
241
+ console.error(" Response data:", JSON.stringify(error.response.data, null, 2));
242
+ }
243
+ process.exit(1);
244
+ }
245
+ }
246
+
247
+ testReadyPlayerMeAPIs();
248
+
test-upload.sh ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ API_BASE_URL="https://nexusbert-stylegpt-milestone2.hf.space"
4
+ EMAIL="omezirizion@gmail.com"
5
+ PASSWORD="Amazon1@"
6
+ IMAGE_FILE="tits.jpeg"
7
+
8
+ echo "=== Testing Wardrobe Upload with tits.jpeg ==="
9
+ echo ""
10
+
11
+ # Step 1: Login
12
+ echo "1. Logging in..."
13
+ LOGIN_RESPONSE=$(curl -s -X POST "${API_BASE_URL}/api/auth/login" \
14
+ -H "Content-Type: application/json" \
15
+ -d "{\"email\":\"${EMAIL}\",\"password\":\"${PASSWORD}\"}")
16
+
17
+ echo "Login response: $LOGIN_RESPONSE"
18
+ echo ""
19
+
20
+ # Extract token (basic extraction, might need adjustment based on response format)
21
+ TOKEN=$(echo $LOGIN_RESPONSE | grep -o '"token":"[^"]*' | cut -d'"' -f4)
22
+
23
+ if [ -z "$TOKEN" ]; then
24
+ echo "❌ Failed to get token. Response: $LOGIN_RESPONSE"
25
+ exit 1
26
+ fi
27
+
28
+ echo "βœ… Login successful"
29
+ echo "Token: ${TOKEN:0:20}..."
30
+ echo ""
31
+
32
+ # Step 2: Check if file exists
33
+ if [ ! -f "$IMAGE_FILE" ]; then
34
+ echo "❌ File not found: $IMAGE_FILE"
35
+ exit 1
36
+ fi
37
+
38
+ echo "2. Found image file: $IMAGE_FILE"
39
+ echo ""
40
+
41
+ # Step 3: Upload
42
+ echo "3. Uploading image (this may take a while)..."
43
+ echo " - Background removal"
44
+ echo " - FashionClip classification"
45
+ echo " - 3D model generation"
46
+ echo " - Ready Player Me asset creation"
47
+ echo ""
48
+
49
+ UPLOAD_RESPONSE=$(curl -s -X POST "${API_BASE_URL}/api/upload" \
50
+ -H "Authorization: Bearer ${TOKEN}" \
51
+ -F "image=@${IMAGE_FILE}" \
52
+ -F "style=casual" \
53
+ --max-time 300)
54
+
55
+ echo "Upload response:"
56
+ echo "$UPLOAD_RESPONSE" | jq '.' 2>/dev/null || echo "$UPLOAD_RESPONSE"
57
+ echo ""
58
+
59
+ # Check if upload was successful
60
+ if echo "$UPLOAD_RESPONSE" | grep -q '"success":true'; then
61
+ echo "βœ… Upload successful!"
62
+
63
+ # Extract and display key information
64
+ if command -v jq &> /dev/null; then
65
+ echo ""
66
+ echo "πŸ“Š Results:"
67
+ COUNT=$(echo "$UPLOAD_RESPONSE" | jq -r '.count // "N/A"')
68
+ echo " - Items uploaded: $COUNT"
69
+
70
+ if [ "$COUNT" != "null" ] && [ "$COUNT" != "N/A" ]; then
71
+ ITEM_NAME=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].name // "N/A"')
72
+ CATEGORY=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].category // "N/A"')
73
+ STYLE=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].style // "N/A"')
74
+ COLOR=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].color // "N/A"')
75
+ HAS_3D=$(echo "$UPLOAD_RESPONSE" | jq -r 'if .items[0].model3dUrl then "Yes" else "No" end')
76
+ ASSET_ID=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].readyPlayerMeAssetId // "N/A"')
77
+ ITEM_ID=$(echo "$UPLOAD_RESPONSE" | jq -r '.items[0].id // "N/A"')
78
+
79
+ echo " - Item name: $ITEM_NAME"
80
+ echo " - Category: $CATEGORY"
81
+ echo " - Style: $STYLE"
82
+ echo " - Color: $COLOR"
83
+ echo " - Has 3D model: $HAS_3D"
84
+ echo " - Ready Player Me Asset ID: $ASSET_ID"
85
+ echo " - Item ID: $ITEM_ID"
86
+ fi
87
+ fi
88
+ else
89
+ echo "❌ Upload failed"
90
+ exit 1
91
+ fi
92
+
test-upload.ts ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from "axios";
2
+ import FormData from "form-data";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import dotenv from "dotenv";
6
+
7
+ dotenv.config();
8
+
9
+ const API_BASE_URL = process.env.API_URL || "https://nexusbert-stylegpt-milestone2.hf.space";
10
+ const TEST_EMAIL = "omezirizion@gmail.com";
11
+ const TEST_PASSWORD = "Amazon1@";
12
+
13
+ async function testUpload() {
14
+ console.log("=== Testing Wardrobe Upload with tits.jpeg ===\n");
15
+
16
+ try {
17
+ // Step 1: Login to get token
18
+ console.log("1. Logging in...");
19
+ const loginResponse = await axios.post(`${API_BASE_URL}/api/auth/login`, {
20
+ email: TEST_EMAIL,
21
+ password: TEST_PASSWORD,
22
+ });
23
+
24
+ if (!loginResponse.data.success || !loginResponse.data.token) {
25
+ console.error("❌ Login failed:", loginResponse.data.error);
26
+ process.exit(1);
27
+ }
28
+
29
+ const token = loginResponse.data.token;
30
+ console.log("βœ… Login successful\n");
31
+
32
+ // Step 2: Check if file exists
33
+ const imagePath = path.join(__dirname, "tits.jpeg");
34
+ if (!fs.existsSync(imagePath)) {
35
+ console.error(`❌ File not found: ${imagePath}`);
36
+ process.exit(1);
37
+ }
38
+ console.log(`2. Found image file: ${imagePath}\n`);
39
+
40
+ // Step 3: Create form data
41
+ console.log("3. Preparing upload...");
42
+ const formData = new FormData();
43
+ formData.append("image", fs.createReadStream(imagePath), {
44
+ filename: "tits.jpeg",
45
+ contentType: "image/jpeg",
46
+ });
47
+ formData.append("style", "casual");
48
+
49
+ console.log("βœ… Form data prepared\n");
50
+
51
+ // Step 4: Upload
52
+ console.log("4. Uploading image (this may take a while)...");
53
+ console.log(" - Background removal");
54
+ console.log(" - FashionClip classification");
55
+ console.log(" - 3D model generation");
56
+ console.log(" - Ready Player Me asset creation\n");
57
+
58
+ const uploadResponse = await axios.post(
59
+ `${API_BASE_URL}/api/upload`,
60
+ formData,
61
+ {
62
+ headers: {
63
+ ...formData.getHeaders(),
64
+ Authorization: `Bearer ${token}`,
65
+ },
66
+ maxContentLength: Infinity,
67
+ maxBodyLength: Infinity,
68
+ timeout: 300000, // 5 minutes timeout
69
+ }
70
+ );
71
+
72
+ if (uploadResponse.data.success) {
73
+ console.log("βœ… Upload successful!\n");
74
+ console.log("πŸ“Š Results:");
75
+ console.log(` - Items uploaded: ${uploadResponse.data.count}`);
76
+ if (uploadResponse.data.items && uploadResponse.data.items.length > 0) {
77
+ const item = uploadResponse.data.items[0];
78
+ console.log(` - Item name: ${item.name}`);
79
+ console.log(` - Category: ${item.category}`);
80
+ console.log(` - Style: ${item.style}`);
81
+ console.log(` - Color: ${item.color || "N/A"}`);
82
+ console.log(` - Has 3D model: ${item.model3dUrl ? "Yes" : "No"}`);
83
+ console.log(` - Ready Player Me Asset ID: ${item.readyPlayerMeAssetId || "N/A"}`);
84
+ console.log(` - Item ID: ${item.id}`);
85
+ }
86
+ if (uploadResponse.data.failed && uploadResponse.data.failed > 0) {
87
+ console.log(` - Failed files: ${uploadResponse.data.failed}`);
88
+ if (uploadResponse.data.failedFiles) {
89
+ console.log(` ${uploadResponse.data.failedFiles.join(", ")}`);
90
+ }
91
+ }
92
+ } else {
93
+ console.error("❌ Upload failed:", uploadResponse.data.error);
94
+ process.exit(1);
95
+ }
96
+ } catch (error: any) {
97
+ console.error("\n❌ Error during upload test:");
98
+ if (error.response) {
99
+ console.error(` Status: ${error.response.status}`);
100
+ console.error(` Error: ${JSON.stringify(error.response.data, null, 2)}`);
101
+ } else if (error.request) {
102
+ console.error(" No response received. Is the server running?");
103
+ } else {
104
+ console.error(` ${error.message}`);
105
+ }
106
+ process.exit(1);
107
+ }
108
+ }
109
+
110
+ testUpload();
111
+