From 5677b3919b58994e762bdabb789b8e397c04ebc7 Mon Sep 17 00:00:00 2001
From: Mahdi Pourismaiel <mpourismaiel@gmail.com>
Date: Thu, 10 Aug 2023 17:34:09 +0330
Subject: [PATCH 1/3] Add helia and upload functionality

Still no ipns
---
 package-lock.json                | 388 +++++++++++++++++++++++++++++--
 watcher/assets/styles/global.css |  18 +-
 watcher/helia.mjs                |  12 +-
 watcher/main.js                  | 170 ++++++++++++--
 watcher/package.json             |   2 +
 watcher/pages/index.html         |   7 +-
 watcher/pages/preload.js         |  11 +
 watcher/pages/renderer.js        |  65 ++++--
 watcher/pages/style.css          |  17 ++
 9 files changed, 618 insertions(+), 72 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 10335c0..ff1e74f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -150,6 +150,11 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@assemblyscript/loader": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.9.4.tgz",
+      "integrity": "sha512-HazVq9zwTVwGmqdwYzu7WyQ6FQVZ7SwET0KKQuKm55jD0IfUpZgN0OPIiZG3zV1iSrVYcN0bdwLRXI/VNCYsUA=="
+    },
     "node_modules/@babel/code-frame": {
       "version": "7.22.5",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
@@ -1764,6 +1769,86 @@
         "progress-events": "^1.0.0"
       }
     },
+    "node_modules/@helia/ipns": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@helia/ipns/-/ipns-1.1.3.tgz",
+      "integrity": "sha512-VnEQ4KPdRhrZTpMsrX6LlVCZdOwRY6YM4Cb0+GfzPPt0iDHSGuZ3dThlteHk7g1CLChzmjEgroUKNKjCU11PZQ==",
+      "dependencies": {
+        "@libp2p/interface-content-routing": "^2.1.0",
+        "@libp2p/interface-peer-id": "^2.0.1",
+        "@libp2p/interface-pubsub": "^4.0.1",
+        "@libp2p/interfaces": "^3.3.1",
+        "@libp2p/logger": "^2.0.6",
+        "@libp2p/peer-id": "^2.0.1",
+        "@libp2p/record": "^3.0.0",
+        "hashlru": "^2.3.0",
+        "interface-datastore": "^8.0.0",
+        "ipns": "^6.0.0",
+        "is-ipfs": "^8.0.1",
+        "multiformats": "^11.0.1",
+        "p-queue": "^7.3.0",
+        "progress-events": "^1.0.0",
+        "uint8arrays": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/@helia/unixfs": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@helia/unixfs/-/unixfs-1.4.1.tgz",
+      "integrity": "sha512-IS/VLLXwGg+53ojyb3JPB3j8kxt5UtkYc92K/OTh3tPm22kMGmTm93AL6iBChGBiWOgVhtjNEwXEsQoDQkVRVw==",
+      "dependencies": {
+        "@helia/interface": "^1.0.0",
+        "@ipld/dag-pb": "^4.0.0",
+        "@libp2p/interfaces": "^3.3.1",
+        "@libp2p/logger": "^2.0.6",
+        "@multiformats/murmur3": "^2.1.2",
+        "hamt-sharding": "^3.0.2",
+        "interface-blockstore": "^5.0.0",
+        "ipfs-unixfs": "^11.0.0",
+        "ipfs-unixfs-exporter": "^13.1.0",
+        "ipfs-unixfs-importer": "^15.1.0",
+        "it-glob": "^2.0.4",
+        "it-last": "^3.0.1",
+        "it-pipe": "^3.0.1",
+        "merge-options": "^3.0.4",
+        "multiformats": "^11.0.1",
+        "progress-events": "^1.0.0",
+        "sparse-array": "^1.3.2"
+      }
+    },
+    "node_modules/@ipld/dag-cbor": {
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.0.4.tgz",
+      "integrity": "sha512-HBNVngk/47pKNLTAelN6ORWgKkjJtQj96Xb+jIBtRShJGCsXgghj1TzTynTTIp1dZxwPe5rVIL6yjZmvdyP2Wg==",
+      "dependencies": {
+        "cborg": "^2.0.1",
+        "multiformats": "^12.0.1"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/@ipld/dag-cbor/node_modules/cborg": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/cborg/-/cborg-2.0.3.tgz",
+      "integrity": "sha512-f1IbyqgRLQK4ruNM+V3WikfYfXQg/f/zC1oneOw1P7F/Dn2OJX6MaXIdei3JMpz361IjY7OENBKcE53nkJFVCQ==",
+      "bin": {
+        "cborg": "cli.js"
+      }
+    },
+    "node_modules/@ipld/dag-cbor/node_modules/multiformats": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.0.1.tgz",
+      "integrity": "sha512-s01wijBJoDUqESWSzePY0lvTw7J3PVO9x2Cc6ASI5AMZM2Gnhh7BC17+nlFhHKU7dDzaCaRfb+NiqNzOsgPUoQ==",
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/@ipld/dag-pb": {
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/@ipld/dag-pb/-/dag-pb-4.0.5.tgz",
@@ -2838,6 +2923,28 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/@multiformats/murmur3": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/@multiformats/murmur3/-/murmur3-2.1.6.tgz",
+      "integrity": "sha512-kpJDN+o8B0gJaaqbdV/spIVPj35hqew4rEw8VzPmcITsLpHSgP8pJDeaVaGGVeX/UM8n4IGctLCxw7PBfVks+A==",
+      "dependencies": {
+        "multiformats": "^12.0.1",
+        "murmurhash3js-revisited": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/@multiformats/murmur3/node_modules/multiformats": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.0.1.tgz",
+      "integrity": "sha512-s01wijBJoDUqESWSzePY0lvTw7J3PVO9x2Cc6ASI5AMZM2Gnhh7BC17+nlFhHKU7dDzaCaRfb+NiqNzOsgPUoQ==",
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/@noble/ciphers": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.1.4.tgz",
@@ -4855,8 +4962,7 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
     "node_modules/base-x": {
       "version": "3.0.9",
@@ -4871,7 +4977,6 @@
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -4973,6 +5078,16 @@
         "node": ">=8"
       }
     },
+    "node_modules/bl": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
+      "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+      "dependencies": {
+        "buffer": "^6.0.3",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
     "node_modules/blakejs": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
@@ -5165,7 +5280,6 @@
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
       "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -8441,6 +8555,19 @@
         "node": ">=4.x"
       }
     },
+    "node_modules/hamt-sharding": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/hamt-sharding/-/hamt-sharding-3.0.2.tgz",
+      "integrity": "sha512-f0DzBD2tSmLFdFsLAvOflIBqFPjerbA7BfmwO8mVho/5hXwgyyYhv+ijIzidQf/DpDX3bRjAQvhGoBFj+DBvPw==",
+      "dependencies": {
+        "sparse-array": "^1.3.1",
+        "uint8arrays": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/handlebars": {
       "version": "4.7.8",
       "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
@@ -9072,7 +9199,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
       "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -9154,8 +9280,7 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/ini": {
       "version": "1.3.8",
@@ -9316,6 +9441,75 @@
         "varint-decoder": "^1.0.0"
       }
     },
+    "node_modules/ipfs-unixfs": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/ipfs-unixfs/-/ipfs-unixfs-11.0.1.tgz",
+      "integrity": "sha512-SD9dqn14bfgMfkPstsR/2Av3zCzYMj2ntQJab4HZucgX4nNV6K7guZh4Hf3kiL8ONff1Ogft1ekFU083DIKEdQ==",
+      "dependencies": {
+        "err-code": "^3.0.1",
+        "protons-runtime": "^5.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/ipfs-unixfs-exporter": {
+      "version": "13.1.6",
+      "resolved": "https://registry.npmjs.org/ipfs-unixfs-exporter/-/ipfs-unixfs-exporter-13.1.6.tgz",
+      "integrity": "sha512-pfFQThwa4/mbnLriHQso3wt25xcx5V0mBAOBPWx2jQv0QhgDtJSASyQrwwA2xE8F9zn0N3CWn7WDHhmM+EQ3sg==",
+      "dependencies": {
+        "@ipld/dag-cbor": "^9.0.0",
+        "@ipld/dag-pb": "^4.0.0",
+        "@multiformats/murmur3": "^2.0.0",
+        "err-code": "^3.0.1",
+        "hamt-sharding": "^3.0.0",
+        "interface-blockstore": "^5.0.0",
+        "ipfs-unixfs": "^11.0.0",
+        "it-filter": "^3.0.2",
+        "it-last": "^3.0.2",
+        "it-map": "^3.0.3",
+        "it-parallel": "^3.0.0",
+        "it-pipe": "^3.0.1",
+        "it-pushable": "^3.1.0",
+        "multiformats": "^11.0.0",
+        "p-queue": "^7.3.0",
+        "progress-events": "^1.0.0",
+        "uint8arrays": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/ipfs-unixfs-importer": {
+      "version": "15.1.7",
+      "resolved": "https://registry.npmjs.org/ipfs-unixfs-importer/-/ipfs-unixfs-importer-15.1.7.tgz",
+      "integrity": "sha512-dhcgfW5tigCcL3GhCCzEnNliU2M35mj23KYlcAHh7SCEMOHjNPZjm7kT8prrV24lJLite1QvS//BdcFS/yYY4Q==",
+      "dependencies": {
+        "@ipld/dag-pb": "^4.0.0",
+        "@multiformats/murmur3": "^2.0.0",
+        "err-code": "^3.0.1",
+        "hamt-sharding": "^3.0.0",
+        "interface-blockstore": "^5.0.0",
+        "interface-store": "^5.0.1",
+        "ipfs-unixfs": "^11.0.0",
+        "it-all": "^3.0.2",
+        "it-batch": "^3.0.2",
+        "it-first": "^3.0.2",
+        "it-parallel-batch": "^3.0.1",
+        "multiformats": "^11.0.0",
+        "progress-events": "^1.0.0",
+        "rabin-wasm": "^0.1.4",
+        "uint8arraylist": "^2.4.3",
+        "uint8arrays": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/ipns": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/ipns/-/ipns-6.0.3.tgz",
@@ -9536,6 +9730,78 @@
         "npm": ">=3"
       }
     },
+    "node_modules/is-ipfs": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-8.0.1.tgz",
+      "integrity": "sha512-hoBSElmPath3aDdtaOpVZsuCh2SXTqvLML+H75S7iDgKdqNmENJ6tsRucP1HLfpqEyZ/uIlj/+ZBxIC/F8B5Eg==",
+      "dependencies": {
+        "@multiformats/mafmt": "^11.0.3",
+        "@multiformats/multiaddr": "^11.0.0",
+        "iso-url": "^1.1.3",
+        "multiformats": "^11.0.0",
+        "uint8arrays": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/is-ipfs/node_modules/@multiformats/mafmt": {
+      "version": "11.1.2",
+      "resolved": "https://registry.npmjs.org/@multiformats/mafmt/-/mafmt-11.1.2.tgz",
+      "integrity": "sha512-3n1o5eLU7WzTAPLuz3AodV7Iql6NWf7Ws8fqVaGT7o5nDDabUPYGBm2cZuh3OrqmwyCY61LrNUIsjzivU6UdpQ==",
+      "dependencies": {
+        "@multiformats/multiaddr": "^12.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/is-ipfs/node_modules/@multiformats/mafmt/node_modules/@multiformats/multiaddr": {
+      "version": "12.1.6",
+      "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-12.1.6.tgz",
+      "integrity": "sha512-/2QwhnBzAJbR/f6halEzkbQLOrjwodrJsplfCbDfOOOZGOVBlNttNavb4fU6ks58yAs1aQ6bZrar8y08R+bagg==",
+      "dependencies": {
+        "@chainsafe/is-ip": "^2.0.1",
+        "@chainsafe/netmask": "^2.0.0",
+        "@libp2p/interface": "^0.1.1",
+        "dns-over-http-resolver": "^2.1.0",
+        "multiformats": "^12.0.1",
+        "uint8arrays": "^4.0.2",
+        "varint": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.6.0"
+      }
+    },
+    "node_modules/is-ipfs/node_modules/@multiformats/mafmt/node_modules/multiformats": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.0.1.tgz",
+      "integrity": "sha512-s01wijBJoDUqESWSzePY0lvTw7J3PVO9x2Cc6ASI5AMZM2Gnhh7BC17+nlFhHKU7dDzaCaRfb+NiqNzOsgPUoQ==",
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/is-ipfs/node_modules/@multiformats/multiaddr": {
+      "version": "11.6.1",
+      "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-11.6.1.tgz",
+      "integrity": "sha512-doST0+aB7/3dGK9+U5y3mtF3jq85KGbke1QiH0KE1F5mGQ9y56mFebTeu2D9FNOm+OT6UHb8Ss8vbSnpGjeLNw==",
+      "dependencies": {
+        "@chainsafe/is-ip": "^2.0.1",
+        "dns-over-http-resolver": "^2.1.0",
+        "err-code": "^3.0.1",
+        "multiformats": "^11.0.0",
+        "uint8arrays": "^4.0.2",
+        "varint": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/is-loopback-addr": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-loopback-addr/-/is-loopback-addr-2.0.1.tgz",
@@ -9750,6 +10016,15 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/it-batch": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/it-batch/-/it-batch-3.0.2.tgz",
+      "integrity": "sha512-Ypepz/vCxNFOvFkUPFvoxGb8WzqainzhflRaJahp1MBo3Y42ICdrgR3xIwOFE6WAgO+UWUM0zeMlbUStsW9AIQ==",
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/it-batched-bytes": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/it-batched-bytes/-/it-batched-bytes-2.0.3.tgz",
@@ -9805,6 +10080,40 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/it-glob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/it-glob/-/it-glob-2.0.4.tgz",
+      "integrity": "sha512-dRe4uw3MMScqx0vzx67T4gNfk6OMg7E2EjSZMiNoqnN9/wbTSrFEC/rZBEGJH3hMECW8ZX1iSjMCFHDAP8TXCA==",
+      "dependencies": {
+        "minimatch": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/it-glob/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/it-glob/node_modules/minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/it-handshake": {
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/it-handshake/-/it-handshake-4.1.3.tgz",
@@ -9821,6 +10130,15 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/it-last": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/it-last/-/it-last-3.0.2.tgz",
+      "integrity": "sha512-aWoA5moJ7XSKe7+YuutBKhySroDDWkfjpo+UknekPh1M5YYdK4YNSPDarR+7o/NqRwzazwgzCi2UZzU0oqsprQ==",
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/it-length": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/it-length/-/it-length-3.0.2.tgz",
@@ -9895,6 +10213,18 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/it-parallel-batch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/it-parallel-batch/-/it-parallel-batch-3.0.1.tgz",
+      "integrity": "sha512-4KTvYVYpCdrYUrAHSeH6o5hnHuDVHWzB8TztV/hdckUZzZIjbax4kVblmnzoYREX8Huj5+50irBu7b+c8jyKQg==",
+      "dependencies": {
+        "it-batch": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
     "node_modules/it-pb-stream": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/it-pb-stream/-/it-pb-stream-4.0.2.tgz",
@@ -10878,7 +11208,6 @@
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
       "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -11258,6 +11587,14 @@
       "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz",
       "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow=="
     },
+    "node_modules/murmurhash3js-revisited": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz",
+      "integrity": "sha512-/sF3ee6zvScXMb1XFJ8gDsSnY+X8PbOyjIuBhtgis10W2Jx4ZjIhikUCIF9c4gpJxVnQIsPAFrSwTCuAjicP6g==",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/mz": {
       "version": "2.7.0",
       "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@@ -11353,7 +11690,6 @@
       "version": "2.6.12",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
       "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
-      "dev": true,
       "dependencies": {
         "whatwg-url": "^5.0.0"
       },
@@ -11372,20 +11708,17 @@
     "node_modules/node-fetch/node_modules/tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "dev": true
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
     },
     "node_modules/node-fetch/node_modules/webidl-conversions": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "dev": true
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
     },
     "node_modules/node-fetch/node_modules/whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
       "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dev": true,
       "dependencies": {
         "tr46": "~0.0.3",
         "webidl-conversions": "^3.0.0"
@@ -12508,6 +12841,22 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/rabin-wasm": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/rabin-wasm/-/rabin-wasm-0.1.5.tgz",
+      "integrity": "sha512-uWgQTo7pim1Rnj5TuWcCewRDTf0PEFTSlaUjWP4eY9EbLV9em08v89oCz/WO+wRxpYuO36XEHp4wgYQnAgOHzA==",
+      "dependencies": {
+        "@assemblyscript/loader": "^0.9.4",
+        "bl": "^5.0.0",
+        "debug": "^4.3.1",
+        "minimist": "^1.2.5",
+        "node-fetch": "^2.6.1",
+        "readable-stream": "^3.6.0"
+      },
+      "bin": {
+        "rabin-wasm": "cli/bin.js"
+      }
+    },
     "node_modules/randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -12578,7 +12927,6 @@
       "version": "3.6.2",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
       "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dev": true,
       "dependencies": {
         "inherits": "^2.0.3",
         "string_decoder": "^1.1.1",
@@ -14399,6 +14747,11 @@
         "source-map": "^0.6.0"
       }
     },
+    "node_modules/sparse-array": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/sparse-array/-/sparse-array-1.3.2.tgz",
+      "integrity": "sha512-ZT711fePGn3+kQyLuv1fpd3rNSkNF8vd5Kv2D+qnOANeyKs3fx6bUMGWRPvgTTcYV64QMqZKZwcuaQSP3AZ0tg=="
+    },
     "node_modules/spdx-correct": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
@@ -15872,8 +16225,7 @@
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "node_modules/uuid": {
       "version": "8.3.2",
@@ -16447,6 +16799,8 @@
     "watcher": {
       "name": "@mjolnir/watcher",
       "dependencies": {
+        "@helia/ipns": "^1.1.3",
+        "@helia/unixfs": "^1.4.1",
         "helia": "^1.3.12"
       },
       "devDependencies": {
diff --git a/watcher/assets/styles/global.css b/watcher/assets/styles/global.css
index 52461a8..56fb84e 100644
--- a/watcher/assets/styles/global.css
+++ b/watcher/assets/styles/global.css
@@ -58,20 +58,6 @@ body {
   font-weight: bold;
 }
 
-.input-file {
-  position: relative;
-}
-
-.input-file input {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  opacity: 0;
-  cursor: pointer;
-}
-
 .input-file__text {
   display: flex;
   align-items: center;
@@ -108,6 +94,10 @@ body {
   font-size: 0.8rem;
 }
 
+.form .result.hidden {
+  display: none;
+}
+
 .form .result.error {
   background-color: #ef4444;
 }
diff --git a/watcher/helia.mjs b/watcher/helia.mjs
index 84b9c6a..7e89381 100644
--- a/watcher/helia.mjs
+++ b/watcher/helia.mjs
@@ -1,5 +1,15 @@
 import { createHelia } from 'helia'
+import { unixfs } from '@helia/unixfs'
+import { ipns } from '@helia/ipns'
 
-export async function createNode () {
+export async function createNode() {
   return await createHelia()
 }
+
+export async function createUnixFs(helia) {
+  return await unixfs(helia)
+}
+
+export async function createIPNS(helia) {
+  return await ipns(helia)
+}
diff --git a/watcher/main.js b/watcher/main.js
index 87ec130..08f7a4c 100644
--- a/watcher/main.js
+++ b/watcher/main.js
@@ -1,8 +1,14 @@
 const path = require('path')
-const { app, BrowserWindow, shell } = require('electron')
+const fs = require('fs')
+const { app, BrowserWindow, shell, ipcMain, dialog } = require('electron')
 
-const gotTheLock = app.requestSingleInstanceLock()
 let mainWindow = null
+const gotTheLock = app.requestSingleInstanceLock()
+const data = {
+  node: null,
+  unixfs: null,
+  ipns: null
+}
 
 const createWindow = () => {
   mainWindow = new BrowserWindow({
@@ -10,7 +16,8 @@ const createWindow = () => {
     height: 600,
     webPreferences: {
       preload: path.join(__dirname, './pages/preload.js'),
-      nodeIntegration: true
+      nodeIntegration: true,
+      enableRemoteModule: true
     }
   })
 
@@ -26,11 +33,156 @@ const createWindow = () => {
     return { action: 'deny' };
   });
 
+  ipcMain.handle('dialog:openDirectory', async () => {
+    const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
+      properties: ['openDirectory']
+    })
+    if (canceled) {
+      return
+    } else {
+      return filePaths[0]
+    }
+  })
+
+  ipcMain.handle('upload:start', async (e, dir) => {
+    if (data.node === null) {
+      createHeliaNode()
+      return
+    }
+
+    try {
+      if (!dir) {
+        throw new Error()
+      }
+
+      const stat = await fs.promises.stat(dir)
+      if (!stat.isDirectory()) {
+        throw new Error()
+      }
+    } catch (err) {
+      return {
+        error: "Selected path is not a directory"
+      }
+    }
+
+    let playlistFilename = null
+    let playlistFile = null
+    const uploadedFiles = []
+    if (fs.readdirSync(dir).length !== 0) {
+      // sort files by created date
+      let fileNames = fs.readdirSync(dir)
+
+      playlistFilename = fileNames.find((filename) => {
+        return filename.endsWith('.m3u8')
+      })
+
+      if (playlistFilename) {
+        mainWindow.webContents.send("upload:message", { message: "found playlist file", playlistFilename })
+        fileNames = fileNames
+          .filter((filename) => filename !== playlistFilename)
+          .map((filename) => {
+            const stat = fs.statSync(path.join(dir, filename))
+            return {
+              filename,
+              stat
+            }
+          }).sort((a, b) => {
+            return a.stat.birthtimeMs - b.stat.birthtimeMs
+          })
+
+        mainWindow.webContents.send("upload:message", { message: "catching up with existing files" })
+        playlistFile = fs.readFileSync(path.join(dir, playlistFilename), 'utf8')
+        const files = fileNames.map(({ filename }) => {
+          return {
+            path: filename,
+            content: fs.readFileSync(path.join(dir, filename))
+          }
+        })
+
+        for await (const file of data.unixfs.addAll(files, { wrapWithDirectory: true })) {
+          mainWindow.webContents.send("upload:message", { message: "uploaded segment", segment: file.path, matchingCID: file.cid.toString()})
+          playlistFile = playlistFile.replace(file.path, file.cid.toString())
+          uploadedFiles.push(file.path)
+        }
+
+        fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
+        mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
+      }
+    }
+
+    mainWindow.webContents.send("upload:message", { message: "waiting for changes" })
+    fs.watch(dir, async (eventType, filename) => {
+      if (playlistFilename) {
+        if (eventType !== 'rename' || filename !== playlistFilename) {
+          return
+        }
+      } else {
+        if (filename.endsWith('.m3u8')) {
+          mainWindow.webContents.send("upload:message", { message: "playlist file is created", filename })
+          playlistFilename = filename
+          playlistFile = fs.readFileSync(path.join(dir, playlistFilename), 'utf8')
+          const files = fs.readdirSync(dir).map(({ filename }) => {
+            return {
+              path: filename,
+              content: fs.readFileSync(path.join(dir, filename))
+            }
+          })
+
+          mainWindow.webContents.send("upload:message", { message: "uploading files" })
+          for await (const file of data.unixfs.addAll(files, { wrapWithDirectory: true })) {
+            mainWindow.webContents.send("upload:message", { message: "uploaded segment", segment: file.path, matchingCID: file.cid.toString()})
+            playlistFile = playlistFile.replace(file.path, file.cid.toString())
+            uploadedFiles.push(file.path)
+          }
+
+          fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
+          mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
+        }
+        return
+      }
+
+      playlistFile = fs.readFileSync(path.join(dir, playlistFilename), 'utf8')
+      const files = fs.readdirSync(dir)
+        .map(({ filename }) => {
+          return {
+            path: filename,
+            content: fs.readFileSync(path.join(dir, filename))
+          }
+        })
+
+      for await (const file of data.unixfs.addAll(files, { wrapWithDirectory: true })) {
+        mainWindow.webContents.send("upload:message", { message: "uploaded segment", segment: file.path, matchingCID: file.cid.toString()})
+        playlistFile = playlistFile.replace(file.path, file.cid.toString())
+        uploadedFiles.push(file.path)
+      }
+      fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
+      mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
+    })
+  })
+
   if (process.env.NODE_ENV !== 'production') {
     mainWindow.webContents.openDevTools()
   }
 }
 
+const createHeliaNode = async () => {
+  try {
+    // Helia is an ESM-only module but Electron currently only supports CJS
+    // at the top level, so we have to use dynamic imports to load it
+    const { createNode, createUnixFs, createIPNS } = await import('./helia.mjs')
+    data.node = await createNode()
+    const id = data.node.libp2p.peerId
+    data.unixfs = await createUnixFs(data.node)
+    data.ipns = await createIPNS(data.node)
+
+    if (process.env.NODE_ENV !== 'production') {
+      console.log(id)
+    }
+  } catch (err) {
+    console.error(err)
+  }
+}
+
 if (!gotTheLock) {
   app.quit()
 } else {
@@ -43,17 +195,7 @@ if (!gotTheLock) {
 
   app.whenReady().then(async () => {
     createWindow()
-
-    try {
-      // Helia is an ESM-only module but Electron currently only supports CJS
-      // at the top level, so we have to use dynamic imports to load it
-      const { createNode } = await import('./helia.mjs')
-      const node = await createNode()
-      const id = node.libp2p.peerId
-      console.log(id)
-    } catch (err) {
-      console.error(err)
-    }
+    createHeliaNode()
 
     app.on('activate', () => {
       if (BrowserWindow.getAllWindows().length === 0) createWindow()
diff --git a/watcher/package.json b/watcher/package.json
index e04aa82..be5f8f3 100644
--- a/watcher/package.json
+++ b/watcher/package.json
@@ -8,6 +8,8 @@
     "start": "electron ./main.js"
   },
   "dependencies": {
+    "@helia/ipns": "^1.1.3",
+    "@helia/unixfs": "^1.4.1",
     "helia": "^1.3.12"
   }
 }
diff --git a/watcher/pages/index.html b/watcher/pages/index.html
index 380336a..1922765 100644
--- a/watcher/pages/index.html
+++ b/watcher/pages/index.html
@@ -15,12 +15,11 @@ <h1 class="title">Mjolnir Superhack</h1>
 
         <div class="form-container">
           <form class="form">
-            <p class="result error">There was an error</p>
+            <p class="result hidden"></p>
             <div class="input-group">
               <label for="file">Select OBS recording directory</label>
               <div class="input-file">
                 <div class="input-file__text">Select directory</div>
-                <input type="file" id="file" name="file" webkitdirectory directory multiple>
               </div>
             </div>
             <div class="actions">
@@ -51,6 +50,10 @@ <h1 class="title">Mjolnir Superhack</h1>
             </ol>
           </div>
         </div>
+
+        <div class="form-container">
+          <div class="log hidden"></div>
+        </div>
       </div>
     </div>
   </body>
diff --git a/watcher/pages/preload.js b/watcher/pages/preload.js
index e69de29..6806770 100644
--- a/watcher/pages/preload.js
+++ b/watcher/pages/preload.js
@@ -0,0 +1,11 @@
+const { contextBridge, ipcRenderer } = require('electron')
+
+contextBridge.exposeInMainWorld('mapi', {
+  selectFolder: () => ipcRenderer.invoke('dialog:openDirectory'),
+  startUpload: (folder) => ipcRenderer.invoke('upload:start', folder),
+  onUploadMessage: (callback) => {
+    ipcRenderer.on('upload:message', function(evt, message) {
+      callback(message)
+    })
+  },
+})
diff --git a/watcher/pages/renderer.js b/watcher/pages/renderer.js
index 11e4396..1bcb4b5 100644
--- a/watcher/pages/renderer.js
+++ b/watcher/pages/renderer.js
@@ -1,50 +1,58 @@
+const data = {
+  selectedDir: null,
+  streaming: false
+}
+
 const watchFileInput = () => {
-  const fileInput = document.querySelector('.input-file input')
-  const fileLabel = document.querySelector('.input-file__text')
+  const fileInput = document.querySelector('.input-file')
+  const fileLabel = fileInput.querySelector('.input-file__text')
+
+  fileInput.addEventListener('click', () => {
+    window.mapi.selectFolder().then((path) => {
+      if (!path) {
+        return
+      }
 
-  fileInput.addEventListener('change', (e) => {
-    // show selected directory
-    const path = e.target.value.split('\\')
-    fileLabel.innerHTML = path[path.length - 1]
+      fileLabel.innerHTML = path
+      data.selectedDir = path
+    })
   })
 }
 
 const watchForm = () => {
   const form = document.querySelector('form')
   const submitButton = form.querySelector('input[type=submit]')
-  const fileInput = form.querySelector('.input-file input')
-  const fileLabel = form.querySelector('.input-file__text')
   const result = document.querySelector('.result')
 
   form.addEventListener('submit', (e) => {
     e.preventDefault()
+    if (data.streaming) {
+      return
+    }
 
     submitButton.disabled = true
-    submitButton.innerHTML = 'Uploading...'
+    submitButton.innerHTML = 'Starting Stream...'
     result.classList.remove('error')
+    result.classList.add('hidden')
 
-    const formData = new FormData()
-    formData.append('file', fileInput.files[0])
-
-    fetch('/upload', {
-      method: 'POST',
-      body: formData
-    })
-      .then((res) => res.json())
+    window.mapi.startUpload(data.selectedDir)
       .then((res) => {
+        if (res && res.error) {
+          throw res.error
+        }
+
+        data.streaming = true
         submitButton.disabled = false
-        submitButton.innerHTML = 'Upload'
-        fileInput.value = ''
-        fileLabel.innerHTML = 'Select directory'
-        result.innerHTML = res.message
+        submitButton.innerHTML = 'Streaming'
+        result.classList.remove('hidden')
+        result.innerHTML = "We are watching the selected directory for segments and streaming them."
       })
       .catch((err) => {
         submitButton.disabled = false
         submitButton.innerHTML = 'Upload'
-        fileInput.value = ''
-        fileLabel.innerHTML = 'Select directory'
         result.classList.add('error')
-        result.innerHTML = err.message
+        result.classList.remove('hidden')
+        result.innerHTML = err && err || "Something went wrong, try again in a few seconds."
       })
   })
 }
@@ -58,8 +66,17 @@ const activateHelpButton = () => {
   })
 }
 
+const logUploadMessage = () => {
+  window.mapi.onUploadMessage(({message, ...rest}) => {
+    const log = document.querySelector('.log')
+    log.classList.remove('hidden')
+    log.innerHTML += `<p>${message} ${rest ? JSON.stringify(rest) : rest}</p>`
+  })
+}
+
 document.addEventListener('DOMContentLoaded', () => {
   watchFileInput()
   watchForm()
   activateHelpButton()
+  logUploadMessage()
 })
diff --git a/watcher/pages/style.css b/watcher/pages/style.css
index c93539b..77d1442 100644
--- a/watcher/pages/style.css
+++ b/watcher/pages/style.css
@@ -57,3 +57,20 @@
 .form .actions .show-help {
   background-color: #1f1f23;
 }
+
+.log {
+  padding: 1rem 2rem;
+  border-radius: 1rem;
+  background-color: #1f1f23;
+  color: #fafafa;
+  margin-bottom: 1rem;
+  font-size: 0.8rem;
+  max-height: 400px;
+  width: 100%;
+  max-width: 600px;
+  overflow: auto;
+}
+
+.log.hidden {
+  display: none;
+}

From a6542d884fd0a7807f2a2a3558fa046b3776a9ca Mon Sep 17 00:00:00 2001
From: Mahdi Pourismaiel <mpourismaiel@gmail.com>
Date: Thu, 10 Aug 2023 21:49:51 +0330
Subject: [PATCH 2/3] Fix log display

---
 watcher/pages/renderer.js | 2 +-
 watcher/pages/style.css   | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/watcher/pages/renderer.js b/watcher/pages/renderer.js
index 1bcb4b5..5dd7ca1 100644
--- a/watcher/pages/renderer.js
+++ b/watcher/pages/renderer.js
@@ -70,7 +70,7 @@ const logUploadMessage = () => {
   window.mapi.onUploadMessage(({message, ...rest}) => {
     const log = document.querySelector('.log')
     log.classList.remove('hidden')
-    log.innerHTML += `<p>${message} ${rest ? JSON.stringify(rest) : rest}</p>`
+    log.innerHTML += `<p>${new Date().toString()} ${message} ${rest ? JSON.stringify(rest) : rest}</p>`
   })
 }
 
diff --git a/watcher/pages/style.css b/watcher/pages/style.css
index 77d1442..aa10f46 100644
--- a/watcher/pages/style.css
+++ b/watcher/pages/style.css
@@ -63,7 +63,8 @@
   border-radius: 1rem;
   background-color: #1f1f23;
   color: #fafafa;
-  margin-bottom: 1rem;
+  margin-top: 2rem;
+  margin-bottom: 2rem;
   font-size: 0.8rem;
   max-height: 400px;
   width: 100%;

From 45fb637e9e22d535d2dbe6f5a4ecfb2008b10941 Mon Sep 17 00:00:00 2001
From: Mahdi Pourismaiel <mpourismaiel@gmail.com>
Date: Thu, 10 Aug 2023 22:37:52 +0330
Subject: [PATCH 3/3] wip

---
 package-lock.json    |  3 ++-
 watcher/main.js      | 19 ++++++++++++++++++-
 watcher/package.json |  3 ++-
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index ff1e74f..37838bd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16801,7 +16801,8 @@
       "dependencies": {
         "@helia/ipns": "^1.1.3",
         "@helia/unixfs": "^1.4.1",
-        "helia": "^1.3.12"
+        "helia": "^1.3.12",
+        "it-to-buffer": "^4.0.2"
       },
       "devDependencies": {
         "electron": "^25.4.0"
diff --git a/watcher/main.js b/watcher/main.js
index 08f7a4c..add2748 100644
--- a/watcher/main.js
+++ b/watcher/main.js
@@ -10,6 +10,12 @@ const data = {
   ipns: null
 }
 
+const uploadPlaylist = async (dir, filename) => {
+  const bytes = fs.readFileSync(path.join(dir, filename))
+  const cid = await data.unixfs.addBytes(bytes)
+  return cid
+}
+
 const createWindow = () => {
   mainWindow = new BrowserWindow({
     width: 800,
@@ -107,6 +113,13 @@ const createWindow = () => {
 
         fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
         mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
+
+        const cid = await uploadPlaylist(dir, playlistFilename)
+        mainWindow.webContents.send("upload:message", { message: "uploaded playlist file", playlistFile: cid.toString() })
+        const tb = await import('it-to-buffer')
+        const newFilename = "./sample.m3u8"
+        const d = await tb.default(await data.unixfs.cat(cid.toString()))
+        fs.writeFileSync(newFilename, d)
       }
     }
 
@@ -137,6 +150,9 @@ const createWindow = () => {
 
           fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
           mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
+
+          const cid = await uploadPlaylist(dir, playlistFilename)
+          mainWindow.webContents.send("upload:message", { message: "uploaded playlist file", playlistFile: cid.toString() })
         }
         return
       }
@@ -158,9 +174,10 @@ const createWindow = () => {
       fs.writeFileSync(path.join(dir, playlistFilename), playlistFile)
       mainWindow.webContents.send("upload:message", { message: "updated playlist file", playlistFile })
     })
+
   })
 
-  if (process.env.NODE_ENV !== 'production') {
+  if (process.env.NODE_ENV !== 'production' && process.env.DISABLE_DEVTOOLS.toLowerCase() !== 'true') {
     mainWindow.webContents.openDevTools()
   }
 }
diff --git a/watcher/package.json b/watcher/package.json
index be5f8f3..3fa483a 100644
--- a/watcher/package.json
+++ b/watcher/package.json
@@ -10,6 +10,7 @@
   "dependencies": {
     "@helia/ipns": "^1.1.3",
     "@helia/unixfs": "^1.4.1",
-    "helia": "^1.3.12"
+    "helia": "^1.3.12",
+    "it-to-buffer": "^4.0.2"
   }
 }