-
Notifications
You must be signed in to change notification settings - Fork 276
Expand file tree
/
Copy pathOpenProcessing.ts
More file actions
154 lines (129 loc) · 5.06 KB
/
Copy pathOpenProcessing.ts
File metadata and controls
154 lines (129 loc) · 5.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import type { AnyEntryMap, CollectionEntry } from "astro:content";
import { readFile, access } from "node:fs/promises";
import { constants as FS } from "node:fs";
import path from "node:path";
const DATA_DIR = path.join(process.cwd(), "src", "cached-data");
const CURATION_2024_FILE = path.join(DATA_DIR, "openprocessing-curation-87649-sketches.json");
const CURATION_2025_FILE = path.join(DATA_DIR, "openprocessing-curation-89576-sketches.json");
const SKETCH_FILE = (id: number) => path.join(DATA_DIR, "openprocessing-sketches", `${id}.json`);
/**
* API Response from a call to the Curation Sketches endpoint, cached in the above files
* see https://documenter.getpostman.com/view/16936458/2s9YC1Xa6X#7cd344f6-6e87-426a-969b-2b4a79701dd1
*/
export type OpenProcessingCurationResponse = Array<{
/** Sketch ID used for constructing URLs */
visualID: number;
/** Title of sketch */
title: string;
/** Description of sketch */
description: string;
instructions: string;
mode: string;
createdOn: string;
userID: string;
submittedOn: string;
/** Author's name */
fullname: string;
}>;
/**
* API Response from a call to the Sketch endpoint
*
* see https://documenter.getpostman.com/view/16936458/2s9YC1Xa6X#7cd344f6-6e87-426a-969b-2b4a79701dd1
*/
export type OpenProcessingSketchResponse = {
/** Sketch ID used for constructing URLs */
visualID: number;
/** Title of sketch */
title: string;
/** Description of sketch */
description: string;
instructions: string;
license: string;
userID: string;
submittedOn: string;
createdOn: string;
mode: string;
/* This is extracted from /code */
width: number;
height: number;
};
// Selected Sketches from the 2025 curation
export const priorityIds = ['2690038', '2484739', '2688829', '2689119', '2690571', '2690405','2684408' , '2693274', '2693345', '2691712']
async function exists(p: string) {
try {
await access(p, FS.F_OK);
return true;
} catch {
return false;
}
}
async function readJson<T>(filePath: string): Promise<T> {
const text = await readFile(filePath, "utf8");
return JSON.parse(text) as T;
}
/**
* Get basic info for the sketches contained in a Curation
* from the OpenProcessing API
*
* @param limit max number of sketches to return
* @returns sketches
*/
export async function getCurationSketches(): Promise<OpenProcessingCurationResponse> {
const payload2024 = await readJson<OpenProcessingCurationResponse>(CURATION_2024_FILE);
const payload2025 = await readJson<OpenProcessingCurationResponse>(CURATION_2025_FILE);
const prioritySketches = payload2025.filter(
(sketch: OpenProcessingCurationResponse[number]) => priorityIds.includes(String(sketch.visualID)))
.sort((a: OpenProcessingCurationResponse[number], b: OpenProcessingCurationResponse[number]) => priorityIds.indexOf(String(a.visualID)) - priorityIds.indexOf(String(b.visualID)));
const allSketches = [
...prioritySketches.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2025' })),
...payload2024.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2024' })),
];
const availableSketches: OpenProcessingCurationResponse = [];
for (const sketch of allSketches) {
if (await exists(SKETCH_FILE(sketch.visualID))) availableSketches.push(sketch);
}
return [
...availableSketches,
] as OpenProcessingCurationResponse;
};
export async function getSketch(id: number): Promise<OpenProcessingSketchResponse> {
return await readJson<OpenProcessingSketchResponse>(SKETCH_FILE(id));
}
export const makeSketchLinkUrl = (id: number) =>
`https://openprocessing.org/sketch/${id}`;
export const makeSketchEmbedUrl = (id: number) =>
`https://openprocessing.org/sketch/${id}/embed/?plusEmbedFullscreen=true&plusEmbedInstructions=false`;
export const makeThumbnailUrl = (id: number) =>
`https://openprocessing-usercontent.s3.amazonaws.com/thumbnails/visualThumbnail${id}@2x.jpg`;
export const getSketchThumbnailSource = async (id: number) => {
const manualThumbs = import.meta.glob<ImageMetadata>('./images/*', { import: 'default' })
const key = `./images/${id}.png`;
if (manualThumbs[key]) {
const img = await manualThumbs[key]()
return img;
}
return makeThumbnailUrl(id)
}
/**
* The size of the thumbnails generated by OpenProcessing in px
*/
export const thumbnailDimensions = 400;
export function isCurationResponse<C extends keyof AnyEntryMap>(
item: OpenProcessingCurationResponse[number] | CollectionEntry<C>,
): item is OpenProcessingCurationResponse[number] {
return "visualID" in (item as any);
}
export async function getRandomCurationSketches(num = 4) {
const curationSketches = await getCurationSketches();
const result: OpenProcessingCurationResponse = [];
const usedIndices: Set<number> = new Set();
const n = Math.min(num, curationSketches.length);
while (result.length < n) {
const randomIndex = Math.floor(Math.random() * curationSketches.length);
if (!usedIndices.has(randomIndex)) {
result.push(curationSketches[randomIndex]);
usedIndices.add(randomIndex);
}
}
return result;
}