diff --git a/makeFilesExecutable.js b/makeFilesExecutable.js new file mode 100644 index 0000000..a2e27a0 --- /dev/null +++ b/makeFilesExecutable.js @@ -0,0 +1,36 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { execSync } from 'child_process'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const binSrc = path.join(__dirname, 'src-tauri', 'binaries'); + +function makeFilesExecutable() { + try { + if (!fs.existsSync(binSrc)) { + console.error(`Binaries directory does not exist: ${binSrc}`); + return; + } + + const files = fs.readdirSync(binSrc); + const nonExeFiles = files.filter(file => !file.endsWith('.exe')); + let count = 0; + + for (const file of nonExeFiles) { + const filePath = path.join(binSrc, file); + if (fs.statSync(filePath).isFile()) { + execSync(`chmod +x "${filePath}"`); + console.log(`Made executable: ${file}`); + count++; + } + } + + console.log(`Successfully made ${count} files executable in ${binSrc}`); + } catch (error) { + console.error(`Error making files executable: ${error.message}`); + } +} + +makeFilesExecutable(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a2aed5f..815a042 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.7.0", "dependencies": { "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-progress": "^1.1.2", @@ -18,11 +20,13 @@ "@radix-ui/react-tooltip": "^1.1.7", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-fs": "^2.2.0", + "@tauri-apps/plugin-http": "^2.3.0", "@tauri-apps/plugin-notification": "^2.2.1", "@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-updater": "^2.5.0", + "@tauri-apps/plugin-upload": "^2.2.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "dotenv": "^16.4.5", @@ -928,6 +932,104 @@ "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.3.tgz", + "integrity": "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", @@ -951,6 +1053,77 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz", + "integrity": "sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", @@ -2303,6 +2476,15 @@ "@tauri-apps/api": "^2.0.0" } }, + "node_modules/@tauri-apps/plugin-http": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.3.0.tgz", + "integrity": "sha512-pigTvz+zzAqbIhCzRiR1GE98Jw7A03j2V+Eiexr9thBI8VfMiwFQMcbgON51xlwnVaI72LdbYKNajU84im8tlg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, "node_modules/@tauri-apps/plugin-notification": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.2.1.tgz", @@ -2348,6 +2530,15 @@ "@tauri-apps/api": "^2.0.0" } }, + "node_modules/@tauri-apps/plugin-upload": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-upload/-/plugin-upload-2.2.1.tgz", + "integrity": "sha512-2EyVhJYLAp2mJH0UzO3QGU0vPk/YWvAfdI2wXbczyzEZY/AZVa9wConyB1TV/NGhyJRim4LwWgkmnEvcKLkECw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", diff --git a/package.json b/package.json index 711d049..69a731f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ }, "dependencies": { "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-progress": "^1.1.2", @@ -20,11 +22,13 @@ "@radix-ui/react-tooltip": "^1.1.7", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-fs": "^2.2.0", + "@tauri-apps/plugin-http": "^2.3.0", "@tauri-apps/plugin-notification": "^2.2.1", "@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-updater": "^2.5.0", + "@tauri-apps/plugin-upload": "^2.2.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "dotenv": "^16.4.5", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1ec9a43..f73beb9 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -661,10 +661,29 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding 2.3.1", "time 0.3.36", "version_check 0.9.5", ] +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna 1.0.3", + "log 0.4.22", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time 0.3.36", + "url 2.5.2", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -847,6 +866,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "deranged" version = "0.3.11" @@ -1012,6 +1037,15 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + [[package]] name = "dpi" version = "0.1.1" @@ -1309,6 +1343,21 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -1316,6 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1383,6 +1433,7 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -1709,6 +1760,25 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes 1.7.1", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.4.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1842,6 +1912,7 @@ dependencies = [ "bytes 1.7.1", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1932,6 +2003,124 @@ dependencies = [ "objc2", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec 1.13.2", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1959,6 +2148,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec 1.13.2", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2215,6 +2425,18 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.3.4" @@ -3332,6 +3554,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna 1.0.3", + "psl-types", +] + [[package]] name = "pytubepp-helper" version = "0.7.0" @@ -3344,12 +3582,14 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-fs", + "tauri-plugin-http", "tauri-plugin-notification", "tauri-plugin-os", "tauri-plugin-process", "tauri-plugin-shell", "tauri-plugin-single-instance", "tauri-plugin-updater", + "tauri-plugin-upload", "tokio", "tokio-tungstenite", ] @@ -3645,6 +3885,17 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "read-progress-stream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6435842fc2fea44b528719eb8c32203bbc1bb2f5b619fbe0c0a3d8350fd8d2a8" +dependencies = [ + "bytes 1.7.1", + "futures 0.3.30", + "pin-project-lite", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -3719,8 +3970,12 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes 1.7.1", + "cookie", + "cookie_store", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", @@ -3742,6 +3997,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-rustls", "tokio-util", @@ -4389,6 +4645,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "sys-locale" version = "0.3.2" @@ -4398,6 +4665,27 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4630,6 +4918,28 @@ dependencies = [ "uuid", ] +[[package]] +name = "tauri-plugin-http" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8137a106e0741fdd357366178fc6e0597abe7d20796f53f44171a1bcec1683" +dependencies = [ + "data-url", + "http", + "regex", + "reqwest", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.11", + "tokio", + "url 2.5.2", + "urlpattern", +] + [[package]] name = "tauri-plugin-notification" version = "2.2.1" @@ -4744,6 +5054,25 @@ dependencies = [ "zip", ] +[[package]] +name = "tauri-plugin-upload" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90ac6d3a783d4406caeae8c75aa05e96346474765517fddfd1dc313ff91aa89" +dependencies = [ + "futures-util", + "log 0.4.22", + "read-progress-stream", + "reqwest", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", + "tokio", + "tokio-util", +] + [[package]] name = "tauri-runtime" version = "2.3.0" @@ -4959,6 +5288,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -4999,7 +5338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "tokio-io", ] @@ -5010,7 +5349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", ] [[package]] @@ -5020,7 +5359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "log 0.4.22", ] @@ -5042,7 +5381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils 0.7.2", - "futures", + "futures 0.1.31", "lazy_static", "log 0.4.22", "mio 0.6.23", @@ -5071,7 +5410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", - "futures", + "futures 0.1.31", ] [[package]] @@ -5081,7 +5420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "iovec", "mio 0.6.23", "tokio-io", @@ -5094,7 +5433,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" dependencies = [ - "futures", + "futures 0.1.31", "native-tls", "tokio-io", ] @@ -5456,6 +5795,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -5733,7 +6084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "319bacd7682c7dfe1444e7cb1aed23bf5b1d837d722925f531e1665bd21a4603" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "hyper 0.10.16", "native-tls", "rand 0.6.5", @@ -5757,7 +6108,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "bytes 0.4.12", - "futures", + "futures 0.1.31", "native-tls", "rand 0.6.5", "sha-1", @@ -6257,6 +6608,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wry" version = "0.48.1" @@ -6352,6 +6715,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "synstructure", +] + [[package]] name = "zbus" version = "5.5.0" @@ -6436,12 +6823,55 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "zip" version = "2.2.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fb56a87..979c512 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -25,6 +25,8 @@ tauri-plugin-fs = "2" tauri-plugin-os = "2" tauri-plugin-process = "2" tauri-plugin-notification = "2" +tauri-plugin-http = "2" +tauri-plugin-upload = "2" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-single-instance = "2" diff --git a/src-tauri/binaries/7zip-aarch64-apple-darwin b/src-tauri/binaries/7zip-aarch64-apple-darwin new file mode 100644 index 0000000..2cde7d8 Binary files /dev/null and b/src-tauri/binaries/7zip-aarch64-apple-darwin differ diff --git a/src-tauri/binaries/7zip-x86_64-apple-darwin b/src-tauri/binaries/7zip-x86_64-apple-darwin new file mode 100644 index 0000000..2cde7d8 Binary files /dev/null and b/src-tauri/binaries/7zip-x86_64-apple-darwin differ diff --git a/src-tauri/binaries/7zip-x86_64-pc-windows-msvc.exe b/src-tauri/binaries/7zip-x86_64-pc-windows-msvc.exe new file mode 100644 index 0000000..b322bf8 Binary files /dev/null and b/src-tauri/binaries/7zip-x86_64-pc-windows-msvc.exe differ diff --git a/src-tauri/binaries/7zip-x86_64-unknown-linux-gnu b/src-tauri/binaries/7zip-x86_64-unknown-linux-gnu new file mode 100644 index 0000000..61b6b6f Binary files /dev/null and b/src-tauri/binaries/7zip-x86_64-unknown-linux-gnu differ diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cf315a..485e81e 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -14,8 +14,15 @@ "process:default", "notification:default", "updater:default", + "upload:default", "core:window:allow-hide", "fs:allow-app-write", - "fs:allow-app-write-recursive" + "fs:allow-app-write-recursive", + { + "identifier": "http:default", + "allow": [ + { "url": "https://github.com" } + ] + } ] } \ No newline at end of file diff --git a/src-tauri/capabilities/shell.json b/src-tauri/capabilities/shell.json index ef4774d..60a20e6 100644 --- a/src-tauri/capabilities/shell.json +++ b/src-tauri/capabilities/shell.json @@ -128,6 +128,21 @@ }, "--raw-info" ] + }, + { + "name": "binaries/7zip", + "args": true, + "sidecar": true + } + ] + }, + { + "identifier": "shell:allow-spawn", + "allow": [ + { + "name": "binaries/7zip", + "args": true, + "sidecar": true } ] } diff --git a/src-tauri/msghost/src/config.rs b/src-tauri/msghost/src/config.rs index 0901e18..fc64d9a 100644 --- a/src-tauri/msghost/src/config.rs +++ b/src-tauri/msghost/src/config.rs @@ -11,7 +11,10 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { port: 3030, theme: "system".to_string() } + Self { + port: 3030, + theme: "system".to_string(), + } } } diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 295dd10..7faed04 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -11,7 +11,10 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { port: 3030, theme: "system".to_string() } + Self { + port: 3030, + theme: "system".to_string(), + } } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e34e351..7a1268b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -50,7 +50,10 @@ async fn wait_for_port_availability(port: u16, max_attempts: u32) -> Result<(), sleep(Duration::from_millis(500)).await; attempts += 1; } - Err(format!("Port {} did not become available after {} attempts", port, max_attempts)) + Err(format!( + "Port {} did not become available after {} attempts", + port, max_attempts + )) } async fn start_websocket_server(app_handle: tauri::AppHandle, port: u16) -> Result<(), String> { @@ -357,6 +360,8 @@ pub async fn run() { .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_notification::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_upload::init()) .manage(websocket_state.clone()) .setup(move |app| { // Create menu items diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json index f8cafce..be81666 100644 --- a/src-tauri/tauri.linux.conf.json +++ b/src-tauri/tauri.linux.conf.json @@ -1,7 +1,7 @@ { "build": { - "beforeDevCommand": "npm run dev && cargo build --manifest-path=./src-tauri/msghost/Cargo.toml", - "beforeBuildCommand": "npm run build && cargo build --release --manifest-path=./src-tauri/msghost/Cargo.toml", + "beforeDevCommand": "cargo build --manifest-path=./src-tauri/msghost/Cargo.toml && node makeFilesExecutable.js && npm run dev", + "beforeBuildCommand": "cargo build --release --manifest-path=./src-tauri/msghost/Cargo.toml && node makeFilesExecutable.js && npm run build", "devUrl": "http://localhost:1422", "frontendDist": "../dist" }, @@ -19,7 +19,22 @@ "csp": null, "capabilities": [ "main-capability", - "shell-scope" + "shell-scope", + { + "identifier": "fs-scope", + "description": "allowed file system scopes", + "permissions": [ + { + "identifier": "fs:scope", + "allow": [ + { "path": "$DOWNLOAD/pytubepp-extension-chrome" }, + { "path": "$DOWNLOAD/pytubepp-extension-chrome/*" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper/*" } + ] + } + ] + } ] } }, @@ -58,6 +73,9 @@ "/etc/xdg/autostart/pytubepp-helper-autostart.desktop": "./autostart/pytubepp-helper-autostart.desktop" } } - } + }, + "externalBin": [ + "binaries/7zip" + ] } } \ No newline at end of file diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json index afa0ead..2bc8a5a 100644 --- a/src-tauri/tauri.macos.conf.json +++ b/src-tauri/tauri.macos.conf.json @@ -1,7 +1,7 @@ { "build": { - "beforeDevCommand": "[[ -n \"$TARGET_ARCH\" ]] && ARCH=\"$TARGET_ARCH\" || ARCH=\"$(uname -m | sed 's/^arm64$/aarch64/')-apple-darwin\" && cargo build --target=$ARCH --manifest-path=./src-tauri/msghost/Cargo.toml && npm run dev", - "beforeBuildCommand": "[[ -n \"$TARGET_ARCH\" ]] && ARCH=\"$TARGET_ARCH\" || ARCH=\"$(uname -m | sed 's/^arm64$/aarch64/')-apple-darwin\" && cargo build --release --target=$ARCH --manifest-path=./src-tauri/msghost/Cargo.toml && node copyFiles.${ARCH}.js && npm run build", + "beforeDevCommand": "[[ -n \"$TARGET_ARCH\" ]] && ARCH=\"$TARGET_ARCH\" || ARCH=\"$(uname -m | sed 's/^arm64$/aarch64/')-apple-darwin\" && cargo build --target=$ARCH --manifest-path=./src-tauri/msghost/Cargo.toml && node makeFilesExecutable.js && npm run dev", + "beforeBuildCommand": "[[ -n \"$TARGET_ARCH\" ]] && ARCH=\"$TARGET_ARCH\" || ARCH=\"$(uname -m | sed 's/^arm64$/aarch64/')-apple-darwin\" && cargo build --release --target=$ARCH --manifest-path=./src-tauri/msghost/Cargo.toml && node copyFiles.${ARCH}.js && node makeFilesExecutable.js && npm run build", "devUrl": "http://localhost:1422", "frontendDist": "../dist" }, @@ -38,7 +38,11 @@ { "path": "$RESOURCE/pytubepp-helper-msghost.json" }, { "path": "$RESOURCE/pytubepp-helper-msghost-moz.json" }, { "path": "$RESOURCE/pytubepp-helper-msghost" }, - { "path": "$RESOURCE/pytubepp-helper-autostart.plist" } + { "path": "$RESOURCE/pytubepp-helper-autostart.plist" }, + { "path": "$DOWNLOAD/pytubepp-extension-chrome" }, + { "path": "$DOWNLOAD/pytubepp-extension-chrome/*" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper/*" } ] } ] @@ -67,6 +71,9 @@ "pytubepp-helper-msghost-moz.json", "pytubepp-helper-msghost", "pytubepp-helper-autostart.plist" + ], + "externalBin": [ + "binaries/7zip" ] } } \ No newline at end of file diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index f472f53..df1e9e8 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -29,7 +29,11 @@ "allow": [ { "path": "$RESOURCE/pytubepp-helper-msghost.json" }, { "path": "$RESOURCE/pytubepp-helper-msghost-moz.json" }, - { "path": "$RESOURCE/pytubepp-helper-msghost.exe" } + { "path": "$RESOURCE/pytubepp-helper-msghost.exe" }, + { "path": "$DOWNLOAD/pytubepp-extension-chrome" }, + { "path": "$DOWNLOAD/pytubepp-extension-chrome/*" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper" }, + { "path": "$TEMP/com.neosubhamoy.pytubepp.helper/*" } ] } ] @@ -62,6 +66,9 @@ "pytubepp-helper-msghost.json", "pytubepp-helper-msghost-moz.json", "pytubepp-helper-msghost.exe" + ], + "externalBin": [ + "binaries/7zip" ] } } \ No newline at end of file diff --git a/src/assets/images/chrome.png b/src/assets/images/chrome.png new file mode 100644 index 0000000..5536802 Binary files /dev/null and b/src/assets/images/chrome.png differ diff --git a/src/assets/images/edge.png b/src/assets/images/edge.png new file mode 100644 index 0000000..961311a Binary files /dev/null and b/src/assets/images/edge.png differ diff --git a/src/assets/images/firefox.png b/src/assets/images/firefox.png new file mode 100644 index 0000000..8e0b84a Binary files /dev/null and b/src/assets/images/firefox.png differ diff --git a/src/assets/images/opera.png b/src/assets/images/opera.png new file mode 100644 index 0000000..0ea623a Binary files /dev/null and b/src/assets/images/opera.png differ diff --git a/src/assets/images/pytubepp.png b/src/assets/images/pytubepp.png new file mode 100644 index 0000000..aac7bac Binary files /dev/null and b/src/assets/images/pytubepp.png differ diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..2f55a32 --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,57 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx new file mode 100644 index 0000000..9fa4894 --- /dev/null +++ b/src/components/ui/collapsible.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" + +const Collapsible = CollapsiblePrimitive.Root + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent + +export { Collapsible, CollapsibleTrigger, CollapsibleContent } diff --git a/src/main.tsx b/src/main.tsx index 028e295..02d9696 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,6 +6,7 @@ import App from "@/App"; import HomePage from "@/pages/home"; import SettingsPage from "@/pages/settings"; import NotificationsPage from "@/pages/notifications"; +import ExtensionManagerPage from "@/pages/extension-manager"; ReactDOM.createRoot(document.getElementById('root')!).render( @@ -15,6 +16,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render( } /> } /> } /> + } /> diff --git a/src/pages/extension-manager.tsx b/src/pages/extension-manager.tsx new file mode 100644 index 0000000..86e389e --- /dev/null +++ b/src/pages/extension-manager.tsx @@ -0,0 +1,279 @@ +import clsx from "clsx"; +import * as fs from "@tauri-apps/plugin-fs" +import { useEffect, useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { getPlatformInfo } from "@/lib/platform-utils"; +import { CurrentExtension, LatestExtensionResponse, PlatformInfo } from "@/types"; +import { ArrowLeft, ChevronsUpDown, CircleHelp, Loader2, RefreshCcw } from "lucide-react"; +import { Link } from "react-router-dom"; +import { downloadDir, tempDir, join } from "@tauri-apps/api/path"; +import { compareVersions } from "@/lib/utils"; +import { fetch } from '@tauri-apps/plugin-http'; +import { Card } from "@/components/ui/card"; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import { download } from '@tauri-apps/plugin-upload'; +import { Command } from "@tauri-apps/plugin-shell"; +import { toast } from "sonner" +import chromeLogo from "@/assets/images/chrome.png" +import firefoxLogo from "@/assets/images/firefox.png" +import edgeLogo from "@/assets/images/edge.png" +import operaLogo from "@/assets/images/opera.png" +import pytubeppLogo from "@/assets/images/pytubepp.png" + +export default function ExtensionManagerPage() { + const [platformInfo, setPlatformInfo] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isUpdating, setIsUpdating] = useState(false); + const [isCollapsibleOpen, setCollapsibleIsOpen] = useState(false) + const [isAccordionOpen, setAccordionIsOpen] = useState(false) + const [isExtensionInstalled, setIsExtensionInstalled] = useState(false); + const [isExtensionUpdateAvailable, setIsExtensionUpdateAvailable] = useState(false); + const [extensionUpdate, setExtensionUpdate] = useState(null) + const [currentExtension, setCurrentExtension] = useState(null) + const [updateStatus, setUpdateStatus] = useState(null) + + async function checkForUpdates() { + setIsLoading(true); + try { + const downloadDirPath = await downloadDir() + const extensionManifestPath = await join(downloadDirPath, "pytubepp-extension-chrome", "manifest.json") + const extensionManifestExists = await fs.exists(extensionManifestPath) + const response = await fetch('https://github.com/neosubhamoy/pytubepp-extension/releases/latest/download/latest.json', { + method: 'GET', + }); + if (response.ok) { + const data: LatestExtensionResponse = await response.json() + setExtensionUpdate(data) + if (extensionManifestExists) { + setIsExtensionInstalled(true) + const currentManifest = JSON.parse(await fs.readTextFile(extensionManifestPath)) + setCurrentExtension(currentManifest) + setIsExtensionUpdateAvailable(compareVersions(data.version, currentManifest.version) === 1) + } else { + setIsExtensionInstalled(false) + setCurrentExtension(null) + setIsExtensionUpdateAvailable(false) + } + } + else { + setIsExtensionUpdateAvailable(false) + setExtensionUpdate(null) + if (extensionManifestExists) { + setIsExtensionInstalled(true) + const currentManifest = JSON.parse(await fs.readTextFile(extensionManifestPath)) + setCurrentExtension(currentManifest) + } else { + setIsExtensionInstalled(false) + setCurrentExtension(null) + } + console.error('Failed to fetch latest extension version'); + } + } catch (error) { + console.error(error); + } finally { + setIsLoading(false); + } + } + + const unpackExtension = async (extension: LatestExtensionResponse, operation: "unpack" | "update") => { + setIsUpdating(true) + try { + setUpdateStatus('Preparing') + const downloadDirPath = await downloadDir() + const tempDirPath = await tempDir() + const extensionDirPath = await join(downloadDirPath, "pytubepp-extension-chrome") + const appTempDirPath = await join(tempDirPath, "com.neosubhamoy.pytubepp.helper") + const tempExtensionDownloadPath = await join(appTempDirPath, `pytubepp-extension-chrome-v${extension.version}.zip`) + + const extensionDirExists = await fs.exists(extensionDirPath) + const appTempDirExists = await fs.exists(appTempDirPath) + + if (!extensionDirExists) await fs.mkdir(extensionDirPath, { recursive: true}).then(() => console.log(`Created: ${extensionDirPath}`)) + if (!appTempDirExists) await fs.mkdir(appTempDirPath, { recursive: true}).then(() => console.log(`Created: ${appTempDirPath}`)) + + setUpdateStatus('Downloading') + await download( + extension.browsers.chrome.url, + tempExtensionDownloadPath, + ({ progress, total }) => console.log(`Downloading: ${progress} of ${total} bytes`) + ); + + setUpdateStatus('Unpacking') + const output = await Command.sidecar('binaries/7zip', ['x', tempExtensionDownloadPath, `-o${extensionDirPath}`, '-aoa']).execute() + if (output.code === 0) { + console.log(output.stdout) + console.log(`Unpacked ${tempExtensionDownloadPath} to ${extensionDirPath}`) + } else { + console.log(output.stdout, output.stderr) + } + + setUpdateStatus('Cleaning') + await fs.remove(tempExtensionDownloadPath) + console.log(`Deleted: ${tempExtensionDownloadPath}`) + + setIsExtensionInstalled(true) + setCurrentExtension({version: extension.version}) + setIsExtensionUpdateAvailable(false) + + if (operation === "unpack") toast(`Successfully unpacked v${extension.version} to ${extensionDirPath}`) + if (operation === "update") toast(`Successfully updated to v${extension.version}. Please reload the extension to reflect changes`) + } catch (error) { + if (operation === "unpack") toast(`Failed to unpack v${extension.version}`) + if (operation === "update") toast(`Failed to update v${extension.version}`) + console.error(error); + } finally { + setIsUpdating(false); + setUpdateStatus(null) + } + } + + useEffect(() => { + getPlatformInfo().then(setPlatformInfo).catch(console.error); + checkForUpdates(); + }, []) + + return ( +
+
+
+ + + +

Extension Manager

+
+
+ + + + +

refresh

+
+
+
+
+ { + isLoading ? ( +
+
+
+
+ +

ckecking...

+
+
+
+
+ ) : ( +
+
+ +
+
+
+ chrome + pytubepp +
+
+

PytubePP Extension (Chrome)

+

Unpacked: { currentExtension ? currentExtension.version : 'none' }   Latest: { extensionUpdate ? extensionUpdate.version : 'unknown' }

+
+
+ +
+
+ +
+

+ How to use unpacked extension +

+ + + +
+ +
    +
  • 1. Clicking on the 'Unpack' button unpacks latest pytubepp-extension for chrome within '~/Downloads/pytubepp-extension-chrome' folder
  • +
  • 2. You need to manually load the unpacked extension folder by visiting 'chrome://extensions' page
  • +
  • 3. If an update is available the 'Update' button will show up, Simply click on the button to update and don't forget to reload the extension by visiting 'chrome://extensions' page after updating
  • +
+
+
+
+
+ setAccordionIsOpen(!isAccordionOpen)}> + + Official Store Listings (Auto-Updates) + + +
+
+ firefox +
+
+

PytubePP Addon (Firefox)

+

Add pytubepp-addon to firefox

+
+
+ +
+ +
+
+ edge +
+
+

PytubePP Extension (Edge)

+

Add pytubepp-extension to edge

+
+
+ +
+ +
+
+ opera +
+
+

PytubePP Extension (Opera)

+

Add pytubepp-extension to opera

+
+
+ +
+
+
+
+ { + !isAccordionOpen && !isCollapsibleOpen && ( +
+
    +
  • * Extension Manager helps you manage unpacked pytubepp-extension (installing and updating) as pytubepp-extension is not available on Chrome Web Store under Blue Zinc guidelines. (unpacked chrome extension works for all chromium based browsers)
  • +
+
+ ) + } +
+
+ ) + } +
+
+ ) +} \ No newline at end of file diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 379f208..045bfd8 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -6,12 +6,15 @@ import { Button } from "@/components/ui/button"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { InstalledPrograms } from "@/types"; import { compareVersions, extractVersion, isInstalled, registerMacFiles } from "@/lib/utils"; -import { CircleCheck, TriangleAlert, CircleAlert, Settings, RefreshCcw, Loader2, PackagePlus, Bell } from "lucide-react"; +import { CircleCheck, TriangleAlert, CircleAlert, Settings, RefreshCcw, Loader2, PackagePlus, Bell, Puzzle } from "lucide-react"; import { getPlatformInfo } from "@/lib/platform-utils"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { toast } from "sonner" import { NotificationBadge } from "@/components/ui/notification-badge"; import { check as checkAppUpdate } from "@tauri-apps/plugin-updater"; +import { fetch } from '@tauri-apps/plugin-http'; +import { join, downloadDir } from "@tauri-apps/api/path"; +import * as fs from "@tauri-apps/plugin-fs" export default function HomePage() { const [isLoading, setIsLoading] = useState(true); @@ -22,6 +25,7 @@ export default function HomePage() { const [distroId, setDistroId] = useState(null) const [distroPkgMngr, setDistroPkgMngr] = useState(null) const [isAppUpdateAvailable, setIsAppUpdateAvailable] = useState(false); + const [isExtensionUpdateAvailable, setIsExtensionUpdateAvailable] = useState(false); const [installedPrograms, setInstalledPrograms] = useState({ winget: { installed: false, @@ -226,6 +230,36 @@ export default function HomePage() { checkForUpdates(); }, []); + useEffect(() => { + const checkForExtensionUpdates = async () => { + try { + const downloadDirPath = await downloadDir() + // const extensionDirPath = await join(downloadDirPath, "pytubepp-extension-chrome") + // const extensionDirExists = await fs.exists(extensionDirPath) + const extensionManifestPath = await join(downloadDirPath, "pytubepp-extension-chrome", "manifest.json") + const extensionManifestExists = await fs.exists(extensionManifestPath) + if (extensionManifestExists) { + const currentManifest = JSON.parse(await fs.readTextFile(extensionManifestPath)) + const response = await fetch('https://github.com/neosubhamoy/pytubepp-extension/releases/latest/download/latest.json', { + method: 'GET', + }); + if (response.ok) { + const data = await response.json() + setIsExtensionUpdateAvailable(compareVersions(data.version, currentManifest.version) === 1) + } + else { + console.error('Failed to fetch latest extension version'); + } + } else { + console.log('Currently installed extension\'s manifest not found') + } + } catch (error) { + console.error(error); + } + }; + checkForExtensionUpdates(); + }, []); + return (
@@ -236,9 +270,25 @@ export default function HomePage() { + + + extension manager + + + + +