mirror of
https://github.com/BradNut/graphbrainz
synced 2025-09-08 17:40:32 +00:00
Add a schema extension API and several extensions (#42)
* Add a schema extension API and several extensions * Update graphql-markdown to use new diffSchema function * Update Node support
This commit is contained in:
parent
687ca43708
commit
898ec78a6f
253 changed files with 8341 additions and 1601 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -36,4 +36,5 @@ jspm_packages
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
|
.env
|
||||||
lib
|
lib
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
|
|
||||||
node_js:
|
node_js:
|
||||||
- "4"
|
|
||||||
- "5"
|
|
||||||
- "6"
|
- "6"
|
||||||
- "7"
|
- "7"
|
||||||
|
- "8"
|
||||||
|
|
||||||
# Use container-based Travis infrastructure.
|
# Use container-based Travis infrastructure.
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- FANART_API_KEY=d9e25d5beda1027a1674c1585882309e
|
||||||
|
- THEAUDIODB_API_KEY=1
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -16,9 +16,11 @@ npm install graphbrainz --save
|
||||||
**[Try out the live demo!][demo]** :bulb: Use the “Docs” sidebar, the
|
**[Try out the live demo!][demo]** :bulb: Use the “Docs” sidebar, the
|
||||||
[schema][], or the [types][] docs to help construct your query.
|
[schema][], or the [types][] docs to help construct your query.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
## Contents
|
|
||||||
|
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [As a standalone server](#as-a-standalone-server)
|
- [As a standalone server](#as-a-standalone-server)
|
||||||
|
|
@ -30,6 +32,7 @@ npm install graphbrainz --save
|
||||||
- [Pagination](#pagination)
|
- [Pagination](#pagination)
|
||||||
- [Questions](#questions)
|
- [Questions](#questions)
|
||||||
- [Schema](#schema)
|
- [Schema](#schema)
|
||||||
|
- [Extending the schema](#extending-the-schema)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
|
@ -101,14 +104,12 @@ GraphBrainz resolvers expect, like so:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { graphql } from 'graphql';
|
import { graphql } from 'graphql';
|
||||||
import { MusicBrainz, CoverArtArchive } from 'graphbrainz/api';
|
import { MusicBrainz, CoverArtArchive } from 'graphbrainz/lib/api';
|
||||||
import createLoaders from 'graphbrainz/loaders';
|
import createContext from 'graphbrainz/lib/context';
|
||||||
import schema from 'graphbrainz/schema';
|
import schema from 'graphbrainz/lib/schema';
|
||||||
|
|
||||||
const client = new MusicBrainz();
|
const client = new MusicBrainz();
|
||||||
const coverArtClient = new CoverArtArchive();
|
const context = createContext({ client })
|
||||||
const loaders = createLoaders(client, coverArtClient);
|
|
||||||
const context = { client, coverArtClient, loaders };
|
|
||||||
|
|
||||||
graphql(schema, `
|
graphql(schema, `
|
||||||
{
|
{
|
||||||
|
|
@ -142,6 +143,8 @@ graphql(schema, `
|
||||||
day).
|
day).
|
||||||
* **`GRAPHBRAINZ_GRAPHIQL`**: Set this to `true` if you want to force the
|
* **`GRAPHBRAINZ_GRAPHIQL`**: Set this to `true` if you want to force the
|
||||||
[GraphiQL][] interface to be available even in production mode.
|
[GraphiQL][] interface to be available even in production mode.
|
||||||
|
* **`GRAPHBRAINZ_EXTENSIONS`**: A JSON array of module paths to load as
|
||||||
|
[extensions](./docs/extensions).
|
||||||
* **`PORT`**: Port number to use, if running the standalone server.
|
* **`PORT`**: Port number to use, if running the standalone server.
|
||||||
|
|
||||||
When running the standalone server, [dotenv][] is used to load these variables
|
When running the standalone server, [dotenv][] is used to load these variables
|
||||||
|
|
@ -361,7 +364,14 @@ GraphBrainz to use that with no rate limiting.
|
||||||
|
|
||||||
## Schema
|
## Schema
|
||||||
|
|
||||||
See the [GraphQL schema][schema] or the [types][] documentation.
|
The [types][] document is the easiest to browse representation of the schema, or
|
||||||
|
you can read the [schema in GraphQL syntax][schema].
|
||||||
|
|
||||||
|
### Extending the schema
|
||||||
|
|
||||||
|
The GraphBrainz schema can easily be extended to add integrations with
|
||||||
|
third-party services. See the [Extensions](./docs/extensions) docs for more
|
||||||
|
info.
|
||||||
|
|
||||||
[demo]: https://graphbrainz.herokuapp.com/
|
[demo]: https://graphbrainz.herokuapp.com/
|
||||||
[Express]: http://expressjs.com/
|
[Express]: http://expressjs.com/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
machine:
|
machine:
|
||||||
node:
|
node:
|
||||||
version: 4.6.0
|
version: 6.0.0
|
||||||
|
|
||||||
|
|
|
||||||
227
docs/extensions/README.md
Normal file
227
docs/extensions/README.md
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
# Extensions
|
||||||
|
|
||||||
|
It is possible to extend the GraphBrainz schema to add integrations with
|
||||||
|
third-party services that provide more information about MusicBrainz entities.
|
||||||
|
Extensions can define new GraphQL types and use the `extend type` syntax to add
|
||||||
|
new fields to any existing GraphBrainz type, including the root query.
|
||||||
|
|
||||||
|
Several extensions are included by default, and you can install any number of
|
||||||
|
additional extensions from a package manager or [write your own](#extension-api).
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
|
||||||
|
|
||||||
|
- [Loading Extensions](#loading-extensions)
|
||||||
|
- [Built-in Extensions](#built-in-extensions)
|
||||||
|
- [Extension API](#extension-api)
|
||||||
|
- [Properties](#properties)
|
||||||
|
- [name](#name)
|
||||||
|
- [description](#description)
|
||||||
|
- [extendContext](#extendcontext)
|
||||||
|
- [extendSchema](#extendschema)
|
||||||
|
- [Example](#example)
|
||||||
|
- [Extension Guidelines](#extension-guidelines)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
## Loading Extensions
|
||||||
|
|
||||||
|
The extensions to load are specified using the `extensions` option to the
|
||||||
|
exported `graphbrainz()` middleware function. Each extension must be an object
|
||||||
|
conforming to the [Extension API](#extension-api), or the path to a module to
|
||||||
|
load via `require()` that exports such an object.
|
||||||
|
|
||||||
|
If you are running GraphBrainz as a standalone server, you may specify
|
||||||
|
extensions via the `GRAPHBRAINZ_EXTENSIONS` environment variable, which will be
|
||||||
|
parsed as a JSON array. For example:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ export GRAPHBRAINZ_EXTENSIONS='["graphbrainz/extensions/fanart-tv"]'
|
||||||
|
$ graphbrainz
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that some extensions may require additional configuration via extra options
|
||||||
|
or environment variables. Check the documentation for each extension you use.
|
||||||
|
|
||||||
|
The default value of the `extensions` option looks like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
[
|
||||||
|
'graphbrainz/extensions/cover-art-archive',
|
||||||
|
'graphbrainz/extensions/fanart-tv',
|
||||||
|
'graphbrainz/extensions/mediawiki',
|
||||||
|
'graphbrainz/extensions/the-audio-db'
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Built-in Extensions
|
||||||
|
|
||||||
|
The following extensions are included with GraphBrainz and loaded by default.
|
||||||
|
See their respective documentation pages for schema info and config options.
|
||||||
|
|
||||||
|
* [Cover Art Archive](./cover-art-archive.md): Retrieve cover art images for
|
||||||
|
releases from the Cover Art Archive.
|
||||||
|
* [fanart.tv](./fanart-tv.md): Retrieve high quality artwork for artists,
|
||||||
|
releases, and labels from fanart.tv.
|
||||||
|
* [MediaWiki](./mediawiki.md): Retrieve information from MediaWiki image pages,
|
||||||
|
like the actual image file URL and EXIF metadata.
|
||||||
|
* [TheAudioDB](./the-audio-db): Retrieve images and information about artists,
|
||||||
|
releases, and recordings from TheAudioDB.com.
|
||||||
|
|
||||||
|
## Extension API
|
||||||
|
|
||||||
|
The core idea behind extensions comes from the [schema stitching][] feature
|
||||||
|
from [graphql-tools][], although GraphBrainz does not currently use the exact
|
||||||
|
technique documented there. Instead, we call `parse` and `extendSchema` from
|
||||||
|
[GraphQL.js][], followed by [addResolveFunctionsToSchema][].
|
||||||
|
|
||||||
|
Extensions must export an object shaped like so:
|
||||||
|
|
||||||
|
```js
|
||||||
|
type Extension = {
|
||||||
|
name: string,
|
||||||
|
description?: string,
|
||||||
|
extendContext?: (context: Context, options: Options) => Context,
|
||||||
|
extendSchema?:
|
||||||
|
{ schemas: Array<string | DocumentNode>, resolvers: ResolverMap } |
|
||||||
|
(schema: GraphQLSchema, options: Options) => GraphQLSchema
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
#### name
|
||||||
|
|
||||||
|
The name of the extension.
|
||||||
|
|
||||||
|
#### description
|
||||||
|
|
||||||
|
A description of the functionality that the extension provides.
|
||||||
|
|
||||||
|
#### extendContext
|
||||||
|
|
||||||
|
An optional function that accepts a base context object (the `context` argument
|
||||||
|
available to resolver functions) and returns a new context object. Extensions
|
||||||
|
that access third-party APIs should add any API client instances they need here.
|
||||||
|
The recommended way is to create a loader with [dataloader][] and add it onto
|
||||||
|
`context.loaders`.
|
||||||
|
|
||||||
|
#### extendSchema
|
||||||
|
|
||||||
|
An optional object or function to extend the GraphBrainz schema.
|
||||||
|
|
||||||
|
If it is an object, it should have a `schemas` array and a `resolvers` object.
|
||||||
|
Each schema must be a string (containing type definitions in GraphQL schema
|
||||||
|
language) or a `DocumentNode` (if the type definitions have already been
|
||||||
|
parsed). The `resolvers` object should contain a mapping of type fields to new
|
||||||
|
resolver functions for those fields. See [addResolveFunctionsToSchema][].
|
||||||
|
|
||||||
|
If it is a function, it should accept `schema` and `options` arguments and
|
||||||
|
return a new schema. Use this if you’d like to perform custom schema extension
|
||||||
|
logic. This may be necessary if you already have a `GraphQLSchema` instance and
|
||||||
|
want to use [mergeSchemas][], for example. In most cases, you should keep it
|
||||||
|
simple and use the object form.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
name: 'Hello World',
|
||||||
|
description: 'A simple example extension.',
|
||||||
|
extendSchema: {
|
||||||
|
schemas: [`
|
||||||
|
extend type Query {
|
||||||
|
helloWorld: String!
|
||||||
|
}
|
||||||
|
`],
|
||||||
|
resolvers: {
|
||||||
|
Query: {
|
||||||
|
helloWorld: {
|
||||||
|
resolve: () => 'It worked!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will allow the following query to be made:
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
{
|
||||||
|
helloWorld
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the code for the [built-in extensions][] for more examples.
|
||||||
|
|
||||||
|
## Extension Guidelines
|
||||||
|
|
||||||
|
Extensions can load and resolve data in any manner they please, and you can
|
||||||
|
write them in any way that conforms to the API. But if you want an extra feather
|
||||||
|
in your cap, there are a few guidelines you should follow in order to maintain
|
||||||
|
consistency with GraphBrainz and the built-in extensions. Here are some tips
|
||||||
|
for writing a good extension:
|
||||||
|
|
||||||
|
* If you need to make HTTP requests, using a [Client][] subclass will get you
|
||||||
|
rate limiting, error handling, and a Promise-based API for free.
|
||||||
|
* Default to following the rate limiting rules of any APIs you wrap. If there
|
||||||
|
are no guidelines on rate limiting, consider playing nice anyway and limiting
|
||||||
|
your client to around 1 to 10 requests per second.
|
||||||
|
* Use a [DataLoader][dataloader] instance to batch and cache requests. Even if
|
||||||
|
the data source doesn’t support batching, it will dedupe in-flight requests
|
||||||
|
for the same key, preventing extra requests before the response has been
|
||||||
|
cached.
|
||||||
|
* Use a configurable cache and make sure you aren’t caching everything
|
||||||
|
indefinitely by accident. The `cacheMap` option to DataLoader is a good place
|
||||||
|
to put it.
|
||||||
|
* Get as much configuration from environment variables as possible, so that
|
||||||
|
people can just run the standalone server instead of writing server code. If
|
||||||
|
you need more complex configuration, use a single object on the `options`
|
||||||
|
object as a namespace for your extension’s options.
|
||||||
|
* Don’t be afraid to rename fields returned by third-party APIs when translating
|
||||||
|
them to the GraphQL schema. Consistency with GraphQL conventions and the
|
||||||
|
GraphBrainz schema is more desirable than consistency with the original API
|
||||||
|
being wrapped. Some general rules:
|
||||||
|
* Use camel case naming and capitalize acronyms (unless they are the only
|
||||||
|
word), e.g. `id`, `url`, `artistID`, `pageURL`.
|
||||||
|
* If it’s ambiguous whether a field refers to an object/list vs. a scalar
|
||||||
|
summary of an object/list, consider clarifying the field name, e.g. `user` →
|
||||||
|
`userID`, `members` → `memberCount`.
|
||||||
|
* Don’t include fields that are already available in MusicBrainz, only include
|
||||||
|
what’s relevant and useful.
|
||||||
|
* Add descriptions for everything: types, fields, arguments, enum values, etc.
|
||||||
|
– with Markdown links wherever they’d be helpful.
|
||||||
|
* When extending the built-in types, prefer adding a single object field that
|
||||||
|
serves as a namespace rather than adding many fields. That way it’s more
|
||||||
|
obvious that the data source isn’t MusicBrainz itself, and you’re less likely
|
||||||
|
to conflict with new MusicBrainz fields in the future.
|
||||||
|
* Prefer using a [Relay][]-compliant schema for lists of objects that (1) have
|
||||||
|
their own IDs and (2) are likely to be paginated. Feel free to add a `nodes`
|
||||||
|
shortcut field to the Connection type (for users who want to skip over
|
||||||
|
`edges`).
|
||||||
|
* If you publish your extension, consider prefixing the package name with
|
||||||
|
`graphbrainz-extension-` and having the default export of its `main` entry
|
||||||
|
point be the extension object. That way, using it is as simple as adding the
|
||||||
|
package name to the list of extensions.
|
||||||
|
* Consider using [graphql-markdown][] to document the schema created by your
|
||||||
|
extension; this will match how GraphBrainz itself is documented. You can use
|
||||||
|
the [diffSchema][] function to document only the schema updates, see
|
||||||
|
[scripts/build-extension-docs.js][build-extension-docs] for how this is done
|
||||||
|
with the built-in extensions.
|
||||||
|
|
||||||
|
[graphql-tools]: http://dev.apollodata.com/tools/graphql-tools/index.html
|
||||||
|
[schema stitching]: http://dev.apollodata.com/tools/graphql-tools/schema-stitching.html
|
||||||
|
[mergeSchemas]: http://dev.apollodata.com/tools/graphql-tools/schema-stitching.html#mergeSchemas
|
||||||
|
[dataloader]: https://github.com/facebook/dataloader
|
||||||
|
[built-in extensions]: ../../src/extensions
|
||||||
|
[Client]: ../../src/api/client.js
|
||||||
|
[graphql-markdown]: https://github.com/exogen/graphql-markdown
|
||||||
|
[diffSchema]: https://github.com/exogen/graphql-markdown#diffschemaoldschema-object-newschema-object-options-object
|
||||||
|
[build-extension-docs]: ../../scripts/build-extension-docs.js
|
||||||
|
[Relay]: https://facebook.github.io/relay/
|
||||||
|
[GraphQL.js]: http://graphql.org/graphql-js/
|
||||||
|
[addResolveFunctionsToSchema]: http://dev.apollodata.com/tools/graphql-tools/resolvers.html#addResolveFunctionsToSchema
|
||||||
378
docs/extensions/cover-art-archive.md
Normal file
378
docs/extensions/cover-art-archive.md
Normal file
|
|
@ -0,0 +1,378 @@
|
||||||
|
# Extension: Cover Art Archive
|
||||||
|
|
||||||
|
Retrieve cover art images for releases from the [Cover Art Archive](https://coverartarchive.org/).
|
||||||
|
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`COVER_ART_ARCHIVE_BASE_URL`**: The base URL at which to access the Cover
|
||||||
|
Art Archive API. Defaults to `http://coverartarchive.org/`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Table of Contents</strong></summary>
|
||||||
|
|
||||||
|
* [Objects](#objects)
|
||||||
|
* [CoverArtArchiveImage](#coverartarchiveimage)
|
||||||
|
* [CoverArtArchiveImageThumbnails](#coverartarchiveimagethumbnails)
|
||||||
|
* [CoverArtArchiveRelease](#coverartarchiverelease)
|
||||||
|
* [Release](#release)
|
||||||
|
* [ReleaseGroup](#releasegroup)
|
||||||
|
* [Enums](#enums)
|
||||||
|
* [CoverArtArchiveImageSize](#coverartarchiveimagesize)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### CoverArtArchiveImage
|
||||||
|
|
||||||
|
An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>fileID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The Internet Archive’s internal file ID for the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>image</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL at which the image can be found.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>thumbnails</strong></td>
|
||||||
|
<td valign="top"><a href="#coverartarchiveimagethumbnails">CoverArtArchiveImageThumbnails</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A set of thumbnails for the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>front</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#boolean">Boolean</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Whether this image depicts the “main front” of the release.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>back</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#boolean">Boolean</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Whether this image depicts the “main back” of the release.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>types</strong></td>
|
||||||
|
<td valign="top">[<a href="../types.md#string">String</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)
|
||||||
|
describing what part(s) of the release the image includes.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>edit</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The MusicBrainz edit ID.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>approved</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#boolean">Boolean</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Whether the image was approved by the MusicBrainz edit system.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>comment</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A free-text comment left for the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### CoverArtArchiveImageThumbnails
|
||||||
|
|
||||||
|
URLs for thumbnails of different sizes for a particular piece of
|
||||||
|
cover art.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>small</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of a small version of the cover art, where the maximum dimension is
|
||||||
|
250px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>large</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of a large version of the cover art, where the maximum dimension is
|
||||||
|
500px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### CoverArtArchiveRelease
|
||||||
|
|
||||||
|
An object containing a list of the cover art images for a release obtained
|
||||||
|
from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),
|
||||||
|
as well as a summary of what artwork is available.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>front</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of an image depicting the album cover or “main front” of the release,
|
||||||
|
i.e. the front of the packaging of the audio recording (or in the case of a
|
||||||
|
digital release, the image associated with it in a digital media store).
|
||||||
|
|
||||||
|
In the MusicBrainz schema, this field is a Boolean value indicating the
|
||||||
|
presence of a front image, whereas here the value is the URL for the image
|
||||||
|
itself if one exists. You can check for null if you just want to determine
|
||||||
|
the presence of an image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#coverartarchiveimagesize">CoverArtArchiveImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve. By default, the returned image will
|
||||||
|
have its full original dimensions, but certain thumbnail sizes may be
|
||||||
|
retrieved as well.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>back</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of an image depicting the “main back” of the release, i.e. the back
|
||||||
|
of the packaging of the audio recording.
|
||||||
|
|
||||||
|
In the MusicBrainz schema, this field is a Boolean value indicating the
|
||||||
|
presence of a back image, whereas here the value is the URL for the image
|
||||||
|
itself. You can check for null if you just want to determine the presence of
|
||||||
|
an image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#coverartarchiveimagesize">CoverArtArchiveImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve. By default, the returned image will
|
||||||
|
have its full original dimensions, but certain thumbnail sizes may be
|
||||||
|
retrieved as well.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>images</strong></td>
|
||||||
|
<td valign="top">[<a href="#coverartarchiveimage">CoverArtArchiveImage</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of images depicting the different sides and surfaces of a release’s
|
||||||
|
media and packaging.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>artwork</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#boolean">Boolean</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Whether there is artwork present for this release.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>count</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of artwork images present for this release.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>release</strong></td>
|
||||||
|
<td valign="top"><a href="#release">Release</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The particular release shown in the returned cover art.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Release
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>coverArtArchive</strong></td>
|
||||||
|
<td valign="top"><a href="#coverartarchiverelease">CoverArtArchiveRelease</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An object containing a list and summary of the cover art images that are
|
||||||
|
present for this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
This field is provided by the Cover Art Archive extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### ReleaseGroup
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>coverArtArchive</strong></td>
|
||||||
|
<td valign="top"><a href="#coverartarchiverelease">CoverArtArchiveRelease</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The cover art for a release in the release group, obtained from the
|
||||||
|
[Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). A
|
||||||
|
release in the release group will be chosen as representative of the release
|
||||||
|
group.
|
||||||
|
This field is provided by the Cover Art Archive extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
### CoverArtArchiveImageSize
|
||||||
|
|
||||||
|
The image sizes that may be requested at the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th align="left">Value</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>SMALL</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A maximum dimension of 250px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>LARGE</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A maximum dimension of 500px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>FULL</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The image’s original dimensions, with no maximum.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
473
docs/extensions/fanart-tv.md
Normal file
473
docs/extensions/fanart-tv.md
Normal file
|
|
@ -0,0 +1,473 @@
|
||||||
|
# Extension: fanart.tv
|
||||||
|
|
||||||
|
Retrieve high quality artwork for artists, releases, and labels from [fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`FANART_API_KEY`**: The fanart.tv API key to use. This is required for any
|
||||||
|
fields added by the extension to successfully resolve.
|
||||||
|
* **`FANART_BASE_URL`**: The base URL at which to access the
|
||||||
|
fanart.tv API. Defaults to `http://webservice.fanart.tv/v3/`.
|
||||||
|
* **`FANART_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`FANART_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Table of Contents</strong></summary>
|
||||||
|
|
||||||
|
* [Objects](#objects)
|
||||||
|
* [Artist](#artist)
|
||||||
|
* [FanArtAlbum](#fanartalbum)
|
||||||
|
* [FanArtArtist](#fanartartist)
|
||||||
|
* [FanArtDiscImage](#fanartdiscimage)
|
||||||
|
* [FanArtImage](#fanartimage)
|
||||||
|
* [FanArtLabel](#fanartlabel)
|
||||||
|
* [FanArtLabelImage](#fanartlabelimage)
|
||||||
|
* [Label](#label)
|
||||||
|
* [ReleaseGroup](#releasegroup)
|
||||||
|
* [Enums](#enums)
|
||||||
|
* [FanArtImageSize](#fanartimagesize)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### Artist
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>fanArt</strong></td>
|
||||||
|
<td valign="top"><a href="#fanartartist">FanArtArtist</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Images of the artist from [fanart.tv](https://fanart.tv/).
|
||||||
|
This field is provided by the fanart.tv extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtAlbum
|
||||||
|
|
||||||
|
An object containing lists of the different types of release group images from
|
||||||
|
[fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>albumCovers</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1000x1000 JPG images of the cover artwork of the release group.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>discImages</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartdiscimage">FanArtDiscImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1000x1000 PNG images of the physical disc media for the release
|
||||||
|
group, with transparent backgrounds.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtArtist
|
||||||
|
|
||||||
|
An object containing lists of the different types of artist images from
|
||||||
|
[fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>backgrounds</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1920x1080 JPG images picturing the artist, suitable for use as
|
||||||
|
backgrounds.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>banners</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1000x185 JPG images containing the artist and their logo or name.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>logos</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 400x155 PNG images containing the artist’s logo or name, with
|
||||||
|
transparent backgrounds.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>logosHD</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 800x310 PNG images containing the artist’s logo or name, with
|
||||||
|
transparent backgrounds.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>thumbnails</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartimage">FanArtImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1000x1000 JPG thumbnail images picturing the artist (usually
|
||||||
|
containing every member of a band).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtDiscImage
|
||||||
|
|
||||||
|
A disc image from [fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>imageID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The ID of the image on fanart.tv.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>url</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#fanartimagesize">FanArtImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>likeCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of likes the image has received by fanart.tv users.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>discNumber</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The disc number.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>size</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The width and height of the (square) disc image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtImage
|
||||||
|
|
||||||
|
A single image from [fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>imageID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The ID of the image on fanart.tv.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>url</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#fanartimagesize">FanArtImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>likeCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of likes the image has received by fanart.tv users.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtLabel
|
||||||
|
|
||||||
|
An object containing lists of the different types of label images from
|
||||||
|
[fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>logos</strong></td>
|
||||||
|
<td valign="top">[<a href="#fanartlabelimage">FanArtLabelImage</a>]</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 400x270 PNG images containing the label’s logo. There will
|
||||||
|
usually be a black version, a color version, and a white version, all with
|
||||||
|
transparent backgrounds.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### FanArtLabelImage
|
||||||
|
|
||||||
|
A music label image from [fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>imageID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The ID of the image on fanart.tv.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>url</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#fanartimagesize">FanArtImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>likeCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of likes the image has received by fanart.tv users.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>color</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The type of color content in the image (usually “white” or “colour”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Label
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>fanArt</strong></td>
|
||||||
|
<td valign="top"><a href="#fanartlabel">FanArtLabel</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Images of the label from [fanart.tv](https://fanart.tv/).
|
||||||
|
This field is provided by the fanart.tv extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### ReleaseGroup
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>fanArt</strong></td>
|
||||||
|
<td valign="top"><a href="#fanartalbum">FanArtAlbum</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Images of the release group from [fanart.tv](https://fanart.tv/).
|
||||||
|
This field is provided by the fanart.tv extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
### FanArtImageSize
|
||||||
|
|
||||||
|
The image sizes that may be requested at [fanart.tv](https://fanart.tv/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th align="left">Value</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>FULL</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The image’s full original dimensions.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>PREVIEW</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A maximum dimension of 200px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
404
docs/extensions/mediawiki.md
Normal file
404
docs/extensions/mediawiki.md
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
# Extension: MediaWiki
|
||||||
|
|
||||||
|
Retrieve information from MediaWiki image pages, like the actual image file URL and EXIF metadata.
|
||||||
|
|
||||||
|
On entities with [URL relationship types][relationships] that represent images,
|
||||||
|
this extension will find those URLs that appear to be MediaWiki image pages, and
|
||||||
|
use the [MediaWiki API][] to fetch information about the image. This information
|
||||||
|
will include the actual file URL, so you can use it as the `src` in an `<img>`
|
||||||
|
tag (for example).
|
||||||
|
|
||||||
|
MediaWiki image URLs are assumed to be those with a path that starts with
|
||||||
|
`/wiki/Image:` or `/wiki/File:`.
|
||||||
|
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`COVER_ART_ARCHIVE_BASE_URL`**: The base URL at which to access to Cover Art
|
||||||
|
Archive API. Defaults to `http://coverartarchive.org/`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
|
|
||||||
|
[relationships]: https://musicbrainz.org/relationships
|
||||||
|
[MediaWiki API]: https://www.mediawiki.org/wiki/API:Main_page
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Table of Contents</strong></summary>
|
||||||
|
|
||||||
|
* [Objects](#objects)
|
||||||
|
* [Artist](#artist)
|
||||||
|
* [Instrument](#instrument)
|
||||||
|
* [Label](#label)
|
||||||
|
* [MediaWikiImage](#mediawikiimage)
|
||||||
|
* [MediaWikiImageMetadata](#mediawikiimagemetadata)
|
||||||
|
* [Place](#place)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### Artist
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mediaWikiImages</strong></td>
|
||||||
|
<td valign="top">[<a href="#mediawikiimage">MediaWikiImage</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Artist images found at MediaWiki URLs in the artist’s URL relationships.
|
||||||
|
Defaults to URL relationships with the type “image”.
|
||||||
|
This field is provided by the MediaWiki extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">type</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The type of URL relationship that will be selected to find images. See
|
||||||
|
the possible [Artist-URL relationship types](https://musicbrainz.org/relationships/artist-url).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Instrument
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mediaWikiImages</strong></td>
|
||||||
|
<td valign="top">[<a href="#mediawikiimage">MediaWikiImage</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Instrument images found at MediaWiki URLs in the instrument’s URL
|
||||||
|
relationships. Defaults to URL relationships with the type “image”.
|
||||||
|
This field is provided by the MediaWiki extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">type</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The type of URL relationship that will be selected to find images. See the
|
||||||
|
possible [Instrument-URL relationship types](https://musicbrainz.org/relationships/instrument-url).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Label
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mediaWikiImages</strong></td>
|
||||||
|
<td valign="top">[<a href="#mediawikiimage">MediaWikiImage</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Label images found at MediaWiki URLs in the label’s URL relationships.
|
||||||
|
Defaults to URL relationships with the type “logo”.
|
||||||
|
This field is provided by the MediaWiki extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">type</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The type of URL relationship that will be selected to find images. See the
|
||||||
|
possible [Label-URL relationship types](https://musicbrainz.org/relationships/label-url).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### MediaWikiImage
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>url</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of the actual image file.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>descriptionURL</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL of the wiki page describing the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>user</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The user who uploaded the file.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>size</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the file in bytes.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>width</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The pixel width of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>height</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The pixel height of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>canonicalTitle</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The canonical title of the file.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>objectName</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The image title, brief description, or file name.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>descriptionHTML</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A description of the image, potentially containing HTML.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>originalDateTimeHTML</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The original date of creation of the image. May be a description rather than
|
||||||
|
a parseable timestamp, and may contain HTML.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>categories</strong></td>
|
||||||
|
<td valign="top">[<a href="../types.md#string">String</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of the categories of the image.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>artistHTML</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The name of the image author, potentially containing HTML.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>creditHTML</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The source of the image, potentially containing HTML.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>licenseShortName</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A short human-readable license name.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>licenseURL</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A web address where the license is described.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>metadata</strong></td>
|
||||||
|
<td valign="top">[<a href="#mediawikiimagemetadata">MediaWikiImageMetadata</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The full list of values in the `extmetadata` field.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### MediaWikiImageMetadata
|
||||||
|
|
||||||
|
An entry in the `extmetadata` field of a MediaWiki image file.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>name</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a>!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The name of the metadata field.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>value</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The value of the metadata field. All values will be converted to strings.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>source</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The source of the value.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Place
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mediaWikiImages</strong></td>
|
||||||
|
<td valign="top">[<a href="#mediawikiimage">MediaWikiImage</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Place images found at MediaWiki URLs in the place’s URL relationships.
|
||||||
|
Defaults to URL relationships with the type “image”.
|
||||||
|
This field is provided by the MediaWiki extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">type</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The type of URL relationship that will be selected to find images. See the
|
||||||
|
possible [Place-URL relationship types](https://musicbrainz.org/relationships/place-url).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
766
docs/extensions/the-audio-db.md
Normal file
766
docs/extensions/the-audio-db.md
Normal file
|
|
@ -0,0 +1,766 @@
|
||||||
|
# Extension: TheAudioDB
|
||||||
|
|
||||||
|
Retrieve images and information about artists, releases, and recordings from [TheAudioDB.com](http://www.theaudiodb.com/).
|
||||||
|
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`THEAUDIODB_API_KEY`**: TheAudioDB API key to use. This is required for any
|
||||||
|
fields added by the extension to successfully resolve.
|
||||||
|
* **`THEAUDIODB_BASE_URL`**: The base URL at which to access TheAudioDB API.
|
||||||
|
Defaults to `http://www.theaudiodb.com/api/v1/json/`.
|
||||||
|
* **`THEAUDIODB_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`THEAUDIODB_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Table of Contents</strong></summary>
|
||||||
|
|
||||||
|
* [Objects](#objects)
|
||||||
|
* [Artist](#artist)
|
||||||
|
* [Recording](#recording)
|
||||||
|
* [ReleaseGroup](#releasegroup)
|
||||||
|
* [TheAudioDBAlbum](#theaudiodbalbum)
|
||||||
|
* [TheAudioDBArtist](#theaudiodbartist)
|
||||||
|
* [TheAudioDBMusicVideo](#theaudiodbmusicvideo)
|
||||||
|
* [TheAudioDBTrack](#theaudiodbtrack)
|
||||||
|
* [Enums](#enums)
|
||||||
|
* [TheAudioDBImageSize](#theaudiodbimagesize)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### Artist
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>theAudioDB</strong></td>
|
||||||
|
<td valign="top"><a href="#theaudiodbartist">TheAudioDBArtist</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Data about the artist from [TheAudioDB](http://www.theaudiodb.com/), a good
|
||||||
|
source of biographical information and images.
|
||||||
|
This field is provided by TheAudioDB extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Recording
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>theAudioDB</strong></td>
|
||||||
|
<td valign="top"><a href="#theaudiodbtrack">TheAudioDBTrack</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Data about the recording from [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
This field is provided by TheAudioDB extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### ReleaseGroup
|
||||||
|
|
||||||
|
:small_blue_diamond: *This type has been extended. See the [base schema](../types.md)
|
||||||
|
for a description and additional fields.*
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>theAudioDB</strong></td>
|
||||||
|
<td valign="top"><a href="#theaudiodbalbum">TheAudioDBAlbum</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Data about the release group from [TheAudioDB](http://www.theaudiodb.com/),
|
||||||
|
a good source of descriptive information, reviews, and images.
|
||||||
|
This field is provided by TheAudioDB extension.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### TheAudioDBAlbum
|
||||||
|
|
||||||
|
An album on [TheAudioDB](http://www.theaudiodb.com/) corresponding with a
|
||||||
|
MusicBrainz Release Group.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>albumID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the album.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>artistID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the artist who released the album.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>description</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A description of the album, often available in several languages.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">lang</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The two-letter code for the language in which to retrieve the biography.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>review</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A review of the album.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>salesCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The worldwide sales figure.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>score</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#float">Float</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The album’s rating as determined by user votes, out of 10.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>scoreVotes</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of users who voted to determine the album’s score.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>discImage</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An image of the physical disc media for the album.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>spineImage</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An image of the spine of the album packaging.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>frontImage</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An image of the front of the album packaging.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>backImage</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An image of the back of the album packaging.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>genre</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical genre of the album (e.g. “Alternative Rock”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mood</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical mood of the album (e.g. “Sad”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>style</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical style of the album (e.g. “Rock/Pop”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>speed</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A rough description of the primary musical speed of the album (e.g. “Medium”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>theme</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical theme of the album (e.g. “In Love”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### TheAudioDBArtist
|
||||||
|
|
||||||
|
An artist on [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>artistID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the artist.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>biography</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A biography of the artist, often available in several languages.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">lang</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The two-letter code for the language in which to retrieve the biography.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>memberCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of members in the musical group, if applicable.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>banner</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A 1000x185 JPG banner image containing the artist and their logo or name.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>fanArt</strong></td>
|
||||||
|
<td valign="top">[<a href="../types.md#urlstring">URLString</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of 1280x720 or 1920x1080 JPG images depicting the artist.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the images to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>logo</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A 400x155 PNG image containing the artist’s logo or name, with a transparent
|
||||||
|
background.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>thumbnail</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A 1000x1000 JPG thumbnail image picturing the artist (usually containing
|
||||||
|
every member of a band).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>genre</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical genre of the artist (e.g. “Alternative Rock”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mood</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical mood of the artist (e.g. “Sad”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>style</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical style of the artist (e.g. “Rock/Pop”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### TheAudioDBMusicVideo
|
||||||
|
|
||||||
|
Details of a music video associated with a track on [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>url</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The URL where the music video can be found.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>companyName</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The video production company of the music video.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>directorName</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The director of the music video.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>screenshots</strong></td>
|
||||||
|
<td valign="top">[<a href="../types.md#urlstring">URLString</a>]!</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A list of still images from the music video.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the images to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>viewCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of views the video has received at the given URL. This will rarely
|
||||||
|
be up to date, so use cautiously.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>likeCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of likes the video has received at the given URL. This will rarely
|
||||||
|
be up to date, so use cautiously.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>dislikeCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of dislikes the video has received at the given URL. This will
|
||||||
|
rarely be up to date, so use cautiously.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>commentCount</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of comments the video has received at the given URL. This will
|
||||||
|
rarely be up to date, so use cautiously.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### TheAudioDBTrack
|
||||||
|
|
||||||
|
A track on [TheAudioDB](http://www.theaudiodb.com/) corresponding with a
|
||||||
|
MusicBrainz Recording.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left">Field</th>
|
||||||
|
<th align="right">Argument</th>
|
||||||
|
<th align="left">Type</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>trackID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the track.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>albumID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the album on which the track appears.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>artistID</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#id">ID</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
TheAudioDB ID of the artist who released the track.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>description</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A description of the track.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">lang</td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>thumbnail</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#urlstring">URLString</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A thumbnail image for the track.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" align="right" valign="top">size</td>
|
||||||
|
<td valign="top"><a href="#theaudiodbimagesize">TheAudioDBImageSize</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The size of the image to retrieve.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>score</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#float">Float</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The track’s rating as determined by user votes, out of 10.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>scoreVotes</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The number of users who voted to determine the album’s score.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>trackNumber</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#int">Int</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The track number of the song on the album.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>musicVideo</strong></td>
|
||||||
|
<td valign="top"><a href="#theaudiodbmusicvideo">TheAudioDBMusicVideo</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The official music video for the track.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>genre</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical genre of the track (e.g. “Alternative Rock”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>mood</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical mood of the track (e.g. “Sad”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>style</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical style of the track (e.g. “Rock/Pop”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" valign="top"><strong>theme</strong></td>
|
||||||
|
<td valign="top"><a href="../types.md#string">String</a></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The primary musical theme of the track (e.g. “In Love”).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
### TheAudioDBImageSize
|
||||||
|
|
||||||
|
The image sizes that may be requested at [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th align="left">Value</th>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>FULL</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The image’s full original dimensions.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="top"><strong>PREVIEW</strong></td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
A maximum dimension of 200px.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
143
docs/schema.md
143
docs/schema.md
|
|
@ -596,62 +596,6 @@ type Coordinates {
|
||||||
longitude: Degrees
|
longitude: Degrees
|
||||||
}
|
}
|
||||||
|
|
||||||
# An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
type CoverArtImage {
|
|
||||||
# The Internet Archive’s internal file ID for the image.
|
|
||||||
fileID: String!
|
|
||||||
|
|
||||||
# The URL at which the image can be found.
|
|
||||||
image: URLString!
|
|
||||||
|
|
||||||
# A set of thumbnails for the image.
|
|
||||||
thumbnails: CoverArtImageThumbnails
|
|
||||||
|
|
||||||
# Whether this image depicts the “main front” of the release.
|
|
||||||
front: Boolean!
|
|
||||||
|
|
||||||
# Whether this image depicts the “main back” of the release.
|
|
||||||
back: Boolean!
|
|
||||||
|
|
||||||
# A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)
|
|
||||||
# describing what part(s) of the release the image includes.
|
|
||||||
types: [String]
|
|
||||||
|
|
||||||
# The MusicBrainz edit ID.
|
|
||||||
edit: Int
|
|
||||||
|
|
||||||
# Whether the image was approved by the MusicBrainz edit system.
|
|
||||||
approved: Boolean
|
|
||||||
|
|
||||||
# A free-text comment left for the image.
|
|
||||||
comment: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# The image sizes that may be requested at the [Cover Art
|
|
||||||
# Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
enum CoverArtImageSize {
|
|
||||||
# A maximum dimension of 250px.
|
|
||||||
SMALL
|
|
||||||
|
|
||||||
# A maximum dimension of 500px.
|
|
||||||
LARGE
|
|
||||||
|
|
||||||
# The image’s original dimensions, with no maximum.
|
|
||||||
FULL
|
|
||||||
}
|
|
||||||
|
|
||||||
# URLs for thumbnails of different sizes for a particular piece of
|
|
||||||
# cover art.
|
|
||||||
type CoverArtImageThumbnails {
|
|
||||||
# The URL of a small version of the cover art, where the
|
|
||||||
# maximum dimension is 250px.
|
|
||||||
small: URLString
|
|
||||||
|
|
||||||
# The URL of a large version of the cover art, where the
|
|
||||||
# maximum dimension is 500px.
|
|
||||||
large: URLString
|
|
||||||
}
|
|
||||||
|
|
||||||
# Year, month (optional), and day (optional) in YYYY-MM-DD format.
|
# Year, month (optional), and day (optional) in YYYY-MM-DD format.
|
||||||
scalar Date
|
scalar Date
|
||||||
|
|
||||||
|
|
@ -1703,10 +1647,6 @@ type Release implements Node, Entity {
|
||||||
# [EANs](https://en.wikipedia.org/wiki/International_Article_Number).
|
# [EANs](https://en.wikipedia.org/wiki/International_Article_Number).
|
||||||
barcode: String
|
barcode: String
|
||||||
|
|
||||||
# A list and summary of the cover art images that are present
|
|
||||||
# for this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
coverArt: ReleaseCoverArt!
|
|
||||||
|
|
||||||
# The status describes how “official” a release is.
|
# The status describes how “official” a release is.
|
||||||
status: ReleaseStatus
|
status: ReleaseStatus
|
||||||
|
|
||||||
|
|
@ -1775,57 +1715,6 @@ type ReleaseConnection {
|
||||||
totalCount: Int
|
totalCount: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
# An object containing a list of the cover art images for a
|
|
||||||
# release obtained from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),
|
|
||||||
# as well as a summary of what artwork is available.
|
|
||||||
type ReleaseCoverArt {
|
|
||||||
# The URL of an image depicting the album cover or “main
|
|
||||||
# front” of the release, i.e. the front of the packaging of the audio recording
|
|
||||||
# (or in the case of a digital release, the image associated with it in a digital
|
|
||||||
# media store).
|
|
||||||
#
|
|
||||||
# In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
# of a front image, whereas here the value is the URL for the image itself if one
|
|
||||||
# exists. You can check for null if you just want to determine the presence of an
|
|
||||||
# image.
|
|
||||||
front(
|
|
||||||
# The size of the image to retrieve. By default, the returned
|
|
||||||
# image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
# retrieved as well.
|
|
||||||
size: CoverArtImageSize = null
|
|
||||||
): URLString
|
|
||||||
|
|
||||||
# The URL of an image depicting the “main back” of the
|
|
||||||
# release, i.e. the back of the packaging of the audio recording.
|
|
||||||
#
|
|
||||||
# In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
# of a back image, whereas here the value is the URL for the image itself. You can
|
|
||||||
# check for null if you just want to determine the presence of an image.
|
|
||||||
back(
|
|
||||||
# The size of the image to retrieve. By default, the returned
|
|
||||||
# image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
# retrieved as well.
|
|
||||||
size: CoverArtImageSize = null
|
|
||||||
): URLString
|
|
||||||
|
|
||||||
# A list of images depicting the different sides and surfaces
|
|
||||||
# of a release’s media and packaging.
|
|
||||||
images: [CoverArtImage]
|
|
||||||
|
|
||||||
# Whether there is artwork present for this release.
|
|
||||||
artwork: Boolean!
|
|
||||||
|
|
||||||
# Whether the Cover Art Archive has received a take-down
|
|
||||||
# request for this release’s artwork, disallowing new uploads.
|
|
||||||
darkened: Boolean!
|
|
||||||
|
|
||||||
# The number of artwork images present for this release.
|
|
||||||
count: Int!
|
|
||||||
|
|
||||||
# The particular release shown in the returned cover art.
|
|
||||||
release: Release!
|
|
||||||
}
|
|
||||||
|
|
||||||
# An edge in a connection.
|
# An edge in a connection.
|
||||||
type ReleaseEdge {
|
type ReleaseEdge {
|
||||||
# The item at the end of the edge
|
# The item at the end of the edge
|
||||||
|
|
@ -1898,10 +1787,6 @@ type ReleaseGroup implements Node, Entity {
|
||||||
# field.
|
# field.
|
||||||
secondaryTypeIDs: [MBID]
|
secondaryTypeIDs: [MBID]
|
||||||
|
|
||||||
# The cover art for a release group, obtained from the [Cover
|
|
||||||
# Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
coverArt: ReleaseGroupCoverArt
|
|
||||||
|
|
||||||
# A list of artists linked to this entity.
|
# A list of artists linked to this entity.
|
||||||
artists(after: String, first: Int): ArtistConnection
|
artists(after: String, first: Int): ArtistConnection
|
||||||
|
|
||||||
|
|
@ -1946,34 +1831,6 @@ type ReleaseGroupConnection {
|
||||||
totalCount: Int
|
totalCount: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
# An object containing the cover art for a release group obtained
|
|
||||||
# from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). For
|
|
||||||
# release groups, just the front cover of a particular release will be selected.
|
|
||||||
type ReleaseGroupCoverArt {
|
|
||||||
# The URL of an image depicting the album cover or “main
|
|
||||||
# front” of a release in the release group, i.e. the front of the packaging of the
|
|
||||||
# audio recording (or in the case of a digital release, the image associated with
|
|
||||||
# it in a digital media store).
|
|
||||||
front(
|
|
||||||
# The size of the image to retrieve. By default, the returned
|
|
||||||
# image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
# retrieved as well.
|
|
||||||
size: CoverArtImageSize = null
|
|
||||||
): URLString
|
|
||||||
|
|
||||||
# A list of images returned by the [Cover Art
|
|
||||||
# Archive](https://musicbrainz.org/doc/Cover_Art_Archive) for a release group. A
|
|
||||||
# particular release’s front image will be included in the list, and likely no
|
|
||||||
# others, even if other images are available.
|
|
||||||
images: [CoverArtImage]
|
|
||||||
|
|
||||||
# Whether there is artwork present for this release group.
|
|
||||||
artwork: Boolean!
|
|
||||||
|
|
||||||
# The particular release shown in the returned cover art.
|
|
||||||
release: Release!
|
|
||||||
}
|
|
||||||
|
|
||||||
# An edge in a connection.
|
# An edge in a connection.
|
||||||
type ReleaseGroupEdge {
|
type ReleaseGroupEdge {
|
||||||
# The item at the end of the edge
|
# The item at the end of the edge
|
||||||
|
|
|
||||||
391
docs/types.md
391
docs/types.md
|
|
@ -20,8 +20,6 @@ You may also be interested in reading the [schema in GraphQL syntax](schema.md).
|
||||||
* [CollectionConnection](#collectionconnection)
|
* [CollectionConnection](#collectionconnection)
|
||||||
* [CollectionEdge](#collectionedge)
|
* [CollectionEdge](#collectionedge)
|
||||||
* [Coordinates](#coordinates)
|
* [Coordinates](#coordinates)
|
||||||
* [CoverArtImage](#coverartimage)
|
|
||||||
* [CoverArtImageThumbnails](#coverartimagethumbnails)
|
|
||||||
* [Disc](#disc)
|
* [Disc](#disc)
|
||||||
* [Event](#event)
|
* [Event](#event)
|
||||||
* [EventConnection](#eventconnection)
|
* [EventConnection](#eventconnection)
|
||||||
|
|
@ -49,12 +47,10 @@ You may also be interested in reading the [schema in GraphQL syntax](schema.md).
|
||||||
* [Relationships](#relationships)
|
* [Relationships](#relationships)
|
||||||
* [Release](#release)
|
* [Release](#release)
|
||||||
* [ReleaseConnection](#releaseconnection)
|
* [ReleaseConnection](#releaseconnection)
|
||||||
* [ReleaseCoverArt](#releasecoverart)
|
|
||||||
* [ReleaseEdge](#releaseedge)
|
* [ReleaseEdge](#releaseedge)
|
||||||
* [ReleaseEvent](#releaseevent)
|
* [ReleaseEvent](#releaseevent)
|
||||||
* [ReleaseGroup](#releasegroup)
|
* [ReleaseGroup](#releasegroup)
|
||||||
* [ReleaseGroupConnection](#releasegroupconnection)
|
* [ReleaseGroupConnection](#releasegroupconnection)
|
||||||
* [ReleaseGroupCoverArt](#releasegroupcoverart)
|
|
||||||
* [ReleaseGroupEdge](#releasegroupedge)
|
* [ReleaseGroupEdge](#releasegroupedge)
|
||||||
* [SearchQuery](#searchquery)
|
* [SearchQuery](#searchquery)
|
||||||
* [Series](#series)
|
* [Series](#series)
|
||||||
|
|
@ -68,7 +64,6 @@ You may also be interested in reading the [schema in GraphQL syntax](schema.md).
|
||||||
* [WorkConnection](#workconnection)
|
* [WorkConnection](#workconnection)
|
||||||
* [WorkEdge](#workedge)
|
* [WorkEdge](#workedge)
|
||||||
* [Enums](#enums)
|
* [Enums](#enums)
|
||||||
* [CoverArtImageSize](#coverartimagesize)
|
|
||||||
* [ReleaseGroupType](#releasegrouptype)
|
* [ReleaseGroupType](#releasegrouptype)
|
||||||
* [ReleaseStatus](#releasestatus)
|
* [ReleaseStatus](#releasestatus)
|
||||||
* [Scalars](#scalars)
|
* [Scalars](#scalars)
|
||||||
|
|
@ -2178,143 +2173,6 @@ The east–west position of a point on the Earth’s surface.
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### CoverArtImage
|
|
||||||
|
|
||||||
An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th align="left">Field</th>
|
|
||||||
<th align="right">Argument</th>
|
|
||||||
<th align="left">Type</th>
|
|
||||||
<th align="left">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>fileID</strong></td>
|
|
||||||
<td valign="top"><a href="#string">String</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The Internet Archive’s internal file ID for the image.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>image</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL at which the image can be found.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>thumbnails</strong></td>
|
|
||||||
<td valign="top"><a href="#coverartimagethumbnails">CoverArtImageThumbnails</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A set of thumbnails for the image.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>front</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether this image depicts the “main front” of the release.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>back</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether this image depicts the “main back” of the release.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>types</strong></td>
|
|
||||||
<td valign="top">[<a href="#string">String</a>]</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)
|
|
||||||
describing what part(s) of the release the image includes.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>edit</strong></td>
|
|
||||||
<td valign="top"><a href="#int">Int</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The MusicBrainz edit ID.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>approved</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether the image was approved by the MusicBrainz edit system.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>comment</strong></td>
|
|
||||||
<td valign="top"><a href="#string">String</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A free-text comment left for the image.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### CoverArtImageThumbnails
|
|
||||||
|
|
||||||
URLs for thumbnails of different sizes for a particular piece of
|
|
||||||
cover art.
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th align="left">Field</th>
|
|
||||||
<th align="right">Argument</th>
|
|
||||||
<th align="left">Type</th>
|
|
||||||
<th align="left">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>small</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL of a small version of the cover art, where the
|
|
||||||
maximum dimension is 250px.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>large</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL of a large version of the cover art, where the
|
|
||||||
maximum dimension is 500px.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### Disc
|
### Disc
|
||||||
|
|
||||||
Information about the physical CD and releases associated with a
|
Information about the physical CD and releases associated with a
|
||||||
|
|
@ -5447,16 +5305,6 @@ release has one. The most common types found on releases are 12-digit
|
||||||
[UPCs](https://en.wikipedia.org/wiki/Universal_Product_Code) and 13-digit
|
[UPCs](https://en.wikipedia.org/wiki/Universal_Product_Code) and 13-digit
|
||||||
[EANs](https://en.wikipedia.org/wiki/International_Article_Number).
|
[EANs](https://en.wikipedia.org/wiki/International_Article_Number).
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>coverArt</strong></td>
|
|
||||||
<td valign="top"><a href="#releasecoverart">ReleaseCoverArt</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A list and summary of the cover art images that are present
|
|
||||||
for this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -5709,125 +5557,6 @@ ignoring pagination.
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### ReleaseCoverArt
|
|
||||||
|
|
||||||
An object containing a list of the cover art images for a
|
|
||||||
release obtained from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),
|
|
||||||
as well as a summary of what artwork is available.
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th align="left">Field</th>
|
|
||||||
<th align="right">Argument</th>
|
|
||||||
<th align="left">Type</th>
|
|
||||||
<th align="left">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>front</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL of an image depicting the album cover or “main
|
|
||||||
front” of the release, i.e. the front of the packaging of the audio recording
|
|
||||||
(or in the case of a digital release, the image associated with it in a digital
|
|
||||||
media store).
|
|
||||||
|
|
||||||
In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
of a front image, whereas here the value is the URL for the image itself if one
|
|
||||||
exists. You can check for null if you just want to determine the presence of an
|
|
||||||
image.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" align="right" valign="top">size</td>
|
|
||||||
<td valign="top"><a href="#coverartimagesize">CoverArtImageSize</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The size of the image to retrieve. By default, the returned
|
|
||||||
image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
retrieved as well.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>back</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL of an image depicting the “main back” of the
|
|
||||||
release, i.e. the back of the packaging of the audio recording.
|
|
||||||
|
|
||||||
In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
of a back image, whereas here the value is the URL for the image itself. You can
|
|
||||||
check for null if you just want to determine the presence of an image.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" align="right" valign="top">size</td>
|
|
||||||
<td valign="top"><a href="#coverartimagesize">CoverArtImageSize</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The size of the image to retrieve. By default, the returned
|
|
||||||
image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
retrieved as well.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>images</strong></td>
|
|
||||||
<td valign="top">[<a href="#coverartimage">CoverArtImage</a>]</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A list of images depicting the different sides and surfaces
|
|
||||||
of a release’s media and packaging.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>artwork</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether there is artwork present for this release.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>darkened</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether the Cover Art Archive has received a take-down
|
|
||||||
request for this release’s artwork, disallowing new uploads.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>count</strong></td>
|
|
||||||
<td valign="top"><a href="#int">Int</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The number of artwork images present for this release.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>release</strong></td>
|
|
||||||
<td valign="top"><a href="#release">Release</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The particular release shown in the returned cover art.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### ReleaseEdge
|
### ReleaseEdge
|
||||||
|
|
||||||
An edge in a connection.
|
An edge in a connection.
|
||||||
|
|
@ -6045,16 +5774,6 @@ that apply to this release group.
|
||||||
The MBIDs associated with the values of the `secondaryTypes`
|
The MBIDs associated with the values of the `secondaryTypes`
|
||||||
field.
|
field.
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>coverArt</strong></td>
|
|
||||||
<td valign="top"><a href="#releasegroupcoverart">ReleaseGroupCoverArt</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The cover art for a release group, obtained from the [Cover
|
|
||||||
Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -6227,78 +5946,6 @@ ignoring pagination.
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
### ReleaseGroupCoverArt
|
|
||||||
|
|
||||||
An object containing the cover art for a release group obtained
|
|
||||||
from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). For
|
|
||||||
release groups, just the front cover of a particular release will be selected.
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th align="left">Field</th>
|
|
||||||
<th align="right">Argument</th>
|
|
||||||
<th align="left">Type</th>
|
|
||||||
<th align="left">Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>front</strong></td>
|
|
||||||
<td valign="top"><a href="#urlstring">URLString</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The URL of an image depicting the album cover or “main
|
|
||||||
front” of a release in the release group, i.e. the front of the packaging of the
|
|
||||||
audio recording (or in the case of a digital release, the image associated with
|
|
||||||
it in a digital media store).
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" align="right" valign="top">size</td>
|
|
||||||
<td valign="top"><a href="#coverartimagesize">CoverArtImageSize</a></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The size of the image to retrieve. By default, the returned
|
|
||||||
image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
retrieved as well.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>images</strong></td>
|
|
||||||
<td valign="top">[<a href="#coverartimage">CoverArtImage</a>]</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A list of images returned by the [Cover Art
|
|
||||||
Archive](https://musicbrainz.org/doc/Cover_Art_Archive) for a release group. A
|
|
||||||
particular release’s front image will be included in the list, and likely no
|
|
||||||
others, even if other images are available.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>artwork</strong></td>
|
|
||||||
<td valign="top"><a href="#boolean">Boolean</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Whether there is artwork present for this release group.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" valign="top"><strong>release</strong></td>
|
|
||||||
<td valign="top"><a href="#release">Release</a>!</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The particular release shown in the returned cover art.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### ReleaseGroupEdge
|
### ReleaseGroupEdge
|
||||||
|
|
||||||
An edge in a connection.
|
An edge in a connection.
|
||||||
|
|
@ -7374,44 +7021,6 @@ these results were found through a search.
|
||||||
|
|
||||||
## Enums
|
## Enums
|
||||||
|
|
||||||
### CoverArtImageSize
|
|
||||||
|
|
||||||
The image sizes that may be requested at the [Cover Art
|
|
||||||
Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<th align="left">Value</th>
|
|
||||||
<th align="left">Description</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td valign="top"><strong>SMALL</strong></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A maximum dimension of 250px.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top"><strong>LARGE</strong></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
A maximum dimension of 500px.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top"><strong>FULL</strong></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The image’s original dimensions, with no maximum.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### ReleaseGroupType
|
### ReleaseGroupType
|
||||||
|
|
||||||
A type used to describe release groups, e.g. album, single, EP,
|
A type used to describe release groups, e.g. album, single, EP,
|
||||||
|
|
|
||||||
1
extensions/cover-art-archive.js
Normal file
1
extensions/cover-art-archive.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../lib/extensions/cover-art-archive')
|
||||||
1
extensions/fanart-tv.js
Normal file
1
extensions/fanart-tv.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../lib/extensions/fanart-tv')
|
||||||
1
extensions/mediawiki.js
Normal file
1
extensions/mediawiki.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../lib/extensions/mediawiki')
|
||||||
1
extensions/the-audio-db.js
Normal file
1
extensions/the-audio-db.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('../lib/extensions/the-audio-db')
|
||||||
12
package.json
12
package.json
|
|
@ -15,13 +15,14 @@
|
||||||
"yarn.lock"
|
"yarn.lock"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.6.0",
|
"node": ">=6.0.0",
|
||||||
"npm": ">=3.8.0"
|
"npm": ">=3.8.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:lib && npm run update-schema && npm run build:docs",
|
"build": "npm run build:lib && npm run update-schema && npm run build:docs",
|
||||||
"build:docs": "npm run build:docs:readme && npm run build:docs:schema && npm run build:docs:types",
|
"build:docs": "npm run build:docs:readme && npm run build:docs:schema && npm run build:docs:types && npm run build:docs:extensions",
|
||||||
"build:docs:readme": "doctoc --title \"## Contents\" README.md",
|
"build:docs:extensions": "babel-node scripts/build-extension-docs.js",
|
||||||
|
"build:docs:readme": "doctoc --notitle README.md docs/extensions/README.md",
|
||||||
"build:docs:schema": "printf '# GraphQL Schema\\n\\n%s\n' \"$(npm run -s print-schema:md)\" > docs/schema.md",
|
"build:docs:schema": "printf '# GraphQL Schema\\n\\n%s\n' \"$(npm run -s print-schema:md)\" > docs/schema.md",
|
||||||
"build:docs:types": "graphql-markdown --require babel-register --prologue 'You may also be interested in reading the [schema in GraphQL syntax](schema.md).' ./src/schema.js > docs/types.md",
|
"build:docs:types": "graphql-markdown --require babel-register --prologue 'You may also be interested in reading the [schema in GraphQL syntax](schema.md).' ./src/schema.js > docs/types.md",
|
||||||
"build:lib": "babel --out-dir lib src",
|
"build:lib": "babel --out-dir lib src",
|
||||||
|
|
@ -71,12 +72,14 @@
|
||||||
"dashify": "^0.2.2",
|
"dashify": "^0.2.2",
|
||||||
"dataloader": "^1.3.0",
|
"dataloader": "^1.3.0",
|
||||||
"debug": "^3.0.0",
|
"debug": "^3.0.0",
|
||||||
|
"deep-diff": "^0.3.8",
|
||||||
"dotenv": "^4.0.0",
|
"dotenv": "^4.0.0",
|
||||||
"es6-error": "^4.0.2",
|
"es6-error": "^4.0.2",
|
||||||
"express": "^4.15.4",
|
"express": "^4.15.4",
|
||||||
"express-graphql": "^0.6.7",
|
"express-graphql": "^0.6.7",
|
||||||
"graphql": "^0.11.7",
|
"graphql": "^0.11.7",
|
||||||
"graphql-relay": "^0.5.2",
|
"graphql-relay": "^0.5.2",
|
||||||
|
"graphql-tools": "^2.5.1",
|
||||||
"lru-cache": "^4.1.1",
|
"lru-cache": "^4.1.1",
|
||||||
"pascalcase": "^0.1.1",
|
"pascalcase": "^0.1.1",
|
||||||
"postinstall-build": "^5.0.1",
|
"postinstall-build": "^5.0.1",
|
||||||
|
|
@ -96,7 +99,7 @@
|
||||||
"coveralls": "^3.0.0",
|
"coveralls": "^3.0.0",
|
||||||
"cross-env": "^5.0.5",
|
"cross-env": "^5.0.5",
|
||||||
"doctoc": "^1.3.0",
|
"doctoc": "^1.3.0",
|
||||||
"graphql-markdown": "^2.2.0",
|
"graphql-markdown": "^3.1.0",
|
||||||
"nodemon": "^1.11.0",
|
"nodemon": "^1.11.0",
|
||||||
"nyc": "^11.1.0",
|
"nyc": "^11.1.0",
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
|
|
@ -110,6 +113,7 @@
|
||||||
},
|
},
|
||||||
"ava": {
|
"ava": {
|
||||||
"require": [
|
"require": [
|
||||||
|
"dotenv/config",
|
||||||
"babel-register"
|
"babel-register"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
470
schema.json
470
schema.json
|
|
@ -2954,22 +2954,6 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "coverArt",
|
|
||||||
"description": "A list and summary of the cover art images that are present\nfor this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "ReleaseCoverArt",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "status",
|
"name": "status",
|
||||||
"description": "The status describes how “official” a release is.",
|
"description": "The status describes how “official” a release is.",
|
||||||
|
|
@ -3332,356 +3316,6 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "ReleaseCoverArt",
|
|
||||||
"description": "An object containing a list of the cover art images for a\nrelease obtained from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),\nas well as a summary of what artwork is available.",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "front",
|
|
||||||
"description": "The URL of an image depicting the album cover or “main\nfront” of the release, i.e. the front of the packaging of the audio recording\n(or in the case of a digital release, the image associated with it in a digital\nmedia store).\n\nIn the MusicBrainz schema, this field is a Boolean value indicating the presence\nof a front image, whereas here the value is the URL for the image itself if one\nexists. You can check for null if you just want to determine the presence of an\nimage.",
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"name": "size",
|
|
||||||
"description": "The size of the image to retrieve. By default, the returned\nimage will have its full original dimensions, but certain thumbnail sizes may be\nretrieved as well.",
|
|
||||||
"type": {
|
|
||||||
"kind": "ENUM",
|
|
||||||
"name": "CoverArtImageSize",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"defaultValue": "null"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "back",
|
|
||||||
"description": "The URL of an image depicting the “main back” of the\nrelease, i.e. the back of the packaging of the audio recording.\n\nIn the MusicBrainz schema, this field is a Boolean value indicating the presence\nof a back image, whereas here the value is the URL for the image itself. You can\ncheck for null if you just want to determine the presence of an image.",
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"name": "size",
|
|
||||||
"description": "The size of the image to retrieve. By default, the returned\nimage will have its full original dimensions, but certain thumbnail sizes may be\nretrieved as well.",
|
|
||||||
"type": {
|
|
||||||
"kind": "ENUM",
|
|
||||||
"name": "CoverArtImageSize",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"defaultValue": "null"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "images",
|
|
||||||
"description": "A list of images depicting the different sides and surfaces\nof a release’s media and packaging.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "LIST",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "CoverArtImage",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "artwork",
|
|
||||||
"description": "Whether there is artwork present for this release.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "darkened",
|
|
||||||
"description": "Whether the Cover Art Archive has received a take-down\nrequest for this release’s artwork, disallowing new uploads.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "count",
|
|
||||||
"description": "The number of artwork images present for this release.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Int",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release",
|
|
||||||
"description": "The particular release shown in the returned cover art.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "Release",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": [],
|
|
||||||
"enumValues": null,
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "ENUM",
|
|
||||||
"name": "CoverArtImageSize",
|
|
||||||
"description": "The image sizes that may be requested at the [Cover Art\nArchive](https://musicbrainz.org/doc/Cover_Art_Archive).",
|
|
||||||
"fields": null,
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": null,
|
|
||||||
"enumValues": [
|
|
||||||
{
|
|
||||||
"name": "SMALL",
|
|
||||||
"description": "A maximum dimension of 250px.",
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LARGE",
|
|
||||||
"description": "A maximum dimension of 500px.",
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FULL",
|
|
||||||
"description": "The image’s original dimensions, with no maximum.",
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"description": "A web address.",
|
|
||||||
"fields": null,
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": null,
|
|
||||||
"enumValues": null,
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "CoverArtImage",
|
|
||||||
"description": "An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "fileID",
|
|
||||||
"description": "The Internet Archive’s internal file ID for the image.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "String",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "image",
|
|
||||||
"description": "The URL at which the image can be found.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "thumbnails",
|
|
||||||
"description": "A set of thumbnails for the image.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "CoverArtImageThumbnails",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "front",
|
|
||||||
"description": "Whether this image depicts the “main front” of the release.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "back",
|
|
||||||
"description": "Whether this image depicts the “main back” of the release.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "types",
|
|
||||||
"description": "A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)\ndescribing what part(s) of the release the image includes.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "LIST",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "String",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "edit",
|
|
||||||
"description": "The MusicBrainz edit ID.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Int",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approved",
|
|
||||||
"description": "Whether the image was approved by the MusicBrainz edit system.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "comment",
|
|
||||||
"description": "A free-text comment left for the image.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "String",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": [],
|
|
||||||
"enumValues": null,
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "CoverArtImageThumbnails",
|
|
||||||
"description": "URLs for thumbnails of different sizes for a particular piece of\ncover art.",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "small",
|
|
||||||
"description": "The URL of a small version of the cover art, where the\nmaximum dimension is 250px.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "large",
|
|
||||||
"description": "The URL of a large version of the cover art, where the\nmaximum dimension is 500px.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": [],
|
|
||||||
"enumValues": null,
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "Medium",
|
"name": "Medium",
|
||||||
|
|
@ -8106,18 +7740,6 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "coverArt",
|
|
||||||
"description": "The cover art for a release group, obtained from the [Cover\nArt Archive](https://musicbrainz.org/doc/Cover_Art_Archive).",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "ReleaseGroupCoverArt",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "artists",
|
"name": "artists",
|
||||||
"description": "A list of artists linked to this entity.",
|
"description": "A list of artists linked to this entity.",
|
||||||
|
|
@ -8319,88 +7941,6 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "ReleaseGroupCoverArt",
|
|
||||||
"description": "An object containing the cover art for a release group obtained\nfrom the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). For\nrelease groups, just the front cover of a particular release will be selected.",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "front",
|
|
||||||
"description": "The URL of an image depicting the album cover or “main\nfront” of a release in the release group, i.e. the front of the packaging of the\naudio recording (or in the case of a digital release, the image associated with\nit in a digital media store).",
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"name": "size",
|
|
||||||
"description": "The size of the image to retrieve. By default, the returned\nimage will have its full original dimensions, but certain thumbnail sizes may be\nretrieved as well.",
|
|
||||||
"type": {
|
|
||||||
"kind": "ENUM",
|
|
||||||
"name": "CoverArtImageSize",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"defaultValue": "null"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "URLString",
|
|
||||||
"ofType": null
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "images",
|
|
||||||
"description": "A list of images returned by the [Cover Art\nArchive](https://musicbrainz.org/doc/Cover_Art_Archive) for a release group. A\nparticular release’s front image will be included in the list, and likely no\nothers, even if other images are available.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "LIST",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "CoverArtImage",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "artwork",
|
|
||||||
"description": "Whether there is artwork present for this release group.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "SCALAR",
|
|
||||||
"name": "Boolean",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release",
|
|
||||||
"description": "The particular release shown in the returned cover art.",
|
|
||||||
"args": [],
|
|
||||||
"type": {
|
|
||||||
"kind": "NON_NULL",
|
|
||||||
"name": null,
|
|
||||||
"ofType": {
|
|
||||||
"kind": "OBJECT",
|
|
||||||
"name": "Release",
|
|
||||||
"ofType": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isDeprecated": false,
|
|
||||||
"deprecationReason": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"inputFields": null,
|
|
||||||
"interfaces": [],
|
|
||||||
"enumValues": null,
|
|
||||||
"possibleTypes": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "SeriesConnection",
|
"name": "SeriesConnection",
|
||||||
|
|
@ -9094,6 +8634,16 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "URLString",
|
||||||
|
"description": "A web address.",
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "URL",
|
"name": "URL",
|
||||||
|
|
|
||||||
66
scripts/build-extension-docs.js
Normal file
66
scripts/build-extension-docs.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { graphql, introspectionQuery } from 'graphql'
|
||||||
|
import { renderSchema, diffSchema } from 'graphql-markdown'
|
||||||
|
import baseSchema, { createSchema } from '../src/schema'
|
||||||
|
|
||||||
|
const extensionModules = [
|
||||||
|
'cover-art-archive',
|
||||||
|
'fanart-tv',
|
||||||
|
'mediawiki',
|
||||||
|
'the-audio-db'
|
||||||
|
]
|
||||||
|
|
||||||
|
function getSchemaJSON (schema) {
|
||||||
|
return graphql(schema, introspectionQuery).then(result => result.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(extensionModules.map(extensionModule => {
|
||||||
|
const extension = require(`../src/extensions/${extensionModule}`).default
|
||||||
|
console.log(`Generating docs for “${extension.name}” extension...`)
|
||||||
|
const schema = createSchema(baseSchema, { extensions: [extension] })
|
||||||
|
return Promise.all([
|
||||||
|
getSchemaJSON(baseSchema),
|
||||||
|
getSchemaJSON(schema)
|
||||||
|
]).then(([baseSchemaJSON, schemaJSON]) => {
|
||||||
|
const outputSchema = diffSchema(baseSchemaJSON, schemaJSON, {
|
||||||
|
processTypeDiff (type) {
|
||||||
|
if (type.description === undefined) {
|
||||||
|
type.description =
|
||||||
|
':small_blue_diamond: *This type has been extended. See the ' +
|
||||||
|
'[base schema](../types.md)\nfor a description and additional ' +
|
||||||
|
'fields.*'
|
||||||
|
}
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const outputPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
`../docs/extensions/${extensionModule}.md`
|
||||||
|
)
|
||||||
|
const stream = fs.createWriteStream(outputPath)
|
||||||
|
const printer = (line) => stream.write(`${line}\n`)
|
||||||
|
const prologuePath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
`../src/extensions/${extensionModule}/prologue.md`
|
||||||
|
)
|
||||||
|
const prologue = fs.readFileSync(prologuePath, 'utf8')
|
||||||
|
renderSchema(outputSchema, {
|
||||||
|
title: `Extension: ${extension.name}`,
|
||||||
|
prologue: prologue.trim()
|
||||||
|
? `${extension.description}\n\n${prologue}`
|
||||||
|
: extension.description,
|
||||||
|
printer,
|
||||||
|
unknownTypeURL: '../types.md'
|
||||||
|
})
|
||||||
|
stream.end()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
stream.on('error', reject)
|
||||||
|
stream.on('finish', () => resolve(extension))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})).then((extensions) => {
|
||||||
|
console.log(`Built docs for ${extensions.length} extension(s).`)
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('Error:', err)
|
||||||
|
})
|
||||||
|
|
@ -29,18 +29,12 @@ export class ClientError extends ExtendableError {
|
||||||
|
|
||||||
export default class Client {
|
export default class Client {
|
||||||
constructor ({
|
constructor ({
|
||||||
baseURL = process.env.MUSICBRAINZ_BASE_URL || 'http://musicbrainz.org/ws/2/',
|
baseURL,
|
||||||
userAgent = `${pkg.name}/${pkg.version} ` +
|
userAgent = `${pkg.name}/${pkg.version} ` +
|
||||||
`( ${pkg.homepage || pkg.author.url || pkg.author.email} )`,
|
`( ${pkg.homepage || pkg.author.url || pkg.author.email} )`,
|
||||||
extraHeaders = {},
|
extraHeaders = {},
|
||||||
errorClass = ClientError,
|
errorClass = ClientError,
|
||||||
timeout = 60000,
|
timeout = 60000,
|
||||||
// MusicBrainz API requests are limited to an *average* of 1 req/sec.
|
|
||||||
// That means if, for example, we only need to make a few API requests to
|
|
||||||
// fulfill a query, we might as well make them all at once - as long as
|
|
||||||
// we then wait a few seconds before making more. In practice this can
|
|
||||||
// seemingly be set to about 5 requests every 5 seconds before we're
|
|
||||||
// considered to exceed the rate limit.
|
|
||||||
limit = 1,
|
limit = 1,
|
||||||
period = 1000,
|
period = 1000,
|
||||||
concurrency = 10,
|
concurrency = 10,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import MusicBrainz, { MusicBrainzError } from './musicbrainz'
|
import MusicBrainz, { MusicBrainzError } from './musicbrainz'
|
||||||
import CoverArtArchive, { CoverArtArchiveError } from './cover-art-archive'
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
MusicBrainz as default,
|
MusicBrainz as default,
|
||||||
MusicBrainz,
|
MusicBrainz,
|
||||||
MusicBrainzError,
|
MusicBrainzError
|
||||||
CoverArtArchive,
|
|
||||||
CoverArtArchiveError
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@ export default class MusicBrainz extends Client {
|
||||||
constructor ({
|
constructor ({
|
||||||
baseURL = process.env.MUSICBRAINZ_BASE_URL || 'http://musicbrainz.org/ws/2/',
|
baseURL = process.env.MUSICBRAINZ_BASE_URL || 'http://musicbrainz.org/ws/2/',
|
||||||
errorClass = MusicBrainzError,
|
errorClass = MusicBrainzError,
|
||||||
|
// MusicBrainz API requests are limited to an *average* of 1 req/sec.
|
||||||
|
// That means if, for example, we only need to make a few API requests to
|
||||||
|
// fulfill a query, we might as well make them all at once - as long as
|
||||||
|
// we then wait a few seconds before making more. In practice this can
|
||||||
|
// seemingly be set to about 5 requests every 5 seconds before we're
|
||||||
|
// considered to exceed the rate limit.
|
||||||
limit = 5,
|
limit = 5,
|
||||||
period = 5500,
|
period = 5500,
|
||||||
...options
|
...options
|
||||||
|
|
|
||||||
27
src/context.js
Normal file
27
src/context.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import createLoaders from './loaders'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:context')
|
||||||
|
|
||||||
|
export function extendContext (extension, context, options) {
|
||||||
|
if (extension.extendContext) {
|
||||||
|
if (typeof extension.extendContext === 'function') {
|
||||||
|
debug(`Extending context via a function from the “${extension.name}” extension.`)
|
||||||
|
context = extension.extendContext(context, options)
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Extension “${extension.name}” contains an invalid \`extendContext\` ` +
|
||||||
|
`value: ${extension.extendContext}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createContext (options) {
|
||||||
|
const { client } = options
|
||||||
|
const loaders = createLoaders(client)
|
||||||
|
const context = { client, loaders }
|
||||||
|
return options.extensions.reduce((context, extension) => {
|
||||||
|
return extendContext(extension, context, options)
|
||||||
|
}, context)
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import Client, { ClientError } from './client'
|
import Client from '../../api/client'
|
||||||
|
|
||||||
export class CoverArtArchiveError extends ClientError {}
|
export default class CoverArtArchiveClient extends Client {
|
||||||
|
|
||||||
export default class CoverArtArchive extends Client {
|
|
||||||
constructor ({
|
constructor ({
|
||||||
baseURL = process.env.COVER_ART_ARCHIVE_BASE_URL || 'http://coverartarchive.org/',
|
baseURL = process.env.COVER_ART_ARCHIVE_BASE_URL || 'http://coverartarchive.org/',
|
||||||
errorClass = CoverArtArchiveError,
|
|
||||||
limit = 10,
|
limit = 10,
|
||||||
period = 1000,
|
period = 1000,
|
||||||
...options
|
...options
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super({ baseURL, errorClass, limit, period, ...options })
|
super({ baseURL, limit, period, ...options })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sinfully attempt to parse HTML responses for the error message.
|
||||||
|
*/
|
||||||
parseErrorMessage (response, body) {
|
parseErrorMessage (response, body) {
|
||||||
if (typeof body === 'string' && body.startsWith('<!')) {
|
if (typeof body === 'string' && body.startsWith('<!')) {
|
||||||
const heading = /<h1>([^<]+)<\/h1>/i.exec(body)
|
const heading = /<h1>([^<]+)<\/h1>/i.exec(body)
|
||||||
|
|
@ -22,25 +22,15 @@ export default class CoverArtArchive extends Client {
|
||||||
return super.parseErrorMessage(response, body)
|
return super.parseErrorMessage(response, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
getImagesURL (entity, mbid) {
|
images (entityType, mbid) {
|
||||||
return `${entity}/${mbid}`
|
return this.get(`${entityType}/${mbid}`, { json: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
images (entity, mbid) {
|
imageURL (entityType, mbid, typeOrID = 'front', size) {
|
||||||
const url = this.getImagesURL(entity, mbid)
|
let url = `${entityType}/${mbid}/${typeOrID}`
|
||||||
return this.get(url, { json: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
getImageURL (entity, mbid, typeOrID = 'front', size) {
|
|
||||||
let url = `${entity}/${mbid}/${typeOrID}`
|
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
url += `-${size}`
|
url += `-${size}`
|
||||||
}
|
}
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
imageURL (entity, mbid, typeOrID = 'front', size) {
|
|
||||||
const url = this.getImageURL(entity, mbid, typeOrID, size)
|
|
||||||
return this.get(url, { method: 'HEAD', followRedirect: false })
|
return this.get(url, { method: 'HEAD', followRedirect: false })
|
||||||
.then(headers => headers.location)
|
.then(headers => headers.location)
|
||||||
}
|
}
|
||||||
36
src/extensions/cover-art-archive/index.js
Normal file
36
src/extensions/cover-art-archive/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import schema from './schema'
|
||||||
|
import resolvers from './resolvers'
|
||||||
|
import createLoaders from './loaders'
|
||||||
|
import CoverArtArchiveClient from './client'
|
||||||
|
import { ONE_DAY } from '../../util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Cover Art Archive',
|
||||||
|
description:
|
||||||
|
'Retrieve cover art images for releases from the [Cover Art Archive](https://coverartarchive.org/).',
|
||||||
|
extendContext (context, { coverArtClient, coverArtArchive = {} } = {}) {
|
||||||
|
const client = coverArtClient || new CoverArtArchiveClient(coverArtArchive)
|
||||||
|
const cacheSize = parseInt(
|
||||||
|
process.env.COVER_ART_ARCHIVE_CACHE_SIZE || process.env.GRAPHBRAINZ_CACHE_SIZE || 8192,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cacheTTL = parseInt(
|
||||||
|
process.env.COVER_ART_ARCHIVE_CACHE_TTL || process.env.GRAPHBRAINZ_CACHE_TTL || ONE_DAY,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...context,
|
||||||
|
// Add the client instance directly onto `context` for backwards
|
||||||
|
// compatibility.
|
||||||
|
coverArtClient: client,
|
||||||
|
loaders: {
|
||||||
|
...context.loaders,
|
||||||
|
...createLoaders({ client, cacheSize, cacheTTL })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extendSchema: {
|
||||||
|
schemas: [schema],
|
||||||
|
resolvers
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/extensions/cover-art-archive/loaders.js
Normal file
53
src/extensions/cover-art-archive/loaders.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import DataLoader from 'dataloader'
|
||||||
|
import LRUCache from 'lru-cache'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:extensions/cover-art-archive')
|
||||||
|
|
||||||
|
export default function createLoaders (options) {
|
||||||
|
const { client } = options
|
||||||
|
const cache = LRUCache({
|
||||||
|
max: options.cacheSize,
|
||||||
|
maxAge: options.cacheTTL,
|
||||||
|
dispose (key) {
|
||||||
|
debug(`Removed from cache. key=${key}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Make the cache Map-like.
|
||||||
|
cache.delete = cache.del
|
||||||
|
cache.clear = cache.reset
|
||||||
|
|
||||||
|
return {
|
||||||
|
coverArtArchive: new DataLoader(keys => {
|
||||||
|
return Promise.all(keys.map(key => {
|
||||||
|
const [ entityType, id ] = key
|
||||||
|
return client.images(entityType, id)
|
||||||
|
.catch(err => {
|
||||||
|
if (err.statusCode === 404) {
|
||||||
|
return { images: [] }
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
}).then(coverArt => ({
|
||||||
|
...coverArt,
|
||||||
|
_entityType: entityType,
|
||||||
|
_id: id,
|
||||||
|
_releaseID: coverArt.release && coverArt.release.split('/').pop()
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}, {
|
||||||
|
cacheKeyFn: ([ entityType, id ]) => `${entityType}/${id}`,
|
||||||
|
cacheMap: cache
|
||||||
|
}),
|
||||||
|
coverArtArchiveURL: new DataLoader(keys => {
|
||||||
|
return Promise.all(keys.map(key => {
|
||||||
|
const [ entityType, id, type, size ] = key
|
||||||
|
return client.imageURL(entityType, id, type, size)
|
||||||
|
}))
|
||||||
|
}, {
|
||||||
|
cacheKeyFn: ([ entityType, id, type, size ]) => {
|
||||||
|
const key = `${entityType}/${id}/${type}`
|
||||||
|
return size ? `${key}-${size}` : key
|
||||||
|
},
|
||||||
|
cacheMap: cache
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/extensions/cover-art-archive/prologue.md
Normal file
12
src/extensions/cover-art-archive/prologue.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`COVER_ART_ARCHIVE_BASE_URL`**: The base URL at which to access the Cover
|
||||||
|
Art Archive API. Defaults to `http://coverartarchive.org/`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
75
src/extensions/cover-art-archive/resolvers.js
Normal file
75
src/extensions/cover-art-archive/resolvers.js
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { resolveLookup } from '../../resolvers'
|
||||||
|
|
||||||
|
const SIZES = new Map([
|
||||||
|
[null, null],
|
||||||
|
[250, 250],
|
||||||
|
[500, 500],
|
||||||
|
['FULL', null],
|
||||||
|
['SMALL', 250],
|
||||||
|
['LARGE', 500]
|
||||||
|
])
|
||||||
|
|
||||||
|
function resolveImage (coverArt, args, { loaders }, info) {
|
||||||
|
// Since migrating the schema to an extension, we lost custom enum values
|
||||||
|
// for the time being. Translate any incoming `size` arg to the old enum
|
||||||
|
// values.
|
||||||
|
const size = SIZES.get(args.size)
|
||||||
|
// Field should be `front` or `back`.
|
||||||
|
const field = info.fieldName
|
||||||
|
if (coverArt.images) {
|
||||||
|
const matches = coverArt.images.filter(image => image[field])
|
||||||
|
if (!matches.length) {
|
||||||
|
return null
|
||||||
|
} else if (matches.length === 1) {
|
||||||
|
const match = matches[0]
|
||||||
|
if (size === 250) {
|
||||||
|
return match.thumbnails.small
|
||||||
|
} else if (size === 500) {
|
||||||
|
return match.thumbnails.large
|
||||||
|
} else {
|
||||||
|
return match.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const entityType = coverArt._entityType
|
||||||
|
const id = coverArt._id
|
||||||
|
const releaseID = coverArt._releaseID
|
||||||
|
if (entityType === 'release-group' && field === 'front') {
|
||||||
|
// Release groups only have an endpoint to retrieve the front image.
|
||||||
|
// If someone requests the back of a release group, return the back of the
|
||||||
|
// release that the release group's cover art response points to.
|
||||||
|
return loaders.coverArtArchiveURL.load(['release-group', id, field, size])
|
||||||
|
} else {
|
||||||
|
return loaders.coverArtArchiveURL.load(['release', releaseID, field, size])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
CoverArtArchiveImage: {
|
||||||
|
fileID: image => image.id
|
||||||
|
},
|
||||||
|
CoverArtArchiveRelease: {
|
||||||
|
front: resolveImage,
|
||||||
|
back: resolveImage,
|
||||||
|
images: coverArt => coverArt.images,
|
||||||
|
artwork: coverArt => coverArt.images.length > 0,
|
||||||
|
count: coverArt => coverArt.images.length,
|
||||||
|
release: (coverArt, args, context, info) => {
|
||||||
|
const mbid = coverArt._releaseID
|
||||||
|
if (mbid) {
|
||||||
|
return resolveLookup(coverArt, { mbid }, context, info)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Release: {
|
||||||
|
coverArtArchive: (release, args, { loaders }) => {
|
||||||
|
return loaders.coverArtArchive.load(['release', release.id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReleaseGroup: {
|
||||||
|
coverArtArchive: (releaseGroup, args, { loaders }) => {
|
||||||
|
return loaders.coverArtArchive.load(['release-group', releaseGroup.id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/extensions/cover-art-archive/schema.js
Normal file
121
src/extensions/cover-art-archive/schema.js
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
export default `
|
||||||
|
|
||||||
|
# An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
type CoverArtArchiveImage {
|
||||||
|
# The Internet Archive’s internal file ID for the image.
|
||||||
|
fileID: String!
|
||||||
|
|
||||||
|
# The URL at which the image can be found.
|
||||||
|
image: URLString!
|
||||||
|
|
||||||
|
# A set of thumbnails for the image.
|
||||||
|
thumbnails: CoverArtArchiveImageThumbnails!
|
||||||
|
|
||||||
|
# Whether this image depicts the “main front” of the release.
|
||||||
|
front: Boolean!
|
||||||
|
|
||||||
|
# Whether this image depicts the “main back” of the release.
|
||||||
|
back: Boolean!
|
||||||
|
|
||||||
|
# A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)
|
||||||
|
# describing what part(s) of the release the image includes.
|
||||||
|
types: [String]!
|
||||||
|
|
||||||
|
# The MusicBrainz edit ID.
|
||||||
|
edit: Int
|
||||||
|
|
||||||
|
# Whether the image was approved by the MusicBrainz edit system.
|
||||||
|
approved: Boolean
|
||||||
|
|
||||||
|
# A free-text comment left for the image.
|
||||||
|
comment: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# The image sizes that may be requested at the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
enum CoverArtArchiveImageSize {
|
||||||
|
# A maximum dimension of 250px.
|
||||||
|
SMALL
|
||||||
|
|
||||||
|
# A maximum dimension of 500px.
|
||||||
|
LARGE
|
||||||
|
|
||||||
|
# The image’s original dimensions, with no maximum.
|
||||||
|
FULL
|
||||||
|
}
|
||||||
|
|
||||||
|
# URLs for thumbnails of different sizes for a particular piece of
|
||||||
|
# cover art.
|
||||||
|
type CoverArtArchiveImageThumbnails {
|
||||||
|
# The URL of a small version of the cover art, where the maximum dimension is
|
||||||
|
# 250px.
|
||||||
|
small: URLString
|
||||||
|
|
||||||
|
# The URL of a large version of the cover art, where the maximum dimension is
|
||||||
|
# 500px.
|
||||||
|
large: URLString
|
||||||
|
}
|
||||||
|
|
||||||
|
# An object containing a list of the cover art images for a release obtained
|
||||||
|
# from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),
|
||||||
|
# as well as a summary of what artwork is available.
|
||||||
|
type CoverArtArchiveRelease {
|
||||||
|
# The URL of an image depicting the album cover or “main front” of the release,
|
||||||
|
# i.e. the front of the packaging of the audio recording (or in the case of a
|
||||||
|
# digital release, the image associated with it in a digital media store).
|
||||||
|
#
|
||||||
|
# In the MusicBrainz schema, this field is a Boolean value indicating the
|
||||||
|
# presence of a front image, whereas here the value is the URL for the image
|
||||||
|
# itself if one exists. You can check for null if you just want to determine
|
||||||
|
# the presence of an image.
|
||||||
|
front(
|
||||||
|
# The size of the image to retrieve. By default, the returned image will
|
||||||
|
# have its full original dimensions, but certain thumbnail sizes may be
|
||||||
|
# retrieved as well.
|
||||||
|
size: CoverArtArchiveImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The URL of an image depicting the “main back” of the release, i.e. the back
|
||||||
|
# of the packaging of the audio recording.
|
||||||
|
#
|
||||||
|
# In the MusicBrainz schema, this field is a Boolean value indicating the
|
||||||
|
# presence of a back image, whereas here the value is the URL for the image
|
||||||
|
# itself. You can check for null if you just want to determine the presence of
|
||||||
|
# an image.
|
||||||
|
back(
|
||||||
|
# The size of the image to retrieve. By default, the returned image will
|
||||||
|
# have its full original dimensions, but certain thumbnail sizes may be
|
||||||
|
# retrieved as well.
|
||||||
|
size: CoverArtArchiveImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# A list of images depicting the different sides and surfaces of a release’s
|
||||||
|
# media and packaging.
|
||||||
|
images: [CoverArtArchiveImage]!
|
||||||
|
|
||||||
|
# Whether there is artwork present for this release.
|
||||||
|
artwork: Boolean!
|
||||||
|
|
||||||
|
# The number of artwork images present for this release.
|
||||||
|
count: Int!
|
||||||
|
|
||||||
|
# The particular release shown in the returned cover art.
|
||||||
|
release: Release
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Release {
|
||||||
|
# An object containing a list and summary of the cover art images that are
|
||||||
|
# present for this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).
|
||||||
|
# This field is provided by the Cover Art Archive extension.
|
||||||
|
coverArtArchive: CoverArtArchiveRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type ReleaseGroup {
|
||||||
|
# The cover art for a release in the release group, obtained from the
|
||||||
|
# [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). A
|
||||||
|
# release in the release group will be chosen as representative of the release
|
||||||
|
# group.
|
||||||
|
# This field is provided by the Cover Art Archive extension.
|
||||||
|
coverArtArchive: CoverArtArchiveRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
60
src/extensions/fanart-tv/client.js
Normal file
60
src/extensions/fanart-tv/client.js
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import Client from '../../api/client'
|
||||||
|
|
||||||
|
export default class FanArtClient extends Client {
|
||||||
|
constructor ({
|
||||||
|
apiKey = process.env.FANART_API_KEY,
|
||||||
|
baseURL = process.env.FANART_BASE_URL || 'http://webservice.fanart.tv/v3/',
|
||||||
|
limit = 10,
|
||||||
|
period = 1000,
|
||||||
|
...options
|
||||||
|
} = {}) {
|
||||||
|
super({ baseURL, limit, period, ...options })
|
||||||
|
this.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
get (path, options = {}) {
|
||||||
|
const ClientError = this.errorClass
|
||||||
|
if (!this.apiKey) {
|
||||||
|
return Promise.reject(new ClientError(
|
||||||
|
'No API key was configured for the fanart.tv client.'
|
||||||
|
))
|
||||||
|
}
|
||||||
|
options = {
|
||||||
|
json: true,
|
||||||
|
...options,
|
||||||
|
qs: {
|
||||||
|
...options.qs,
|
||||||
|
api_key: this.apiKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.get(path, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
musicEntity (entityType, mbid) {
|
||||||
|
const ClientError = this.errorClass
|
||||||
|
switch (entityType) {
|
||||||
|
case 'artist':
|
||||||
|
return this.musicArtist(mbid)
|
||||||
|
case 'label':
|
||||||
|
return this.musicLabel(mbid)
|
||||||
|
case 'release-group':
|
||||||
|
return this.musicAlbum(mbid)
|
||||||
|
default:
|
||||||
|
return Promise.reject(new ClientError(
|
||||||
|
`Entity type unsupported: ${entityType}`
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
musicArtist (mbid) {
|
||||||
|
return this.get(`music/${mbid}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
musicAlbum (mbid) {
|
||||||
|
return this.get(`music/albums/${mbid}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
musicLabel (mbid) {
|
||||||
|
return this.get(`music/${mbid}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/extensions/fanart-tv/index.js
Normal file
34
src/extensions/fanart-tv/index.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import schema from './schema'
|
||||||
|
import resolvers from './resolvers'
|
||||||
|
import createLoader from './loader'
|
||||||
|
import FanArtClient from './client'
|
||||||
|
import { ONE_DAY } from '../../util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'fanart.tv',
|
||||||
|
description:
|
||||||
|
'Retrieve high quality artwork for artists, releases, and labels from ' +
|
||||||
|
'[fanart.tv](https://fanart.tv/).',
|
||||||
|
extendContext (context, { fanArt = {} } = {}) {
|
||||||
|
const client = new FanArtClient(fanArt)
|
||||||
|
const cacheSize = parseInt(
|
||||||
|
process.env.FANART_CACHE_SIZE || process.env.GRAPHBRAINZ_CACHE_SIZE || 8192,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cacheTTL = parseInt(
|
||||||
|
process.env.FANART_CACHE_TTL || process.env.GRAPHBRAINZ_CACHE_TTL || ONE_DAY,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...context,
|
||||||
|
loaders: {
|
||||||
|
...context.loaders,
|
||||||
|
fanArt: createLoader({ client, cacheSize, cacheTTL })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extendSchema: {
|
||||||
|
schemas: [schema],
|
||||||
|
resolvers
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/extensions/fanart-tv/loader.js
Normal file
51
src/extensions/fanart-tv/loader.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import DataLoader from 'dataloader'
|
||||||
|
import LRUCache from 'lru-cache'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:extensions/fanart-tv')
|
||||||
|
|
||||||
|
export default function createLoader (options) {
|
||||||
|
const { client } = options
|
||||||
|
const cache = LRUCache({
|
||||||
|
max: options.cacheSize,
|
||||||
|
maxAge: options.cacheTTL,
|
||||||
|
dispose (key) {
|
||||||
|
debug(`Removed from cache. key=${key}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Make the cache Map-like.
|
||||||
|
cache.delete = cache.del
|
||||||
|
cache.clear = cache.reset
|
||||||
|
|
||||||
|
const loader = new DataLoader(keys => {
|
||||||
|
return Promise.all(keys.map(key => {
|
||||||
|
const [ entityType, id ] = key
|
||||||
|
return client.musicEntity(entityType, id)
|
||||||
|
.catch(err => {
|
||||||
|
if (err.statusCode === 404) {
|
||||||
|
// 404s are OK, just return empty data.
|
||||||
|
return {
|
||||||
|
artistbackground: [],
|
||||||
|
artistthumb: [],
|
||||||
|
musiclogo: [],
|
||||||
|
hdmusiclogo: [],
|
||||||
|
musicbanner: [],
|
||||||
|
musiclabel: [],
|
||||||
|
albums: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
}).then(body => {
|
||||||
|
if (entityType === 'artist') {
|
||||||
|
const releaseGroupIDs = Object.keys(body.albums)
|
||||||
|
debug(`Priming album cache with ${releaseGroupIDs.length} album(s).`)
|
||||||
|
releaseGroupIDs.forEach(key => loader.prime(['release-group', key], body))
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}, {
|
||||||
|
cacheKeyFn: ([ entityType, id ]) => `${entityType}/${id}`,
|
||||||
|
cacheMap: cache
|
||||||
|
})
|
||||||
|
return loader
|
||||||
|
}
|
||||||
14
src/extensions/fanart-tv/prologue.md
Normal file
14
src/extensions/fanart-tv/prologue.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`FANART_API_KEY`**: The fanart.tv API key to use. This is required for any
|
||||||
|
fields added by the extension to successfully resolve.
|
||||||
|
* **`FANART_BASE_URL`**: The base URL at which to access the
|
||||||
|
fanart.tv API. Defaults to `http://webservice.fanart.tv/v3/`.
|
||||||
|
* **`FANART_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`FANART_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
63
src/extensions/fanart-tv/resolvers.js
Normal file
63
src/extensions/fanart-tv/resolvers.js
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
const imageResolvers = {
|
||||||
|
imageID: image => image.id,
|
||||||
|
url: (image, args) => {
|
||||||
|
return args.size === 'PREVIEW'
|
||||||
|
? image.url.replace('/fanart/', '/preview/')
|
||||||
|
: image.url
|
||||||
|
},
|
||||||
|
likeCount: image => image.likes
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
FanArtImage: {
|
||||||
|
...imageResolvers
|
||||||
|
},
|
||||||
|
FanArtDiscImage: {
|
||||||
|
...imageResolvers,
|
||||||
|
discNumber: image => image.disc
|
||||||
|
},
|
||||||
|
FanArtLabelImage: {
|
||||||
|
...imageResolvers,
|
||||||
|
color: image => image.colour
|
||||||
|
},
|
||||||
|
FanArtArtist: {
|
||||||
|
backgrounds: artist => {
|
||||||
|
return artist.artistbackground
|
||||||
|
},
|
||||||
|
thumbnails: artist => {
|
||||||
|
return artist.artistthumb
|
||||||
|
},
|
||||||
|
logos: artist => {
|
||||||
|
return artist.musiclogo
|
||||||
|
},
|
||||||
|
logosHD: artist => {
|
||||||
|
return artist.hdmusiclogo
|
||||||
|
},
|
||||||
|
banners: artist => {
|
||||||
|
return artist.musicbanner
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FanArtLabel: {
|
||||||
|
logos: label => label.musiclabel
|
||||||
|
},
|
||||||
|
FanArtAlbum: {
|
||||||
|
albumCovers: album => album.albumcover || [],
|
||||||
|
discImages: album => album.cdart || []
|
||||||
|
},
|
||||||
|
Artist: {
|
||||||
|
fanArt: (artist, args, context) => {
|
||||||
|
return context.loaders.fanArt.load(['artist', artist.id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Label: {
|
||||||
|
fanArt: (label, args, context) => {
|
||||||
|
return context.loaders.fanArt.load(['label', label.id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReleaseGroup: {
|
||||||
|
fanArt: (releaseGroup, args, context) => {
|
||||||
|
return context.loaders.fanArt.load(['release-group', releaseGroup.id])
|
||||||
|
.then(artist => artist.albums[releaseGroup.id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
src/extensions/fanart-tv/schema.js
Normal file
127
src/extensions/fanart-tv/schema.js
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
export default `
|
||||||
|
|
||||||
|
# The image sizes that may be requested at [fanart.tv](https://fanart.tv/).
|
||||||
|
enum FanArtImageSize {
|
||||||
|
# The image’s full original dimensions.
|
||||||
|
FULL
|
||||||
|
|
||||||
|
# A maximum dimension of 200px.
|
||||||
|
PREVIEW
|
||||||
|
}
|
||||||
|
|
||||||
|
# A single image from [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtImage {
|
||||||
|
# The ID of the image on fanart.tv.
|
||||||
|
imageID: ID
|
||||||
|
|
||||||
|
# The URL of the image.
|
||||||
|
url(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: FanArtImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The number of likes the image has received by fanart.tv users.
|
||||||
|
likeCount: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
# A disc image from [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtDiscImage {
|
||||||
|
# The ID of the image on fanart.tv.
|
||||||
|
imageID: ID
|
||||||
|
|
||||||
|
# The URL of the image.
|
||||||
|
url(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: FanArtImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The number of likes the image has received by fanart.tv users.
|
||||||
|
likeCount: Int
|
||||||
|
|
||||||
|
# The disc number.
|
||||||
|
discNumber: Int
|
||||||
|
|
||||||
|
# The width and height of the (square) disc image.
|
||||||
|
size: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
# A music label image from [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtLabelImage {
|
||||||
|
# The ID of the image on fanart.tv.
|
||||||
|
imageID: ID
|
||||||
|
|
||||||
|
# The URL of the image.
|
||||||
|
url(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: FanArtImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The number of likes the image has received by fanart.tv users.
|
||||||
|
likeCount: Int
|
||||||
|
|
||||||
|
# The type of color content in the image (usually “white” or “colour”).
|
||||||
|
color: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# An object containing lists of the different types of artist images from
|
||||||
|
# [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtArtist {
|
||||||
|
# A list of 1920x1080 JPG images picturing the artist, suitable for use as
|
||||||
|
# backgrounds.
|
||||||
|
backgrounds: [FanArtImage]
|
||||||
|
|
||||||
|
# A list of 1000x185 JPG images containing the artist and their logo or name.
|
||||||
|
banners: [FanArtImage]
|
||||||
|
|
||||||
|
# A list of 400x155 PNG images containing the artist’s logo or name, with
|
||||||
|
# transparent backgrounds.
|
||||||
|
logos: [FanArtImage]
|
||||||
|
|
||||||
|
# A list of 800x310 PNG images containing the artist’s logo or name, with
|
||||||
|
# transparent backgrounds.
|
||||||
|
logosHD: [FanArtImage]
|
||||||
|
|
||||||
|
# A list of 1000x1000 JPG thumbnail images picturing the artist (usually
|
||||||
|
# containing every member of a band).
|
||||||
|
thumbnails: [FanArtImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
# An object containing lists of the different types of label images from
|
||||||
|
# [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtLabel {
|
||||||
|
# A list of 400x270 PNG images containing the label’s logo. There will
|
||||||
|
# usually be a black version, a color version, and a white version, all with
|
||||||
|
# transparent backgrounds.
|
||||||
|
logos: [FanArtLabelImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
# An object containing lists of the different types of release group images from
|
||||||
|
# [fanart.tv](https://fanart.tv/).
|
||||||
|
type FanArtAlbum {
|
||||||
|
# A list of 1000x1000 JPG images of the cover artwork of the release group.
|
||||||
|
albumCovers: [FanArtImage]
|
||||||
|
|
||||||
|
# A list of 1000x1000 PNG images of the physical disc media for the release
|
||||||
|
# group, with transparent backgrounds.
|
||||||
|
discImages: [FanArtDiscImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Artist {
|
||||||
|
# Images of the artist from [fanart.tv](https://fanart.tv/).
|
||||||
|
# This field is provided by the fanart.tv extension.
|
||||||
|
fanArt: FanArtArtist
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Label {
|
||||||
|
# Images of the label from [fanart.tv](https://fanart.tv/).
|
||||||
|
# This field is provided by the fanart.tv extension.
|
||||||
|
fanArt: FanArtLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type ReleaseGroup {
|
||||||
|
# Images of the release group from [fanart.tv](https://fanart.tv/).
|
||||||
|
# This field is provided by the fanart.tv extension.
|
||||||
|
fanArt: FanArtAlbum
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
54
src/extensions/mediawiki/client.js
Normal file
54
src/extensions/mediawiki/client.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import URL from 'url'
|
||||||
|
import Client from '../../api/client'
|
||||||
|
|
||||||
|
export default class MediaWikiClient extends Client {
|
||||||
|
constructor ({
|
||||||
|
limit = 10,
|
||||||
|
period = 1000,
|
||||||
|
...options
|
||||||
|
} = {}) {
|
||||||
|
super({ limit, period, ...options })
|
||||||
|
}
|
||||||
|
|
||||||
|
imageInfo (page) {
|
||||||
|
const pageURL = URL.parse(page, true)
|
||||||
|
const ClientError = this.errorClass
|
||||||
|
|
||||||
|
if (!pageURL.pathname.startsWith('/wiki/')) {
|
||||||
|
return Promise.reject(new ClientError(
|
||||||
|
`MediaWiki page URL does not have the expected /wiki/ prefix: ${page}`
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiURL = URL.format({
|
||||||
|
protocol: pageURL.protocol,
|
||||||
|
auth: pageURL.auth,
|
||||||
|
host: pageURL.host,
|
||||||
|
pathname: '/w/api.php',
|
||||||
|
query: {
|
||||||
|
action: 'query',
|
||||||
|
titles: pageURL.pathname.slice(6),
|
||||||
|
prop: 'imageinfo',
|
||||||
|
iiprop: 'url|size|canonicaltitle|user|extmetadata',
|
||||||
|
format: 'json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.get(apiURL, { json: true })
|
||||||
|
.then(body => {
|
||||||
|
const pageIDs = Object.keys(body.query.pages)
|
||||||
|
if (pageIDs.length !== 1) {
|
||||||
|
throw new ClientError(
|
||||||
|
`Query returned multiple pages: [${pageIDs.join(', ')}]`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const imageInfo = body.query.pages[pageIDs[0]].imageinfo
|
||||||
|
if (imageInfo.length !== 1) {
|
||||||
|
throw new ClientError(
|
||||||
|
`Query returned info for ${imageInfo.length} images, expected 1.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return imageInfo[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/extensions/mediawiki/index.js
Normal file
34
src/extensions/mediawiki/index.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import schema from './schema'
|
||||||
|
import resolvers from './resolvers'
|
||||||
|
import createLoader from './loader'
|
||||||
|
import MediaWikiClient from './client'
|
||||||
|
import { ONE_DAY } from '../../util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MediaWiki',
|
||||||
|
description:
|
||||||
|
'Retrieve information from MediaWiki image pages, like the actual image ' +
|
||||||
|
'file URL and EXIF metadata.',
|
||||||
|
extendContext (context, { mediaWiki = {} } = {}) {
|
||||||
|
const client = new MediaWikiClient(mediaWiki)
|
||||||
|
const cacheSize = parseInt(
|
||||||
|
process.env.MEDIAWIKI_CACHE_SIZE || process.env.GRAPHBRAINZ_CACHE_SIZE || 8192,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cacheTTL = parseInt(
|
||||||
|
process.env.MEDIAWIKI_CACHE_TTL || process.env.GRAPHBRAINZ_CACHE_TTL || ONE_DAY,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...context,
|
||||||
|
loaders: {
|
||||||
|
...context.loaders,
|
||||||
|
mediaWiki: createLoader({ client, cacheSize, cacheTTL })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extendSchema: {
|
||||||
|
schemas: [schema],
|
||||||
|
resolvers
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/extensions/mediawiki/loader.js
Normal file
22
src/extensions/mediawiki/loader.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import DataLoader from 'dataloader'
|
||||||
|
import LRUCache from 'lru-cache'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:extensions/mediawiki')
|
||||||
|
|
||||||
|
export default function createLoader (options) {
|
||||||
|
const { client } = options
|
||||||
|
const cache = LRUCache({
|
||||||
|
max: options.cacheSize,
|
||||||
|
maxAge: options.cacheTTL,
|
||||||
|
dispose (key) {
|
||||||
|
debug(`Removed from cache. key=${key}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Make the cache Map-like.
|
||||||
|
cache.delete = cache.del
|
||||||
|
cache.clear = cache.reset
|
||||||
|
|
||||||
|
return new DataLoader(keys => {
|
||||||
|
return Promise.all(keys.map(key => client.imageInfo(key)))
|
||||||
|
}, { cacheMap: cache })
|
||||||
|
}
|
||||||
24
src/extensions/mediawiki/prologue.md
Normal file
24
src/extensions/mediawiki/prologue.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
On entities with [URL relationship types][relationships] that represent images,
|
||||||
|
this extension will find those URLs that appear to be MediaWiki image pages, and
|
||||||
|
use the [MediaWiki API][] to fetch information about the image. This information
|
||||||
|
will include the actual file URL, so you can use it as the `src` in an `<img>`
|
||||||
|
tag (for example).
|
||||||
|
|
||||||
|
MediaWiki image URLs are assumed to be those with a path that starts with
|
||||||
|
`/wiki/Image:` or `/wiki/File:`.
|
||||||
|
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`COVER_ART_ARCHIVE_BASE_URL`**: The base URL at which to access to Cover Art
|
||||||
|
Archive API. Defaults to `http://coverartarchive.org/`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`COVER_ART_ARCHIVE_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
|
|
||||||
|
[relationships]: https://musicbrainz.org/relationships
|
||||||
|
[MediaWiki API]: https://www.mediawiki.org/wiki/API:Main_page
|
||||||
80
src/extensions/mediawiki/resolvers.js
Normal file
80
src/extensions/mediawiki/resolvers.js
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import URL from 'url'
|
||||||
|
|
||||||
|
function resolveMediaWikiImages (source, args, { loaders }) {
|
||||||
|
const isURL = (relation) => relation['target-type'] === 'url'
|
||||||
|
let rels = source.relations ? source.relations.filter(isURL) : []
|
||||||
|
if (!rels.length) {
|
||||||
|
rels = loaders.lookup.load([source._type, source.id, { inc: 'url-rels' }])
|
||||||
|
.then(source => source.relations.filter(isURL))
|
||||||
|
}
|
||||||
|
return Promise.resolve(rels).then(rels => {
|
||||||
|
const pages = rels.filter(rel => {
|
||||||
|
if (rel.type === args.type) {
|
||||||
|
const url = URL.parse(rel.url.resource)
|
||||||
|
if (url.pathname.match(/^\/wiki\/(File|Image):/)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}).map(rel => rel.url.resource)
|
||||||
|
return loaders.mediaWiki.loadMany(pages)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
MediaWikiImage: {
|
||||||
|
descriptionURL: imageInfo => imageInfo.descriptionurl,
|
||||||
|
canonicalTitle: imageInfo => imageInfo.canonicaltitle,
|
||||||
|
objectName: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.ObjectName
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
descriptionHTML: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.ImageDescription
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
originalDateTimeHTML: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.DateTimeOriginal
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
categories: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.Categories
|
||||||
|
return data ? data.value.split('|') : []
|
||||||
|
},
|
||||||
|
artistHTML: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.Artist
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
creditHTML: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.Credit
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
licenseShortName: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.LicenseShortName
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
licenseURL: imageInfo => {
|
||||||
|
const data = imageInfo.extmetadata.LicenseUrl
|
||||||
|
return data ? data.value : null
|
||||||
|
},
|
||||||
|
metadata: imageInfo => Object.keys(imageInfo.extmetadata).map(key => {
|
||||||
|
const data = imageInfo.extmetadata[key]
|
||||||
|
return { ...data, name: key }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
MediaWikiImageMetadata: {
|
||||||
|
value: obj => obj.value == null ? obj.value : `${obj.value}`
|
||||||
|
},
|
||||||
|
Artist: {
|
||||||
|
mediaWikiImages: resolveMediaWikiImages
|
||||||
|
},
|
||||||
|
Instrument: {
|
||||||
|
mediaWikiImages: resolveMediaWikiImages
|
||||||
|
},
|
||||||
|
Label: {
|
||||||
|
mediaWikiImages: resolveMediaWikiImages
|
||||||
|
},
|
||||||
|
Place: {
|
||||||
|
mediaWikiImages: resolveMediaWikiImages
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/extensions/mediawiki/schema.js
Normal file
108
src/extensions/mediawiki/schema.js
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
export default `
|
||||||
|
|
||||||
|
type MediaWikiImage {
|
||||||
|
# The URL of the actual image file.
|
||||||
|
url: URLString!
|
||||||
|
|
||||||
|
# The URL of the wiki page describing the image.
|
||||||
|
descriptionURL: URLString
|
||||||
|
|
||||||
|
# The user who uploaded the file.
|
||||||
|
user: String
|
||||||
|
|
||||||
|
# The size of the file in bytes.
|
||||||
|
size: Int
|
||||||
|
|
||||||
|
# The pixel width of the image.
|
||||||
|
width: Int
|
||||||
|
|
||||||
|
# The pixel height of the image.
|
||||||
|
height: Int
|
||||||
|
|
||||||
|
# The canonical title of the file.
|
||||||
|
canonicalTitle: String
|
||||||
|
|
||||||
|
# The image title, brief description, or file name.
|
||||||
|
objectName: String
|
||||||
|
|
||||||
|
# A description of the image, potentially containing HTML.
|
||||||
|
descriptionHTML: String
|
||||||
|
|
||||||
|
# The original date of creation of the image. May be a description rather than
|
||||||
|
# a parseable timestamp, and may contain HTML.
|
||||||
|
originalDateTimeHTML: String
|
||||||
|
|
||||||
|
# A list of the categories of the image.
|
||||||
|
categories: [String]!
|
||||||
|
|
||||||
|
# The name of the image author, potentially containing HTML.
|
||||||
|
artistHTML: String
|
||||||
|
|
||||||
|
# The source of the image, potentially containing HTML.
|
||||||
|
creditHTML: String
|
||||||
|
|
||||||
|
# A short human-readable license name.
|
||||||
|
licenseShortName: String
|
||||||
|
|
||||||
|
# A web address where the license is described.
|
||||||
|
licenseURL: URLString
|
||||||
|
|
||||||
|
# The full list of values in the \`extmetadata\` field.
|
||||||
|
metadata: [MediaWikiImageMetadata]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# An entry in the \`extmetadata\` field of a MediaWiki image file.
|
||||||
|
type MediaWikiImageMetadata {
|
||||||
|
# The name of the metadata field.
|
||||||
|
name: String!
|
||||||
|
# The value of the metadata field. All values will be converted to strings.
|
||||||
|
value: String
|
||||||
|
# The source of the value.
|
||||||
|
source: String
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Artist {
|
||||||
|
# Artist images found at MediaWiki URLs in the artist’s URL relationships.
|
||||||
|
# Defaults to URL relationships with the type “image”.
|
||||||
|
# This field is provided by the MediaWiki extension.
|
||||||
|
mediaWikiImages(
|
||||||
|
# The type of URL relationship that will be selected to find images. See
|
||||||
|
# the possible [Artist-URL relationship types](https://musicbrainz.org/relationships/artist-url).
|
||||||
|
type: String = "image"
|
||||||
|
): [MediaWikiImage]!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Instrument {
|
||||||
|
# Instrument images found at MediaWiki URLs in the instrument’s URL
|
||||||
|
# relationships. Defaults to URL relationships with the type “image”.
|
||||||
|
# This field is provided by the MediaWiki extension.
|
||||||
|
mediaWikiImages(
|
||||||
|
# The type of URL relationship that will be selected to find images. See the
|
||||||
|
# possible [Instrument-URL relationship types](https://musicbrainz.org/relationships/instrument-url).
|
||||||
|
type: String = "image"
|
||||||
|
): [MediaWikiImage]!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Label {
|
||||||
|
# Label images found at MediaWiki URLs in the label’s URL relationships.
|
||||||
|
# Defaults to URL relationships with the type “logo”.
|
||||||
|
# This field is provided by the MediaWiki extension.
|
||||||
|
mediaWikiImages(
|
||||||
|
# The type of URL relationship that will be selected to find images. See the
|
||||||
|
# possible [Label-URL relationship types](https://musicbrainz.org/relationships/label-url).
|
||||||
|
type: String = "logo"
|
||||||
|
): [MediaWikiImage]!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Place {
|
||||||
|
# Place images found at MediaWiki URLs in the place’s URL relationships.
|
||||||
|
# Defaults to URL relationships with the type “image”.
|
||||||
|
# This field is provided by the MediaWiki extension.
|
||||||
|
mediaWikiImages(
|
||||||
|
# The type of URL relationship that will be selected to find images. See the
|
||||||
|
# possible [Place-URL relationship types](https://musicbrainz.org/relationships/place-url).
|
||||||
|
type: String = "image"
|
||||||
|
): [MediaWikiImage]!
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
70
src/extensions/the-audio-db/client.js
Normal file
70
src/extensions/the-audio-db/client.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import Client from '../../api/client'
|
||||||
|
|
||||||
|
export default class TheAudioDBClient extends Client {
|
||||||
|
constructor ({
|
||||||
|
apiKey = process.env.THEAUDIODB_API_KEY,
|
||||||
|
baseURL = process.env.THEAUDIODB_BASE_URL || 'http://www.theaudiodb.com/api/v1/json/',
|
||||||
|
limit = 10,
|
||||||
|
period = 1000,
|
||||||
|
...options
|
||||||
|
} = {}) {
|
||||||
|
super({ baseURL, limit, period, ...options })
|
||||||
|
this.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
get (path, options = {}) {
|
||||||
|
const ClientError = this.errorClass
|
||||||
|
if (!this.apiKey) {
|
||||||
|
return Promise.reject(new ClientError(
|
||||||
|
'No API key was configured for TheAudioDB client.'
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return super.get(`${this.apiKey}/${path}`, { json: true, ...options })
|
||||||
|
}
|
||||||
|
|
||||||
|
entity (entityType, mbid) {
|
||||||
|
const ClientError = this.errorClass
|
||||||
|
switch (entityType) {
|
||||||
|
case 'artist':
|
||||||
|
return this.artist(mbid)
|
||||||
|
case 'release-group':
|
||||||
|
return this.album(mbid)
|
||||||
|
case 'recording':
|
||||||
|
return this.track(mbid)
|
||||||
|
default:
|
||||||
|
return Promise.reject(new ClientError(
|
||||||
|
`Entity type unsupported: ${entityType}`
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
artist (mbid) {
|
||||||
|
return this.get('artist-mb.php', { qs: { i: mbid } })
|
||||||
|
.then(body => {
|
||||||
|
if (body.artists && body.artists.length === 1) {
|
||||||
|
return body.artists[0]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
album (mbid) {
|
||||||
|
return this.get('album-mb.php', { qs: { i: mbid } })
|
||||||
|
.then(body => {
|
||||||
|
if (body.album && body.album.length === 1) {
|
||||||
|
return body.album[0]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
track (mbid) {
|
||||||
|
return this.get('track-mb.php', { qs: { i: mbid } })
|
||||||
|
.then(body => {
|
||||||
|
if (body.track && body.track.length === 1) {
|
||||||
|
return body.track[0]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/extensions/the-audio-db/index.js
Normal file
34
src/extensions/the-audio-db/index.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import schema from './schema'
|
||||||
|
import resolvers from './resolvers'
|
||||||
|
import createLoader from './loader'
|
||||||
|
import TheAudioDBClient from './client'
|
||||||
|
import { ONE_DAY } from '../../util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TheAudioDB',
|
||||||
|
description:
|
||||||
|
'Retrieve images and information about artists, releases, and recordings ' +
|
||||||
|
'from [TheAudioDB.com](http://www.theaudiodb.com/).',
|
||||||
|
extendContext (context, { theAudioDB = {} } = {}) {
|
||||||
|
const client = new TheAudioDBClient(theAudioDB)
|
||||||
|
const cacheSize = parseInt(
|
||||||
|
process.env.THEAUDIODB_CACHE_SIZE || process.env.GRAPHBRAINZ_CACHE_SIZE || 8192,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cacheTTL = parseInt(
|
||||||
|
process.env.THEAUDIODB_CACHE_TTL || process.env.GRAPHBRAINZ_CACHE_TTL || ONE_DAY,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...context,
|
||||||
|
loaders: {
|
||||||
|
...context.loaders,
|
||||||
|
theAudioDB: createLoader({ client, cacheSize, cacheTTL })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extendSchema: {
|
||||||
|
schemas: [schema],
|
||||||
|
resolvers
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/extensions/the-audio-db/loader.js
Normal file
28
src/extensions/the-audio-db/loader.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import DataLoader from 'dataloader'
|
||||||
|
import LRUCache from 'lru-cache'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:extensions/the-audio-db')
|
||||||
|
|
||||||
|
export default function createLoader (options) {
|
||||||
|
const { client } = options
|
||||||
|
const cache = LRUCache({
|
||||||
|
max: options.cacheSize,
|
||||||
|
maxAge: options.cacheTTL,
|
||||||
|
dispose (key) {
|
||||||
|
debug(`Removed from cache. key=${key}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Make the cache Map-like.
|
||||||
|
cache.delete = cache.del
|
||||||
|
cache.clear = cache.reset
|
||||||
|
|
||||||
|
return new DataLoader(keys => {
|
||||||
|
return Promise.all(keys.map(key => {
|
||||||
|
const [ entityType, id ] = key
|
||||||
|
return client.entity(entityType, id)
|
||||||
|
}))
|
||||||
|
}, {
|
||||||
|
cacheKeyFn: ([ entityType, id ]) => `${entityType}/${id}`,
|
||||||
|
cacheMap: cache
|
||||||
|
})
|
||||||
|
}
|
||||||
14
src/extensions/the-audio-db/prologue.md
Normal file
14
src/extensions/the-audio-db/prologue.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
This extension uses its own cache, separate from the MusicBrainz loader cache.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This extension can be configured using environment variables:
|
||||||
|
|
||||||
|
* **`THEAUDIODB_API_KEY`**: TheAudioDB API key to use. This is required for any
|
||||||
|
fields added by the extension to successfully resolve.
|
||||||
|
* **`THEAUDIODB_BASE_URL`**: The base URL at which to access TheAudioDB API.
|
||||||
|
Defaults to `http://www.theaudiodb.com/api/v1/json/`.
|
||||||
|
* **`THEAUDIODB_CACHE_SIZE`**: The number of items to keep in the cache.
|
||||||
|
Defaults to `GRAPHBRAINZ_CACHE_SIZE` if defined, or `8192`.
|
||||||
|
* **`THEAUDIODB_CACHE_TTL`**: The number of seconds to keep items in the
|
||||||
|
cache. Defaults to `GRAPHBRAINZ_CACHE_TTL` if defined, or `86400000` (one day).
|
||||||
107
src/extensions/the-audio-db/resolvers.js
Normal file
107
src/extensions/the-audio-db/resolvers.js
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
function handleImageSize (resolver) {
|
||||||
|
return (source, args, context, info) => {
|
||||||
|
const getURL = (url) => args.size === 'PREVIEW' ? `${url}/preview` : url
|
||||||
|
const url = resolver(source, args, context, info)
|
||||||
|
if (!url) {
|
||||||
|
return null
|
||||||
|
} else if (Array.isArray(url)) {
|
||||||
|
return url.map(getURL)
|
||||||
|
} else {
|
||||||
|
return getURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
TheAudioDBArtist: {
|
||||||
|
artistID: artist => artist.idArtist,
|
||||||
|
biography: (artist, args) => {
|
||||||
|
const lang = args.lang.toUpperCase()
|
||||||
|
return artist[`strBiography${lang}`] || null
|
||||||
|
},
|
||||||
|
memberCount: artist => artist.intMembers,
|
||||||
|
banner: handleImageSize(artist => artist.strArtistBanner),
|
||||||
|
fanArt: handleImageSize(artist => {
|
||||||
|
return [
|
||||||
|
artist.strArtistFanart,
|
||||||
|
artist.strArtistFanart2,
|
||||||
|
artist.strArtistFanart3
|
||||||
|
].filter(Boolean)
|
||||||
|
}),
|
||||||
|
logo: handleImageSize(artist => artist.strArtistLogo),
|
||||||
|
thumbnail: handleImageSize(artist => artist.strArtistThumb),
|
||||||
|
genre: artist => artist.strGenre || null,
|
||||||
|
mood: artist => artist.strMood || null,
|
||||||
|
style: artist => artist.strStyle || null
|
||||||
|
},
|
||||||
|
TheAudioDBAlbum: {
|
||||||
|
albumID: album => album.idAlbum,
|
||||||
|
artistID: album => album.idArtist,
|
||||||
|
description: (album, args) => {
|
||||||
|
const lang = args.lang.toUpperCase()
|
||||||
|
return album[`strDescription${lang}`] || null
|
||||||
|
},
|
||||||
|
salesCount: album => album.intSales,
|
||||||
|
score: album => album.intScore,
|
||||||
|
scoreVotes: album => album.intScoreVotes,
|
||||||
|
discImage: handleImageSize(album => album.strAlbumCDart),
|
||||||
|
spineImage: handleImageSize(album => album.strAlbumSpine),
|
||||||
|
frontImage: handleImageSize(album => album.strAlbumThumb),
|
||||||
|
backImage: handleImageSize(album => album.strAlbumThumbBack),
|
||||||
|
review: album => album.strReview || null,
|
||||||
|
genre: album => album.strGenre || null,
|
||||||
|
mood: album => album.strMood || null,
|
||||||
|
style: album => album.strStyle || null,
|
||||||
|
speed: album => album.strSpeed || null,
|
||||||
|
theme: album => album.strTheme || null
|
||||||
|
},
|
||||||
|
TheAudioDBTrack: {
|
||||||
|
trackID: track => track.idTrack,
|
||||||
|
albumID: track => track.idAlbum,
|
||||||
|
artistID: track => track.idArtist,
|
||||||
|
description: (track, args) => {
|
||||||
|
const lang = args.lang.toUpperCase()
|
||||||
|
return track[`strDescription${lang}`] || null
|
||||||
|
},
|
||||||
|
thumbnail: handleImageSize(track => track.strTrackThumb),
|
||||||
|
score: track => track.intScore,
|
||||||
|
scoreVotes: track => track.intScoreVotes,
|
||||||
|
trackNumber: track => track.intTrackNumber,
|
||||||
|
musicVideo: track => track,
|
||||||
|
genre: track => track.strGenre || null,
|
||||||
|
mood: track => track.strMood || null,
|
||||||
|
style: track => track.strStyle || null,
|
||||||
|
theme: track => track.strTheme || null
|
||||||
|
},
|
||||||
|
TheAudioDBMusicVideo: {
|
||||||
|
url: track => track.strMusicVid || null,
|
||||||
|
companyName: track => track.strMusicVidCompany || null,
|
||||||
|
directorName: track => track.strMusicVidDirector || null,
|
||||||
|
screenshots: handleImageSize(track => {
|
||||||
|
return [
|
||||||
|
track.strMusicVidScreen1,
|
||||||
|
track.strMusicVidScreen2,
|
||||||
|
track.strMusicVidScreen3
|
||||||
|
].filter(Boolean)
|
||||||
|
}),
|
||||||
|
viewCount: track => track.intMusicVidViews,
|
||||||
|
likeCount: track => track.intMusicVidLikes,
|
||||||
|
dislikeCount: track => track.intMusicVidDislikes,
|
||||||
|
commentCount: track => track.intMusicVidComments
|
||||||
|
},
|
||||||
|
Artist: {
|
||||||
|
theAudioDB: (artist, args, context) => {
|
||||||
|
return context.loaders.theAudioDB.load(['artist', artist.id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Recording: {
|
||||||
|
theAudioDB: (recording, args, context) => {
|
||||||
|
return context.loaders.theAudioDB.load(['recording', recording.id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReleaseGroup: {
|
||||||
|
theAudioDB: (releaseGroup, args, context) => {
|
||||||
|
return context.loaders.theAudioDB.load(['release-group', releaseGroup.id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
231
src/extensions/the-audio-db/schema.js
Normal file
231
src/extensions/the-audio-db/schema.js
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
export default `
|
||||||
|
|
||||||
|
# The image sizes that may be requested at [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
enum TheAudioDBImageSize {
|
||||||
|
# The image’s full original dimensions.
|
||||||
|
FULL
|
||||||
|
|
||||||
|
# A maximum dimension of 200px.
|
||||||
|
PREVIEW
|
||||||
|
}
|
||||||
|
|
||||||
|
# An artist on [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
type TheAudioDBArtist {
|
||||||
|
# TheAudioDB ID of the artist.
|
||||||
|
artistID: ID
|
||||||
|
|
||||||
|
# A biography of the artist, often available in several languages.
|
||||||
|
biography(
|
||||||
|
# The two-letter code for the language in which to retrieve the biography.
|
||||||
|
lang: String = "en"
|
||||||
|
): String
|
||||||
|
|
||||||
|
# The number of members in the musical group, if applicable.
|
||||||
|
memberCount: Int
|
||||||
|
|
||||||
|
# A 1000x185 JPG banner image containing the artist and their logo or name.
|
||||||
|
banner(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# A list of 1280x720 or 1920x1080 JPG images depicting the artist.
|
||||||
|
fanArt(
|
||||||
|
# The size of the images to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): [URLString]!
|
||||||
|
|
||||||
|
# A 400x155 PNG image containing the artist’s logo or name, with a transparent
|
||||||
|
# background.
|
||||||
|
logo(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# A 1000x1000 JPG thumbnail image picturing the artist (usually containing
|
||||||
|
# every member of a band).
|
||||||
|
thumbnail(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The primary musical genre of the artist (e.g. “Alternative Rock”).
|
||||||
|
genre: String
|
||||||
|
|
||||||
|
# The primary musical mood of the artist (e.g. “Sad”).
|
||||||
|
mood: String
|
||||||
|
|
||||||
|
# The primary musical style of the artist (e.g. “Rock/Pop”).
|
||||||
|
style: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# An album on [TheAudioDB](http://www.theaudiodb.com/) corresponding with a
|
||||||
|
# MusicBrainz Release Group.
|
||||||
|
type TheAudioDBAlbum {
|
||||||
|
# TheAudioDB ID of the album.
|
||||||
|
albumID: ID
|
||||||
|
|
||||||
|
# TheAudioDB ID of the artist who released the album.
|
||||||
|
artistID: ID
|
||||||
|
|
||||||
|
# A description of the album, often available in several languages.
|
||||||
|
description(
|
||||||
|
# The two-letter code for the language in which to retrieve the biography.
|
||||||
|
lang: String = "en"
|
||||||
|
): String
|
||||||
|
|
||||||
|
# A review of the album.
|
||||||
|
review: String
|
||||||
|
|
||||||
|
# The worldwide sales figure.
|
||||||
|
salesCount: Int
|
||||||
|
|
||||||
|
# The album’s rating as determined by user votes, out of 10.
|
||||||
|
score: Float
|
||||||
|
|
||||||
|
# The number of users who voted to determine the album’s score.
|
||||||
|
scoreVotes: Int
|
||||||
|
|
||||||
|
# An image of the physical disc media for the album.
|
||||||
|
discImage(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# An image of the spine of the album packaging.
|
||||||
|
spineImage(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# An image of the front of the album packaging.
|
||||||
|
frontImage(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# An image of the back of the album packaging.
|
||||||
|
backImage(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The primary musical genre of the album (e.g. “Alternative Rock”).
|
||||||
|
genre: String
|
||||||
|
|
||||||
|
# The primary musical mood of the album (e.g. “Sad”).
|
||||||
|
mood: String
|
||||||
|
|
||||||
|
# The primary musical style of the album (e.g. “Rock/Pop”).
|
||||||
|
style: String
|
||||||
|
|
||||||
|
# A rough description of the primary musical speed of the album (e.g. “Medium”).
|
||||||
|
speed: String
|
||||||
|
|
||||||
|
# The primary musical theme of the album (e.g. “In Love”).
|
||||||
|
theme: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# A track on [TheAudioDB](http://www.theaudiodb.com/) corresponding with a
|
||||||
|
# MusicBrainz Recording.
|
||||||
|
type TheAudioDBTrack {
|
||||||
|
# TheAudioDB ID of the track.
|
||||||
|
trackID: ID
|
||||||
|
|
||||||
|
# TheAudioDB ID of the album on which the track appears.
|
||||||
|
albumID: ID
|
||||||
|
|
||||||
|
# TheAudioDB ID of the artist who released the track.
|
||||||
|
artistID: ID
|
||||||
|
|
||||||
|
# A description of the track.
|
||||||
|
description(
|
||||||
|
lang: String = "en"
|
||||||
|
): String
|
||||||
|
|
||||||
|
# A thumbnail image for the track.
|
||||||
|
thumbnail(
|
||||||
|
# The size of the image to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): URLString
|
||||||
|
|
||||||
|
# The track’s rating as determined by user votes, out of 10.
|
||||||
|
score: Float
|
||||||
|
|
||||||
|
# The number of users who voted to determine the album’s score.
|
||||||
|
scoreVotes: Int
|
||||||
|
|
||||||
|
# The track number of the song on the album.
|
||||||
|
trackNumber: Int
|
||||||
|
|
||||||
|
# The official music video for the track.
|
||||||
|
musicVideo: TheAudioDBMusicVideo
|
||||||
|
|
||||||
|
# The primary musical genre of the track (e.g. “Alternative Rock”).
|
||||||
|
genre: String
|
||||||
|
|
||||||
|
# The primary musical mood of the track (e.g. “Sad”).
|
||||||
|
mood: String
|
||||||
|
|
||||||
|
# The primary musical style of the track (e.g. “Rock/Pop”).
|
||||||
|
style: String
|
||||||
|
|
||||||
|
# The primary musical theme of the track (e.g. “In Love”).
|
||||||
|
theme: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# Details of a music video associated with a track on [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
type TheAudioDBMusicVideo {
|
||||||
|
# The URL where the music video can be found.
|
||||||
|
url: URLString
|
||||||
|
|
||||||
|
# The video production company of the music video.
|
||||||
|
companyName: String
|
||||||
|
|
||||||
|
# The director of the music video.
|
||||||
|
directorName: String
|
||||||
|
|
||||||
|
# A list of still images from the music video.
|
||||||
|
screenshots(
|
||||||
|
# The size of the images to retrieve.
|
||||||
|
size: TheAudioDBImageSize = FULL
|
||||||
|
): [URLString]!
|
||||||
|
|
||||||
|
# The number of views the video has received at the given URL. This will rarely
|
||||||
|
# be up to date, so use cautiously.
|
||||||
|
viewCount: Int
|
||||||
|
|
||||||
|
# The number of likes the video has received at the given URL. This will rarely
|
||||||
|
# be up to date, so use cautiously.
|
||||||
|
likeCount: Int
|
||||||
|
|
||||||
|
# The number of dislikes the video has received at the given URL. This will
|
||||||
|
# rarely be up to date, so use cautiously.
|
||||||
|
dislikeCount: Int
|
||||||
|
|
||||||
|
# The number of comments the video has received at the given URL. This will
|
||||||
|
# rarely be up to date, so use cautiously.
|
||||||
|
commentCount: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Artist {
|
||||||
|
# Data about the artist from [TheAudioDB](http://www.theaudiodb.com/), a good
|
||||||
|
# source of biographical information and images.
|
||||||
|
# This field is provided by TheAudioDB extension.
|
||||||
|
theAudioDB: TheAudioDBArtist
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Recording {
|
||||||
|
# Data about the recording from [TheAudioDB](http://www.theaudiodb.com/).
|
||||||
|
# This field is provided by TheAudioDB extension.
|
||||||
|
theAudioDB: TheAudioDBTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type ReleaseGroup {
|
||||||
|
# Data about the release group from [TheAudioDB](http://www.theaudiodb.com/),
|
||||||
|
# a good source of descriptive information, reviews, and images.
|
||||||
|
# This field is provided by TheAudioDB extension.
|
||||||
|
theAudioDB: TheAudioDBAlbum
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
38
src/index.js
38
src/index.js
|
|
@ -1,9 +1,11 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import graphqlHTTP from 'express-graphql'
|
import graphqlHTTP from 'express-graphql'
|
||||||
import compression from 'compression'
|
import compression from 'compression'
|
||||||
import MusicBrainz, { CoverArtArchive } from './api'
|
import MusicBrainz from './api'
|
||||||
import schema from './schema'
|
import schema, { createSchema } from './schema'
|
||||||
import createLoaders from './loaders'
|
import { createContext } from './context'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz')
|
||||||
|
|
||||||
const formatError = (err) => ({
|
const formatError = (err) => ({
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
|
@ -13,19 +15,37 @@ const formatError = (err) => ({
|
||||||
|
|
||||||
const middleware = ({
|
const middleware = ({
|
||||||
client = new MusicBrainz(),
|
client = new MusicBrainz(),
|
||||||
coverArtClient = new CoverArtArchive(),
|
extensions = process.env.GRAPHBRAINZ_EXTENSIONS
|
||||||
...options
|
? JSON.parse(process.env.GRAPHBRAINZ_EXTENSIONS)
|
||||||
|
: [
|
||||||
|
'./extensions/cover-art-archive',
|
||||||
|
'./extensions/fanart-tv',
|
||||||
|
'./extensions/mediawiki',
|
||||||
|
'./extensions/the-audio-db'
|
||||||
|
],
|
||||||
|
...middlewareOptions
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
|
debug(`Loading ${extensions.length} extension(s).`)
|
||||||
|
const options = {
|
||||||
|
client,
|
||||||
|
extensions: extensions.map(extensionModule => {
|
||||||
|
if (typeof extensionModule === 'object') {
|
||||||
|
return extensionModule
|
||||||
|
}
|
||||||
|
const extension = require(extensionModule)
|
||||||
|
return extension.default ? extension.default : extension
|
||||||
|
}),
|
||||||
|
...middlewareOptions
|
||||||
|
}
|
||||||
const DEV = process.env.NODE_ENV !== 'production'
|
const DEV = process.env.NODE_ENV !== 'production'
|
||||||
const graphiql = DEV || process.env.GRAPHBRAINZ_GRAPHIQL === 'true'
|
const graphiql = DEV || process.env.GRAPHBRAINZ_GRAPHIQL === 'true'
|
||||||
const loaders = createLoaders(client, coverArtClient)
|
|
||||||
return graphqlHTTP({
|
return graphqlHTTP({
|
||||||
schema,
|
schema: createSchema(schema, options),
|
||||||
context: { client, coverArtClient, loaders },
|
context: createContext(options),
|
||||||
pretty: DEV,
|
pretty: DEV,
|
||||||
graphiql,
|
graphiql,
|
||||||
formatError: DEV ? formatError : undefined,
|
formatError: DEV ? formatError : undefined,
|
||||||
...options
|
...middlewareOptions
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import DataLoader from 'dataloader'
|
import DataLoader from 'dataloader'
|
||||||
import LRUCache from 'lru-cache'
|
import LRUCache from 'lru-cache'
|
||||||
import { toPlural } from './types/helpers'
|
import { toPlural } from './types/helpers'
|
||||||
|
import { ONE_DAY } from './util'
|
||||||
|
|
||||||
const debug = require('debug')('graphbrainz:loaders')
|
const debug = require('debug')('graphbrainz:loaders')
|
||||||
const ONE_DAY = 24 * 60 * 60 * 1000
|
|
||||||
|
|
||||||
export default function createLoaders (client, coverArtClient) {
|
export default function createLoaders (client) {
|
||||||
// All loaders share a single LRU cache that will remember 8192 responses,
|
// All loaders share a single LRU cache that will remember 8192 responses,
|
||||||
// each cached for 1 day.
|
// each cached for 1 day.
|
||||||
const cache = LRUCache({
|
const cache = LRUCache({
|
||||||
|
|
@ -70,36 +70,5 @@ export default function createLoaders (client, coverArtClient) {
|
||||||
cacheMap: cache
|
cacheMap: cache
|
||||||
})
|
})
|
||||||
|
|
||||||
const coverArt = new DataLoader(keys => {
|
return { lookup, browse, search }
|
||||||
return Promise.all(keys.map(key => {
|
|
||||||
const [ entityType, id ] = key
|
|
||||||
return coverArtClient.images(...key).catch(err => {
|
|
||||||
if (err.statusCode === 404) {
|
|
||||||
return { images: [] }
|
|
||||||
}
|
|
||||||
throw err
|
|
||||||
}).then(coverArt => {
|
|
||||||
coverArt._parentType = entityType
|
|
||||||
coverArt._parentID = id
|
|
||||||
if (entityType === 'release') {
|
|
||||||
coverArt._release = id
|
|
||||||
} else {
|
|
||||||
coverArt._release = coverArt.release && coverArt.release.split('/').pop()
|
|
||||||
}
|
|
||||||
return coverArt
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}, {
|
|
||||||
cacheKeyFn: key => `cover-art/${coverArtClient.getImagesURL(...key)}`,
|
|
||||||
cacheMap: cache
|
|
||||||
})
|
|
||||||
|
|
||||||
const coverArtURL = new DataLoader(keys => {
|
|
||||||
return Promise.all(keys.map(key => coverArtClient.imageURL(...key)))
|
|
||||||
}, {
|
|
||||||
cacheKeyFn: key => `cover-art/url/${coverArtClient.getImageURL(...key)}`,
|
|
||||||
cacheMap: cache
|
|
||||||
})
|
|
||||||
|
|
||||||
return { lookup, browse, search, coverArt, coverArtURL }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,50 @@
|
||||||
import { GraphQLSchema, GraphQLObjectType } from 'graphql'
|
import { GraphQLSchema, GraphQLObjectType, extendSchema, parse } from 'graphql'
|
||||||
|
import { addResolveFunctionsToSchema } from 'graphql-tools'
|
||||||
import { lookup, browse, search } from './queries'
|
import { lookup, browse, search } from './queries'
|
||||||
import { nodeField } from './types/node'
|
import { nodeField } from './types/node'
|
||||||
|
|
||||||
|
const debug = require('debug')('graphbrainz:schema')
|
||||||
|
|
||||||
|
export function applyExtension (extension, schema, options = {}) {
|
||||||
|
let outputSchema = schema
|
||||||
|
if (extension.extendSchema) {
|
||||||
|
if (typeof extension.extendSchema === 'object') {
|
||||||
|
debug(`Extending schema via an object from the “${extension.name}” extension.`)
|
||||||
|
const { schemas = [], resolvers } = extension.extendSchema
|
||||||
|
outputSchema = schemas.reduce((updatedSchema, extensionSchema) => {
|
||||||
|
if (typeof extensionSchema === 'string') {
|
||||||
|
extensionSchema = parse(extensionSchema)
|
||||||
|
}
|
||||||
|
return extendSchema(updatedSchema, extensionSchema)
|
||||||
|
}, outputSchema)
|
||||||
|
if (resolvers) {
|
||||||
|
addResolveFunctionsToSchema(outputSchema, resolvers)
|
||||||
|
}
|
||||||
|
} else if (typeof extension.extendSchema === 'function') {
|
||||||
|
debug(`Extending schema via a function from the “${extension.name}” extension.`)
|
||||||
|
outputSchema = extension.extendSchema(schema, options)
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`The “${extension.name}” extension contains an invalid ` +
|
||||||
|
`\`extendSchema\` value: ${extension.extendSchema}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix for `graphql-tools` creating a new Query type with no description.
|
||||||
|
if (outputSchema._queryType.description === undefined) {
|
||||||
|
outputSchema._queryType.description = schema._queryType.description
|
||||||
|
}
|
||||||
|
return outputSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSchema (schema, options = {}) {
|
||||||
|
const extensions = options.extensions || []
|
||||||
|
return extensions.reduce((updatedSchema, extension) => {
|
||||||
|
return applyExtension(extension, updatedSchema, options)
|
||||||
|
}, schema)
|
||||||
|
}
|
||||||
|
|
||||||
export default new GraphQLSchema({
|
export default new GraphQLSchema({
|
||||||
query: new GraphQLObjectType({
|
query: new GraphQLObjectType({
|
||||||
name: 'Query',
|
name: 'Query',
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import {
|
|
||||||
GraphQLObjectType,
|
|
||||||
GraphQLNonNull,
|
|
||||||
GraphQLList,
|
|
||||||
GraphQLBoolean,
|
|
||||||
GraphQLString,
|
|
||||||
GraphQLInt
|
|
||||||
} from 'graphql/type'
|
|
||||||
import { URLString } from './scalars'
|
|
||||||
|
|
||||||
export const CoverArtImageThumbnails = new GraphQLObjectType({
|
|
||||||
name: 'CoverArtImageThumbnails',
|
|
||||||
description: `URLs for thumbnails of different sizes for a particular piece of
|
|
||||||
cover art.`,
|
|
||||||
fields: () => ({
|
|
||||||
small: {
|
|
||||||
type: URLString,
|
|
||||||
description: `The URL of a small version of the cover art, where the
|
|
||||||
maximum dimension is 250px.`
|
|
||||||
},
|
|
||||||
large: {
|
|
||||||
type: URLString,
|
|
||||||
description: `The URL of a large version of the cover art, where the
|
|
||||||
maximum dimension is 500px.`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export default new GraphQLObjectType({
|
|
||||||
name: 'CoverArtImage',
|
|
||||||
description: 'An individual piece of album artwork from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).',
|
|
||||||
fields: () => ({
|
|
||||||
fileID: {
|
|
||||||
type: new GraphQLNonNull(GraphQLString),
|
|
||||||
description: 'The Internet Archive’s internal file ID for the image.',
|
|
||||||
resolve: image => image.id
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
type: new GraphQLNonNull(URLString),
|
|
||||||
description: 'The URL at which the image can be found.'
|
|
||||||
},
|
|
||||||
thumbnails: {
|
|
||||||
type: CoverArtImageThumbnails,
|
|
||||||
description: 'A set of thumbnails for the image.'
|
|
||||||
},
|
|
||||||
front: {
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
description: 'Whether this image depicts the “main front” of the release.'
|
|
||||||
},
|
|
||||||
back: {
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
description: 'Whether this image depicts the “main back” of the release.'
|
|
||||||
},
|
|
||||||
types: {
|
|
||||||
type: new GraphQLList(GraphQLString),
|
|
||||||
description: `A list of [image types](https://musicbrainz.org/doc/Cover_Art/Types)
|
|
||||||
describing what part(s) of the release the image includes.`
|
|
||||||
},
|
|
||||||
edit: {
|
|
||||||
type: GraphQLInt,
|
|
||||||
description: 'The MusicBrainz edit ID.'
|
|
||||||
},
|
|
||||||
approved: {
|
|
||||||
type: GraphQLBoolean,
|
|
||||||
description: 'Whether the image was approved by the MusicBrainz edit system.'
|
|
||||||
},
|
|
||||||
comment: {
|
|
||||||
type: GraphQLString,
|
|
||||||
description: 'A free-text comment left for the image.'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
import {
|
|
||||||
GraphQLObjectType,
|
|
||||||
GraphQLList,
|
|
||||||
GraphQLNonNull,
|
|
||||||
GraphQLBoolean,
|
|
||||||
GraphQLInt
|
|
||||||
} from 'graphql/type'
|
|
||||||
import CoverArtImage from './cover-art-image'
|
|
||||||
import { CoverArtImageSize } from './enums'
|
|
||||||
import Release from './release'
|
|
||||||
import { URLString } from './scalars'
|
|
||||||
import { resolveLookup } from '../resolvers'
|
|
||||||
import { getFields } from '../util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a resolver that will call `resolveFn` only if the requested field on
|
|
||||||
* the object is null or not present.
|
|
||||||
*/
|
|
||||||
function createFallbackResolver (resolveFn) {
|
|
||||||
return function resolve (coverArt, args, context, info) {
|
|
||||||
const value = coverArt[info.fieldName]
|
|
||||||
if (value == null) {
|
|
||||||
return resolveFn(coverArt, args, context, info)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveImage (coverArt, { size }, { loaders }, info) {
|
|
||||||
if (size === 'FULL') {
|
|
||||||
size = null
|
|
||||||
}
|
|
||||||
const field = info.fieldName
|
|
||||||
if (coverArt.images) {
|
|
||||||
const matches = coverArt.images.filter(image => image[field])
|
|
||||||
if (!matches.length) {
|
|
||||||
return null
|
|
||||||
} else if (matches.length === 1) {
|
|
||||||
const match = matches[0]
|
|
||||||
if (size === 250) {
|
|
||||||
return match.thumbnails.small
|
|
||||||
} else if (size === 500) {
|
|
||||||
return match.thumbnails.large
|
|
||||||
} else {
|
|
||||||
return match.image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (coverArt[field] !== false) {
|
|
||||||
const {
|
|
||||||
_parentType: entityType = 'release',
|
|
||||||
_parentID: id = coverArt._release
|
|
||||||
} = coverArt
|
|
||||||
return loaders.coverArtURL.load([entityType, id, field, size])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = {
|
|
||||||
type: CoverArtImageSize,
|
|
||||||
description: `The size of the image to retrieve. By default, the returned
|
|
||||||
image will have its full original dimensions, but certain thumbnail sizes may be
|
|
||||||
retrieved as well.`,
|
|
||||||
defaultValue: null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get around both the circular dependency between the release and cover art
|
|
||||||
* types, and not have to define an identical `release` field twice on
|
|
||||||
* `ReleaseCoverArt` and `ReleaseGroupCoverArt`.
|
|
||||||
*/
|
|
||||||
function createReleaseField () {
|
|
||||||
return {
|
|
||||||
type: new GraphQLNonNull(Release),
|
|
||||||
description: 'The particular release shown in the returned cover art.',
|
|
||||||
resolve: (coverArt, args, context, info) => {
|
|
||||||
const id = coverArt._release
|
|
||||||
const fields = Object.keys(getFields(info))
|
|
||||||
if (fields.length > 1 || fields[0] !== 'mbid') {
|
|
||||||
return resolveLookup(coverArt, { mbid: id }, context, info)
|
|
||||||
}
|
|
||||||
return { id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This type combines two sets of data from different places. One is a *summary*
|
|
||||||
// of the images available at the Cover Art Archive, found in the `cover-art-archive`
|
|
||||||
// field on releases. The other is the actual list of images with their metadata,
|
|
||||||
// fetched from the Cover Art Archive itself rather than MusicBrainz. Depending
|
|
||||||
// on what fields are requested, we may only need to fetch one or the other, or
|
|
||||||
// both. Much of the summary data can be reconstructed if we already fetched the
|
|
||||||
// full image list, for example.
|
|
||||||
export const ReleaseCoverArt = new GraphQLObjectType({
|
|
||||||
name: 'ReleaseCoverArt',
|
|
||||||
description: `An object containing a list of the cover art images for a
|
|
||||||
release obtained from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive),
|
|
||||||
as well as a summary of what artwork is available.`,
|
|
||||||
fields: () => ({
|
|
||||||
front: {
|
|
||||||
type: URLString,
|
|
||||||
description: `The URL of an image depicting the album cover or “main
|
|
||||||
front” of the release, i.e. the front of the packaging of the audio recording
|
|
||||||
(or in the case of a digital release, the image associated with it in a digital
|
|
||||||
media store).
|
|
||||||
|
|
||||||
In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
of a front image, whereas here the value is the URL for the image itself if one
|
|
||||||
exists. You can check for null if you just want to determine the presence of an
|
|
||||||
image.`,
|
|
||||||
args: { size },
|
|
||||||
resolve: resolveImage
|
|
||||||
},
|
|
||||||
back: {
|
|
||||||
type: URLString,
|
|
||||||
description: `The URL of an image depicting the “main back” of the
|
|
||||||
release, i.e. the back of the packaging of the audio recording.
|
|
||||||
|
|
||||||
In the MusicBrainz schema, this field is a Boolean value indicating the presence
|
|
||||||
of a back image, whereas here the value is the URL for the image itself. You can
|
|
||||||
check for null if you just want to determine the presence of an image.`,
|
|
||||||
args: { size },
|
|
||||||
resolve: resolveImage
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
type: new GraphQLList(CoverArtImage),
|
|
||||||
description: `A list of images depicting the different sides and surfaces
|
|
||||||
of a release’s media and packaging.`,
|
|
||||||
resolve: createFallbackResolver((coverArt, args, { loaders }) => {
|
|
||||||
if (coverArt.count === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return loaders.coverArt.load(['release', coverArt._release])
|
|
||||||
.then(coverArt => coverArt.images)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
artwork: {
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
description: 'Whether there is artwork present for this release.',
|
|
||||||
resolve: createFallbackResolver(coverArt => coverArt.images.length > 0)
|
|
||||||
},
|
|
||||||
darkened: {
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
description: `Whether the Cover Art Archive has received a take-down
|
|
||||||
request for this release’s artwork, disallowing new uploads.`,
|
|
||||||
resolve: createFallbackResolver((coverArt, args, { loaders }) => {
|
|
||||||
return loaders.lookup.load(['release', coverArt._release])
|
|
||||||
.then(release => release['cover-art-archive'].darkened)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
count: {
|
|
||||||
type: new GraphQLNonNull(GraphQLInt),
|
|
||||||
description: 'The number of artwork images present for this release.',
|
|
||||||
resolve: createFallbackResolver(coverArt => coverArt.images.length)
|
|
||||||
},
|
|
||||||
release: createReleaseField()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const ReleaseGroupCoverArt = new GraphQLObjectType({
|
|
||||||
name: 'ReleaseGroupCoverArt',
|
|
||||||
description: `An object containing the cover art for a release group obtained
|
|
||||||
from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive). For
|
|
||||||
release groups, just the front cover of a particular release will be selected.`,
|
|
||||||
fields: () => ({
|
|
||||||
front: {
|
|
||||||
type: URLString,
|
|
||||||
description: `The URL of an image depicting the album cover or “main
|
|
||||||
front” of a release in the release group, i.e. the front of the packaging of the
|
|
||||||
audio recording (or in the case of a digital release, the image associated with
|
|
||||||
it in a digital media store).`,
|
|
||||||
args: { size },
|
|
||||||
resolve: resolveImage
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
type: new GraphQLList(CoverArtImage),
|
|
||||||
description: `A list of images returned by the [Cover Art
|
|
||||||
Archive](https://musicbrainz.org/doc/Cover_Art_Archive) for a release group. A
|
|
||||||
particular release’s front image will be included in the list, and likely no
|
|
||||||
others, even if other images are available.`
|
|
||||||
},
|
|
||||||
artwork: {
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
description: 'Whether there is artwork present for this release group.',
|
|
||||||
resolve: createFallbackResolver(coverArt => coverArt.images.length > 0)
|
|
||||||
},
|
|
||||||
release: createReleaseField()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { GraphQLObjectType, GraphQLList } from 'graphql/type'
|
import { GraphQLObjectType, GraphQLList } from 'graphql/type'
|
||||||
import Node from './node'
|
import Node from './node'
|
||||||
import Entity from './entity'
|
import Entity from './entity'
|
||||||
import { ReleaseGroupCoverArt } from './cover-art'
|
|
||||||
import { DateType } from './scalars'
|
import { DateType } from './scalars'
|
||||||
import { ReleaseGroupType } from './enums'
|
import { ReleaseGroupType } from './enums'
|
||||||
import {
|
import {
|
||||||
|
|
@ -59,14 +58,6 @@ e.g. album, single, soundtrack, compilation, etc. A release group can have a
|
||||||
description: `Additional [types](https://musicbrainz.org/doc/Release_Group/Type)
|
description: `Additional [types](https://musicbrainz.org/doc/Release_Group/Type)
|
||||||
that apply to this release group.`
|
that apply to this release group.`
|
||||||
}),
|
}),
|
||||||
coverArt: {
|
|
||||||
type: ReleaseGroupCoverArt,
|
|
||||||
description: `The cover art for a release group, obtained from the [Cover
|
|
||||||
Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).`,
|
|
||||||
resolve: (releaseGroup, args, { loaders }) => {
|
|
||||||
return loaders.coverArt.load(['release-group', releaseGroup.id])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
artists,
|
artists,
|
||||||
releases,
|
releases,
|
||||||
relationships,
|
relationships,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
GraphQLObjectType,
|
GraphQLObjectType,
|
||||||
GraphQLNonNull,
|
|
||||||
GraphQLString,
|
GraphQLString,
|
||||||
GraphQLList
|
GraphQLList
|
||||||
} from 'graphql/type'
|
} from 'graphql/type'
|
||||||
|
|
@ -8,7 +7,6 @@ import Node from './node'
|
||||||
import Entity from './entity'
|
import Entity from './entity'
|
||||||
import { ASIN, DateType } from './scalars'
|
import { ASIN, DateType } from './scalars'
|
||||||
import Media from './media'
|
import Media from './media'
|
||||||
import { ReleaseCoverArt } from './cover-art'
|
|
||||||
import { ReleaseStatus } from './enums'
|
import { ReleaseStatus } from './enums'
|
||||||
import ReleaseEvent from './release-event'
|
import ReleaseEvent from './release-event'
|
||||||
import {
|
import {
|
||||||
|
|
@ -74,19 +72,6 @@ release has one. The most common types found on releases are 12-digit
|
||||||
[UPCs](https://en.wikipedia.org/wiki/Universal_Product_Code) and 13-digit
|
[UPCs](https://en.wikipedia.org/wiki/Universal_Product_Code) and 13-digit
|
||||||
[EANs](https://en.wikipedia.org/wiki/International_Article_Number).`
|
[EANs](https://en.wikipedia.org/wiki/International_Article_Number).`
|
||||||
},
|
},
|
||||||
coverArt: {
|
|
||||||
type: new GraphQLNonNull(ReleaseCoverArt),
|
|
||||||
description: `A list and summary of the cover art images that are present
|
|
||||||
for this release from the [Cover Art Archive](https://musicbrainz.org/doc/Cover_Art_Archive).`,
|
|
||||||
resolve: (release, args, { loaders }) => {
|
|
||||||
const coverArt = release['cover-art-archive']
|
|
||||||
if (coverArt) {
|
|
||||||
coverArt._release = release.id
|
|
||||||
return coverArt
|
|
||||||
}
|
|
||||||
return loaders.coverArt.load(['release', release.id])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...fieldWithID('status', {
|
...fieldWithID('status', {
|
||||||
type: ReleaseStatus,
|
type: ReleaseStatus,
|
||||||
description: 'The status describes how “official” a release is.'
|
description: 'The status describes how “official” a release is.'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import util from 'util'
|
import util from 'util'
|
||||||
|
|
||||||
|
export const ONE_DAY = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
export function getFields (info, fragments = info.fragments) {
|
export function getFields (info, fragments = info.fragments) {
|
||||||
if (info.kind !== 'Field') {
|
if (info.kind !== 'Field') {
|
||||||
info = info.fieldNodes[0]
|
info = info.fieldNodes[0]
|
||||||
|
|
@ -13,6 +15,8 @@ export function getFields (info, fragments = info.fragments) {
|
||||||
throw new Error(`Fragment '${name}' was not passed to getFields()`)
|
throw new Error(`Fragment '${name}' was not passed to getFields()`)
|
||||||
}
|
}
|
||||||
fragment.selectionSet.selections.reduce(reducer, fields)
|
fragment.selectionSet.selections.reduce(reducer, fields)
|
||||||
|
} else if (selection.kind === 'InlineFragment') {
|
||||||
|
selection.selectionSet.selections.reduce(reducer, fields)
|
||||||
} else {
|
} else {
|
||||||
fields[selection.name.value] = selection
|
fields[selection.name.value] = selection
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
import { CoverArtArchiveError } from '../../src/api'
|
import client from '../../helpers/client/cover-art-archive'
|
||||||
import client from '../helpers/client/cover-art-archive'
|
|
||||||
|
|
||||||
test('can retrieve a front image URL', t => {
|
test('can retrieve a front image URL', t => {
|
||||||
return client.imageURL('release', '76df3287-6cda-33eb-8e9a-044b5e15ffdd', 'front')
|
return client.imageURL('release', '76df3287-6cda-33eb-8e9a-044b5e15ffdd', 'front')
|
||||||
|
|
@ -37,7 +36,7 @@ test('can retrieve a list of release images', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('throws an error if given an invalid MBID', t => {
|
test('throws an error if given an invalid MBID', t => {
|
||||||
return t.throws(client.images('release', 'xyz'), CoverArtArchiveError)
|
return t.throws(client.images('release', 'xyz'), client.errorClass)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('uses the default error impementation if there is no HTML error', t => {
|
test('uses the default error impementation if there is no HTML error', t => {
|
||||||
174
test/extensions/cover-art-archive/schema.js
Normal file
174
test/extensions/cover-art-archive/schema.js
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
import test from 'ava'
|
||||||
|
import { graphql } from 'graphql'
|
||||||
|
import extension from '../../../src/extensions/cover-art-archive'
|
||||||
|
import baseSchema, { applyExtension } from '../../../src/schema'
|
||||||
|
import baseContext from '../../helpers/context'
|
||||||
|
|
||||||
|
const schema = applyExtension(extension, baseSchema)
|
||||||
|
const context = extension.extendContext(baseContext)
|
||||||
|
|
||||||
|
function testData (t, query, handler) {
|
||||||
|
return graphql(schema, query, null, context).then(result => {
|
||||||
|
if (result.errors !== undefined) {
|
||||||
|
console.log(result.errors)
|
||||||
|
}
|
||||||
|
t.is(result.errors, undefined)
|
||||||
|
return handler(t, result.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('releases have a cover art summary', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
release(mbid: "b84ee12a-09ef-421b-82de-0441a926375b") {
|
||||||
|
coverArtArchive {
|
||||||
|
artwork
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const { coverArtArchive } = data.lookup.release
|
||||||
|
t.true(coverArtArchive.artwork)
|
||||||
|
t.true(coverArtArchive.count >= 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('releases have a set of cover art images', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
release(mbid: "b84ee12a-09ef-421b-82de-0441a926375b") {
|
||||||
|
coverArtArchive {
|
||||||
|
front
|
||||||
|
back
|
||||||
|
images {
|
||||||
|
front
|
||||||
|
back
|
||||||
|
types
|
||||||
|
approved
|
||||||
|
edit
|
||||||
|
comment
|
||||||
|
fileID
|
||||||
|
image
|
||||||
|
thumbnails {
|
||||||
|
small
|
||||||
|
large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const { coverArtArchive } = data.lookup.release
|
||||||
|
t.is(coverArtArchive.front, 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/1611507818.jpg')
|
||||||
|
t.is(coverArtArchive.back, 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/13536418798.jpg')
|
||||||
|
t.true(coverArtArchive.images.length >= 10)
|
||||||
|
t.true(coverArtArchive.images.some(image => image.front === true))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.back === true))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.types.indexOf('Front') >= 0))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.types.indexOf('Back') >= 0))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.types.indexOf('Liner') >= 0))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.types.indexOf('Poster') >= 0))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.types.indexOf('Medium') >= 0))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.edit === 18544122))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.comment === ''))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.fileID === '1611507818'))
|
||||||
|
t.true(coverArtArchive.images.some(image => image.image === 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/13536422691.jpg'))
|
||||||
|
t.true(coverArtArchive.images.every(image => image.approved === true))
|
||||||
|
t.true(coverArtArchive.images.every(image => image.thumbnails.small))
|
||||||
|
t.true(coverArtArchive.images.every(image => image.thumbnails.large))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('can request a size for front and back cover art', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
release(mbid: "b84ee12a-09ef-421b-82de-0441a926375b") {
|
||||||
|
coverArtArchive {
|
||||||
|
front(size: LARGE)
|
||||||
|
back(size: SMALL)
|
||||||
|
fullFront: front(size: FULL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const { coverArtArchive } = data.lookup.release
|
||||||
|
t.is(coverArtArchive.front, 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/1611507818-500.jpg')
|
||||||
|
t.is(coverArtArchive.back, 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/13536418798-250.jpg')
|
||||||
|
t.is(coverArtArchive.fullFront, 'http://coverartarchive.org/release/b84ee12a-09ef-421b-82de-0441a926375b/1611507818.jpg')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('release groups have a front cover art image', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
releaseGroup(mbid: "f5093c06-23e3-404f-aeaa-40f72885ee3a") {
|
||||||
|
coverArtArchive {
|
||||||
|
artwork
|
||||||
|
front
|
||||||
|
images {
|
||||||
|
front
|
||||||
|
image
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
mbid
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const { coverArtArchive } = data.lookup.releaseGroup
|
||||||
|
t.true(coverArtArchive.artwork)
|
||||||
|
t.is(coverArtArchive.front, 'http://coverartarchive.org/release/25fbfbb4-b1ee-4448-aadf-ae3bc2e2dd27/1675312275.jpg')
|
||||||
|
t.is(coverArtArchive.release.mbid, '25fbfbb4-b1ee-4448-aadf-ae3bc2e2dd27')
|
||||||
|
t.is(coverArtArchive.release.title, 'The Dark Side of the Moon')
|
||||||
|
t.is(coverArtArchive.images.length, 1)
|
||||||
|
t.true(coverArtArchive.images[0].front)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('release groups have different cover art sizes available', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
releaseGroup(mbid: "f5093c06-23e3-404f-aeaa-40f72885ee3a") {
|
||||||
|
coverArtArchive {
|
||||||
|
small: front(size: SMALL)
|
||||||
|
large: front(size: LARGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const { coverArtArchive } = data.lookup.releaseGroup
|
||||||
|
t.is(coverArtArchive.small, 'http://coverartarchive.org/release/25fbfbb4-b1ee-4448-aadf-ae3bc2e2dd27/1675312275-250.jpg')
|
||||||
|
t.is(coverArtArchive.large, 'http://coverartarchive.org/release/25fbfbb4-b1ee-4448-aadf-ae3bc2e2dd27/1675312275-500.jpg')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('can retrieve cover art in searches', testData, `
|
||||||
|
{
|
||||||
|
search {
|
||||||
|
releases(query: "You Want It Darker") {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
coverArtArchive {
|
||||||
|
artwork
|
||||||
|
front
|
||||||
|
back
|
||||||
|
images {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
const releases = data.search.releases.edges.map(edge => edge.node)
|
||||||
|
t.is(releases.length, 25)
|
||||||
|
t.true(releases.some(release => release.coverArtArchive.artwork === true))
|
||||||
|
t.true(releases.some(release => release.coverArtArchive.images.length > 0))
|
||||||
|
t.true(releases.some(release => release.coverArtArchive.front === null))
|
||||||
|
t.true(releases.some(release => release.coverArtArchive.back === null))
|
||||||
|
})
|
||||||
128
test/extensions/fanart-tv/schema.js
Normal file
128
test/extensions/fanart-tv/schema.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import test from 'ava'
|
||||||
|
import { graphql } from 'graphql'
|
||||||
|
import extension from '../../../src/extensions/fanart-tv'
|
||||||
|
import baseSchema, { applyExtension } from '../../../src/schema'
|
||||||
|
import baseContext from '../../helpers/context'
|
||||||
|
|
||||||
|
const schema = applyExtension(extension, baseSchema)
|
||||||
|
const context = extension.extendContext(baseContext)
|
||||||
|
|
||||||
|
function testData (t, query, handler) {
|
||||||
|
return graphql(schema, query, null, context).then(result => {
|
||||||
|
if (result.errors !== undefined) {
|
||||||
|
console.log(result.errors)
|
||||||
|
}
|
||||||
|
t.is(result.errors, undefined)
|
||||||
|
return handler(t, result.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('artists have a fanArt field and preview images', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
artist(mbid: "5b11f4ce-a62d-471e-81fc-a69a8278c7da") {
|
||||||
|
fanArt {
|
||||||
|
backgrounds {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
banners {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
logos {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
logosHD {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
thumbnails {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
const { fanArt } = data.lookup.artist
|
||||||
|
const allImages = []
|
||||||
|
.concat(fanArt.backgrounds)
|
||||||
|
.concat(fanArt.banners)
|
||||||
|
.concat(fanArt.logos)
|
||||||
|
.concat(fanArt.logosHD)
|
||||||
|
.concat(fanArt.thumbnails)
|
||||||
|
allImages.forEach(image => {
|
||||||
|
t.not(image.url, image.fullSizeURL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('release groups have a fanArt field and preview images', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
releaseGroup(mbid: "f5093c06-23e3-404f-aeaa-40f72885ee3a") {
|
||||||
|
fanArt {
|
||||||
|
albumCovers {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
}
|
||||||
|
discImages {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
discNumber
|
||||||
|
size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
const { fanArt } = data.lookup.releaseGroup
|
||||||
|
const allImages = []
|
||||||
|
.concat(fanArt.albumCovers)
|
||||||
|
.concat(fanArt.discImages)
|
||||||
|
allImages.forEach(image => {
|
||||||
|
t.not(image.url, image.fullSizeURL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('labels have a fanArt field and preview images', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
label(mbid: "0cf56645-50ec-4411-aeb6-c9f4ce0f8edb") {
|
||||||
|
fanArt {
|
||||||
|
logos {
|
||||||
|
imageID
|
||||||
|
url(size: PREVIEW)
|
||||||
|
fullSizeURL: url
|
||||||
|
likeCount
|
||||||
|
color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
const { fanArt } = data.lookup.label
|
||||||
|
fanArt.logos.forEach(image => {
|
||||||
|
t.not(image.url, image.fullSizeURL)
|
||||||
|
})
|
||||||
|
})
|
||||||
424
test/extensions/fanart-tv/snapshots/schema.js.md
Normal file
424
test/extensions/fanart-tv/snapshots/schema.js.md
Normal file
|
|
@ -0,0 +1,424 @@
|
||||||
|
# Snapshot report for `test/extensions/fanart-tv/schema.js`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `schema.js.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://ava.li).
|
||||||
|
|
||||||
|
## artists have a fanArt field and preview images
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
artist: {
|
||||||
|
fanArt: {
|
||||||
|
backgrounds: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-52c91734c39dd.jpg',
|
||||||
|
imageID: '108996',
|
||||||
|
likeCount: 7,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-52c91734c39dd.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-4ddaf131354a8.jpg',
|
||||||
|
imageID: '2539',
|
||||||
|
likeCount: 7,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-4ddaf131354a8.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-524daf0607406.jpg',
|
||||||
|
imageID: '99990',
|
||||||
|
likeCount: 6,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-524daf0607406.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-4de90b913b2a1.jpg',
|
||||||
|
imageID: '4153',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-4de90b913b2a1.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54dc843050470.jpg',
|
||||||
|
imageID: '151698',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54dc843050470.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-52c917ac71459.jpg',
|
||||||
|
imageID: '108997',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-52c917ac71459.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-55baa96c8c47f.jpg',
|
||||||
|
imageID: '172578',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-55baa96c8c47f.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-500187e32be79.jpg',
|
||||||
|
imageID: '42530',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-500187e32be79.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-56d0287053099.jpg',
|
||||||
|
imageID: '190330',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-56d0287053099.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-56d02870536ef.jpg',
|
||||||
|
imageID: '190331',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-56d02870536ef.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54dc845f81d1b.jpg',
|
||||||
|
imageID: '151699',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54dc845f81d1b.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-524daf0607e44.jpg',
|
||||||
|
imageID: '99991',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-524daf0607e44.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54ac79e578054.jpg',
|
||||||
|
imageID: '146472',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistbackground/nirvana-54ac79e578054.jpg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
banners: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musicbanner/nirvana-515f7e1a6f50b.jpg',
|
||||||
|
imageID: '78008',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musicbanner/nirvana-515f7e1a6f50b.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musicbanner/nirvana-591789a12da78.jpg',
|
||||||
|
imageID: '218845',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musicbanner/nirvana-591789a12da78.jpg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
logos: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musiclogo/nirvana-4e4b9bc06dcc4.png',
|
||||||
|
imageID: '8957',
|
||||||
|
likeCount: 4,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/musiclogo/nirvana-4e4b9bc06dcc4.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
logosHD: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-5261733fe6c60.png',
|
||||||
|
imageID: '101686',
|
||||||
|
likeCount: 6,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-5261733fe6c60.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518a696cda12f.png',
|
||||||
|
imageID: '81480',
|
||||||
|
likeCount: 4,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518a696cda12f.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518ada7d98805.png',
|
||||||
|
imageID: '81521',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518ada7d98805.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518a696c1ab0b.png',
|
||||||
|
imageID: '81479',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-518a696c1ab0b.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-561900ee87f11.png',
|
||||||
|
imageID: '181150',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-561900ee87f11.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-59039625adb45.png',
|
||||||
|
imageID: '217621',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-59039625adb45.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-561900db6b999.png',
|
||||||
|
imageID: '181149',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-561900db6b999.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-5619010106ffb.png',
|
||||||
|
imageID: '181151',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/hdmusiclogo/nirvana-5619010106ffb.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
thumbnails: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb61ff40a15a.jpg',
|
||||||
|
imageID: '31455',
|
||||||
|
likeCount: 4,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb61ff40a15a.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb61fd2f3204.jpg',
|
||||||
|
imageID: '31454',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb61fd2f3204.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb6205204d6e.jpg',
|
||||||
|
imageID: '31456',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-4fb6205204d6e.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-515ddb61b444b.jpg',
|
||||||
|
imageID: '77828',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/5b11f4ce-a62d-471e-81fc-a69a8278c7da/artistthumb/nirvana-515ddb61b444b.jpg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## labels have a fanArt field and preview images
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
label: {
|
||||||
|
fanArt: {
|
||||||
|
logos: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## release groups have a fanArt field and preview images
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
releaseGroup: {
|
||||||
|
fanArt: {
|
||||||
|
albumCovers: [
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-4decb408b6604.jpg',
|
||||||
|
imageID: '4417',
|
||||||
|
likeCount: 11,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-4decb408b6604.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-53afcfaa65a86.jpg',
|
||||||
|
imageID: '128223',
|
||||||
|
likeCount: 6,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-53afcfaa65a86.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-5647389f76c9a.jpg',
|
||||||
|
imageID: '183166',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-5647389f76c9a.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-555319062404d.jpg',
|
||||||
|
imageID: '163522',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-555319062404d.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-535054d4e2e32.jpg',
|
||||||
|
imageID: '118729',
|
||||||
|
likeCount: 3,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-535054d4e2e32.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-53cf470795637.jpg',
|
||||||
|
imageID: '130679',
|
||||||
|
likeCount: 2,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-53cf470795637.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-55c4af535f7e9.jpg',
|
||||||
|
imageID: '173720',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-55c4af535f7e9.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-55cbdf80ef05c.jpg',
|
||||||
|
imageID: '174560',
|
||||||
|
likeCount: 1,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-55cbdf80ef05c.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a82b1a7c6cd.jpg',
|
||||||
|
imageID: '225420',
|
||||||
|
likeCount: 0,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a82b1a7c6cd.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a82d31adb2f.jpg',
|
||||||
|
imageID: '225421',
|
||||||
|
likeCount: 0,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a82d31adb2f.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a31c7ad5931.jpg',
|
||||||
|
imageID: '225172',
|
||||||
|
likeCount: 0,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a31c7ad5931.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a48bfb008e3.jpg',
|
||||||
|
imageID: '225266',
|
||||||
|
likeCount: 0,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a48bfb008e3.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a48df6822b4.jpg',
|
||||||
|
imageID: '225267',
|
||||||
|
likeCount: 0,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/albumcover/the-dark-side-of-the-moon-59a48df6822b4.jpg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
discImages: [
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047db2bbb28.png',
|
||||||
|
imageID: '179437',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047db2bbb28.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047e045d648.png',
|
||||||
|
imageID: '179438',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047e045d648.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 3,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-50aa6521ef0f3.png',
|
||||||
|
imageID: '62047',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-50aa6521ef0f3.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047e56b51f8.png',
|
||||||
|
imageID: '179439',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-56047e56b51f8.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-5957ea48b6728.png',
|
||||||
|
imageID: '221698',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-5957ea48b6728.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a0791276643.png',
|
||||||
|
imageID: '225077',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a0791276643.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-501c2a49803a0.png',
|
||||||
|
imageID: '46158',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-501c2a49803a0.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 2,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-501c3001b1c7c.png',
|
||||||
|
imageID: '46162',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-501c3001b1c7c.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 2,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a74d8e41558.png',
|
||||||
|
imageID: '225375',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a74d8e41558.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 3,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a74dbb41789.png',
|
||||||
|
imageID: '225376',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a74dbb41789.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 4,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a7719de4451.png',
|
||||||
|
imageID: '225385',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a7719de4451.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 5,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a81404c6562.png',
|
||||||
|
imageID: '225414',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a81404c6562.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 6,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a822507d221.png',
|
||||||
|
imageID: '225416',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a822507d221.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-54c55c43410af.png',
|
||||||
|
imageID: '148887',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-54c55c43410af.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a3237b3607a.png',
|
||||||
|
imageID: '225188',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a3237b3607a.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-532569d57474d.png',
|
||||||
|
imageID: '115624',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-532569d57474d.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discNumber: 1,
|
||||||
|
fullSizeURL: 'https://assets.fanart.tv/fanart/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a375ff8866e.png',
|
||||||
|
imageID: '225215',
|
||||||
|
size: 1000,
|
||||||
|
url: 'https://assets.fanart.tv/preview/music/83d91898-7763-47d7-b03b-b92132375c47/cdart/the-dark-side-of-the-moon-59a375ff8866e.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
BIN
test/extensions/fanart-tv/snapshots/schema.js.snap
Normal file
BIN
test/extensions/fanart-tv/snapshots/schema.js.snap
Normal file
Binary file not shown.
101
test/extensions/mediawiki/schema.js
Normal file
101
test/extensions/mediawiki/schema.js
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import test from 'ava'
|
||||||
|
import { graphql } from 'graphql'
|
||||||
|
import extension from '../../../src/extensions/mediawiki'
|
||||||
|
import baseSchema, { applyExtension } from '../../../src/schema'
|
||||||
|
import baseContext from '../../helpers/context'
|
||||||
|
|
||||||
|
const schema = applyExtension(extension, baseSchema)
|
||||||
|
const context = extension.extendContext(baseContext)
|
||||||
|
|
||||||
|
function testData (t, query, handler) {
|
||||||
|
return graphql(schema, query, null, context).then(result => {
|
||||||
|
if (result.errors !== undefined) {
|
||||||
|
console.log(result.errors)
|
||||||
|
}
|
||||||
|
t.is(result.errors, undefined)
|
||||||
|
return handler(t, result.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fragment = `
|
||||||
|
url
|
||||||
|
descriptionURL
|
||||||
|
user
|
||||||
|
size
|
||||||
|
width
|
||||||
|
height
|
||||||
|
canonicalTitle
|
||||||
|
objectName
|
||||||
|
descriptionHTML
|
||||||
|
originalDateTimeHTML
|
||||||
|
categories
|
||||||
|
artistHTML
|
||||||
|
creditHTML
|
||||||
|
licenseShortName
|
||||||
|
licenseURL
|
||||||
|
metadata {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
source
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
test('artists have a mediaWikiImages field', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
artist(mbid: "5b11f4ce-a62d-471e-81fc-a69a8278c7da") {
|
||||||
|
mediaWikiImages {
|
||||||
|
${fragment}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('instruments have a mediaWikiImages field', testData, `
|
||||||
|
{
|
||||||
|
search {
|
||||||
|
instruments(query: "guitar", first: 20) {
|
||||||
|
nodes {
|
||||||
|
mediaWikiImages {
|
||||||
|
${fragment}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('labels have a mediaWikiImages field', testData, `
|
||||||
|
{
|
||||||
|
search {
|
||||||
|
labels(query: "Sony", first: 50) {
|
||||||
|
nodes {
|
||||||
|
mediaWikiImages {
|
||||||
|
${fragment}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('places have a mediaWikiImages field', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
place(mbid: "b5297256-8482-4cba-968a-25db61563faf") {
|
||||||
|
mediaWikiImages {
|
||||||
|
${fragment}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
580
test/extensions/mediawiki/snapshots/schema.js.md
Normal file
580
test/extensions/mediawiki/snapshots/schema.js.md
Normal file
|
|
@ -0,0 +1,580 @@
|
||||||
|
# Snapshot report for `test/extensions/mediawiki/schema.js`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `schema.js.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://ava.li).
|
||||||
|
|
||||||
|
## artists have a mediaWikiImages field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
artist: {
|
||||||
|
mediaWikiImages: [
|
||||||
|
{
|
||||||
|
artistHTML: '<a rel="nofollow" class="external text" href="http://www.flickr.com/people/77758445@N00">P.B. Rage</a> from USA',
|
||||||
|
canonicalTitle: 'File:Nirvana around 1992.jpg',
|
||||||
|
categories: [
|
||||||
|
'1992 in California',
|
||||||
|
'1992 in television',
|
||||||
|
'Fashion in 1992',
|
||||||
|
'Flickr images reviewed by trusted users',
|
||||||
|
'Krist Novoselic',
|
||||||
|
'Kurt Cobain',
|
||||||
|
'MTV Video Music Awards',
|
||||||
|
'Music events in 1992',
|
||||||
|
'Nirvana (band)',
|
||||||
|
'Pauley Pavilion',
|
||||||
|
'September 1992 in the United States',
|
||||||
|
],
|
||||||
|
creditHTML: '<a rel="nofollow" class="external text" href="http://www.flickr.com/photos/77758445@N00/11295743/">More Kurt -- too rad</a>',
|
||||||
|
descriptionHTML: 'Kurt Cobain (front) and Krist Novoselic (left) live at the 1992 <a href="//commons.wikimedia.org/wiki/MTV_Video_Music_Awards" class="mw-redirect" title="MTV Video Music Awards">MTV Video Music Awards</a>.',
|
||||||
|
descriptionURL: 'https://commons.wikimedia.org/wiki/File:Nirvana_around_1992.jpg',
|
||||||
|
height: 346,
|
||||||
|
licenseShortName: 'CC BY-SA 2.0',
|
||||||
|
licenseURL: 'https://creativecommons.org/licenses/by-sa/2.0',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
name: 'DateTime',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: '2015-11-21 12:22:55',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ObjectName',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: 'Nirvana around 1992',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CommonsMetadataExtension',
|
||||||
|
source: 'extension',
|
||||||
|
value: '1.2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Categories',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: '1992 in California|1992 in television|Fashion in 1992|Flickr images reviewed by trusted users|Krist Novoselic|Kurt Cobain|MTV Video Music Awards|Music events in 1992|Nirvana (band)|Pauley Pavilion|September 1992 in the United States',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Assessments',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ImageDescription',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Kurt Cobain (front) and Krist Novoselic (left) live at the 1992 <a href="//commons.wikimedia.org/wiki/MTV_Video_Music_Awards" class="mw-redirect" title="MTV Video Music Awards">MTV Video Music Awards</a>.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DateTimeOriginal',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '1992-09-09',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Credit',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<a rel="nofollow" class="external text" href="http://www.flickr.com/photos/77758445@N00/11295743/">More Kurt -- too rad</a>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Artist',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<a rel="nofollow" class="external text" href="http://www.flickr.com/people/77758445@N00">P.B. Rage</a> from USA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LicenseShortName',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'CC BY-SA 2.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'UsageTerms',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Creative Commons Attribution-Share Alike 2.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AttributionRequired',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'true',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LicenseUrl',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'https://creativecommons.org/licenses/by-sa/2.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Copyrighted',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'True',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Restrictions',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'License',
|
||||||
|
source: 'commons-templates',
|
||||||
|
value: 'cc-by-sa-2.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
objectName: 'Nirvana around 1992',
|
||||||
|
originalDateTimeHTML: '1992-09-09',
|
||||||
|
size: 31369,
|
||||||
|
url: 'https://upload.wikimedia.org/wikipedia/commons/1/19/Nirvana_around_1992.jpg',
|
||||||
|
user: 'Kigsz',
|
||||||
|
width: 367,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## instruments have a mediaWikiImages field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
search: {
|
||||||
|
instruments: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [
|
||||||
|
{
|
||||||
|
artistHTML: '<a href="//commons.wikimedia.org/wiki/User:TenIslands" title="User:TenIslands">TenIslands</a>',
|
||||||
|
canonicalTitle: 'File:2 Portuguese guitars.jpg',
|
||||||
|
categories: [
|
||||||
|
'Derivative versions',
|
||||||
|
'PD-self',
|
||||||
|
'Portuguese guitar',
|
||||||
|
'Self-published work',
|
||||||
|
],
|
||||||
|
creditHTML: '<span class="int-own-work" lang="en">Own work</span>',
|
||||||
|
descriptionHTML: 'Left: Coimbra Portuguese guitar.<br>Right: Lisbon Portuguese guitar.',
|
||||||
|
descriptionURL: 'https://commons.wikimedia.org/wiki/File:2_Portuguese_guitars.jpg',
|
||||||
|
height: 2304,
|
||||||
|
licenseShortName: 'Public domain',
|
||||||
|
licenseURL: null,
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
name: 'DateTime',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: '2008-07-20 16:57:38',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ObjectName',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: '2 Portuguese guitars',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CommonsMetadataExtension',
|
||||||
|
source: 'extension',
|
||||||
|
value: '1.2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Categories',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: 'Derivative versions|PD-self|Portuguese guitar|Self-published work',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Assessments',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ImageDescription',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Left: Coimbra Portuguese guitar.<br>Right: Lisbon Portuguese guitar.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DateTimeOriginal',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '2008-07-19',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Credit',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<span class="int-own-work" lang="en">Own work</span>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Artist',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<a href="//commons.wikimedia.org/wiki/User:TenIslands" title="User:TenIslands">TenIslands</a>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LicenseShortName',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Public domain',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'UsageTerms',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Public domain',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AttributionRequired',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'false',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Copyrighted',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'False',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Restrictions',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'License',
|
||||||
|
source: 'commons-templates',
|
||||||
|
value: 'pd',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
objectName: '2 Portuguese guitars',
|
||||||
|
originalDateTimeHTML: '2008-07-19',
|
||||||
|
size: 2094702,
|
||||||
|
url: 'https://upload.wikimedia.org/wikipedia/commons/0/01/2_Portuguese_guitars.jpg',
|
||||||
|
user: 'TenIslands',
|
||||||
|
width: 1746,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## labels have a mediaWikiImages field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
search: {
|
||||||
|
labels: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mediaWikiImages: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## places have a mediaWikiImages field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
place: {
|
||||||
|
mediaWikiImages: [
|
||||||
|
{
|
||||||
|
artistHTML: '<a href="//commons.wikimedia.org/wiki/User:Dllu" title="User:Dllu">Dllu</a>',
|
||||||
|
canonicalTitle: 'File:Paramount Theater in Seattle showing Wicked.jpg',
|
||||||
|
categories: [
|
||||||
|
'Paramount Northwest Theater',
|
||||||
|
'Self-published work',
|
||||||
|
],
|
||||||
|
creditHTML: '<span class="int-own-work" lang="en">Own work</span>',
|
||||||
|
descriptionHTML: 'The <a href="https://en.wikipedia.org/wiki/Paramount_Theatre_(Seattle)" class="extiw" title="w:Paramount Theatre (Seattle)">Paramount Theatre (Seattle)</a> showing <a href="https://en.wikipedia.org/wiki/Wicked_(musical)" class="extiw" title="w:Wicked (musical)">Wicked</a>.',
|
||||||
|
descriptionURL: 'https://commons.wikimedia.org/wiki/File:Paramount_Theater_in_Seattle_showing_Wicked.jpg',
|
||||||
|
height: 3840,
|
||||||
|
licenseShortName: 'CC BY-SA 4.0',
|
||||||
|
licenseURL: 'https://creativecommons.org/licenses/by-sa/4.0',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
name: 'DateTime',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: '2015-08-01 06:59:27',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ObjectName',
|
||||||
|
source: 'mediawiki-metadata',
|
||||||
|
value: 'Paramount Theater in Seattle showing Wicked',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CommonsMetadataExtension',
|
||||||
|
source: 'extension',
|
||||||
|
value: '1.2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Categories',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: 'Paramount Northwest Theater|Self-published work',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Assessments',
|
||||||
|
source: 'commons-categories',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ImageDescription',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'The <a href="https://en.wikipedia.org/wiki/Paramount_Theatre_(Seattle)" class="extiw" title="w:Paramount Theatre (Seattle)">Paramount Theatre (Seattle)</a> showing <a href="https://en.wikipedia.org/wiki/Wicked_(musical)" class="extiw" title="w:Wicked (musical)">Wicked</a>.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DateTimeOriginal',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '2015-07-31 20:32:43',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Credit',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<span class="int-own-work" lang="en">Own work</span>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Artist',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '<a href="//commons.wikimedia.org/wiki/User:Dllu" title="User:Dllu">Dllu</a>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LicenseShortName',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'CC BY-SA 4.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'UsageTerms',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'Creative Commons Attribution-Share Alike 4.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AttributionRequired',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'true',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LicenseUrl',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'https://creativecommons.org/licenses/by-sa/4.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Copyrighted',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: 'True',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Restrictions',
|
||||||
|
source: 'commons-desc-page',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'License',
|
||||||
|
source: 'commons-templates',
|
||||||
|
value: 'cc-by-sa-4.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
objectName: 'Paramount Theater in Seattle showing Wicked',
|
||||||
|
originalDateTimeHTML: '2015-07-31 20:32:43',
|
||||||
|
size: 12529142,
|
||||||
|
url: 'https://upload.wikimedia.org/wikipedia/commons/c/ce/Paramount_Theater_in_Seattle_showing_Wicked.jpg',
|
||||||
|
user: 'Dllu',
|
||||||
|
width: 2566,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
BIN
test/extensions/mediawiki/snapshots/schema.js.snap
Normal file
BIN
test/extensions/mediawiki/snapshots/schema.js.snap
Normal file
Binary file not shown.
118
test/extensions/the-audio-db/schema.js
Normal file
118
test/extensions/the-audio-db/schema.js
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import test from 'ava'
|
||||||
|
import { graphql } from 'graphql'
|
||||||
|
import extension from '../../../src/extensions/the-audio-db'
|
||||||
|
import baseSchema, { applyExtension } from '../../../src/schema'
|
||||||
|
import baseContext from '../../helpers/context'
|
||||||
|
|
||||||
|
const schema = applyExtension(extension, baseSchema)
|
||||||
|
const context = extension.extendContext(baseContext)
|
||||||
|
|
||||||
|
function testData (t, query, handler) {
|
||||||
|
return graphql(schema, query, null, context).then(result => {
|
||||||
|
if (result.errors !== undefined) {
|
||||||
|
console.log(result.errors)
|
||||||
|
}
|
||||||
|
t.is(result.errors, undefined)
|
||||||
|
return handler(t, result.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('artists have a theAudioDB field', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
artist(mbid: "5b11f4ce-a62d-471e-81fc-a69a8278c7da") {
|
||||||
|
theAudioDB {
|
||||||
|
artistID
|
||||||
|
biography
|
||||||
|
biographyJP: biography(lang: "jp")
|
||||||
|
memberCount
|
||||||
|
banner
|
||||||
|
bannerPreview: banner(size: PREVIEW)
|
||||||
|
fanArt
|
||||||
|
fanArtPreview: fanArt(size: PREVIEW)
|
||||||
|
logo
|
||||||
|
logoPreview: logo(size: PREVIEW)
|
||||||
|
thumbnail
|
||||||
|
thumbnailPreview: thumbnail(size: PREVIEW)
|
||||||
|
genre
|
||||||
|
mood
|
||||||
|
style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('release groups have a theAudioDB field', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
releaseGroup(mbid: "aa997ea0-2936-40bd-884d-3af8a0e064dc") {
|
||||||
|
theAudioDB {
|
||||||
|
albumID
|
||||||
|
artistID
|
||||||
|
description
|
||||||
|
descriptionES: description(lang: "es")
|
||||||
|
review
|
||||||
|
salesCount
|
||||||
|
score
|
||||||
|
scoreVotes
|
||||||
|
discImage
|
||||||
|
discImagePreview: discImage(size: PREVIEW)
|
||||||
|
spineImage
|
||||||
|
spineImagePreview: spineImage(size: PREVIEW)
|
||||||
|
frontImage
|
||||||
|
frontImagePreview: frontImage(size: PREVIEW)
|
||||||
|
backImage
|
||||||
|
backImagePreview: backImage(size: PREVIEW)
|
||||||
|
genre
|
||||||
|
mood
|
||||||
|
style
|
||||||
|
speed
|
||||||
|
theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('recordings have a theAudioDB field', testData, `
|
||||||
|
{
|
||||||
|
lookup {
|
||||||
|
recording(mbid: "1109d8da-ce4a-4739-9414-242dc3e9b81c") {
|
||||||
|
theAudioDB {
|
||||||
|
trackID
|
||||||
|
albumID
|
||||||
|
artistID
|
||||||
|
description
|
||||||
|
descriptionES: description(lang: "es")
|
||||||
|
thumbnail
|
||||||
|
thumbnailPreview: thumbnail(size: PREVIEW)
|
||||||
|
score
|
||||||
|
scoreVotes
|
||||||
|
trackNumber
|
||||||
|
musicVideo {
|
||||||
|
url
|
||||||
|
companyName
|
||||||
|
directorName
|
||||||
|
screenshots
|
||||||
|
screenshotsPreview: screenshots(size: PREVIEW)
|
||||||
|
viewCount
|
||||||
|
likeCount
|
||||||
|
dislikeCount
|
||||||
|
commentCount
|
||||||
|
}
|
||||||
|
genre
|
||||||
|
mood
|
||||||
|
style
|
||||||
|
theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, (t, data) => {
|
||||||
|
t.snapshot(data)
|
||||||
|
})
|
||||||
125
test/extensions/the-audio-db/snapshots/schema.js.md
Normal file
125
test/extensions/the-audio-db/snapshots/schema.js.md
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Snapshot report for `test/extensions/the-audio-db/schema.js`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `schema.js.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://ava.li).
|
||||||
|
|
||||||
|
## artists have a theAudioDB field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
artist: {
|
||||||
|
theAudioDB: {
|
||||||
|
artistID: '111319',
|
||||||
|
banner: 'http://www.theaudiodb.com/images/media/artist/banner/wppvrr1365966313.jpg',
|
||||||
|
bannerPreview: 'http://www.theaudiodb.com/images/media/artist/banner/wppvrr1365966313.jpg/preview',
|
||||||
|
biography: `Nirvana was an American rock band that was formed by singer and guitarist Kurt Cobain and bassist Krist Novoselic in Aberdeen, Washington, in 1987. Nirvana went through a succession of drummers, the longest-lasting being Dave Grohl, who joined the band in 1990. Despite releasing only three full-length studio albums in their seven-year career, Nirvana has come to be regarded as one of the most influential and important rock bands of the modern era.␊
|
||||||
|
In the late 1980s Nirvana established itself as part of the Seattle grunge scene, releasing its first album Bleach for the independent record label Sub Pop in 1989. The band eventually came to develop a sound that relied on dynamic contrasts, often between quiet verses and loud, heavy choruses. After signing to major label DGC Records, Nirvana found unexpected success with "Smells Like Teen Spirit", the first single from the band's second album Nevermind (1991). Nirvana's sudden success widely popularized alternative rock as a whole, and the band's frontman Cobain found himself referred to in the media as the "spokesman of a generation", with Nirvana being considered the "flagship band" of Generation X. In response, Nirvana's third studio album, In Utero (1993), featured an abrasive, less-mainstream sound and challenged the group's audience. The album did not match the sales figures of Nevermind, but was still a commercial success and critically acclaimed.␊
|
||||||
|
Nirvana's brief run ended following the death of Kurt Cobain in 1994, but various posthumous releases have been issued since, overseen by Novoselic, Grohl, and Cobain's widow Courtney Love. Since its debut, the band has sold over 25 million records in the United States alone, and over 75 million records worldwide, making them one of the best-selling bands of all time. Nirvana was inducted into the Rock and Roll Hall of Fame in 2014, in its first year of eligibility.`,
|
||||||
|
biographyJP: `ニルヴァーナ (Nirvana) は、アメリカのバンド。1980年代終盤にシーンに出現し、1994年のカート自殺による活動停止までの数年に亘って、全世界の若者世代の圧倒的な支持を受けた。彼の死亡後も世界中のミュージシャンに多大な影響を与え続けている。単語「ニルヴァーナ」には、仏教用語の涅槃の境地という意味合いと「生け贄」という意味合いがある。␊
|
||||||
|
「スメルズ・ライク・ティーン・スピリット」の爆発的ヒットによりバンドは一気に有名になり、1990年代以降のロックに絶大な影響を与え、しばしばオルタナティヴ・ロックシーンにおいて『ニルヴァーナ以降』という言い方をされる。␊
|
||||||
|
全世界でのトータルセールスは、約7500万枚[1][2]。␊
|
||||||
|
「ローリング・ストーンの選ぶ歴史上最も偉大な100組のアーティスト」において第30位。`,
|
||||||
|
fanArt: [
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/nirvana-4ddaf131354a8.jpg',
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/ussvpr1342344599.jpg',
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/uusxqw1342344614.jpg',
|
||||||
|
],
|
||||||
|
fanArtPreview: [
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/nirvana-4ddaf131354a8.jpg/preview',
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/ussvpr1342344599.jpg/preview',
|
||||||
|
'http://media.theaudiodb.com/images/media/artist/fanart/uusxqw1342344614.jpg/preview',
|
||||||
|
],
|
||||||
|
genre: 'Rock',
|
||||||
|
logo: 'http://www.theaudiodb.com/images/media/artist/logo/xyryvu1363124407.png',
|
||||||
|
logoPreview: 'http://www.theaudiodb.com/images/media/artist/logo/xyryvu1363124407.png/preview',
|
||||||
|
memberCount: 3,
|
||||||
|
mood: 'Sad',
|
||||||
|
style: 'Rock/Pop',
|
||||||
|
thumbnail: 'http://www.theaudiodb.com/images/media/artist/thumb/ryppyp1363124444.jpg',
|
||||||
|
thumbnailPreview: 'http://www.theaudiodb.com/images/media/artist/thumb/ryppyp1363124444.jpg/preview',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## recordings have a theAudioDB field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
recording: {
|
||||||
|
theAudioDB: {
|
||||||
|
albumID: '2284335',
|
||||||
|
artistID: '131613',
|
||||||
|
description: `"Despacito" (American Spanish: ; English: "Slowly") is a single by Puerto Rican singer Luis Fonsi featuring Puerto Rican rapper Daddy Yankee from Fonsi's upcoming studio album. On January 12, 2017, Universal Music Latin released "Despacito" and its music video, which shows both artists performing the song in La Perla neighborhood of Old San Juan, Puerto Rico and the local bar La Factoría. The song's music video is the first video to reach over three billion views on YouTube. The song was written by Luis Fonsi, Erika Ender and Daddy Yankee, and was produced by Andrés Torres and Mauricio Rengifo.␊
|
||||||
|
␊
|
||||||
|
It is a reggaeton-pop song composed in common time with lyrics about having a sexual relationship, performed in a smooth and romantic way. Commercially, the song topped the charts of 47 countries and reached the top 10 of ten others, making it both Fonsi and Daddy Yankee's most successful single to date. It became the first song primarily in Spanish to top the Billboard Hot 100 since "Macarena" (Bayside Boys Mix) in 1996. The official video for "Despacito" on YouTube received its 1 billionth view on April 20, 2017 after 97 days, becoming the second-fastest video on the site to reach the milestone -- behind Adele's "Hello". It received its 2 billionth view on June 16 and its 3 billionth view on August 4 after 154 and 204 days, respectively, making it the fastest video on the site to reach both milestones. With its 3.3 million certified sales plus track-equivalent streams, "Despacito" is one of the best-selling Latin singles in the United States.`,
|
||||||
|
descriptionES: null,
|
||||||
|
genre: 'Latin',
|
||||||
|
mood: 'Sensual',
|
||||||
|
musicVideo: {
|
||||||
|
commentCount: 1449046,
|
||||||
|
companyName: null,
|
||||||
|
directorName: 'Carlos Pérez',
|
||||||
|
dislikeCount: 2168098,
|
||||||
|
likeCount: 21015918,
|
||||||
|
screenshots: [],
|
||||||
|
screenshotsPreview: [],
|
||||||
|
url: 'https://www.youtube.com/watch?v=kJQP7kiw5Fk',
|
||||||
|
viewCount: 2147483647,
|
||||||
|
},
|
||||||
|
score: 10,
|
||||||
|
scoreVotes: 1,
|
||||||
|
style: 'Latin',
|
||||||
|
theme: 'In Love',
|
||||||
|
thumbnail: 'http://media.theaudiodb.com/images/media/track/thumb/vqqpry1506425784.jpg',
|
||||||
|
thumbnailPreview: 'http://media.theaudiodb.com/images/media/track/thumb/vqqpry1506425784.jpg/preview',
|
||||||
|
trackID: '34838814',
|
||||||
|
trackNumber: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
## release groups have a theAudioDB field
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
releaseGroup: {
|
||||||
|
theAudioDB: {
|
||||||
|
albumID: '2162908',
|
||||||
|
artistID: '111492',
|
||||||
|
backImage: null,
|
||||||
|
backImagePreview: null,
|
||||||
|
description: `Random Access Memories is the upcoming fourth studio album by French electronic music duo Daft Punk. It will be released by Daft Life under exclusive license to Columbia Records on May 17, 2013 in Australia, May 20, 2013 in the United Kingdom and on May 21, 2013 in the United States. Work started on the record concurrently with the Tron: Legacy score, without a clear plan as to what its structure would be. Shortly after Daft Punk signed with Columbia, a gradual promotional rollout began for the album including billboards, television advertising and a web series.␊
|
||||||
|
Random Access Memories pays tribute to the late 1970s and early 80s era of music in the United States, particularly the sound of Los Angeles recordings of the period. Daft Punk recorded the album largely using live instrumentation with session musicians, and limited the use of electronics to drum machines, a modular synthesizer and vintage vocoders. The album also features collaborations with Panda Bear, Julian Casablancas, Todd Edwards, DJ Falcon, Chilly Gonzales, Giorgio Moroder, Nile Rodgers, Paul Williams and Pharrell Williams. Critical reception to the album has generally been positive.`,
|
||||||
|
descriptionES: `Random Access Memories es el cuarto álbum de estudio del dúo francés Daft Punk. Fue lanzado oficialmente el 17 de mayo en Australia, lanzado después en el Reino Unido el 20 de mayo y para Estados Unidos el 21 de mayo de 2013, bajo licencia de Daft Life. El inicio de grabación de este disco inició cuando el dúo preparaba el soundtrack de la película Tron: Legacy, sin un plan claro en cuanto a lo que sería su estructura. Después de haber anunciado su nuevo contrato con Columbia Records, Daft Punk empezó a promocionar el nuevo álbum con cartéles, anuncios televisivos y series para internet.␊
|
||||||
|
␊
|
||||||
|
Random Access Memories hace un tributo a la música estadounidense de la época de los 1970s y la primera parte de los 1980s, particularmente al sonido de Los Ángeles durante esa época. Daft Punk grabó el álbum en gran parte con orquesta en vivo con sesiónes musicales y con un uso limitado de máquinas de percusión, sintetizador modular, y con una vendimia de vocoders. El álbum contiene un gran número de colaboradores, entre ellos se destácan: Panda Bear, Chilly Gonzales, DJ Falcon, Julian Casablancas, Todd Edwards, Paul Williams, Pharrell Williams, Nile Rodgers y Ghallmarck. El álbum fue recibido con críticas positivas.␊
|
||||||
|
␊
|
||||||
|
Durante la primera mitad de 2013, vendió 614 000 copias en los Estados Unidos, donde se convirtió en el décimo álbum más vendido durante dicho periodo.`,
|
||||||
|
discImage: 'http://www.theaudiodb.com/images/media/album/cdart/random-access-memories-5194a5974107d.png',
|
||||||
|
discImagePreview: 'http://www.theaudiodb.com/images/media/album/cdart/random-access-memories-5194a5974107d.png/preview',
|
||||||
|
frontImage: 'http://www.theaudiodb.com/images/media/album/thumb/random-access-memories-51764651042e5.jpg',
|
||||||
|
frontImagePreview: 'http://www.theaudiodb.com/images/media/album/thumb/random-access-memories-51764651042e5.jpg/preview',
|
||||||
|
genre: 'House',
|
||||||
|
mood: 'Happy',
|
||||||
|
review: null,
|
||||||
|
salesCount: 0,
|
||||||
|
score: 8.7,
|
||||||
|
scoreVotes: 6,
|
||||||
|
speed: 'Medium',
|
||||||
|
spineImage: null,
|
||||||
|
spineImagePreview: null,
|
||||||
|
style: 'Electronic',
|
||||||
|
theme: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
BIN
test/extensions/the-audio-db/snapshots/schema.js.snap
Normal file
BIN
test/extensions/the-audio-db/snapshots/schema.js.snap
Normal file
Binary file not shown.
BIN
test/fixtures/09158487d72de391f789b038b8e5d137
vendored
Normal file
BIN
test/fixtures/09158487d72de391f789b038b8e5d137
vendored
Normal file
Binary file not shown.
29
test/fixtures/09158487d72de391f789b038b8e5d137.headers
vendored
Normal file
29
test/fixtures/09158487d72de391f789b038b8e5d137.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:23:11 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "843",
|
||||||
|
"x-ratelimit-reset": "1508394191",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"bc0fd422eb415020ac9aba896844f774\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/instrument/ba4705aa-ff1d-48d5-ae80-7b2046fb451e?inc=url-rels&fmt=json",
|
||||||
|
"time": 358,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0978572ac8e46f70600f576c9e36f019
vendored
Normal file
BIN
test/fixtures/0978572ac8e46f70600f576c9e36f019
vendored
Normal file
Binary file not shown.
29
test/fixtures/0978572ac8e46f70600f576c9e36f019.headers
vendored
Normal file
29
test/fixtures/0978572ac8e46f70600f576c9e36f019.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:12 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "944",
|
||||||
|
"x-ratelimit-reset": "1508394913",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"9fdd463cf441954e7ba61de213c25c21\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/b914f217-8be8-486b-a733-82f03275704a?inc=url-rels&fmt=json",
|
||||||
|
"time": 375,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0afc244749f3fddc9a96823dd9b2a898
vendored
Normal file
BIN
test/fixtures/0afc244749f3fddc9a96823dd9b2a898
vendored
Normal file
Binary file not shown.
29
test/fixtures/0afc244749f3fddc9a96823dd9b2a898.headers
vendored
Normal file
29
test/fixtures/0afc244749f3fddc9a96823dd9b2a898.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:18 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1060",
|
||||||
|
"x-ratelimit-reset": "1508394919",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"952c89cf2a01358c9a3c8295a61c2d06\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/d49fe768-e91c-404f-9542-3008c4ef9b51?inc=url-rels&fmt=json",
|
||||||
|
"time": 410,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0bc1bed2d611a955f254756470f98df4
vendored
Normal file
BIN
test/fixtures/0bc1bed2d611a955f254756470f98df4
vendored
Normal file
Binary file not shown.
29
test/fixtures/0bc1bed2d611a955f254756470f98df4.headers
vendored
Normal file
29
test/fixtures/0bc1bed2d611a955f254756470f98df4.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:34 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "938",
|
||||||
|
"x-ratelimit-reset": "1508394935",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"19db13c6fdb3773a9bae8cd36693257a\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/23e04f48-06e0-4d32-911d-8f6259c62a13?inc=url-rels&fmt=json",
|
||||||
|
"time": 370,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0c643a69d0dd86ae242e226c1d85469e
vendored
Normal file
BIN
test/fixtures/0c643a69d0dd86ae242e226c1d85469e
vendored
Normal file
Binary file not shown.
29
test/fixtures/0c643a69d0dd86ae242e226c1d85469e.headers
vendored
Normal file
29
test/fixtures/0c643a69d0dd86ae242e226c1d85469e.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:46 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1059",
|
||||||
|
"x-ratelimit-reset": "1508394947",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"2bb8aa78cfd610c5e1a7b121b54efa8f\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/place/b5297256-8482-4cba-968a-25db61563faf?fmt=json",
|
||||||
|
"time": 327,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0d366b2eeee8615a4b25b8e601c6020a
vendored
Normal file
BIN
test/fixtures/0d366b2eeee8615a4b25b8e601c6020a
vendored
Normal file
Binary file not shown.
29
test/fixtures/0d366b2eeee8615a4b25b8e601c6020a.headers
vendored
Normal file
29
test/fixtures/0d366b2eeee8615a4b25b8e601c6020a.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:34:56 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1040",
|
||||||
|
"x-ratelimit-reset": "1508394897",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"15d3949951847ce0b26e10c0e96e96db\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/08f37a61-1c54-4257-b31d-810fa2ac5cd5?inc=url-rels&fmt=json",
|
||||||
|
"time": 378,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/0e99fc598c63fd29efff1916c1284bbf
vendored
Normal file
BIN
test/fixtures/0e99fc598c63fd29efff1916c1284bbf
vendored
Normal file
Binary file not shown.
29
test/fixtures/0e99fc598c63fd29efff1916c1284bbf.headers
vendored
Normal file
29
test/fixtures/0e99fc598c63fd29efff1916c1284bbf.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:40 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1062",
|
||||||
|
"x-ratelimit-reset": "1508394941",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"59e4337aff496a685585c597d32fe368\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/c82b5688-df49-4e21-935b-0b08d13ec98a?inc=url-rels&fmt=json",
|
||||||
|
"time": 360,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/1bd080df7553608290b4e67175a1a6dc
vendored
Normal file
BIN
test/fixtures/1bd080df7553608290b4e67175a1a6dc
vendored
Normal file
Binary file not shown.
29
test/fixtures/1bd080df7553608290b4e67175a1a6dc.headers
vendored
Normal file
29
test/fixtures/1bd080df7553608290b4e67175a1a6dc.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:29 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "851",
|
||||||
|
"x-ratelimit-reset": "1508394929",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"03f3db33b7222966888909cd6d8d1163\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/decde7d5-dbfe-498d-80ee-dce96ae032e2?inc=url-rels&fmt=json",
|
||||||
|
"time": 380,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/1bd60b1a1ed81102d4d7271e2237e1bf
vendored
Normal file
BIN
test/fixtures/1bd60b1a1ed81102d4d7271e2237e1bf
vendored
Normal file
Binary file not shown.
29
test/fixtures/1bd60b1a1ed81102d4d7271e2237e1bf.headers
vendored
Normal file
29
test/fixtures/1bd60b1a1ed81102d4d7271e2237e1bf.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:01 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1127",
|
||||||
|
"x-ratelimit-reset": "1508394903",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"cd510299cdc136110ff7fbcb1332d568\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/151085ba-42d0-477a-83f9-eed3f758c743?inc=url-rels&fmt=json",
|
||||||
|
"time": 356,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/1ca35d44ecbebc340e09347b6f40c6f7
vendored
Normal file
1
test/fixtures/1ca35d44ecbebc340e09347b6f40c6f7
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"type-id":"cc00f97f-cf3d-3ae2-9163-041cb1a0d726","id":"c0ea0405-ae3f-4851-bf85-277fadff80e2","type":"String instrument","relations":[],"description":"","disambiguation":"","name":"Hawaiian guitar"}
|
||||||
27
test/fixtures/1ca35d44ecbebc340e09347b6f40c6f7.headers
vendored
Normal file
27
test/fixtures/1ca35d44ecbebc340e09347b6f40c6f7.headers
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:21:19 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"content-length": "198",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1101",
|
||||||
|
"x-ratelimit-reset": "1508394081",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "\"bada5afab5ba761514e6acb58d47f9ad\"",
|
||||||
|
"access-control-allow-origin": "*"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/instrument/c0ea0405-ae3f-4851-bf85-277fadff80e2?inc=url-rels&fmt=json",
|
||||||
|
"time": 341,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/1cd29fc131359a049232fbae39509f53
vendored
Normal file
BIN
test/fixtures/1cd29fc131359a049232fbae39509f53
vendored
Normal file
Binary file not shown.
43
test/fixtures/1cd29fc131359a049232fbae39509f53.headers
vendored
Normal file
43
test/fixtures/1cd29fc131359a049232fbae39509f53.headers
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 05:19:02 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"content-length": "993",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"server": "mw1203.eqiad.wmnet",
|
||||||
|
"x-powered-by": "HHVM/3.18.6-dev",
|
||||||
|
"x-content-type-options": "nosniff",
|
||||||
|
"cache-control": "private, must-revalidate, max-age=0",
|
||||||
|
"p3p": "CP=\"This is not a P3P policy! See https://commons.wikimedia.org/wiki/Special:CentralAutoLogin/P3P for more info.\"",
|
||||||
|
"content-encoding": "gzip",
|
||||||
|
"x-frame-options": "DENY",
|
||||||
|
"content-disposition": "inline; filename=\"api-result.json\"",
|
||||||
|
"vary": "Accept-Encoding,Treat-as-Untrusted,X-Forwarded-Proto,Cookie,Authorization",
|
||||||
|
"backend-timing": "D=44564 t=1508390342727140",
|
||||||
|
"x-varnish": "288262213, 11547095, 566162766, 635099657",
|
||||||
|
"via": "1.1 varnish-v4, 1.1 varnish-v4, 1.1 varnish-v4, 1.1 varnish-v4",
|
||||||
|
"accept-ranges": "bytes",
|
||||||
|
"age": "0",
|
||||||
|
"x-cache": "cp1068 pass, cp2019 pass, cp4018 pass, cp4018 pass",
|
||||||
|
"x-cache-status": "pass",
|
||||||
|
"strict-transport-security": "max-age=106384710; includeSubDomains; preload",
|
||||||
|
"set-cookie": [
|
||||||
|
"WMF-Last-Access=19-Oct-2017;Path=/;HttpOnly;secure;Expires=Mon, 20 Nov 2017 00:00:00 GMT",
|
||||||
|
"GeoIP=US:WA:Seattle:47.61:-122.30:v4; Path=/; secure; Domain=.wikimedia.org"
|
||||||
|
],
|
||||||
|
"x-analytics": "ns=-1;special=Badtitle;https=1;nocookies=1",
|
||||||
|
"x-client-ip": "66.235.47.149"
|
||||||
|
},
|
||||||
|
"url": "http://commons.wikimedia.org:443/w/api.php?action=query&titles=File%3ANirvana_around_1992.jpg&prop=imageinfo&iiprop=url%7Csize%7Ccanonicaltitle%7Cuser%7Cextmetadata&format=json",
|
||||||
|
"time": 341,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "commons.wikimedia.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/2025c61957af1edf111c891d3271ffbc
vendored
Normal file
BIN
test/fixtures/2025c61957af1edf111c891d3271ffbc
vendored
Normal file
Binary file not shown.
29
test/fixtures/2025c61957af1edf111c891d3271ffbc.headers
vendored
Normal file
29
test/fixtures/2025c61957af1edf111c891d3271ffbc.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:40 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1065",
|
||||||
|
"x-ratelimit-reset": "1508394941",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"4cc0cf5456c65b053a648dab7d98aed1\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/643330aa-157a-49a2-91f1-973f958e0ddb?inc=url-rels&fmt=json",
|
||||||
|
"time": 351,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/23aba1f4957dd22a5b21bd71c8dc9be3
vendored
Normal file
BIN
test/fixtures/23aba1f4957dd22a5b21bd71c8dc9be3
vendored
Normal file
Binary file not shown.
29
test/fixtures/23aba1f4957dd22a5b21bd71c8dc9be3.headers
vendored
Normal file
29
test/fixtures/23aba1f4957dd22a5b21bd71c8dc9be3.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:18 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1063",
|
||||||
|
"x-ratelimit-reset": "1508394919",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"e74c7af5f57a0a9863635ba41bcfc213\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/069ec010-171a-433f-baaf-410d89ae8edc?inc=url-rels&fmt=json",
|
||||||
|
"time": 469,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/24013351b3e17570d846426ee2b36c7a
vendored
Normal file
1
test/fixtures/24013351b3e17570d846426ee2b36c7a
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"disambiguation":"","type-id":"cc00f97f-cf3d-3ae2-9163-041cb1a0d726","description":"","type":"String instrument","relations":[],"id":"8ecb065e-fa6a-4009-98bd-bd742307d0e8","name":"table steel guitar"}
|
||||||
27
test/fixtures/24013351b3e17570d846426ee2b36c7a.headers
vendored
Normal file
27
test/fixtures/24013351b3e17570d846426ee2b36c7a.headers
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:21:25 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"content-length": "201",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "851",
|
||||||
|
"x-ratelimit-reset": "1508394085",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "\"2b6ff5fc3682605e4979b3eebccc7add\"",
|
||||||
|
"access-control-allow-origin": "*"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/instrument/8ecb065e-fa6a-4009-98bd-bd742307d0e8?inc=url-rels&fmt=json",
|
||||||
|
"time": 424,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/25fff225406f7452de82b60beda3c931
vendored
Normal file
BIN
test/fixtures/25fff225406f7452de82b60beda3c931
vendored
Normal file
Binary file not shown.
29
test/fixtures/25fff225406f7452de82b60beda3c931.headers
vendored
Normal file
29
test/fixtures/25fff225406f7452de82b60beda3c931.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:45 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1147",
|
||||||
|
"x-ratelimit-reset": "1508394947",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"f11743900c546b3137d19347dbb672e1\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/8110eff2-51f0-42ba-9de5-7857576e9f6a?inc=url-rels&fmt=json",
|
||||||
|
"time": 369,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/2694cd9eea8687ccbda862aab56a0e78
vendored
Normal file
BIN
test/fixtures/2694cd9eea8687ccbda862aab56a0e78
vendored
Normal file
Binary file not shown.
29
test/fixtures/2694cd9eea8687ccbda862aab56a0e78.headers
vendored
Normal file
29
test/fixtures/2694cd9eea8687ccbda862aab56a0e78.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:45 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1151",
|
||||||
|
"x-ratelimit-reset": "1508394947",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"a7c64b03fdf2eeefe4fd599df5fe102b\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/b5f8b0b3-e7ee-4db2-a612-8b326ee28d1f?inc=url-rels&fmt=json",
|
||||||
|
"time": 367,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/2ae8ed6772af1b8fa844a5ac6d2b12b8
vendored
Normal file
BIN
test/fixtures/2ae8ed6772af1b8fa844a5ac6d2b12b8
vendored
Normal file
Binary file not shown.
29
test/fixtures/2ae8ed6772af1b8fa844a5ac6d2b12b8.headers
vendored
Normal file
29
test/fixtures/2ae8ed6772af1b8fa844a5ac6d2b12b8.headers
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"statusCode": 200,
|
||||||
|
"headers": {
|
||||||
|
"date": "Thu, 19 Oct 2017 06:35:23 GMT",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"transfer-encoding": "chunked",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"keep-alive": "timeout=15",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-ratelimit-limit": "1200",
|
||||||
|
"x-ratelimit-remaining": "1129",
|
||||||
|
"x-ratelimit-reset": "1508394925",
|
||||||
|
"server": "Plack::Handler::Starlet",
|
||||||
|
"etag": "W/\"5a0e28a18580315e9c99186aec796a62\"",
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"content-encoding": "gzip"
|
||||||
|
},
|
||||||
|
"url": "http://musicbrainz.org:80/ws/2/label/40b9bd0d-e22f-41f8-885b-04bda967b3b9?inc=url-rels&fmt=json",
|
||||||
|
"time": 353,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"User-Agent": "graphbrainz/6.1.0 ( https://github.com/exogen/graphbrainz )",
|
||||||
|
"host": "musicbrainz.org",
|
||||||
|
"accept-encoding": "gzip, deflate",
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/fixtures/2b7cd5ca496a1d302cc587411cd36ad4
vendored
Normal file
BIN
test/fixtures/2b7cd5ca496a1d302cc587411cd36ad4
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue