pixelkiln 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -5448,12 +5448,7 @@ var PixelLabProvider = class _PixelLabProvider {
5448
5448
  const character = await this.client.getCharacter(jobId);
5449
5449
  if (character.status === "failed") return { status: "failed", error: "character generation failed upstream" };
5450
5450
  if (character.status !== "completed" || !character.rotation_urls) return { status: "processing" };
5451
- const order = character.directions === 4 ? CHARACTER_DIRECTIONS_4 : CHARACTER_DIRECTIONS_8;
5452
- const sources = [];
5453
- for (const direction of order) {
5454
- const url = character.rotation_urls[direction];
5455
- if (url) sources.push({ url, role: direction });
5456
- }
5451
+ const sources = rotationSources(character);
5457
5452
  if (!sources.length) return { status: "failed", error: "character completed with no rotation images" };
5458
5453
  return {
5459
5454
  status: "ready",
@@ -5745,6 +5740,27 @@ var PixelLabProvider = class _PixelLabProvider {
5745
5740
  })))
5746
5741
  };
5747
5742
  }
5743
+ /**
5744
+ * A character's rotation and frame URLs carry its last edit time as a
5745
+ * cache-buster, so the record's URLs can name bytes a cache still holds
5746
+ * after an edit in PixelLab's editor. Objects keep stable URLs.
5747
+ */
5748
+ async refreshSources(objectId, context) {
5749
+ if (context.generator !== "character") return void 0;
5750
+ const [characterId, groupId] = objectId.split("#");
5751
+ const character = await this.client.getCharacter(characterId).catch((err) => {
5752
+ if (err instanceof PixelLabError && err.status === 404) return null;
5753
+ throw err;
5754
+ });
5755
+ if (!character) return null;
5756
+ if (!groupId) return character.status === "completed" ? rotationSources(character) : void 0;
5757
+ const recorded = context.metadata?.character ?? {};
5758
+ const direction = recorded.direction ?? "south";
5759
+ const group = character.animations.find((g) => g.animation_group_id === groupId);
5760
+ const frames = group?.directions.find((d) => d.direction === direction && d.frames.length)?.frames ?? (recorded.animationName ? findAnimation(character, recorded.animationName, direction, recorded.animationId ?? null)?.frames : void 0);
5761
+ if (!frames) return null;
5762
+ return frames.map((url, index) => ({ url, role: `frame-${String(index).padStart(2, "0")}` }));
5763
+ }
5748
5764
  };
5749
5765
  function remoteCharacter(character) {
5750
5766
  return {
@@ -5775,6 +5791,15 @@ function firstUrl(urls) {
5775
5791
  if (!urls) return null;
5776
5792
  return Object.values(urls).find((u) => typeof u === "string") ?? null;
5777
5793
  }
5794
+ function rotationSources(character) {
5795
+ const order = character.directions === 4 ? CHARACTER_DIRECTIONS_4 : CHARACTER_DIRECTIONS_8;
5796
+ const sources = [];
5797
+ for (const direction of order) {
5798
+ const url = character.rotation_urls?.[direction];
5799
+ if (url) sources.push({ url, role: direction });
5800
+ }
5801
+ return sources;
5802
+ }
5778
5803
  function findAnimation(character, name, direction, animationId) {
5779
5804
  for (const group of character.animations) {
5780
5805
  const match = group.directions.find((d) => d.direction === direction && d.frames.length > 0);
@@ -7331,7 +7356,40 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
7331
7356
  result.skipped++;
7332
7357
  continue;
7333
7358
  }
7334
- const sources = entry.sourceUrls?.length ? entry.sourceUrls : entry.sourceUrl ? [{ url: entry.sourceUrl }] : opts.repair && entry.outputs.length ? entry.outputs.map((o) => ({ url: "", role: o.role, mediaType: o.mediaType })) : [];
7359
+ const recordedSources = entry.sourceUrls?.length ? entry.sourceUrls : entry.sourceUrl ? [{ url: entry.sourceUrl }] : opts.repair && entry.outputs.length ? entry.outputs.map((o) => ({ url: "", role: o.role, mediaType: o.mediaType })) : [];
7360
+ let sources = recordedSources;
7361
+ let rotated = false;
7362
+ if (opts.refresh && entry.status === "downloaded" && entry.objectId && provider.refreshSources) {
7363
+ let current;
7364
+ try {
7365
+ current = await provider.refreshSources(entry.objectId, {
7366
+ generator: entry.generator,
7367
+ metadata: entry.providerMetadata?.[provider.id],
7368
+ sources: recordedSources
7369
+ });
7370
+ } catch (err) {
7371
+ const message6 = err instanceof Error ? err.message : String(err);
7372
+ upsert(lock, key, { status: "download-failed", error: `download failed: ${message6}` });
7373
+ result.failed++;
7374
+ log2(` FAILED ${key}: ${message6}`);
7375
+ await saveLock(lockPath, lock);
7376
+ continue;
7377
+ }
7378
+ if (current === null) {
7379
+ upsert(lock, key, { status: "download-failed", error: `download failed: ${entry.objectId} no longer exists upstream` });
7380
+ result.failed++;
7381
+ log2(` FAILED ${key}: ${entry.objectId} no longer exists upstream`);
7382
+ await saveLock(lockPath, lock);
7383
+ continue;
7384
+ }
7385
+ if (current) {
7386
+ sources = current.map((source) => ({
7387
+ ...source,
7388
+ mediaType: source.mediaType ?? recordedSources.find((r) => r.role === source.role)?.mediaType
7389
+ }));
7390
+ rotated = sources.length !== recordedSources.length || sources.some((source, index) => source.url !== recordedSources[index]?.url || source.role !== recordedSources[index]?.role);
7391
+ }
7392
+ }
7335
7393
  if (!sources.length) {
7336
7394
  upsert(lock, key, {
7337
7395
  status: "download-failed",
@@ -7460,6 +7518,10 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
7460
7518
  log2(` wrote ${path13.relative(process.cwd(), target)}`);
7461
7519
  }
7462
7520
  if (opts.refresh && !wrote) {
7521
+ if (rotated) {
7522
+ const persistent = sources.filter((source) => shouldPersistSourceUrl(source.url));
7523
+ upsert(lock, key, { sourceUrl: persistent[0]?.url ?? null, sourceUrls: persistent });
7524
+ }
7463
7525
  result.unchanged = (result.unchanged ?? 0) + 1;
7464
7526
  continue;
7465
7527
  }
@@ -10177,6 +10239,32 @@ async function samePixels(a, b) {
10177
10239
  }
10178
10240
  }
10179
10241
  var metadataFps = (entry) => entry ? frameSetFps(entry) : null;
10242
+ function describeCharacter(spec, entry) {
10243
+ const recorded = entry?.providerMetadata?.[entry.provider]?.character;
10244
+ if (spec?.character) {
10245
+ const character = spec.character;
10246
+ return {
10247
+ kind: character.kind,
10248
+ parentKey: character.parentAssetId ? lockKey(spec.styleId, character.parentAssetId) : null,
10249
+ mode: character.kind === "animation" ? character.animation.mode : character.mode,
10250
+ directions: character.directions,
10251
+ direction: character.animation?.direction ?? null,
10252
+ characterId: recorded?.characterId ?? entry?.objectId?.split("#")[0] ?? null
10253
+ };
10254
+ }
10255
+ if (entry?.generator === "character") {
10256
+ const kind = recorded?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
10257
+ return {
10258
+ kind,
10259
+ parentKey: null,
10260
+ mode: recorded?.mode ?? "standard",
10261
+ directions: recorded?.directions ?? entry.outputs.length,
10262
+ direction: recorded?.direction ?? null,
10263
+ characterId: recorded?.characterId ?? entry.objectId?.split("#")[0] ?? null
10264
+ };
10265
+ }
10266
+ return null;
10267
+ }
10180
10268
  async function fileInfo(absolutePath) {
10181
10269
  try {
10182
10270
  const info = await stat2(absolutePath);
@@ -10385,6 +10473,7 @@ async function buildGallerySnapshot(opts) {
10385
10473
  currentSpecHash: spec.specHash,
10386
10474
  revision: entry?.revision ?? null,
10387
10475
  revisionParentKey: spec.revision ? lockKey(spec.styleId, spec.revision.sourceAssetId) : entry?.revision ? lockKey(spec.styleId, entry.revision.sourceAssetId) : null,
10476
+ character: describeCharacter(spec, entry),
10388
10477
  outputs,
10389
10478
  quality,
10390
10479
  providerMetadata: entry?.providerMetadata ?? {},
@@ -10446,6 +10535,7 @@ async function buildGallerySnapshot(opts) {
10446
10535
  currentSpecHash: null,
10447
10536
  revision: entry.revision,
10448
10537
  revisionParentKey: entry.revision ? lockKey(entry.styleId, entry.revision.sourceAssetId) : null,
10538
+ character: describeCharacter(void 0, entry),
10449
10539
  outputs,
10450
10540
  quality: null,
10451
10541
  providerMetadata: entry.providerMetadata ?? {},
@@ -10502,6 +10592,11 @@ async function buildGallerySnapshot(opts) {
10502
10592
  view: style?.view ?? null,
10503
10593
  noBackground: style?.noBackground ?? true,
10504
10594
  quality: Boolean(style?.quality),
10595
+ characters: style?.generator === "character" ? {
10596
+ bases: styleItems.filter((item) => item.character?.kind === "base").length,
10597
+ states: styleItems.filter((item) => item.character?.kind === "state").length,
10598
+ animations: styleItems.filter((item) => item.character?.kind === "animation").length
10599
+ } : null,
10505
10600
  tags: style?.tags ?? [],
10506
10601
  extends: typeof rawStyle?.extends === "string" ? rawStyle.extends : null,
10507
10602
  ownFields: rawStyle ? Object.keys(rawStyle).filter((key) => key !== "extends") : [],