@gentbajko/slopify 3.14.2 → 3.15.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.
@@ -234,6 +234,7 @@ export function studioRoutes(deps) {
234
234
  channelPlaylists: readChannelPlaylists(deps.db),
235
235
  pairing: studioPairing(deps.db),
236
236
  autoComment: readSetting(deps.db, autoCommentKey) === "on",
237
+ unpacked: deps.unpackedExtension ?? null,
237
238
  });
238
239
  })
239
240
  // Post and pin each video's comment once it is public (opt-in: it posts in your name).
package/dist/main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { serve } from "@hono/node-server";
5
6
  import ffmpegStatic from "ffmpeg-static";
@@ -78,6 +79,7 @@ import { documentsDir, nodeDocumentsHost } from "./slices/storage/documents.js";
78
79
  import { createFilesService, ensureFilesFolders, filesLayoutOf, filesMoveKey, settleFilesLocation, } from "./slices/storage/files-location.js";
79
80
  import { createPlaces, recoverPlaces } from "./slices/storage/places.js";
80
81
  import { reconcileStorage } from "./slices/storage/reconcile.js";
82
+ import { installUnpackedExtension, unpackedExtensionDir, } from "./slices/studio/unpacked.js";
81
83
  import { previewPictures } from "./slices/style-preview/images.js";
82
84
  import { ffmpegStylePreview } from "./slices/style-preview/render.js";
83
85
  import { createStylePreviews, stylePreviewDir } from "./slices/style-preview/service.js";
@@ -496,6 +498,18 @@ export async function boot(config, options = {}) {
496
498
  void autostart.refresh().catch((error) => {
497
499
  log.write("warn", "autostart.refresh", { detail: causedBy(error) });
498
500
  });
501
+ // The Chrome extension, unpacked in a folder this install keeps current; loaded from there
502
+ // it reloads itself after each update (`slices/studio/unpacked.ts`). A container's data
503
+ // folder isn't the host's, so there it stays a download.
504
+ const extensionDist = fileURLToPath(new URL("../dist/extension", import.meta.url));
505
+ let unpackedExtension;
506
+ if (!container)
507
+ try {
508
+ unpackedExtension = installUnpackedExtension(join(extensionDist, "slopify-studio-chrome.zip"), unpackedExtensionDir(paths.dataDir));
509
+ }
510
+ catch (error) {
511
+ log.write("warn", "studio.extension", { detail: causedBy(error) });
512
+ }
499
513
  const app = createApp({
500
514
  autostart,
501
515
  rebuild,
@@ -541,7 +555,8 @@ export async function boot(config, options = {}) {
541
555
  log,
542
556
  version,
543
557
  webDist: fileURLToPath(new URL("../dist/web", import.meta.url)),
544
- extensionDist: fileURLToPath(new URL("../dist/extension", import.meta.url)),
558
+ extensionDist,
559
+ unpackedExtension,
545
560
  flushSoon: flusher.soon,
546
561
  probe: nodeCliProbe,
547
562
  hostCliStatus: hostCli?.status,
@@ -0,0 +1,5 @@
1
+ # Slopify 3.15.0
2
+
3
+ Released 9 October 2026.
4
+
5
+ - **The Studio extension updates itself with Slopify (Chrome, Edge, Brave).** Slopify now keeps the extension in a folder of its own and puts each new version there when it updates. Load the extension once from that folder (Settings → YouTube Studio shows it, with Copy), and from then on it switches to the new version by itself, once you haven't used it for 45 minutes, so a fill is never interrupted. No more downloading, unzipping and reloading after every update, and no waiting for the store. The store version keeps updating through the store as before. (Extension 1.3.3.)
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "id": "3.15.0",
4
+ "title": "Slopify 3.15.0",
5
+ "version": "3.15.0",
6
+ "date": "2026-10-09"
7
+ },
2
8
  {
3
9
  "id": "3.14.2",
4
10
  "title": "Slopify 3.14.2",
@@ -0,0 +1,45 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, normalize, sep } from "node:path";
3
+ import { strFromU8, unzipSync } from "fflate";
4
+ export function unpackedExtensionDir(dataDir) {
5
+ return join(dataDir, "extension", "chrome");
6
+ }
7
+ // Puts the zip's build in `target` when the version there differs. The new build is written
8
+ // beside it and swapped in whole, so Chrome never reads half of one and half of another.
9
+ export function installUnpackedExtension(zipPath, target) {
10
+ const files = unzipSync(readFileSync(zipPath));
11
+ const manifest = files["manifest.json"];
12
+ if (manifest === undefined)
13
+ throw new Error(`The Slopify Studio extension bundled with Slopify has no manifest.json (${zipPath}). Update or reinstall Slopify.`);
14
+ const version = String(JSON.parse(strFromU8(manifest)).version);
15
+ if (installedVersion(target) === version)
16
+ return { path: target, version };
17
+ const next = `${target}.next`;
18
+ const old = `${target}.old`;
19
+ rmSync(next, { recursive: true, force: true });
20
+ rmSync(old, { recursive: true, force: true });
21
+ for (const [name, bytes] of Object.entries(files)) {
22
+ if (name.endsWith("/"))
23
+ continue;
24
+ const path = normalize(name);
25
+ if (isAbsolute(path) || path === ".." || path.startsWith(`..${sep}`))
26
+ throw new Error(`The Slopify Studio extension bundled with Slopify has a file outside its folder (${name}). Update or reinstall Slopify.`);
27
+ mkdirSync(dirname(join(next, path)), { recursive: true });
28
+ writeFileSync(join(next, path), bytes, { mode: 0o644 });
29
+ }
30
+ mkdirSync(dirname(target), { recursive: true });
31
+ if (existsSync(target))
32
+ renameSync(target, old);
33
+ renameSync(next, target);
34
+ rmSync(old, { recursive: true, force: true });
35
+ return { path: target, version };
36
+ }
37
+ export function installedVersion(target) {
38
+ try {
39
+ const parsed = JSON.parse(readFileSync(join(target, "manifest.json"), "utf8"));
40
+ return typeof parsed.version === "string" ? parsed.version : undefined;
41
+ }
42
+ catch {
43
+ return undefined;
44
+ }
45
+ }