Skip to content

Commit 8ff657e

Browse files
committed
Reference implementation update
1 parent 4b8b1ce commit 8ff657e

6 files changed

Lines changed: 143 additions & 106 deletions

File tree

docs/schemas/page.json

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,6 @@
4444
"format": "uri",
4545
"description": "Canonical URL"
4646
},
47-
"robots": {
48-
"type": "array",
49-
"items": {
50-
"type": "string",
51-
"enum": ["noindex", "nofollow", "noarchive", "nosnippet", "notranslate", "noimageindex"]
52-
},
53-
"uniqueItems": true,
54-
"description": "Robot directives"
55-
},
5647
"schema": {
5748
"type": "object",
5849
"description": "Optional Schema.org structured data (JSON-LD format)"
@@ -162,29 +153,45 @@
162153
},
163154
{
164155
"type": "object",
165-
"required": ["type", "sources", "title"],
156+
"required": ["type", "name", "url"],
166157
"properties": {
167158
"type": { "const": "video" },
168-
"sources": {
169-
"type": "array",
170-
"minItems": 1,
171-
"items": {
172-
"type": "object",
173-
"required": ["url"],
174-
"properties": {
175-
"url": { "type": "string", "format": "uri" },
176-
"format": { "type": "string" },
177-
"quality": { "type": "string" },
178-
"size": { "type": "integer" }
159+
"name": { "type": "string", "description": "Video title" },
160+
"url": {
161+
"oneOf": [
162+
{ "type": "string", "format": "uri" },
163+
{
164+
"type": "array",
165+
"minItems": 1,
166+
"items": {
167+
"type": "object",
168+
"required": ["href", "mediaType"],
169+
"properties": {
170+
"href": { "type": "string", "format": "uri" },
171+
"mediaType": { "type": "string" },
172+
"rel": { "type": "string" }
173+
}
174+
}
179175
}
180-
}
176+
],
177+
"description": "Video URL(s) - ActivityStreams 2.0 format"
178+
},
179+
"duration": {
180+
"type": "string",
181+
"pattern": "^PT(?=.*[HMS])(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$",
182+
"description": "ISO 8601 duration (e.g., PT5M20S)"
181183
},
182-
"poster": { "type": "string", "format": "uri" },
183-
"title": { "type": "string" },
184-
"description": { "type": "string" },
185-
"duration": { "type": "integer", "minimum": 0 },
186184
"width": { "type": "integer", "minimum": 0 },
187185
"height": { "type": "integer", "minimum": 0 },
186+
"icon": {
187+
"type": "object",
188+
"properties": {
189+
"type": { "const": "Image" },
190+
"url": { "type": "string", "format": "uri" }
191+
},
192+
"description": "Thumbnail/poster image"
193+
},
194+
"summary": { "type": "string", "description": "Video description" },
188195
"captions": {
189196
"type": "array",
190197
"items": {
@@ -208,37 +215,50 @@
208215
}
209216
}
210217
},
211-
"embed": { "type": "string", "format": "uri" },
212218
"transcript": { "type": "string" }
213219
},
214220
"additionalProperties": false
215221
},
216222
{
217223
"type": "object",
218-
"required": ["type", "sources", "title"],
224+
"required": ["type", "name", "url"],
219225
"properties": {
220226
"type": { "const": "audio" },
221-
"sources": {
222-
"type": "array",
223-
"minItems": 1,
224-
"items": {
225-
"type": "object",
226-
"required": ["url"],
227-
"properties": {
228-
"url": { "type": "string", "format": "uri" },
229-
"format": { "type": "string" },
230-
"bitrate": { "type": "integer" },
231-
"size": { "type": "integer" }
227+
"name": { "type": "string", "description": "Audio title" },
228+
"url": {
229+
"oneOf": [
230+
{ "type": "string", "format": "uri" },
231+
{
232+
"type": "array",
233+
"minItems": 1,
234+
"items": {
235+
"type": "object",
236+
"required": ["href", "mediaType"],
237+
"properties": {
238+
"href": { "type": "string", "format": "uri" },
239+
"mediaType": { "type": "string" }
240+
}
241+
}
232242
}
233-
}
243+
],
244+
"description": "Audio URL(s) - ActivityStreams 2.0 format"
234245
},
235-
"title": { "type": "string" },
236-
"description": { "type": "string" },
237-
"artist": { "type": "string" },
238-
"album": { "type": "string" },
239-
"duration": { "type": "integer", "minimum": 0 },
240-
"coverArt": { "type": "string", "format": "uri" },
241-
"transcript": { "type": "string" },
246+
"duration": {
247+
"type": "string",
248+
"pattern": "^PT(?=.*[HMS])(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$",
249+
"description": "ISO 8601 duration (e.g., PT1H for 1 hour)"
250+
},
251+
"icon": {
252+
"type": "object",
253+
"properties": {
254+
"type": { "const": "Image" },
255+
"url": { "type": "string", "format": "uri" }
256+
},
257+
"description": "Cover art/thumbnail image"
258+
},
259+
"summary": { "type": "string", "description": "Audio description" },
260+
"attributedTo": { "type": "string", "description": "Artist/creator name or Person object" },
261+
"partOf": { "type": "string", "description": "Album/series name or Collection object" },
242262
"chapters": {
243263
"type": "array",
244264
"items": {
@@ -249,7 +269,8 @@
249269
"title": { "type": "string" }
250270
}
251271
}
252-
}
272+
},
273+
"transcript": { "type": "string" }
253274
},
254275
"additionalProperties": false
255276
},

examples/blog-post.scp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
{"collection":{"id":"blog-example","section":"blog","type":"snapshot","generated":"2025-01-15T10:00:00Z","version":"0.1"}}
2-
{"url":"https://example.com/blog/getting-started","title":"Getting Started with SCP","description":"Learn how to implement Site Content Protocol","author":"Jane Smith","published":"2025-01-10T08:00:00Z","modified":"2025-01-12T14:22:00Z","language":"en","canonical":"https://example.com/blog/getting-started","robots":["noarchive"],"content":[{"type":"heading","level":1,"text":"Getting Started with SCP"},{"type":"text","text":"SCP provides a structured format for web content."},{"type":"heading","level":2,"text":"Installation"},{"type":"code","language":"bash","code":"npm install scp-generator"},{"type":"text","text":"That's it! You're ready to go."},{"type":"link","url":"https://docs.example.com","text":"Read the docs","rel":["noopener"]},{"type":"image","url":"https://example.com/images/diagram.png","alt":"Architecture diagram"}]}
2+
{"url":"https://example.com/blog/getting-started","title":"Getting Started with SCP","description":"Learn how to implement Site Content Protocol","author":"Jane Smith","published":"2025-01-10T08:00:00Z","modified":"2025-01-12T14:22:00Z","language":"en","canonical":"https://example.com/blog/getting-started","content":[{"type":"heading","level":1,"text":"Getting Started with SCP"},{"type":"text","text":"SCP provides a structured format for web content."},{"type":"heading","level":2,"text":"Installation"},{"type":"code","language":"bash","code":"npm install scp-generator"},{"type":"text","text":"That's it! You're ready to go."},{"type":"link","url":"https://docs.example.com","text":"Read the docs","rel":["noopener"]},{"type":"image","url":"https://example.com/images/diagram.png","alt":"Architecture diagram"}]}

reference-impl/scp/generator.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def add_page(
5858
author: str | None = None,
5959
published: str | None = None,
6060
canonical: str | None = None,
61-
robots: list[str] | None = None,
6261
schema_data: dict | None = None,
6362
) -> None:
6463
"""Add a page to the collection.
@@ -73,7 +72,6 @@ def add_page(
7372
author: Optional author name
7473
published: Optional ISO 8601 publication date
7574
canonical: Optional canonical URL
76-
robots: Optional list of robot directives
7775
schema_data: Optional Schema.org structured data
7876
"""
7977
page = {
@@ -91,8 +89,6 @@ def add_page(
9189
page["published"] = published
9290
if canonical:
9391
page["canonical"] = canonical
94-
if robots:
95-
page["robots"] = robots
9692
if schema_data:
9793
page["schema"] = schema_data # type: ignore[assignment]
9894

reference-impl/scp/parser.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ def __init__(self, data: dict):
5454
self.modified = data["modified"]
5555
self.language = data["language"]
5656
self.canonical = data.get("canonical")
57-
self.robots = data.get("robots", [])
5857
self.schema_data = data.get("schema")
5958
self.content = data["content"]
6059

reference-impl/tests/test_roundtrip.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ def test_roundtrip_all_content_types(tmp_path: Path) -> None:
8888
{"type": "quote", "text": "Quote", "citation": "Author"},
8989
{
9090
"type": "video",
91-
"sources": [{"url": "https://example.com/video.mp4"}],
92-
"title": "Video",
91+
"name": "Video",
92+
"url": [{"href": "https://example.com/video.mp4", "mediaType": "video/mp4"}],
9393
},
9494
{
9595
"type": "audio",
96-
"sources": [{"url": "https://example.com/audio.mp3"}],
97-
"title": "Audio",
96+
"name": "Audio",
97+
"url": [{"href": "https://example.com/audio.mp3", "mediaType": "audio/mpeg"}],
9898
},
9999
{"type": "structured", "format": "json-ld", "data": {"@type": "Thing"}},
100100
],

schemas/page.json

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,6 @@
4444
"format": "uri",
4545
"description": "Canonical URL"
4646
},
47-
"robots": {
48-
"type": "array",
49-
"items": {
50-
"type": "string",
51-
"enum": ["noindex", "nofollow", "noarchive", "nosnippet", "notranslate", "noimageindex"]
52-
},
53-
"uniqueItems": true,
54-
"description": "Robot directives"
55-
},
5647
"schema": {
5748
"type": "object",
5849
"description": "Optional Schema.org structured data (JSON-LD format)"
@@ -162,29 +153,45 @@
162153
},
163154
{
164155
"type": "object",
165-
"required": ["type", "sources", "title"],
156+
"required": ["type", "name", "url"],
166157
"properties": {
167158
"type": { "const": "video" },
168-
"sources": {
169-
"type": "array",
170-
"minItems": 1,
171-
"items": {
172-
"type": "object",
173-
"required": ["url"],
174-
"properties": {
175-
"url": { "type": "string", "format": "uri" },
176-
"format": { "type": "string" },
177-
"quality": { "type": "string" },
178-
"size": { "type": "integer" }
159+
"name": { "type": "string", "description": "Video title" },
160+
"url": {
161+
"oneOf": [
162+
{ "type": "string", "format": "uri" },
163+
{
164+
"type": "array",
165+
"minItems": 1,
166+
"items": {
167+
"type": "object",
168+
"required": ["href", "mediaType"],
169+
"properties": {
170+
"href": { "type": "string", "format": "uri" },
171+
"mediaType": { "type": "string" },
172+
"rel": { "type": "string" }
173+
}
174+
}
179175
}
180-
}
176+
],
177+
"description": "Video URL(s) - ActivityStreams 2.0 format"
178+
},
179+
"duration": {
180+
"type": "string",
181+
"pattern": "^PT(?=.*[HMS])(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$",
182+
"description": "ISO 8601 duration (e.g., PT5M20S)"
181183
},
182-
"poster": { "type": "string", "format": "uri" },
183-
"title": { "type": "string" },
184-
"description": { "type": "string" },
185-
"duration": { "type": "integer", "minimum": 0 },
186184
"width": { "type": "integer", "minimum": 0 },
187185
"height": { "type": "integer", "minimum": 0 },
186+
"icon": {
187+
"type": "object",
188+
"properties": {
189+
"type": { "const": "Image" },
190+
"url": { "type": "string", "format": "uri" }
191+
},
192+
"description": "Thumbnail/poster image"
193+
},
194+
"summary": { "type": "string", "description": "Video description" },
188195
"captions": {
189196
"type": "array",
190197
"items": {
@@ -208,37 +215,50 @@
208215
}
209216
}
210217
},
211-
"embed": { "type": "string", "format": "uri" },
212218
"transcript": { "type": "string" }
213219
},
214220
"additionalProperties": false
215221
},
216222
{
217223
"type": "object",
218-
"required": ["type", "sources", "title"],
224+
"required": ["type", "name", "url"],
219225
"properties": {
220226
"type": { "const": "audio" },
221-
"sources": {
222-
"type": "array",
223-
"minItems": 1,
224-
"items": {
225-
"type": "object",
226-
"required": ["url"],
227-
"properties": {
228-
"url": { "type": "string", "format": "uri" },
229-
"format": { "type": "string" },
230-
"bitrate": { "type": "integer" },
231-
"size": { "type": "integer" }
227+
"name": { "type": "string", "description": "Audio title" },
228+
"url": {
229+
"oneOf": [
230+
{ "type": "string", "format": "uri" },
231+
{
232+
"type": "array",
233+
"minItems": 1,
234+
"items": {
235+
"type": "object",
236+
"required": ["href", "mediaType"],
237+
"properties": {
238+
"href": { "type": "string", "format": "uri" },
239+
"mediaType": { "type": "string" }
240+
}
241+
}
232242
}
233-
}
243+
],
244+
"description": "Audio URL(s) - ActivityStreams 2.0 format"
234245
},
235-
"title": { "type": "string" },
236-
"description": { "type": "string" },
237-
"artist": { "type": "string" },
238-
"album": { "type": "string" },
239-
"duration": { "type": "integer", "minimum": 0 },
240-
"coverArt": { "type": "string", "format": "uri" },
241-
"transcript": { "type": "string" },
246+
"duration": {
247+
"type": "string",
248+
"pattern": "^PT(?=.*[HMS])(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$",
249+
"description": "ISO 8601 duration (e.g., PT1H for 1 hour)"
250+
},
251+
"icon": {
252+
"type": "object",
253+
"properties": {
254+
"type": { "const": "Image" },
255+
"url": { "type": "string", "format": "uri" }
256+
},
257+
"description": "Cover art/thumbnail image"
258+
},
259+
"summary": { "type": "string", "description": "Audio description" },
260+
"attributedTo": { "type": "string", "description": "Artist/creator name or Person object" },
261+
"partOf": { "type": "string", "description": "Album/series name or Collection object" },
242262
"chapters": {
243263
"type": "array",
244264
"items": {
@@ -249,7 +269,8 @@
249269
"title": { "type": "string" }
250270
}
251271
}
252-
}
272+
},
273+
"transcript": { "type": "string" }
253274
},
254275
"additionalProperties": false
255276
},

0 commit comments

Comments
 (0)