diff --git a/README.md b/README.md
new file mode 100644
index 0000000..16ce2fc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# CDRM Extension
+
+An extension to show keys from DRM protected content, which are used to decrypt content.
+
+## Notes
+
+Keep these extension core files inside `src`:
+
+- `background.js`
+- `content.js`
+- `inject.js`
+- `manifest.json`
+
+Frontend React source stays in `frontend`.
+
+The build process will take care of everything into `extension-release`.
+
+To update the version across the entire project, simply change the version number in the root `package.json`. The build script will handle version sync automatically to both the extension's version and the frontend's title bar.
+
+## Build instructions
+
+### Prerequisites
+
+- Node.js v21 or higher. [Download Node.js here](https://nodejs.org/en/download).
+
+### How to build by yourself
+
+- Open terminal at the project root
+
+- Run the build script:
+
+```bash
+npm run buildext
+```
+
+This will:
+
+- Sync the version number from the root `package.json` to `src/manifest.json` and `frontend/package.json`
+- Install frontend dependencies if needed
+- Build the React frontend
+- Clean and prepare the `extension-release` folder
+- Copy extension files in `src`, built frontend assets, and icons into `extension-release`
+
+### How to load the extension in Google Chrome or Chromium browsers
+
+1. Go to `chrome://extensions/`
+2. Enable **Developer mode**
+3. Click **Load unpacked** and select the `extension-release` folder
+4. Verify the extension is working by clicking its icon or opening the developer console (F12) to check for any logs or errors.
diff --git a/buildext.js b/buildext.js
index de5beb0..632d6fb 100644
--- a/buildext.js
+++ b/buildext.js
@@ -2,6 +2,7 @@ import { execSync } from "child_process";
import fs from "fs";
import path from "path";
import url from "url";
+import syncVersion from "./syncVersion.js";
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const frontendDir = path.join(__dirname, "frontend");
@@ -10,21 +11,23 @@ const srcDir = path.join(__dirname, "src");
const iconDir = path.join(__dirname, "icons");
const releaseDir = path.join(__dirname, "extension-release");
-function run(cmd, cwd) {
+const run = (cmd, cwd) => {
console.log(`đ ī¸ Running: ${cmd}`);
execSync(cmd, { cwd, stdio: "inherit" });
-}
+};
-async function copyDir(src, dest) {
+const copyDir = async (src, dest) => {
await fs.promises.mkdir(dest, { recursive: true });
await fs.promises.cp(src, dest, {
recursive: true,
force: true,
filter: (src) => !src.endsWith(".map"),
});
-}
+};
+
+const main = async () => {
+ await syncVersion();
-async function main() {
console.log("đ Starting extension build...");
// 1. Install frontend deps if needed
@@ -57,7 +60,7 @@ async function main() {
await copyDir(iconDir, path.join(releaseDir, "icons"));
console.log("â
Build complete! extension-release ready.");
-}
+};
main().catch((e) => {
console.error("â Build failed:", e);
diff --git a/frontend/index.html b/frontend/index.html
index e830ac7..cb4ef3c 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,7 +3,7 @@
- CDRM Decryption Extension
+ CDRM Decryption Extension v%APPVERSION%
diff --git a/frontend/package.json b/frontend/package.json
index b4df8fd..326d96a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,30 +1,30 @@
{
- "name": "frontend",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "lint": "eslint .",
- "preview": "vite preview"
- },
- "dependencies": {
- "@tailwindcss/vite": "^4.1.11",
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
- "react-router-dom": "^7.7.0",
- "tailwindcss": "^4.1.11"
- },
- "devDependencies": {
- "@eslint/js": "^9.31.0",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
- "@vitejs/plugin-react": "^4.7.0",
- "eslint": "^9.31.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.3.0",
- "vite": "^7.0.5"
- }
-}
+ "name": "frontend",
+ "private": true,
+ "version": "2.1.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.1.11",
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "react-router-dom": "^7.7.0",
+ "tailwindcss": "^4.1.11"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.31.0",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "@vitejs/plugin-react": "^4.7.0",
+ "eslint": "^9.31.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.3.0",
+ "vite": "^7.0.5"
+ }
+}
\ No newline at end of file
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index a4c9f4d..01889a6 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -1,9 +1,21 @@
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
+import react from "@vitejs/plugin-react";
+import { readFileSync } from "fs";
+import { defineConfig } from "vite";
+
+const packageJson = JSON.parse(readFileSync("./package.json", "utf8"));
+
+const replaceVersionPlugin = () => {
+ return {
+ name: "replace-version",
+ transformIndexHtml(html) {
+ return html.replace("%APPVERSION%", packageJson.version);
+ },
+ };
+};
// https://vite.dev/config/
export default defineConfig({
base: "./",
- plugins: [react(), tailwindcss()],
+ plugins: [react(), tailwindcss(), replaceVersionPlugin()],
});
diff --git a/package.json b/package.json
index 74ba2f6..2372915 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cdrm-extension",
- "version": "1.0.0",
+ "version": "2.1.0",
"description": "",
"main": "background.js",
"scripts": {
diff --git a/src/manifest.json b/src/manifest.json
index 5c54ba4..00a5ab7 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -1,41 +1,49 @@
{
- "manifest_version": 2,
- "name": "CDRM Extension 2.0",
- "version": "2.0",
- "description": "Decrypt DRM Protected content",
- "permissions": [
- "webRequest",
- "webRequestBlocking",
- "",
- "activeTab",
- "storage",
- "tabs",
- "contextMenus"
+ "manifest_version": 2,
+ "name": "CDRM Extension",
+ "version": "2.1.0",
+ "description": "Decrypt DRM protected content",
+ "permissions": [
+ "webRequest",
+ "webRequestBlocking",
+ "",
+ "activeTab",
+ "storage",
+ "tabs",
+ "contextMenus"
+ ],
+ "background": {
+ "scripts": [
+ "background.js"
],
- "background": {
- "scripts": ["background.js"],
- "persistent": true
- },
- "content_scripts": [
- {
- "matches": [""],
- "js": ["content.js"],
- "run_at": "document_start",
- "all_frames": true
- }
- ],
- "web_accessible_resources": ["inject.js"],
- "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
- "browser_action": {
- "default_icon": {
- "16": "icons/icon16.png",
- "32": "icons/icon32.png",
- "128": "icons/icon128.png"
- }
- },
- "icons": {
- "16": "icons/icon16.png",
- "32": "icons/icon32.png",
- "128": "icons/icon128.png"
+ "persistent": true
+ },
+ "content_scripts": [
+ {
+ "matches": [
+ ""
+ ],
+ "js": [
+ "content.js"
+ ],
+ "run_at": "document_start",
+ "all_frames": true
}
-}
+ ],
+ "web_accessible_resources": [
+ "inject.js"
+ ],
+ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
+ "browser_action": {
+ "default_icon": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png",
+ "128": "icons/icon128.png"
+ }
+ },
+ "icons": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png",
+ "128": "icons/icon128.png"
+ }
+}
\ No newline at end of file
diff --git a/syncVersion.js b/syncVersion.js
new file mode 100644
index 0000000..7acba5d
--- /dev/null
+++ b/syncVersion.js
@@ -0,0 +1,46 @@
+import fs from "fs/promises";
+import path from "path";
+import { fileURLToPath } from "url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const syncVersion = async () => {
+ const rootPkgPath = path.join(__dirname, "package.json");
+ const frontendPkgPath = path.join(__dirname, "frontend", "package.json");
+ const manifestPath = path.join(__dirname, "src", "manifest.json");
+
+ // Read root package.json version
+ const rootPkgRaw = await fs.readFile(rootPkgPath, "utf-8");
+ const rootPkg = JSON.parse(rootPkgRaw);
+ const version = rootPkg.version;
+
+ if (!version) {
+ console.warn("â ī¸ No version field found in root package.json, skipping sync.");
+ return;
+ }
+
+ // Update frontend/package.json if exists
+ try {
+ const frontendPkgRaw = await fs.readFile(frontendPkgPath, "utf-8");
+ const frontendPkg = JSON.parse(frontendPkgRaw);
+ frontendPkg.version = version;
+ await fs.writeFile(frontendPkgPath, JSON.stringify(frontendPkg, null, 2));
+ console.log(`đ Updated frontend/package.json version to ${version}`);
+ } catch {
+ console.log("âšī¸ frontend/package.json not found or unreadable, skipping version update.");
+ }
+
+ // Update src/manifest.json version
+ try {
+ const manifestRaw = await fs.readFile(manifestPath, "utf-8");
+ const manifest = JSON.parse(manifestRaw);
+ manifest.version = version;
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
+ console.log(`đ Updated src/manifest.json version to ${version}`);
+ } catch (err) {
+ console.error(`â Failed to update src/manifest.json version: ${err.message}`);
+ }
+};
+
+export default syncVersion;