diff --git a/package.json b/package.json index d9a587c2..ed2a3c79 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,9 @@ "generate-sponsorblock-config": "node tools/generate-sponsorblock-config.js", "update-rale-component": "cd tools/RALE && rm TrackerTask.xml && curl -o TrackerTask.zip https://devtools.web.roku.com/roku-advanced-layout-editor/app/TrackerTask.zip && unzip TrackerTask.zip && rm TrackerTask.zip", "postinstall": "cd playlet-lib && npm install && cd ../playlet-app && npm install && cd ../playlet-web && npm install", - "update-deps": "npx npm-check-updates -u && cd playlet-app && npx npm-check-updates -u && cd ../playlet-lib && npx npm-check-updates -u && cd ../playlet-web && npx npm-check-updates -u" + "update-deps": "npx npm-check-updates -u && cd playlet-app && npx npm-check-updates -u && cd ../playlet-lib && npx npm-check-updates -u && cd ../playlet-web && npx npm-check-updates -u", + "registry:write": "node tools/registry.js --input registry.json", + "registry:read": "node tools/registry.js --output registry.json", + "registry:clear": "node tools/registry.js --clear" } } diff --git a/playlet-lib/src/components/Web/PlayletWebServer/Middleware/RegistryRouter.bs b/playlet-lib/src/components/Web/PlayletWebServer/Middleware/RegistryRouter.bs new file mode 100644 index 00000000..4d61473a --- /dev/null +++ b/playlet-lib/src/components/Web/PlayletWebServer/Middleware/RegistryRouter.bs @@ -0,0 +1,49 @@ +#if DEBUG + import "pkg:/source/utils/RegistryUtils.bs" + + namespace Http + + class RegistryRouter extends HttpRouter + + function new() + super() + end function + + @get("/debug/registry") + function GetRegistry(context as object) as boolean + response = context.response + + registryValues = RegistryUtils.ReadAll() + response.Json(registryValues) + return true + end function + + @post("/debug/registry") + function SetRegistry(context as object) as boolean + request = context.request + response = context.response + + payload = request.Json() + if payload = invalid + response.Default(400, "Invalid JSON") + return true + end if + + RegistryUtils.WriteAll(payload) + response.Default(204, "OK") + return true + end function + + @delete("/debug/registry") + function ClearRegistry(context as object) as boolean + response = context.response + + RegistryUtils.DeleteAll() + response.Default(204, "OK") + return true + end function + + end class + + end namespace +#end if diff --git a/playlet-lib/src/components/Web/PlayletWebServer/PlayletWebServer.bs b/playlet-lib/src/components/Web/PlayletWebServer/PlayletWebServer.bs index 2ae61752..3699b2ef 100644 --- a/playlet-lib/src/components/Web/PlayletWebServer/PlayletWebServer.bs +++ b/playlet-lib/src/components/Web/PlayletWebServer/PlayletWebServer.bs @@ -7,6 +7,7 @@ import "pkg:/components/Web/PlayletWebServer/Middleware/InvidiousRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/PlayletLibUrlsRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/PlayQueueRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/PreferencesRouter.bs" +import "pkg:/components/Web/PlayletWebServer/Middleware/RegistryRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/SearchHistoryRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/StateApiRouter.bs" import "pkg:/components/Web/PlayletWebServer/Middleware/ViewRouter.bs" @@ -51,6 +52,8 @@ function SetupRoutes(server as object) server.UseRouter(new Http.HttpStaticFilesRouter("/config", "libpkg:/config", etags, { staticFiles: true })) server.UseRouter(new Http.HttpStaticFilesRouter("/logs", "cachefs:/logs", etags, { fsCheck: true })) #if DEBUG + server.UseRouter(new Http.RegistryRouter()) + options = { showDirectories: true, showHidden: true, staticFiles: true } server.UseRouter(new Http.HttpStaticFilesRouter("/debug/libpkg", "libpkg:/", etags, options)) options = { showDirectories: true, showHidden: true, fsCheck: true, staticFiles: true } diff --git a/playlet-lib/src/source/utils/RegistryUtils.bs b/playlet-lib/src/source/utils/RegistryUtils.bs index 126aae75..e70d34cc 100644 --- a/playlet-lib/src/source/utils/RegistryUtils.bs +++ b/playlet-lib/src/source/utils/RegistryUtils.bs @@ -13,24 +13,24 @@ namespace RegistryUtils ' "invidious_instances" is deprecated, but we keep for migration purposes const INVIDIOUS_INSTANCES = "invidious_instances" - function Read(key as string, section = DEFAULT_SECTION as string) as dynamic - sec = CreateObject("roRegistrySection", section) - if sec.Exists(key) - return sec.Read(key) + function Read(key as string, sectionName = DEFAULT_SECTION as string) as dynamic + section = CreateObject("roRegistrySection", sectionName) + if section.Exists(key) + return section.Read(key) end if return invalid end function - function Write(key as string, value as string, section = DEFAULT_SECTION as string) - sec = CreateObject("roRegistrySection", section) - sec.Write(key, value) - sec.Flush() + function Write(key as string, value as string, sectionName = DEFAULT_SECTION as string) + section = CreateObject("roRegistrySection", sectionName) + section.Write(key, value) + section.Flush() end function - function Delete(key as string, section = DEFAULT_SECTION as string) - sec = CreateObject("roRegistrySection", section) - sec.Delete(key) - sec.Flush() + function Delete(key as string, sectionName = DEFAULT_SECTION as string) + section = CreateObject("roRegistrySection", sectionName) + section.Delete(key) + section.Flush() end function function ReadAll() as object @@ -46,4 +46,22 @@ namespace RegistryUtils return result end function + function WriteAll(values as object) as void + DeleteAll() + for each sectionName in values + section = CreateObject("roRegistrySection", sectionName) + section.WriteMulti(values[sectionName]) + section.Flush() + end for + end function + + function DeleteAll() as void + registry = CreateObject("roRegistry") + sectionNames = registry.GetSectionList() + for each sectionName in sectionNames + registry.Delete(sectionName) + end for + registry.Flush() + end function + end namespace diff --git a/tools/registry.js b/tools/registry.js new file mode 100644 index 00000000..958d6abf --- /dev/null +++ b/tools/registry.js @@ -0,0 +1,75 @@ +// Description: Read, write and delete registry. + +const fs = require('fs'); +const { ArgumentParser, BooleanOptionalAction } = require('argparse'); +const getEnvVars = require('./get-env-vars'); + +(async () => { + const parser = new ArgumentParser({ + description: 'Manage registry' + }); + + parser.add_argument('--input', { help: 'Input file path, for importing to Roku device' }); + parser.add_argument('--output', { help: 'Output file path, for exporting from Roku device' }); + parser.add_argument('--clear', { help: 'Clear registry', action: BooleanOptionalAction, default: false }); + + const args = parser.parse_args(); + const input = args.input; + const output = args.output; + const clear = args.clear; + + // only one action can be specified at a time between input, output and clear + const actions = [input, output, clear].filter(Boolean); + if (actions.length !== 1) { + console.error('Only one (and exactly one) action can be specified at a time between --input, --output and --clear'); + process.exit(1); + } + + const config = getEnvVars(['ROKU_DEV_TARGET']); + + const playletServer = `http://${config.ROKU_DEV_TARGET}:8888/debug/registry`; + + if (input) { + const registry = JSON.parse(fs.readFileSync(input, { encoding: 'utf8', flag: 'r' })); + + for (const section in registry) { + for (const key in registry[section]) { + registry[section][key] = JSON.stringify(registry[section][key]); + } + } + + await fetch(playletServer, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(registry), + }); + } + else if (output) { + if (fs.existsSync(output)) { + throw new Error(`File "${output}" already exists`); + } + const response = await fetch(playletServer); + const registry = await response.json(); + + for (const section in registry) { + for (const key in registry[section]) { + // We expect all values to be JSON strings. + // If this changes, we let JSON.parse throw an error. + registry[section][key] = JSON.parse(registry[section][key]); + } + } + + fs.writeFileSync(output, JSON.stringify(registry, null, 4)); + } + else if (clear) { + await fetch(playletServer, { + method: 'DELETE', + }); + } + else { + console.error('No action specified'); + process.exit(1); + } +})(); diff --git a/tools/run-test-app.js b/tools/run-test-app.js index e96a6938..1072ad4a 100644 --- a/tools/run-test-app.js +++ b/tools/run-test-app.js @@ -1,8 +1,7 @@ // Description: Deploys a test app to a Roku device and waits for it to finish // This will log the test report to the console and throw an error if the tests fail -const { ArgumentParser } = require('argparse') -const { BooleanOptionalAction } = require('argparse'); +const { ArgumentParser, BooleanOptionalAction } = require('argparse') const getEnvVars = require('./get-env-vars'); const rokuDeploy = require('roku-deploy'); const path = require('path');