MusicBrainz Server

repository·master·Indexed 21 days ago

https://github.com/metabrainz/musicbrainz-server

The web-facing application and API layer for the MusicBrainz open music metadata database. It provides a web frontend to the MusicBrainz Database, allowing access to a user-maintained community database of music metadata.

Tokens
24.6K
Snippets
71
Records
104
Agent score
77%

What's inside musicbrainz-server

  1. Overview of MusicBrainz Server

    master

    MusicBrainz Server is the web frontend to the MusicBrainz Database, providing access to a user-maintained open community database of music metadata. It is accessible publicly at http://musicbrainz.org.

    Key resources:

    Note on Breaking Changes: Changes to the database schema or the API/web service are announced on the MusicBrainz blog.

  2. Identify the licenses used in musicbrainz-server

    master

    The musicbrainz-server repository uses multiple licenses. Most of the source code is licensed under the GNU General Public License (GPL) Version 2 or later.

    A specific subset of files is licensed under the BSD 2-clause license:

    • root/static/lib/leaflet/
  3. Understand the MusicBrainz Server directory structure

    master

    The server codebase is organized into several key directories:

    • admin/: Scripts for server maintenance and administration.
    • lib/: Core library code. The -Ilib flag in plackup -Ilib adds this to @INC on startup.
    • DBDefs/: Server configuration.
    • MusicBrainz/Server/: The primary Perl server code.
    • Controller/: Catalyst actions.
    • Data/: Methods for database fetching. Results are typically converted to Entity classes in lib/MusicBrainz/Server/Entity/. Accessing a model via $c->model('Foo') refers to MusicBrainz::Server::Data::Foo.
    • Edit/: Code for the edit system, including Moose classes for different edit types.
    • Entity/: Moose classes for all entities.
    • Form/: HTML::FormHandler classes used for request validation.
    • root/: Template Toolkit templates (.tt files). Structure usually mirrors Catalyst action paths.
    • static/: Static website resources.
    • scripts/: Client-side JavaScript.
    • tests/: JavaScript unit tests.
    • t/: Server-side Perl tests.
    • styles/: CSS and Less files.
  4. Implement component-level state management with reducers

    master

    For large-scale applications with top-down data flow, avoid a single monolithic reducer. Instead, define separate reducers for each child component and encapsulate child actions within a parent action. This allows the parent to manage child state without needing to know every individual child action.

    Component File Structure

    Typically, a component file should define:

    • type ActionT: A union type of all actions the component can dispatch.
    • type StateT: A read-only type describing the component's state.
    • type PropsT: Usually includes dispatch ((ActionT) => void) and state (StateT).
    • function createInitialState(...): Initializes StateT.
    • function reducer(state: StateT, action: ActionT): Handles state transitions via a switch statement.

    State Immutability

    StateT must be deeply read-only. Use the spread operator for shallow copies or the mutate-cow library for complex, deeply-nested updates.

    Encapsulating Child Actions

    To call a child reducer from a parent, wrap the child's action in a parent action type (e.g., update-child).

    Example of parent-child reducer integration:

    import {
      type ActionT as ChildActionT,
      reducer as childReducer,
    } from './Child.js';
    
    type ActionT =
          | {readonly type: 'update-child', readonly action: ChildActionT}
          // ...
          ;
    
    function reducer(state: StateT, action: ActionT): StateT {
      match (action) {
        {type: 'update-child', const action} => {
          const childAction = action;
          state.child = childReducer(state.child, childAction);
        }
      }
    }
    
    function ParentComponent(props: PropsT) {
      const [state, dispatch] = React.useReducer(
        reducer,
        props,
        createInitialState,
      );
    
      const childDispatch = React.useCallback((action: ChildActionT) => {
        dispatch({type: 'update-child', action});
      }, [dispatch]);
    
      return <Child dispatch={childDispatch} />;
    }
    import {
      type ActionT as ChildActionT,
      reducer as childReducer,
    } from './Child.js';
    
    type ActionT =
          | {readonly type: 'update-child', readonly action: ChildActionT}
          // ...
          ;
    
    function reducer(state: StateT, action: ActionT): StateT {
      match (action) {
        {type: 'update-child', const action} => {
          const childAction = action;
          state.child = childReducer(state.child, childAction);
        }
      }
    }
    
    function ParentComponent(props: PropsT) {
      const [state, dispatch] = React.useReducer(
        reducer,
        props,
        createInitialState,
      );
    
      const childDispatch = React.useCallback((action: ChildActionT) => {
        dispatch({type: 'update-child', action});
      }, [dispatch]);
    
      return <Child dispatch={childDispatch} />;
    }
  5. Manage React state in complex applications

    master

    For simple components, use React.useState.

    For complex pages (apps) where state needs to be shared across deeply nested components (e.g., a relationship editor), use the Lifting State Up pattern:

    1. Implement a single reducer in the top-level component.
    2. Pass state downward via props.

    Performance Optimization: To prevent the entire page from re-rendering on every state change:

    • Split large components into smaller, specialized components.
    • Pass only the specific props a component needs (e.g., pass the result of a boolean check numItems > x instead of the raw numItems count).
  6. Run Perl unit tests

    master

    Most tests require a test database created via script/create_test_db.sh. Tests are located in the t/ directory and are best run using the prove program from Test::Harness.

    To run all tests:

    prove -l t/

    To run a specific controller test using a regular expression:

    prove -l t/tests.t :: --tests WS::2::LookupArtist

    To run multiple tests matching a pattern:

    prove -l t/tests.t :: --tests '(Data::URL|Entity::URL)'
    # Create the test database
    $ script/create_test_db.sh
    
    # Run all tests
    $ prove -l t/
    
    # Run a specific test via regex
    $ prove -l t/tests.t :: --tests WS::2::LookupArtist
  7. Prepare local database for schema testing

    master

    Before testing schema changes, you may need to initialize the local database with specific roles and a production schema database. Run the following commands to create the musicbrainz_prod_schema database and the required roles (musicbrainz, musicbrainz_ro, caa_redirect, and sir).

    psql -U postgres -d template1 -c 'CREATE DATABASE musicbrainz_prod_schema;'
    psql -U postgres -d template1 -c 'CREATE ROLE musicbrainz;'
    psql -U postgres -d template1 -c 'CREATE ROLE musicbrainz_ro;'
    psql -U postgres -d template1 -c 'CREATE ROLE caa_redirect;'
    psql -U postgres -d template1 -c 'CREATE ROLE sir;'
    psql -U postgres -d template1 -c 'GRANT CREATE ON DATABASE musicbrainz_prod_schema TO musicbrainz;'
  8. Run JavaScript unit tests

    master

    JavaScript tests use tape and must be compiled before running.

    1. Compile resources:
      script/compile_resources.sh tests
    2. Run in browser: Open http://localhost:5000/static/scripts/tests/web.html on your local development server.
    3. Run via CLI: If you have a (headless) Chrome instance installed, run:
      node t/web.js
    # Compile
    $ script/compile_resources.sh tests
    
    # Run via CLI (requires Chrome)
    $ node t/web.js
  9. Prerequisites for MusicBrainz Server installation

    master

    Before installing MusicBrainz Server, ensure your environment meets the following minimum requirements:

    • Operating System: Ubuntu/Debian (other Unix-like systems are supported at your own risk)
    • Node.js: version 24 or higher
    • Perl: version 5.42 or higher
    • PostgreSQL: version 18 or higher

    For full installation instructions, refer to the INSTALL.md file in the repository.

  10. Rebuild PostgreSQL indexes after collation version mismatch

    master

    If you upgrade your system's glibc or libicu versions, PostgreSQL may log warnings regarding collation version mismatches (e.g., WARNING: collation "XYZ" has version mismatch). To resolve this, you must rebuild all indexes affected by the changed collations.

    MusicBrainz provides a maintenance script to automate this process. The script connects to the MAINTENANCE database (as defined in lib/DBDefs.pm) and executes REINDEX statements for every index using the default or musicbrainz collations.

    By default, the script uses CONCURRENTLY to avoid disrupting existing database traffic. If you prefer to speed up the process and do not mind temporarily locking tables against writes, you can disable concurrent reindexing using the --noconcurrently flag.

    # Rebuild indexes concurrently (default, minimizes disruption)
    ./admin/RebuildIndexesUsingCollations.pl
    
    # Rebuild indexes without concurrency (faster, but locks tables against writes)
    ./admin/RebuildIndexesUsingCollations.pl --noconcurrently
  11. Install a master MusicBrainz server

    master

    Installing a master server is intended only for those producing replication packets or creating a complete fork of the MusicBrainz data. For most users, a mirror or standalone setup is preferred.

    To install a master server:

    1. Follow the main INSTALL.md guide, but do not run InitDb.pl immediately.
    2. Clone the dbmirror repository and build it using the provided makefile.
    3. Ensure your DBDefs.pm file includes RT_MASTER in the appropriate configuration section.
    4. Run InitDb (typically using a data dump). You must include the --with-pending flag and provide the path to the pending.so file generated during the dbmirror build process. This step installs the dbmirror extension and adds replication functions to the database.
    # Example command structure for InitDb
    ./InitDb.pl --with-pending /path/to/pending.so [other-options]