Page MenuHomePhabricator

Create a REST endpoint solution for "imageinfo"
Open, In Progress, Needs TriagePublic

Description

action=query&prop=imageinfo is one of the most heavily used modules in the MediaWiki Action API. A large part of its value -- particularly the extmetadata property -- is provided by the GetExtendedMetadata hook, whose primary and most complete implementation is the CommonsMetadata extension. This hook fills in the file metadata information with structured, machine-readable fields like license, author, description, and credit (parsed from Commons templates).

Providing an equivalent REST endpoint will allow us to have some of the tools use a stable REST infrastructure that allows for better cacheability and error handling, and will reduce stress on our servers by migrating one of the most highly used Action API endpoint into a REST one. A potential user for this endpoint is "Popups" extension, that loads the pages (and imageinfo) for readers multiple times in an article; providing a cacheable endpoint can be impactful for this use case.

Problem with a core REST endpoint

The natural next step is to provide an equivalent REST endpoint. However, REST APIs must have stable, predictable response shapes — a consumer must be able to trust the schema regardless of which extensions are installed. This rules out putting the endpoint in core and relying on a hook to fill in the content, because it would create a conditional structure. This conditional shape violates REST API design principles and makes schema documentation, client caching, and contract testing unreliable.

Because CommonsMetadata is the meaningful owner of the extended metadata content, the REST endpoint belongs in CommonsMetadata, not in core. Placing it there means the response shape is always complete and authoritative when the module is available.

Proposed endpoint

A new REST module registered in CommonsMetadata:

GET /w/rest.php/commonsmetadata/v0-beta/file/{title}/info

Response structure

json
{
  "requested_title": "File:Example.jpg",
  "title": "Example.jpg",
  "canonical_title": "File:Example.jpg",
  "page_id": 46,
  "timestamp": "2026-05-01T18:45:19Z",
  "comment": null,
  "parsed_comment": null,
  "size": 2728817,
  "width": 1536,
  "height": 1024,
  "mime": "image/png",
  "media_type": "BITMAP",
  "repo": null,
  "url": "/w/images/9/9b/Example.jpg",
  "description_url": "/wiki/File:Example.jpg",
  "page_count": null,
  "duration": null,
  "description_short_url": "/w/index.php?curid=46",
  "sha1": "...abcd...",
  "technical": {
    "metadata": { ... },
    "common_metadata": null,
    "bit_depth": 8,
    "bad_file": false
  },
  "uploader": {
    "id": 2,
    "name": "Admin",
    "anon": false,
    "temp": false
  },
  "extended": {
    "DateTime": {
      "value": "2026-05-01 18:45:19",
      "source": "mediawiki-metadata",
      "hidden": ""
    },
    "Categories": { ... },
    "ImageDescription": { ... },
    "LicenseShortName": { ... },
    "Artist": { ... },
  }
}

extended is always present (nullable if no metadata is available). It is populated by FormatMetadata::fetchExtendedMetadata(), which is the method that triggers the GetExtendedMetadata hook -- meaning it is the data provider for the pipeline used by prop=imageinfo&iiprop=extmetadata in the Action API.

Acceptance criteria

  • GET /w/rest.php/commonsmetadata/v0-beta/file/{title}/imageinfo returns HTTP 200 with the documented schema for an existing file
  • Returns HTTP 404 for a nonexistent file title
  • Returns HTTP 403 when the requesting user lacks read permission on the file page
  • extended contains CommonsMetadata fields (description, license, author, etc.) for files with CommonsMetadata templates
  • extended is null for files with no parseable metadata
  • Response schema is stable — no fields appear or disappear based on file content

Questions

The work is currently marked as "WIP" while the questions are explored; feedback (and ideas) are welcome to make sure the endpoint answers the base needs of the biggest use cases so it is impactful primarily for the production systems.

  • extended relies on formatMetadata->fetchExtendedMetadata( $file ) which takes the information from the templates, but it might not be completely stable/consistent between different image types. Is this an issue for the REST API? should we create a base array with all possible values being null, and merge those with the fetchExtendedMetadata($file) response so we always get the same reponse shape?
  • Would this work for non commons images? does it need to? This seems to reproduce the behavior of the action API for the extended metadata, but would mean that for base details it would only work where the extension is available -- meaning, in Commons or through the InstantCommons. Is that sufficient for base use cases to be impactful on migrating some of the tools to this endpoint to reduce the load (can Popups use this consistently.)

Event Timeline

Change #1283156 had a related patch set uploaded (by Mooeypoo; author: Mooeypoo):

[mediawiki/extensions/CommonsMetadata@master] [WIP] CommonsMetadata: Add REST endpoint for file imageinfo

https://gerrit.wikimedia.org/r/1283156

Minor requests for changes:

  • REST APIs use snake_casing. Please update property names to match that convention.
  • user is confusing. Consider changing it to uploader (assuming that's what it is; confirm it's not the most recent change person or something instead) and the name field to user_name (again, confirming that's actually what this field is for)
  • For versioning, suggestion is to keep it v0 if we expect it to change. I'd also suggest keeping the -beta if there is a chance we remove it entirely (like if things go sideways after launch).
  • Rename the endpoint to just be /info, given that there are more types of media available in commons. File is also already in the route.

Minor requests for changes:

  • REST APIs use snake_casing. Please update property names to match that convention.
  • user is confusing. Consider changing it to uploader (assuming that's what it is; confirm it's not the most recent change person or something instead) and the name field to user_name (again, confirming that's actually what this field is for)
  • For versioning, suggestion is to keep it v0 if we expect it to change. I'd also suggest keeping the -beta if there is a chance we remove it entirely (like if things go sideways after launch).
  • Rename the endpoint to just be /info, given that there are more types of media available in commons. File is also already in the route.

Done. The new payload looks like this on my local:

{
  "requested_title": "File:Room.png",
  "title": "Room.png",
  "canonical_title": "File:Room.png",
  "page_id": 46,
  "timestamp": "2026-05-01T18:45:19Z",
  "comment": null,
  "parsed_comment": null,
  "size": 2728817,
  "width": 1536,
  "height": 1024,
  "mime": "image/png",
  "media_type": "BITMAP",
  "repo": null,
  "url": "/w/images/9/9b/Room.png",
  "description_url": "/wiki/File:Room.png",
  "page_count": null,
  "duration": null,
  "description_short_url": "/w/index.php?curid=46",
  "sha1": "e61a498b8ab996b6a13db81e1b8086158ac3c496",
  "technical": {
    "metadata": {
      "frameCount": 0,
      "loopCount": 1,
      "duration": 0,
      "bitDepth": 8,
      "colorType": "truecolour",
      "metadata": {
        "_MW_PNG_VERSION": 1
      }
    },
    "common_metadata": null,
    "bit_depth": 8,
    "bad_file": false
  },
  "uploader": {
    "id": 2,
    "name": "Admin",
    "anon": false,
    "temp": false
  },
  "extended": {
    "DateTime": {
      "value": "2026-05-01 18:45:19",
      "source": "mediawiki-metadata",
      "hidden": ""
    },
    "ObjectName": {
      "value": "Room",
      "source": "mediawiki-metadata"
    },
    "CommonsMetadataExtension": {
      "value": 1.2,
      "source": "extension",
      "hidden": ""
    },
    "Categories": {
      "value": "",
      "source": "commons-categories",
      "hidden": ""
    },
    "Assessments": {
      "value": "",
      "source": "commons-categories",
      "hidden": ""
    },
    "Restrictions": {
      "value": "",
      "source": "commons-desc-page",
      "hidden": ""
    }
  }
}

(note that I didn't put in the commons info templates, so that date is all null or empty)

extended relies on formatMetadata->fetchExtendedMetadata( $file ) which takes the information from the templates, but it might not be completely stable/consistent between different image types. Is this an issue for the REST API? should we create a base array with all possible values being null, and merge those with the fetchExtendedMetadata($file) response so we always get the same reponse shape?

That feels like overkill to me. I'm okay with the top-level fields being consistent.

Another suggestion to improve RESTiness -- what if we make the extended property an array of consistently structured objects? That will make it more well structured and acceptable if there is a null response (empty array).

It seems like all objects have a value, source, and hidden properties. Sooo, what if we make it a generic object? That means that it would be a predictable response, at least. Something like:

"extended": [
    {
        "name": "Categories", 
        "value": "",
        "source": "commons-categories",
        "hidden": ""
    },
    {
        "name": "Restrictions", 
        "value": "",
        "source": "commons-desc-page",
        "hidden": ""
    }
]

We might want to rename extended to something more like additional_info, extension_metadata, something like that? I would like it to be more emphatic that it is optional/not always available info. Other concern is that the extend concept & parameter is claimed.

Recommendation based on conversations with Moriel, let's also keep it in the extension module for now, instead of bringing it into core. It gives us more flexibility for changes as we start using it and learn more. We can then reassess and potentially include in Core once we are confident it's unlikely to change and actually creates the performance improvements we want to see.

aaron changed the task status from Open to In Progress.Jun 30 2026, 3:42 PM

Based on a follow up conversation here -- if we do proceed with the extended approach suggested above, it makes the interface stable. The extended properties are the only things that are actually populated by the CommonsMetadata extension and could be optional on the response body, in the event that the extension is not installed. That means that we could potentially bring this capability into Core directly.

For now we decided to proceed with launching this as a beta module for the extension. Farther down the road, we should consider if it might make more sense to pull this into core. A key reason for that could be creating more of a media module, which would include this, general file info, and thumbnail generation/retrieval. Just a thought, we could also have it be a good candidate as an early module demonstrating Action API conversion to REST, especially if we can combine it with the other Core capabilities.

I'll look at converting the extended property as described in https://phabricator.wikimedia.org/T425499#11914502 during our current sprint.

Change #1326078 had a related patch set uploaded (by ArielGlenn; author: ArielGlenn):

[mediawiki/extensions/CommonsMetadata@master] Update the extended metadata property to be an array of fixed format entries

https://gerrit.wikimedia.org/r/1326078

The extended metadata is not a list of arbitrary user-generated text fields. It is provided server-side with a finite and statically declared set of keys, the same as any other API response data. And, more importantly, consumers know about and are interested in specific fields.

This need seems consistent in all callers I'm aware of, including: VisualEditor extension, MediaSearch extension, Wikipedia iOS app, Wikipedia Android app, and Commons Android app. It is typically accessed as extmetadata.LicenseShortName, extmetadata.Credit, or extmetadata.GPSLatitude. I did not find any consumer iterating these or otherwise presenting/proxying them as generic list data.

It's not obvious to me what problem is being solved by turning this into a flat list. Every caller would have to traverse this in full and then search by name or reverse-engineer into an object before they can read the data.

I assume there is not a general problem with JSON objects in REST APIs, and that we don't want to change https://en.wikipedia.org/w/rest.php/v1/search/page?q=earth&limit=1 from

{
      "id": 9228,
      "key": "Earth",
      "title": "Earth",
      "description": "Third planet from the Sun"
}

to

[
      { "name": "id", "value": 9228 },
      { "name": "key", "value": "Earth" },
      { "name": "title", "value": "Earth" },
      { "name": "description", "value": "Third planet from the Sun" }
}

If the problem is that the schema does not declare the CommonsMetadata extended properties — why is that?

I assumed the schema was kept empty (type": "object", "additionalProperties": true) as a way to get a proof of concept shippped more quickly. It seems backwards to change the output to fit this incomplete schema.

The extended metadata is not a list of arbitrary user-generated text fields. It is provided server-side with a finite and statically declared set of keys, the same as any other API response data. And, more importantly, consumers know about and are interested in specific fields.

Wait, but we have cases where there are different parameter names given by the extended metadata depending on templates used, like artist vs painter vs author (and sometimes a combination of all). As far as I could see, the metadata is read from the templates on the page, which might not be author-made but are user-made, by the templates defined.

We ran into this in a lighter-weight way in the Attribution API, too, where we are MOSTLY normalizing things when possible, but there were some occasional inconsistencies.

This need seems consistent in all callers I'm aware of, including: VisualEditor extension, MediaSearch extension, Wikipedia iOS app, Wikipedia Android app, and Commons Android app. It is typically accessed as extmetadata.LicenseShortName, extmetadata.Credit, or extmetadata.GPSLatitude. I did not find any consumer iterating these or otherwise presenting/proxying them as generic list data.

It's not obvious to me what problem is being solved by turning this into a flat list. Every caller would have to traverse this in full and then search by name or reverse-engineer into an object before they can read the data.

I assume there is not a general problem with JSON objects in REST APIs, and that we don't want to change https://en.wikipedia.org/w/rest.php/v1/search/page?q=earth&limit=1 from

{
      "id": 9228,
      "key": "Earth",
      "title": "Earth",
      "description": "Third planet from the Sun"
}

to

[
      { "name": "id", "value": 9228 },
      { "name": "key", "value": "Earth" },
      { "name": "title", "value": "Earth" },
      { "name": "description", "value": "Third planet from the Sun" }
}

If the problem is that the schema does not declare the CommonsMetadata extended properties — why is that?

The problem is that the extended properties (and the extended ones alone) are not standardized enough to be declared.

I do agree that in the large scheme of things it's a bit of a minor issue for the API. We could have a sub-object whose keys are not entirely consistent for the REST response while still adhering to the "restful" idea of standardized schema -- but with the conversation above, I think the idea here was to cover the base for all cases (current and future) and noting to the caller that the keys are not always consistent. This is especially true if the system is used in 3rd parties where templates covering images have different set of structures that will give you a different output.

So, it's more of a decision to make the new REST endpoint (especially given the vNext endpoints) more opinionated about how to represent fluxuating data than potentially a rigid "we must solve this technical problem" and since the technical implementation (switching to a new array structure) was relatively super cheap, it seemed like a good enough solution.

[…]

The problem is that the extended properties (and the extended ones alone) are not standardized enough to be declared.

They are.

The extended metadata is not a list of arbitrary user-generated text fields. It is provided server-side with a finite and statically declared set of keys, the same as any other API response data. And, more importantly, consumers know about and are interested in specific fields.

Wait, but we have cases where there are different parameter names given by the extended metadata depending on templates used, like artist vs painter vs author (and sometimes a combination of all). As far as I could see, the metadata is read from the templates on the page, which might not be author-made but are user-made, by the templates defined.

The CommonsMetadata extension fully controls what it returns. The property names are not template- or user-controlled.

The presence of any particular property is optional. Optional properties are supported in JSON schemas.

These keys are standardised, which is what how we document them, and machine-read them at scale for attribution for over a decade without worry that an on-wiki change might break the format and require changes in first-party and third-party consumers everywhere.