# **Comprehensive Technical Architecture for Client-Side Music Metadata Aggregation and Reconciliation**

## **1\. Executive Summary and Strategic Architecture**

The objective of this report is to define the technical implementation strategy for "MetaMuLate v2.0," a client-side, browser-based Single Page Application (SPA) aimed at aggregating, reconciling, and exporting granular music metadata. The core utility of this system lies in its ability to generate a "Golden Record" of metadata—specifically focusing on International Standard Recording Codes (ISRCs), phonographic copyrights (P-Lines), and precise label affiliation—by cross-referencing disparate data repositories. The target repositories identified for this integration are Apple Music, Discogs, Amazon Music, and MusicBrainz, alongside supplemental sources for lyrics and cover art such as Genius, Lyrics.ovh, and the Cover Art Archive.

The primary architectural constraint is the requirement for a "Zero-Setup" self-contained HTML file. This constraint fundamentally alters the standard approach to API integration. In a traditional server-side environment, API secrets are secured in environment variables, and Cross-Origin Resource Sharing (CORS) is managed via backend proxies. In a client-side environment, the browser enforces the Same-Origin Policy (SOP), which restricts how documents loaded from one origin (or in this case, the file:// protocol or a local server) interact with resources from another origin. This report provides an exhaustive analysis of the methods required to navigate these security models, primarily through the use of public CORS proxies, specific browser configurations, and the leveraging of permissive APIs.

The system architecture is defined by three functional zones as outlined in the provided technical design documents.1 **Zone A (Discovery)** serves as the entry point where the user initiates a query. The research dictates that this zone, currently hardcoded for iTunes, must be refactored to trigger parallel asynchronous requests to all target data sources. **Zone B (Reconciliation)** is the logic core, where the application must visualize discrepancies. This requires a robust normalization engine capable of comparing deeply nested JSON structures—such as MusicBrainz’s label-info arrays versus Discogs’ companies objects—against a standardized internal schema. Finally, **Zone C (Output)** handles the serialization of the reconciled "Golden Record" into an XLSX format using the SheetJS library. The integrity of this final output is contingent upon the accuracy of the upstream scrapers and the conflict resolution logic defined herein.

This report evaluates the viability of each source, detailing the authentication mechanisms (JWT for Apple, Personal Tokens for Discogs), data retrieval strategies (REST APIs vs. DOM scraping for Amazon), and the specific JSON paths required to extract critical metadata. It concludes with a comprehensive implementation guide for modifying the provided HTML file to support this multi-source architecture.

## ---

**2\. Client-Side Network Architecture and Security Considerations**

### **2.1 The Cross-Origin Resource Sharing (CORS) Challenge**

The most significant hurdle in implementing a client-side metadata scraper is the browser's security model. When the MetaMuLate application, running locally, attempts to fetch data from an external API like https://api.discogs.com, the browser recognizes that the request is cross-origin. For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.3

Before sending the actual request (e.g., a GET request with an Authorization header), the browser dispatches a "preflight" request using the HTTP OPTIONS method. This preflight request asks the server if it permits the actual request from the current origin. The server must respond with specific headers, primarily Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers. If the server does not explicitly allow the origin (which is often null for local files) or the specific headers being sent (such as Authorization or User-Agent), the browser will block the request, and the JavaScript code will receive a network error.4

This mechanism poses a critical challenge for the MetaMuLate project because most commercial APIs (Discogs, Genius) do not send wildcard (\*) CORS headers for authenticated requests. They often require specific, whitelisted domains, making direct client-side calls from a local file impossible without intermediation.6

### **2.2 Proxy-Based Mitigation Strategies**

To circumvent CORS restrictions without deploying a backend server, the architecture must rely on CORS proxies. These proxies act as middleware: they receive the request from the client browser, forward it to the target API (server-to-server communication is not subject to CORS), receive the response, and then return it to the browser with the necessary permissive CORS headers injected.

#### **2.2.1 Public CORS Proxies**

Several public proxy services facilitate this interaction, though they come with trade-offs regarding reliability and data privacy.

* **CorsProxy.io:** This service is currently one of the most reliable options for high-throughput requests. It supports HTTPS and correctly handles redirects, which is essential for fetching cover art URLs that often redirect to CDNs. The implementation pattern involves appending the target URL to the proxy base: https://corsproxy.io/?url={TargetURL}. This wrapper allows the fetch API to complete successfully as the response will contain Access-Control-Allow-Origin: \*.7  
* **AllOrigins.win:** This proxy is particularly useful for scraping raw HTML content. It offers a JSON response format where the target page's HTML is wrapped in a contents string. This avoids issues where the target page might have malformed headers that confuse the browser. It is less suitable for binary data but excellent for text-based scraping strategies.9  
* **Cors-Anywhere:** This is a widely used Node.js proxy. While the public demo server is often rate-limited and requires a temporary access request button to be clicked by the user, the underlying code is robust. For a "Zero-Setup" distribution, relying on the public instance is risky due to potential downtime or throttling. However, it remains a viable fallback if the user is instructed to enable temporary access.11

#### **2.2.2 Local Proxy Solutions**

For advanced users or persistent stability, the report recommends a hybrid approach where the application can check for a locally running proxy. Tools like local-cors-proxy (a simple npm package) can be run by the user to create a customized proxy on localhost. The HTML file can be configured to check for this local service first before falling back to public proxies. This aligns with the "self-contained" philosophy by keeping the dependency external to the HTML file but within the user's local control.

### **2.3 Browser-Specific Security Configurations**

An alternative to proxies is modifying the browser's security settings. Chrome, for instance, can be launched with the \--disable-web-security flag. This completely disables the SOP, allowing the local HTML file to make direct requests to any API. While this removes all CORS hurdles and simplifies the code significantly, it is a drastic security reduction and should only be recommended for isolated, developer-centric usage scenarios. It is not a viable strategy for a tool intended for general distribution to non-technical staff due to the high risk of cross-site attacks during general browsing.13

### **2.4 Authentication Security in Client-Side Code**

A critical vulnerability in client-side apps is the exposure of API secrets. In a compiled application or server-side script, secrets (like the Discogs Consumer Secret or Apple Private Key) are hidden. In a text-based HTML file, they are visible to anyone who acts "View Source."

To mitigate this while maintaining functionality:

* **User-Provided Secrets:** The application should not hardcode high-privilege secrets. Instead, it should prompt the user to input their own API tokens (e.g., "Enter your Discogs Personal Access Token") and store these in the browser's localStorage. This shifts the security responsibility to the user and prevents the distribution of a shared, revocable key.14  
* **Long-Lived Tokens:** For services like Apple Music, where token generation requires complex signing (ES256), the system should accept a pre-generated "developer token" with a long expiration (6 months) rather than attempting to sign tokens in the browser using raw private keys, which would be a severe security anti-pattern.15

## ---

**3\. Primary Data Source: Apple Music Integration**

### **3.1 Source Overview**

Apple Music acts as the primary "anchor" for the MetaMuLate system. Its data is highly standardized, rigorously cleaned, and, crucially, serves as a reliable source for **ISRCs** (International Standard Recording Codes). Unlike crowd-sourced databases, Apple’s metadata reflects the official digital supply chain, making it the definitive source for track identifiers and P-Lines.17

### **3.2 Authentication Mechanism: The Signed Developer Token**

Accessing the Apple Music API differs from standard REST APIs. It requires a JSON Web Token (JWT) signed with the **ES256** (Elliptic Curve Digital Signature Algorithm) standard. This token authenticates the developing entity rather than a specific user.

#### **3.2.1 Token Construction and Signing**

The token consists of a header (specifying the ES256 algorithm and the Key ID kid) and a payload containing the Issuer (iss \- the Team ID), Issue Time (iat), and Expiration (exp).

* **Challenge:** Most client-side crypto libraries support HMAC (HS256) or RSA (RS256) readily, but ES256 support can be cumbersome in pure JavaScript without external WASM dependencies. Furthermore, signing requires the private .p8 key file. Requesting a user to paste their private key into a browser input is bad security practice.18  
* **Recommended Workflow:** The user should generate this token externally (using a Python or Node.js script) and provide the resulting string to the application. Apple Developer Tokens are valid for up to 6 months, making this a biannual configuration task rather than a daily one. This aligns with the "Zero-Setup" goal by treating the token as a configuration string rather than a dynamic cryptographic process.16

### **3.3 MusicKit JS vs. Direct API Calls**

Apple provides **MusicKit JS**, a comprehensive library for web integrations. It handles the API requests, playback, and user authorization flows. However, for a pure metadata scraper, MusicKit JS might be overhead. Direct calls to the API endpoints using fetch with the Authorization: Bearer header are lighter and allow for more granular control over the data parsing.17

### **3.4 Data Retrieval Strategy**

The primary endpoint for metadata discovery is the Catalog Search API.

* **Endpoint:** https://api.music.apple.com/v1/catalog/{storefront}/search  
* **Storefront:** The system must define a storefront (e.g., us, gb, jp). The default should be us, but this affects metadata availability (e.g., P-Lines may differ by territory).  
* **Parameters:** term (the search query), types (set to songs,albums), and limit (to control the number of candidates).

#### **3.4.1 JSON Path Mapping**

The response from Apple Music is deeply nested. The scraper must navigate this structure to extract the "Golden Record" fields.

* **Track Name:** results.songs.data\[i\].attributes.name  
* **Artist Name:** results.songs.data\[i\].attributes.artistName  
* **Album Name:** results.songs.data\[i\].attributes.albumName  
* **ISRC:** results.songs.data\[i\].attributes.isrc. This is a critical field, as Apple is the most reliable source for this identifier.  
* **P-Line:** results.songs.data\[i\].attributes.copyright (or specifically the copyright field on the associated album object if track-level data is missing).  
* **Record Label:** Often found in attributes.recordLabel or parsed from the copyright string.  
* **Cover Art:** results.songs.data\[i\].attributes.artwork.url. Note that the URL contains {w} and {h} placeholders which the scraper must string-replace with desired dimensions (e.g., 600 and 600\) to generate a valid link.21

### **3.5 Handling "Uniformity"**

Apple Music data is generally "clean." Normalization logic (trimming, lower-casing) should be applied before comparison. The API returns an explicit isrc field, unlike other sources that might bury it. This makes Apple Music the primary candidate for the "Source of Truth" in the ISRC column of the Reconciliation Matrix.

## ---

**3.6 Spotify Integration (Added v9.95!)**

### **3.6.1 Source Overview**

Spotify serves as the **first second-tier source** after Apple Music, positioned to provide complementary streaming metadata. While Apple Music is the discovery anchor, Spotify offers independent ISRC verification, label information, popularity metrics, and crucially, artist-level genre data (since Spotify deprecated track/album-level genres).

### **3.6.2 Authentication: OAuth 2.0 Client Credentials**

Spotify uses OAuth 2.0 with the **Client Credentials** flow for application-level access (no user authorization required for public catalog data).

* **Credentials Required:** Client ID and Client Secret from the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard).
* **Token Endpoint:** `POST https://accounts.spotify.com/api/token`
* **Token Caching:** Access tokens are valid for 1 hour. The implementation caches tokens in localStorage with a 1-minute buffer before expiry, triggering automatic refresh when needed.

**Implementation Pattern:**
```javascript
const authHeader = btoa(`${clientId}:${clientSecret}`);
const response = await fetch(tokenUrl, {
    method: 'POST',
    headers: {
        'Authorization': `Basic ${authHeader}`,
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: 'grant_type=client_credentials'
});
```

### **3.6.3 CORS Considerations**

Unlike most APIs requiring a proxy, Spotify's Web API supports CORS when the application is served from a web server. However, the **token endpoint** (`accounts.spotify.com`) has CORS restrictions.

* **Served Mode (https://):** API calls work directly; token endpoint may need proxy depending on browser.
* **File Mode (file://):** Both token endpoint and API calls require proxy.

The implementation auto-detects the protocol and applies proxy conditionally.

### **3.6.4 Data Retrieval Strategy**

**Step 1: Track Search (with ISRC priority)**

Spotify supports ISRC-based lookup which provides exact matches:

```
GET https://api.spotify.com/v1/search?q=isrc:{ISRC}&type=track&limit=1
```

Fallback strategies:
1. `q=track:{title} artist:{name}&type=track`
2. `q={artist} {title}&type=track`

**Step 2: Album Enrichment**

The track response contains limited album data. Full label and copyright information requires an album lookup:

```
GET https://api.spotify.com/v1/albums/{albumId}
```

**Step 3: Artist Genre Fallback**

Spotify deprecated track/album-level genres. When genre is needed, the artist endpoint provides reliable genre data:

```
GET https://api.spotify.com/v1/artists/{artistId}
```

### **3.6.5 JSON Path Mapping**

| Field | JSON Path | Notes |
|-------|-----------|-------|
| **ISRC** | `tracks.items[0].external_ids.isrc` | Primary identifier |
| **Track Name** | `tracks.items[0].name` | |
| **Artist** | `tracks.items[0].artists[].name` | Array, join with ", " |
| **Album** | `tracks.items[0].album.name` | |
| **Duration** | `tracks.items[0].duration_ms` | Convert to MM:SS |
| **Popularity** | `tracks.items[0].popularity` | 0-100 scale |
| **Label** | `album.label` | Requires album lookup |
| **P-Line** | `album.copyrights[type="P"].text` | Filter for type "P" |
| **UPC** | `album.external_ids.upc` | Album-level only |
| **Genre** | `artist.genres[]` | From artist profile |

### **3.6.6 Visual Indicator for Artist-Sourced Genres**

Since genre data comes from the artist profile rather than the track/album, the UI displays a visual indicator (user icon) next to genre values sourced from artist profiles. This maintains provenance transparency in the reconciliation matrix.

## ---

**3.7 Swappable Discovery Source Architecture (Added v9.95!)**

### **3.7.1 Overview**

MetaMuLate v9.95! introduces the ability to swap between Apple Music and Spotify as the primary discovery source. This architectural change addresses use cases where:

1. Users prefer Spotify's catalog coverage for certain genres or regions
2. Apple Music developer tokens are unavailable
3. Spotify's popularity metrics are desired in discovery results

### **3.7.2 DiscoveryConfig Module**

The `DiscoveryConfig` object manages discovery source state:

```javascript
const DiscoveryConfig = {
    sources: {
        apple: { key: 'apple', name: 'Apple Music', requiresAuth: false },
        spotify: { key: 'spotify', name: 'Spotify', requiresAuth: true }
    },
    getSource()          // Returns current discovery source from localStorage
    setSource(key)       // Persists selection to localStorage
    getSecondarySource() // Returns the "other" source for matrix positioning
    isConfigured(key)    // Checks if source has valid credentials
};
```

### **3.7.3 Discovery Function Routing**

The `discover()` and `loadMore()` functions dynamically route to the appropriate search function:

```javascript
if (DiscoveryConfig.getSource() === 'spotify') {
    results = await Scrapers.discovery.searchSpotify(term, offset);
} else {
    results = await Scrapers.discovery.search(term, offset);  // Apple/iTunes
}
```

### **3.7.4 Dynamic Matrix Column Ordering**

The `getDefaultMatrixConfig()` function dynamically positions sources:

| Discovery Source | Matrix Order (Left to Right) |
|------------------|------------------------------|
| Apple (default) | Apple → Spotify → MusicBrainz → Snowflake → Lyrics |
| Spotify | Spotify → Apple → MusicBrainz → Snowflake → Lyrics |

When the discovery source changes, the matrix config version is incremented, triggering a refresh of column ordering for existing users.

### **3.7.5 Spotify Discovery Search**

The `searchSpotify()` function mirrors Apple's discovery format:

* **Endpoint:** `https://api.spotify.com/v1/search?q=artist:{term}&type=track&limit=25`
* **Authentication:** Uses cached OAuth token from `_getSpotifyToken()`
* **Response Mapping:** Tracks are mapped to the standard discovery result schema
* **Fallback:** If Spotify credentials are missing, falls back to Apple search automatically

### **3.7.6 UI Integration**

* **Connections Modal:** Radio button toggle at the top of the modal
* **Search Placeholder:** Dynamically updates to show active source
* **Visual Feedback:** Toast notification on source change
* **Disabled State:** Spotify option disabled if not configured

## ---

**3.8 Multi-Result Background Loading Architecture**

### **3.8.1 Overview**

Sources that return multiple matching results implement background loading to provide immediate feedback while fetching additional results asynchronously.

### **3.8.2 Supported Sources**

| Source | Search Limit | Detail Limit | Background Loading |
|--------|--------------|--------------|-------------------|
| MusicBrainz | 10 | 5 | ✓ |
| Discogs | 5 | 3 | ✓ |
| Spotify | 10 | 5 | ✓ |
| Wikidata | 5 | 3 | ✓ |
| Genius | 5 | 3 | ✓ |

### **3.8.3 Implementation Pattern**

Each multi-result scraper follows this pattern:

1. **Initial Search:** Fetch search results up to `searchLimit`
2. **First Result:** Process and return the first result synchronously
3. **Background Fetch:** Start async loop for remaining results up to `detailLimit`
4. **Callback Notification:** Each completed result triggers `onAdditionalResult` callback
5. **UI Update:** `BackgroundFetchManager` updates matrix pagination controls

### **3.8.4 MultiResultConfig**

Configurable limits stored in localStorage:

```javascript
const MultiResultConfig = {
    getSearchLimit(sourceKey)  // Get search limit for source
    getDetailLimit(sourceKey)  // Get detail limit for source  
    setSearchLimit(key, value) // Set custom search limit (1-25)
    setDetailLimit(key, value) // Set custom detail limit (1-10)
    supportsMultiResult(key)   // Check if source supports multi-result
};
```

## ---

**4\. Primary Data Source: Discogs Integration**

### **4.1 Source Overview**

Discogs is a crowd-sourced database focused heavily on physical releases (Vinyl, CD). It is the authoritative source for **Catalog Numbers** and precise **Label** credits. It is less reliable for digital-only identifiers like ISRCs but excels in providing release year and detailed credit lists (Producers, Mixers).22

### **4.2 Authentication: Personal Access Tokens**

Discogs offers two authentication methods: full OAuth 1.0a and Personal Access Tokens. OAuth is complex to implement in a serverless client-side app due to the multi-step handshake and callback requirements.

* **Recommended Method:** The **Personal Access Token**. Users can generate a token from their Discogs Developer settings. This token acts as a simple API Key.  
* **Implementation:** The token is passed in the Authorization header: Authorization: Discogs token={UserToken}. This allows full access to the database search endpoints without the complexity of OAuth.24

### **4.3 The User-Agent Constraint**

Discogs enforces a strict User-Agent policy. Any request that does not include a custom User-Agent header identifying the application (e.g., MetaMuLate/2.0) is rejected.

* **Browser Limitation:** Browsers treat User-Agent as a "forbidden header name," meaning client-side JavaScript cannot override it in a fetch request. This is a significant blocker for direct API calls.25  
* **Solution:** This necessitates the use of the CORS proxy. A robust proxy like corsproxy.io or a custom-configured local proxy will forward the request. If using a proxy that allows header rewriting, the User-Agent can be injected. Alternatively, relying on the proxy's own User-Agent (which is often generic enough to pass Discogs' filter) is a viable workaround. The scraper should attempt to set a custom header like X-User-Agent if the proxy supports mapping it.24

### **4.4 Data Retrieval Strategy**

Discogs data is split between a "Search" endpoint and a "Release" endpoint. The scraper must perform a two-step process.

**Step 1: Search**

* **Endpoint:** https://api.discogs.com/database/search  
* **Parameters:** q (query), type=release, token (auth).  
* **Response:** Returns a list of releases. The scraper should select the most relevant result (fuzzy matching title/artist) and extract the resource\_url.

**Step 2: Deep Detail Fetch**

* **Endpoint:** The resource\_url from Step 1 (e.g., https://api.discogs.com/releases/{ID}).  
* **Data Mapping:**  
  * **Labels:** Located in the labels array. Each object contains name and catno (Catalog Number). The scraper should iterate this array to find the primary label.  
  * **Companies:** Deeper metadata is in the companies array. This is where P-Line data resides. The scraper logic must iterate through this array and look for entity\_type\_name values such as "Phonographic Copyright (p)" or "Copyright (c)" to extract the correct P-Line entity and year.22  
  * **ISRC:** Discogs generally does not have a dedicated ISRC field at the release level. It may occasionally appear in the identifiers array with type: 'ISRC', but this is rare. The scraper should default to "N/A" for Discogs ISRC unless explicitly found in identifiers.22

## ---

**5\. Primary Data Source: Amazon Music Scraping**

### **5.1 The Challenge of "Closed" APIs**

Amazon Music is the most difficult target. Research confirms that the "Amazon Music Web API" is in a closed beta, restricted to approved partners. There is no public documentation for generating API keys for personal or open-source projects.26 Furthermore, the internal API endpoints used by the Amazon Music web player (music-api.amazon.com) are protected by complex session cookies and device fingerprinting, making direct API reverse-engineering fragile and prone to breakage.27

### **5.2 Scraping Strategy: HTML Parsing via Proxy**

The most viable method for a client-side tool is scraping the public-facing Amazon product pages. Amazon pages are rich in structured data, often embedded as JSON-LD.

**Workflow:**

1. **Search Phase:** Since Amazon's internal search API is protected, the scraper should use a "Site Search" technique via a search engine or attempt to construct a direct Amazon search URL: https://www.amazon.com/s?k={Artist}+{Track}\&i=digital-music.  
2. **Fetching:** The request must be routed through a CORS proxy to bypass origin checks.  
3. **Parsing:** The response is raw HTML. The browser's native DOMParser API is the tool of choice here.  
   JavaScript  
   const parser \= new DOMParser();  
   const doc \= parser.parseFromString(htmlText, 'text/html');

4. **Extraction:**  
   * **ASIN:** Extract the Amazon Standard Identification Number from the search result link (/dp/B0...).  
   * **Product Page Fetch:** Fetch the specific product page using the ASIN.  
   * **JSON-LD Extraction:** Scrape the DOM for \<script type="application/ld+json"\>. This script block often contains clean, machine-readable metadata including name, byArtist, and potentially isrc or gtin.28  
   * **Fallback Scraping:** If JSON-LD is missing, the scraper must traverse the DOM for specific table rows. P-Line data is often found in a table under "Product details," usually labeled "Copyright" or containing the ℗ symbol. Regex (/(℗|©)\\s\*(\\d{4})\\s\*(.\*)/) is required to parse this unstructured text.

### **5.3 Countermeasures and Reliability**

Amazon employs aggressive anti-scraping measures, including CAPTCHAs and IP blocking.

* **Rate Limiting:** The implementation must aggressively rate-limit Amazon requests (e.g., one request every few seconds) to avoid triggering bot detection.  
* **Error Handling:** The scraper must detect CAPTCHA responses (checking for specific text like "Enter the characters you see below"). If detected, the application should prompt the user to open the URL in a separate tab to solve the CAPTCHA manually, establishing a valid session cookie that the proxy might be able to leverage (depending on the proxy's cookie handling, though this is limited in cross-origin contexts).30

## ---

**6\. Primary Data Source: MusicBrainz & AcoustID**

### **6.1 MusicBrainz: The Relational Database**

MusicBrainz (MB) offers the most structured and relational data of all sources. Its strength lies in the persistent unique identifiers (MBIDs) assigned to every entity (Artist, Release Group, Release, Recording, Work).32

#### **6.1.1 API Interaction**

MusicBrainz supports a robust JSON API (fmt=json). Crucially, it supports CORS, allowing direct requests from the browser.

* **Rate Limiting:** MB enforces a strict rate limit of **1 request per second** per IP. The MetaMuLate implementation *must* include a request queue manager to enforce this delay, otherwise, the client IP will be banned.33  
* **User-Agent:** A custom User-Agent header is mandatory. User-Agent: MetaMuLate/2.0 ( contact@example.com ).

#### **6.1.2 The Lookup Workflow**

Retrieving comprehensive metadata from MusicBrainz requires traversing its entity graph. A simple track search is insufficient because "Label" information is attached to "Releases," not "Recordings."

**Step 1: Recording Search**

Find the MBID for the track.

GET https://musicbrainz.org/ws/2/recording?query=recording:"{Title}" AND artist:"{Artist}"\&fmt=json

**Step 2: Hydration Lookup**

Once the Recording MBID is obtained, perform a lookup that includes all necessary relationships. The inc parameter is key here.

GET https://musicbrainz.org/ws/2/recording/{MBID}?inc=isrcs+releases+artist-credits+labels\&fmt=json

#### **6.1.3 JSON Path Extraction**

* **ISRC:** Located at the root of the recording object: recording.isrcs (an array of strings). This provides excellent validation against Apple Music's ISRC.34  
* **Labels and P-Line:** The scraper must iterate through the releases array nested within the recording response.  
  * Filter for releases where status is "Official".  
  * Access release.label-info. This array contains objects with label.name and catalog-number.  
  * The P-Line date is derived from release.date. The P-Line entity is the Label name.

### **6.2 AcoustID Integration**

AcoustID offers an alternative lookup method based on audio fingerprints (Chromaprint). While MetaMuLate is primarily text-search based, AcoustID can be integrated as a fallback verification layer if the user provides an audio file.

* **Client-Side Fingerprinting:** Using WebAssembly (WASM) ports of the Chromaprint library, the browser can calculate the fingerprint of a local file.  
* **Lookup:** GET https://api.acoustid.org/v2/lookup?client={Key}\&meta=recordings+releasegroups\&duration={sec}\&fingerprint={fp}.  
* **Bridge to MB:** The AcoustID response contains MusicBrainz Recording IDs, which can then be fed into the MusicBrainz scraper logic described above.35

## ---

**7\. Supplemental Sources: Lyrics & Cover Art**

### **7.1 Genius (Lyrics)**

Genius is the standard for lyrics.

* **API vs. Scraping:** The Genius API (api.genius.com) allows searching for songs but *does not* return the lyrics text, only the URL to the lyrics page.  
* **Implementation:**  
  1. Search API: GET /search?q={Query} (Requires Client Access Token).  
  2. Extract url from the response.  
  3. Fetch the URL via the CORS Proxy.  
  4. **DOM Parsing:** Use DOMParser to find the lyrics container. The class names are often obfuscated (e.g., Lyrics\_\_Container-sc-1ynbvzw-6), so the scraper should look for the data attribute data-lyrics-container="true" or fallback to generic text extraction within the main content div.37

### **7.2 Lyrics.ovh**

A completely open-source, free alternative that requires no authentication.

* **Endpoint:** https://api.lyrics.ovh/v1/{Artist}/{Title}.  
* **Response:** A simple JSON object {"lyrics": "..."}.  
* **Viability:** This should be the *first* attempt for lyrics due to its simplicity and lack of rate limits/tokens. If it fails (404), the system falls back to Genius.39

### **7.3 Cover Art Archive (CAA)**

CAA is hosted by the Internet Archive and linked via MusicBrainz.

* **Access:** Direct access via https://coverartarchive.org/release/{MBID}/front.  
* **Redirect Handling:** This URL returns a 307 Redirect to the actual image. The fetch API follows this automatically.  
* **CORS:** CAA supports CORS, allowing direct integration without a proxy.41

## ---

**8\. The Reconciliation Engine: Logic and Normalization**

### **8.1 The "Matrix of Truth"**

The core value proposition of MetaMuLate is the **Reconciliation Engine**. This logic layer sits between the raw API responses and the UI. Its purpose is to ingest disparate data structures and normalize them into a single comparison object.

**Data Normalization:**

Raw strings from APIs cannot be compared directly due to formatting differences ("The Beatles" vs "Beatles, The"). The engine must implement a normalize() function:

1. **Lowercasing:** Convert all inputs to lowercase.  
2. **Trimming:** Remove leading/trailing whitespace.  
3. **Punctuation Stripping:** Remove special characters (e.g., &, ., \-).  
4. **Article Removal:** Strip leading articles like "The".

### **8.2 Conflict Detection Algorithm**

For every data point (e.g., Label Name), the engine compares the normalized values from Apple, Discogs, and Amazon.

* **Uniform Status:** If normalize(Apple) \=== normalize(Discogs) \=== normalize(Amazon), the status is set to **UNIFORM**. The UI displays a Green Check and automatically selects the value for the Golden Record.  
* **Conflict Status:** If any value differs, the status is **CONFLICT**. The UI displays an Amber Warning. The system applies a "Priority Heuristic" to suggest a default (e.g., prefer Apple for ISRC, Discogs for Label), but the user must explicitly confirm or override via the matrix interface.

### **8.3 The Golden Record Schema**

The final internal object uses the schema defined in the design document:

JavaScript

{  
  "artist\_name": "String", // Consensus or Manual Selection  
  "track\_name": "String",  
  "track\_isrc": "String", // Priority: Apple  
  "release\_name": "String",  
  "p\_line\_year": "Number", // Extracted via Regex from P-Line text  
  "p\_line\_name": "String",  
  "country\_recording": "String", // ISO Code  
  "lyrics\_snippet": "String", // From Genius/Lyrics.ovh  
  "cover\_image\_url": "String",  
  "label\_name": "String" // Priority: Discogs  
}

## ---

**9\. Implementation Specification for metadata\_scrape\_03.html**

To transform the provided HTML file into the fully functional MetaMuLate v2.0, the following code injection strategy must be executed.

### **9.1 Global State and Configuration**

Inject a settings module to handle user tokens and proxy configuration.

JavaScript

const Config \= {  
    proxyUrl: localStorage.getItem('proxy\_url') |

| 'https://corsproxy.io/?url=',  
    tokens: {  
        apple: localStorage.getItem('apple\_token'),  
        discogs: localStorage.getItem('discogs\_token'),  
        genius: localStorage.getItem('genius\_token')  
    }  
};

### **9.2 Refactoring app.search()**

The existing app.search() function must be rewritten to handle parallel execution.

1. **Trigger:** On search, display loading state in Zone A.  
2. **Execution:** Use Promise.allSettled() to fire requests to AppleScraper.search(), DiscogsScraper.search(), and MusicBrainzScraper.search() simultaneously.  
3. **Aggregation:** As promises resolve, populate the trackList container. The UI should update incrementally (e.g., "Found 3 results from Apple... Found 5 from Discogs...").

### **9.3 Implementing Scraper Modules**

Inject dedicated objects for each source.

**Apple Scraper Logic:**

* Construct URL: https://api.music.apple.com/v1/catalog/us/search?term=${query}...  
* Header: Authorization: Bearer ${Config.tokens.apple}.  
* Parse results.songs.data for ISRC and P-line.

**Discogs Scraper Logic:**

* Construct URL: Config.proxyUrl \+ encodeURIComponent('https://api.discogs.com/database/search?q=${query}...')  
* Header: Authorization: Discogs token=${Config.tokens.discogs}.  
* **Crucial:** Manually inject/overwrite User-Agent if the proxy supports it, or rely on the proxy's default.

**MusicBrainz Scraper Logic:**

* Implement a Queue to ensure requests are spaced by 1.1 seconds.  
* Logic: Search Recording \-\> Get MBID \-\> Lookup Recording (with inc=isrcs+releases+labels).

### **9.4 Matrix Rendering Logic**

Target the div \#reconciliationMatrix.

* Clear existing content.  
* Generate HTML rows dynamically based on the normalized data.  
* **Event Listeners:** Attach click events to every cell (.matrix-cell). On click, update the GoldenRecord state variable for that field and visually highlight the selected cell (bg-blue-100).

### **9.5 Export Functionality**

Target the button \#btnExport.

* Override app.exportXLSX().  
* The function should read the GoldenRecord state (not the DOM).  
* Use XLSX.utils.json\_to\_sheet() to create the worksheet.  
* Use XLSX.writeFile to trigger the browser download.

## ---

**10\. Conclusion**

The transformation of the MetaMuLate HTML file into a powerful metadata aggregator is technically achievable within the constraints of a client-side architecture. The solution relies on a "Hybrid-Proxy" network model where a public or local CORS proxy bridges the gap between the browser and restrictive APIs like Discogs and Amazon. By leveraging the specific strengths of each repository—Apple for ISRCs, MusicBrainz for relationships, and Discogs for physical release details—and synthesizing them through a client-side Reconciliation Engine, the tool will provide high-value, validated metadata for non-technical users. The use of long-lived tokens and user-configurable settings ensures the application remains secure and functional without backend infrastructure.

## ---

**11\. Tables and Data Structures**

### **11.1 API Feature & Constraint Matrix**

| Source | Primary Data Value | Authentication | CORS Status | Scraping Strategy | Rate Limit |
| :---- | :---- | :---- | :---- | :---- | :---- |
| **Apple Music** | ISRC, P-Line, Artwork | Developer Token (JWT ES256) | Allowed (with Token) | Direct API Call | High |
| **Discogs** | Labels, Catalog \#, Credits | Personal Access Token | Restricted (User-Agent) | Proxy Required | 60/min |
| **MusicBrainz** | Relationships, Linked Entities | None (User-Agent req.) | Allowed | Direct API Call | 1 req/sec |
| **Amazon Music** | P-Line (Text), ASIN | Cookies (Internal API) | Restricted | DOM Parsing via Proxy | Aggressive |
| **Genius** | Lyrics Text | Access Token | Restricted | Proxy Required | Varies |
| **Lyrics.ovh** | Lyrics Text | None | Allowed | Direct API Call | Open |

### **11.2 Internal "Golden Record" Data Mapping**

| Internal Field | Apple Music Map | Discogs Map | MusicBrainz Map | Amazon Map |
| :---- | :---- | :---- | :---- | :---- |
| **Track Name** | attributes.name | title | title | title (DOM) |
| **Artist** | attributes.artistName | title (Split "Artist \- Title") | artist-credit.name | byArtist (JSON-LD) |
| **ISRC** | attributes.isrc | identifiers (Type: ISRC) | isrcs (Array) | N/A |
| **Release** | attributes.albumName | title (Release Lookup) | releases.title | name (JSON-LD) |
| **Label** | attributes.recordLabel | labels.name | releases.label-info.label.name | Brand (JSON-LD) |
| **P-Line** | attributes.copyright | companies (Type: P-Copyright) | *Derived from Release Date* | Copyright (DOM Text) |
| **Year** | releaseDate (YYYY) | year | first-release-date (YYYY) | ProductionDate |

---

*(End of Report)*

#### **Works cited**

1. MetaMuLate Technical Design \- V3  
2. metadata\_scrape\_03.html  
3. Using the Fetch API \- MDN Web Docs, accessed January 22, 2026, [https://developer.mozilla.org/en-US/docs/Web/API/Fetch\_API/Using\_Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)  
4. Solving CORS Error from the Client Side Using React JS | by Olaitan Akano | Medium, accessed January 22, 2026, [https://olaitanakano.medium.com/solving-cors-error-from-the-client-side-using-react-js-33d999e205ac](https://olaitanakano.medium.com/solving-cors-error-from-the-client-side-using-react-js-33d999e205ac)  
5. Cross-Origin Resource Sharing (CORS) \- HTTP \- MDN Web Docs, accessed January 22, 2026, [https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)  
6. Enable CORS in fetch api \[duplicate\] \- Stack Overflow, accessed January 22, 2026, [https://stackoverflow.com/questions/51017702/enable-cors-in-fetch-api](https://stackoverflow.com/questions/51017702/enable-cors-in-fetch-api)  
7. 10 Free to Use CORS Proxies \- Nordic APIs, accessed January 22, 2026, [https://nordicapis.com/10-free-to-use-cors-proxies/](https://nordicapis.com/10-free-to-use-cors-proxies/)  
8. The Proxy for Developers \- CorsProxy.io, accessed January 22, 2026, [https://corsproxy.io/home/](https://corsproxy.io/home/)  
9. What is the allOrigins bot & How to block it? \- DataDome, accessed January 22, 2026, [https://datadome.co/bots/allorigins/](https://datadome.co/bots/allorigins/)  
10. All Origins, accessed January 22, 2026, [https://allorigins.win/](https://allorigins.win/)  
11. List of free CORS proxies : r/webdev \- Reddit, accessed January 22, 2026, [https://www.reddit.com/r/webdev/comments/1ii43ns/list\_of\_free\_cors\_proxies/](https://www.reddit.com/r/webdev/comments/1ii43ns/list_of_free_cors_proxies/)  
12. Is there any alternative to CORS google chrome extension? How to make successful ajax request without using CORS? \- Stack Overflow, accessed January 22, 2026, [https://stackoverflow.com/questions/48940347/is-there-any-alternative-to-cors-google-chrome-extension-how-to-make-successful](https://stackoverflow.com/questions/48940347/is-there-any-alternative-to-cors-google-chrome-extension-how-to-make-successful)  
13. Setting default headers for all http requests is not working \- Stack Overflow, accessed January 22, 2026, [https://stackoverflow.com/questions/35827511/setting-default-headers-for-all-http-requests-is-not-working](https://stackoverflow.com/questions/35827511/setting-default-headers-for-all-http-requests-is-not-working)  
14. I am frustrated with CORS : r/webdev \- Reddit, accessed January 22, 2026, [https://www.reddit.com/r/webdev/comments/134io13/i\_am\_frustrated\_with\_cors/](https://www.reddit.com/r/webdev/comments/134io13/i_am_frustrated_with_cors/)  
15. Storing the Apple Music User Token with MusicKit JS | by Gavin Kasdorf | Medium, accessed January 22, 2026, [https://medium.com/@gavinkasdorf/apples-musickit-js-allows-you-to-access-an-apple-music-user-s-playlists-and-library-listen-to-32f77ff54d48](https://medium.com/@gavinkasdorf/apples-musickit-js-allows-you-to-access-an-apple-music-user-s-playlists-and-library-listen-to-32f77ff54d48)  
16. Do Apple Music User Tokens expire? \- Stack Overflow, accessed January 22, 2026, [https://stackoverflow.com/questions/62916634/do-apple-music-user-tokens-expire](https://stackoverflow.com/questions/62916634/do-apple-music-user-tokens-expire)  
17. Apple Music API | Apple Developer Documentation, accessed January 22, 2026, [https://developer.apple.com/documentation/applemusicapi/](https://developer.apple.com/documentation/applemusicapi/)  
18. Generating Developer Tokens | Apple Developer Documentation, accessed January 22, 2026, [https://developer.apple.com/documentation/applemusicapi/generating-developer-tokens](https://developer.apple.com/documentation/applemusicapi/generating-developer-tokens)  
19. How to create a JWT Token for Apple Music Kit · panva jose · Discussion \#158 \- GitHub, accessed January 22, 2026, [https://github.com/panva/jose/discussions/158](https://github.com/panva/jose/discussions/158)  
20. Accessing Music Content • MusicKit on the Web \- Apple, accessed January 22, 2026, [https://js-cdn.music.apple.com/musickit/v3/docs/index.html?path=/story/accessing-music-content--page](https://js-cdn.music.apple.com/musickit/v3/docs/index.html?path=/story/accessing-music-content--page)  
21. Web API Player V1.0 | Amazon Music Web API \- Amazon Developers, accessed January 22, 2026, [https://developer.amazon.com/docs/music/API\_web\_player.html](https://developer.amazon.com/docs/music/API_web_player.html)  
22. Home \- Discogs API Documentation, accessed January 22, 2026, [https://www.discogs.com/developers](https://www.discogs.com/developers)  
23. Discogs Javascript library \- Documentation \- Collections Database, accessed January 22, 2026, [https://collectionsdb.com/en/docs/discogs-lib](https://collectionsdb.com/en/docs/discogs-lib)  
24. CORS error on everything BUT /database/search?q \- Forum \- Discogs, accessed January 22, 2026, [https://www.discogs.com/forum/thread/869638?page=1](https://www.discogs.com/forum/thread/869638?page=1)  
25. Aurelia can't request API (CORS request & Access-Control-Allow-Headers) \- Discogs, accessed January 22, 2026, [https://www.discogs.com/forum/thread/739492](https://www.discogs.com/forum/thread/739492)  
26. Web API Overview V1.0 | Amazon Music Web API \- Amazon Developers, accessed January 22, 2026, [https://developer.amazon.com/docs/music/API\_web\_overview.html](https://developer.amazon.com/docs/music/API_web_overview.html)  
27. Music streaming services APIs \#1134 \- GitHub, accessed January 22, 2026, [https://github.com/librespot-org/librespot/discussions/1134](https://github.com/librespot-org/librespot/discussions/1134)  
28. JSON-LD Product Example Code, accessed January 22, 2026, [https://jsonld.com/product/](https://jsonld.com/product/)  
29. Product \- Schema.org Type, accessed January 22, 2026, [https://schema.org/Product](https://schema.org/Product)  
30. I Finally Found a Way to Scrape Amazon Without Going Insane | by Sherry Techdell, accessed January 22, 2026, [https://medium.com/@sherryordonell/i-finally-found-a-way-to-scrape-amazon-without-going-insane-7080b3b61775](https://medium.com/@sherryordonell/i-finally-found-a-way-to-scrape-amazon-without-going-insane-7080b3b61775)  
31. How to Scrape Data from Amazon: A Quick Guide \- DEV Community, accessed January 22, 2026, [https://dev.to/iconicdatascrap/how-to-scrape-data-from-amazon-a-quick-guide-jj7](https://dev.to/iconicdatascrap/how-to-scrape-data-from-amazon-a-quick-guide-jj7)  
32. MusicBrainz API, accessed January 22, 2026, [https://musicbrainz.org/doc/MusicBrainz\_API](https://musicbrainz.org/doc/MusicBrainz_API)  
33. HTTP 503 & other CORS errors trying to get some results, accessed January 22, 2026, [https://community.metabrainz.org/t/http-503-other-cors-errors-trying-to-get-some-results/741095](https://community.metabrainz.org/t/http-503-other-cors-errors-trying-to-get-some-results/741095)  
34. MusicBrainz API / Examples \- MusicBrainz, accessed January 22, 2026, [https://musicbrainz.org/doc/MusicBrainz\_API/Examples](https://musicbrainz.org/doc/MusicBrainz_API/Examples)  
35. acoustid \- NPM, accessed January 22, 2026, [https://www.npmjs.com/package/acoustid](https://www.npmjs.com/package/acoustid)  
36. Web Service \- AcoustID, accessed January 22, 2026, [https://acoustid.org/webservice](https://acoustid.org/webservice)  
37. Scraping song lyrics from Genius.com \- John W. Miller, accessed January 22, 2026, [https://www.johnwmillr.com/scraping-genius-lyrics/](https://www.johnwmillr.com/scraping-genius-lyrics/)  
38. Scraping Song Lyrics: A Fun and Practical Guide | by Rachit Soni \- Medium, accessed January 22, 2026, [https://medium.com/@rachit.lsoni/scraping-song-lyrics-a-fun-and-practical-guide-c0b07e8e7312](https://medium.com/@rachit.lsoni/scraping-song-lyrics-a-fun-and-practical-guide-c0b07e8e7312)  
39. Lyrics.ovh | Free API Explorer \- JuheAPI, accessed January 22, 2026, [https://www.juheapi.com/freeapis/lyrics-ovh](https://www.juheapi.com/freeapis/lyrics-ovh)  
40. lyrics.ovh: Only the lyrics, accessed January 22, 2026, [https://lyrics.ovh/](https://lyrics.ovh/)  
41. Cover Art Archive / API \- MusicBrainz, accessed January 22, 2026, [https://musicbrainz.org/doc/Cover\_Art\_Archive/API](https://musicbrainz.org/doc/Cover_Art_Archive/API)