@gentbajko/slopify 3.14.1 → 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.
- package/dist/edge/http/studio.js +1 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/main.js +16 -1
- package/dist/patch-notes/3.14.2.md +5 -0
- package/dist/patch-notes/3.15.0.md +5 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/storage/arrange.js +4 -1
- package/dist/slices/studio/unpacked.js +45 -0
- package/dist/web/assets/{index-wNwk0cqO.js → index-_soH0ZuO.js} +3 -3
- package/dist/web/assets/{pdf-Bw4akucO.js → pdf-BYQLQv5-.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/edge/http/studio.js
CHANGED
|
@@ -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).
|
|
Binary file
|
|
Binary file
|
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
|
|
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.14.2
|
|
2
|
+
|
|
3
|
+
Released 9 October 2026.
|
|
4
|
+
|
|
5
|
+
- **New videos get their readable folder too.** A project made after 3.14.0 kept its old folder once it finished, because a few placeholder steps that never run still looked like work waiting. Slopify now sorts a finished project into Upload, Working and History shortly after it's done, as it does for older projects.
|
|
@@ -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,16 @@
|
|
|
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
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"id": "3.14.2",
|
|
10
|
+
"title": "Slopify 3.14.2",
|
|
11
|
+
"version": "3.14.2",
|
|
12
|
+
"date": "2026-10-09"
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"id": "3.14.1",
|
|
4
16
|
"title": "Slopify 3.14.1",
|
|
@@ -22,7 +22,10 @@ export function arrangeKeys(db) {
|
|
|
22
22
|
// Whether anything of the project runs or may start: its files stay where they are until then.
|
|
23
23
|
export function projectSettled(db, projectId) {
|
|
24
24
|
const busy = db
|
|
25
|
-
.prepare(`SELECT 1 FROM revision_work WHERE project_id=? AND (state='running' OR (state='pending' AND dispatch_state!='held'
|
|
25
|
+
.prepare(`SELECT 1 FROM revision_work w WHERE w.project_id=? AND (w.state='running' OR (w.state='pending' AND w.dispatch_state!='held'
|
|
26
|
+
-- A deferred "…:future" stand-in stays pending after its real steps have run; the run
|
|
27
|
+
-- leaves it out (rebuild/runtime-store.ts executionStages), so it is not work to wait for.
|
|
28
|
+
AND EXISTS(SELECT 1 FROM revision_work_pieces p WHERE p.work_id=w.id AND p.work_key NOT LIKE '%:future')))
|
|
26
29
|
UNION ALL SELECT 1 FROM stages WHERE project_id=? AND state='running'
|
|
27
30
|
UNION ALL SELECT 1 FROM project_trash WHERE project_id=? LIMIT 1`)
|
|
28
31
|
.get(projectId, projectId, projectId);
|
|
@@ -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
|
+
}
|