| 584 | 584 | * @returns {string | null} AICC path |
| 585 | 585 | */ |
| 586 | 586 | function parseAICC(url) { |
| 587 | | - const pattern = /^https?:\/\/aicharactercards\.com\/character-cards\/([^/]+)\/([^/]+)\/?$|([^/]+)\/([^/]+)$/; |
| 587 | + try { |
| 588 | | - const match = url.match(pattern); |
| 588 | + if (isValidUrl(url)) { |
| 589 | | - if (match) { |
| 589 | + const urlObj = new URL(url); |
| 590 | | - // Match group 1 & 2 for full URL, 3 & 4 for relative path |
| 590 | + // Split the path and remove empty strings caused by trailing slashes |
| 591 | | - return match[1] && match[2] ? `${match[1]}/${match[2]}` : `${match[3]}/${match[4]}`; |
| 591 | + const parts = urlObj.pathname.split('/').filter(Boolean); |
| 592 | + if (parts.length >= 2) { |
| 593 | + // Always grab the last two segments (author/character) |
| 594 | + return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`; |
| 595 | + } |
| 596 | + } else { |
| 597 | + // Fallback for relative paths or raw "author/character" strings |
| 598 | + const parts = url.split('/').filter(Boolean); |
| 599 | + if (parts.length >= 2) { |
| 600 | + return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`; |
| 601 | + } |
| 602 | + } |
| 603 | + } catch (e) { |
| 604 | + console.error('Error parsing AICC URL:', e); |
| 592 | 605 | } |
| 593 | 606 | return null; |
| 594 | 607 | } |