diff --git a/schema/definitions.json b/schema/definitions.json index 1d0f044f..668f5f7c 100644 --- a/schema/definitions.json +++ b/schema/definitions.json @@ -10,7 +10,7 @@ "filename": { "type": "string", - "pattern": "^[\\w\\-\\.]+\\.html$" + "pattern": "^[\\w\\-\\.]+\\.(html|pdf)$" }, "relativePath": { @@ -75,6 +75,7 @@ "Discontinued Draft", "Draft Note", "Draft Registry", + "Final Deliverable", "First Public Working Draft", "Note", "Proposed Recommendation", @@ -101,10 +102,12 @@ "enum": [ "A Collection of Interesting Ideas", "Draft Community Group Report", + "Draft Deliverable", "Draft Finding", "Draft Registry", "Editor's Draft", "Experimental", + "Final Deliverable", "Informational", "Internet Standard", "Living Standard", diff --git a/specs.json b/specs.json index 033a03da..3a5df423 100644 --- a/specs.json +++ b/specs.json @@ -1,7 +1,30 @@ [ + { + "url": "https://aomediacodec.github.io/afgs1-spec/", + "categories": [ + "-browser" + ] + }, { "url": "https://aomediacodec.github.io/av1-avif/", - "organization": "Alliance for Open Media", + "groups": [ + { + "name": "Storage and Transport Formats Working Group", + "url": "https://aomedia.org/about/#storage-and-transport-formats-working-group" + } + ], + "nightly": { + "status": "Final Deliverable" + } + }, + { + "url": "https://aomediacodec.github.io/av1-hdr10plus/", + "nightly": { + "status": "Final Deliverable" + } + }, + { + "url": "https://aomediacodec.github.io/av1-isobmff/", "groups": [ { "name": "Storage and Transport Formats Working Group", @@ -9,6 +32,30 @@ } ] }, + { + "url": "https://aomediacodec.github.io/av1-mpeg2-ts/", + "groups": [ + { + "name": "Storage and Transport Formats Working Group", + "url": "https://aomedia.org/about/#storage-and-transport-formats-working-group" + } + ] + }, + { + "url": "https://aomediacodec.github.io/av1-rtp-spec/", + "groups": [ + { + "name": "Storage and Transport Formats Working Group", + "url": "https://aomedia.org/about/#storage-and-transport-formats-working-group" + } + ] + }, + { + "url": "https://aomediacodec.github.io/av1-spec/av1-spec.pdf", + "nightly": { + "url": "https://aomediacodec.github.io/av1-spec/" + } + }, { "url": "https://compat.spec.whatwg.org/", "nightly": { diff --git a/src/compute-repository.js b/src/compute-repository.js index 52d02d65..8b8f33bd 100644 --- a/src/compute-repository.js +++ b/src/compute-repository.js @@ -130,6 +130,7 @@ module.exports = async function (specs, options) { "index.src.html", "index.bs", "spec.bs", + "index.md", "index.html" ); diff --git a/src/compute-shorttitle.js b/src/compute-shorttitle.js index b13d7d7d..af0bedd2 100644 --- a/src/compute-shorttitle.js +++ b/src/compute-shorttitle.js @@ -28,16 +28,20 @@ module.exports = function (title) { const level = title.match(/\s(\d+(\.\d+)?)$/); const shortTitle = title - .replace(/\s/g, ' ') // Replace non-breaking spaces - .replace(/ \d+(\.\d+)?$/, '') // Drop level number for now - .replace(/( -)? Level$/, '') // Drop "Level" - .replace(/ Module$/, '') // Drop "Module" (now followed by level) - .replace(/ Proposal$/, '') // Drop "Proposal" (TC39 proposals) - .replace(/ Specification$/, '') // Drop "Specification" - .replace(/ Standard$/, '') // Drop "Standard" and "Living Standard" - .replace(/ Living$/, '') - .replace(/ \([^\)]+ Edition\)/, '') // Drop edition indication - .replace(/^.*\(([^\)]+)\).*$/, '$1'); // Use abbr between parentheses + .trim() + .replace(/\s/g, ' ') // Replace non-breaking spaces + .replace(/ \d+(\.\d+)?$/, '') // Drop level number for now + .replace(/( -)? Level$/i, '') // Drop "Level" + .replace(/ \(\v\d+(\.\d+)?\)/i, '') // Drop "(vx.y)" + .replace(/\(Draft\)/i, '') // Drop "(Draft)" indication + .replace(/ Module$/i, '') // Drop "Module" (now followed by level) + .replace(/ Proposal$/i, '') // Drop "Proposal" (TC39 proposals) + .replace(/ Specification$/i, '') // Drop "Specification" + .replace(/ Standard$/i, '') // Drop "Standard" and "Living Standard" + .replace(/ Living$/i, '') + .replace(/ \([^\)]+ Edition\)/i, '') // Drop edition indication + .replace(/^.*\(([^\)]+)\).*$/, '$1') // Use abbr between parentheses + .trim(); if (level) { return shortTitle + " " + level[1]; diff --git a/src/determine-filename.js b/src/determine-filename.js index 78bf40f4..ef0339d6 100644 --- a/src/determine-filename.js +++ b/src/determine-filename.js @@ -11,7 +11,7 @@ module.exports = async function (url) { // Extract filename directly from the URL when possible - const match = url.match(/\/([^/]+\.html)$/); + const match = url.match(/\/([^/]+\.(html|pdf))$/); if (match) { return match[1]; } diff --git a/src/fetch-groups.js b/src/fetch-groups.js index 04b4a253..060ad76a 100644 --- a/src/fetch-groups.js +++ b/src/fetch-groups.js @@ -126,6 +126,17 @@ module.exports = async function (specs, options) { }]; } + // For the Alliance for Open Media (AOM), let's consider that the Codec WG + // is the default group, noting that it is not super clear which AOM group + // develops which spec in practice: https://aomedia.org/about/ + if (info && info.owner === "aomediacodec") { + spec.organization = spec.organization ?? "Alliance for Open Media"; + spec.groups = spec.groups ?? [{ + name: "Codec Working Group", + url: "https://aomedia.org/about/#codec-working-group" + }] + } + // All specs that remain should be developed by some W3C group. spec.organization = spec.organization ?? "W3C"; diff --git a/src/fetch-info.js b/src/fetch-info.js index 10c81cc5..763e36cf 100644 --- a/src/fetch-info.js +++ b/src/fetch-info.js @@ -558,10 +558,27 @@ async function fetchInfoFromSpecs(specs, options) { throw new Error(titleAndStatus.error + `, in ${url} for ${spec.shortname}`); } else { - return { + const res = { nightly: { url, status: titleAndStatus.status }, title: titleAndStatus.title }; + + // The AOM has Draft Deliverables and Final Deliverables. Most AOM + // specs don't say what they are, we'll assume that they are drafts. + if (spec.organization === "Alliance for Open Media") { + if (res.nightly.status === "Editor's Draft" || + res.nightly.status === "AOM Working Group Draft") { + res.nightly.status = "Draft Deliverable"; + } + if (spec.nightly?.url && spec.url !== spec.nightly.url) { + res.release = { + url: spec.url, + status: "Final Deliverable" + }; + } + } + + return res; } } finally { diff --git a/test/compute-shorttitle.js b/test/compute-shorttitle.js index f718a853..192c20fb 100644 --- a/test/compute-shorttitle.js +++ b/test/compute-shorttitle.js @@ -79,6 +79,12 @@ describe("compute-shorttitle module", () => { "Foo Bar"); }); + it("drops '(Draft)' from title", () => { + assertTitle( + "(Draft) Beer", + "Beer"); + }); + it("preserves title when needed", () => { assertTitle( "Edition Module Standard Foo", @@ -96,4 +102,10 @@ describe("compute-shorttitle module", () => { "Hypertext Transfer Protocol (HTTP/1.1): Foo bar", "HTTP/1.1 Foo bar") }); + + it("applies rules in order", () => { + assertTitle( + " AOMedia Film Grain Synthesis (v1.0) (AFGS1) specification (Draft) ", + "AFGS1") + }); }); diff --git a/test/determine-filename.js b/test/determine-filename.js index c76987b3..78cafb29 100644 --- a/test/determine-filename.js +++ b/test/determine-filename.js @@ -6,12 +6,18 @@ describe("determine-filename module", function () { this.slow(5000); this.timeout(30000); - it("extracts filename from URL", async () => { + it("extracts filename from URL (.html)", async () => { const url = "https://example.org/spec/filename.html"; const filename = await determineFilename(url); assert.equal(filename, "filename.html"); }); + it("extracts filename from URL (.pdf)", async () => { + const url = "https://example.org/spec/filename.pdf"; + const filename = await determineFilename(url); + assert.equal(filename, "filename.pdf"); + }); + it("finds index.html filenames", async () => { const url = "https://w3c.github.io/presentation-api/"; const filename = await determineFilename(url); diff --git a/test/fetch-groups.js b/test/fetch-groups.js index d5b6df1c..f0b7fc9b 100644 --- a/test/fetch-groups.js +++ b/test/fetch-groups.js @@ -93,6 +93,15 @@ describe("fetch-groups module (without API keys)", function () { }]); }); + it("handles AOM specs", async () => { + const res = await fetchGroupsFor("https://aomediacodec.github.io/afgs1-spec/"); + assert.equal(res.organization, "Alliance for Open Media"); + assert.deepStrictEqual(res.groups, [{ + name: "Codec Working Group", + url: "https://aomedia.org/about/#codec-working-group" + }]); + }); + it("preserves provided info", async () => { const spec = { url: "https://url.spec.whatwg.org/", diff --git a/test/fetch-info.js b/test/fetch-info.js index e59c75b9..4edbec13 100644 --- a/test/fetch-info.js +++ b/test/fetch-info.js @@ -198,6 +198,24 @@ describe("fetch-info module", function () { assert.equal(info[spec.shortname].nightly.status, "Editor's Draft"); assert.equal(info[spec.shortname].title, "Intl.Segmenter Proposal"); }); + + it("creates a release for final AOM deliverables published as PDF", async () => { + const spec = { + organization: "Alliance for Open Media", + url: "https://aomediacodec.github.io/av1-spec/av1-spec.pdf", + shortname: "av1-spec", + nightly: { + url: "https://aomediacodec.github.io/av1-spec/" + } + }; + const info = await fetchInfo([spec]); + assert.ok(info[spec.shortname]); + assert.equal(info[spec.shortname].source, "spec"); + assert.equal(info[spec.shortname].nightly.url, spec.nightly.url); + assert.ok(info[spec.shortname].release); + assert.equal(info[spec.shortname].release.url, spec.url); + assert.equal(info[spec.shortname].release.status, "Final Deliverable"); + }); }); describe("fetch from W3C API", () => {