Spaces:
Runtime error
Runtime error
pushed
Browse files- READY_PLAYER_ME_FIXES.md +122 -0
- READY_PLAYER_ME_TESTING_GUIDE.md +326 -0
- src/routes/upload.ts +60 -3
- src/utils/readyPlayerMe.ts +42 -8
- stylegptUI +1 -1
- test-ready-player-me.ts +248 -0
- test-upload.sh +92 -0
- test-upload.ts +111 -0
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 166 |
-
|
| 167 |
-
},
|
| 168 |
}
|
| 169 |
);
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
} catch (error: any) {
|
| 172 |
-
console.error("Error uploading asset file:"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
} catch (error: any) {
|
| 201 |
-
console.error("Error creating asset:"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
| 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 |
+
|