Browse Source

Electron works!

master
ポール ウェッブ 1 month ago
parent
commit
e505e58bf2
9 changed files with 396 additions and 35 deletions
  1. 51
    0
      app/create-window.js
  2. 60
    0
      app/electron.js
  3. 17
    10
      app/index.html
  4. 1
    1
      app/index.js
  5. 31
    0
      app/models/sdk-init.js
  6. 64
    0
      app/sdk.js
  7. 147
    5
      app/views/home.js
  8. 22
    5
      package.json
  9. 3
    14
      scripts/rollup.base.js

+ 51
- 0
app/create-window.js View File

@@ -0,0 +1,51 @@
1
+"use strict";
2
+
3
+
4
+
5
+//  N A T I V E
6
+
7
+const path = require("path");
8
+const url = require("url");
9
+
10
+//  I M P O R T
11
+
12
+const { BrowserWindow } = require("electron");
13
+
14
+//  U T I L
15
+
16
+let mainWindow;
17
+
18
+
19
+
20
+//  P R O G R A M
21
+
22
+function createWindow() {
23
+  mainWindow = new BrowserWindow({
24
+    height: 800,
25
+    webPreferences: {
26
+      nodeIntegration: true
27
+    },
28
+    width: 1200
29
+  });
30
+
31
+  mainWindow.loadURL(
32
+    url.format({
33
+      pathname: path.join(__dirname, "../dist/index.html"),
34
+      protocol: "file:",
35
+      slashes: true
36
+    })
37
+  );
38
+
39
+  mainWindow.on("closed", () => {
40
+    // Dereference the window object, usually you would store windows
41
+    // in an array if your app supports multi windows, this is the time
42
+    // when you should delete the corresponding element.
43
+    mainWindow = null;
44
+  });
45
+}
46
+
47
+
48
+
49
+//  E X P O R T
50
+
51
+module.exports = exports = createWindow;

+ 60
- 0
app/electron.js View File

@@ -0,0 +1,60 @@
1
+"use strict"; /* global dialog */
2
+
3
+
4
+
5
+//  I M P O R T S
6
+
7
+const { app } = require("electron");
8
+const findProcess = require("find-process");
9
+const path = require("path");
10
+
11
+//  U T I L S
12
+
13
+const createWindow = require("./create-window");
14
+const SDK = require("./sdk");
15
+
16
+// require("electron-reload")(__dirname); // Simple auto reload
17
+require("electron-reload")(__dirname, {
18
+  electron: path.join(__dirname, "node_modules", ".bin", "electron")
19
+});
20
+
21
+let sdk;
22
+
23
+
24
+
25
+// This method will be called when Electron has finished
26
+// initialization and is ready to create browser windows.
27
+// Some APIs can only be used after this event occurs.
28
+app.on("ready", () => {
29
+  createWindow();
30
+
31
+  // Determine if the LBRY SDK is already running, or if it needs to be started
32
+  // This allows you to run the sdk binary separately and have your app connect to it
33
+  const processListArgs = process.platform === "win32" ?
34
+    "lbrynet" :
35
+    "lbrynet start";
36
+
37
+  findProcess("name", processListArgs).then(processList => {
38
+    const isSDKRunning = processList.length > 0;
39
+
40
+    if (!isSDKRunning) {
41
+      sdk = new SDK();
42
+
43
+      sdk.on("exit", () => {
44
+        sdk = null;
45
+        dialog.showErrorBox("SDK has quit");
46
+        app.quit();
47
+      });
48
+
49
+      sdk.launch();
50
+    }
51
+  });
52
+});
53
+
54
+// Quit when all windows are closed.
55
+app.on("window-all-closed", () => {
56
+  // On OS X it is common for applications and their menu bar
57
+  // to stay active until the user quits explicitly with Cmd + Q
58
+  if (process.platform !== "darwin")
59
+    app.quit();
60
+});

+ 17
- 10
app/index.html View File

@@ -2,8 +2,15 @@
2 2
 <html lang="en">
3 3
   <head>
4 4
     <meta charset="utf-8"/>
5
+    <meta
6
+      http-equiv="Content-Security-Policy"
7
+      content="
8
+        default-src 'self' filesystem:;
9
+        connect-src *;
10
+    "/>
11
+
5 12
     <title><%= appName %> ∙ <%= appTagline %></title>
6
-    <base href="/"/>
13
+    <!-- <base href="/"/> -->
7 14
 
8 15
     <meta name="apple-mobile-web-app-capable" content="yes"/>
9 16
     <meta name="author" content="<%= appName %>"/>
@@ -20,22 +27,22 @@
20 27
 
21 28
     <!--/ Open Graph /-->
22 29
     <meta property="og:description" content="<%= appTagline %>"/>
23
-    <meta property="og:image" content="/og-image.png"/>
30
+    <meta property="og:image" content="<%= workingDirectory %>/og-image.png"/>
24 31
     <meta property="og:image:height" content="<%= ogImageHeight %>"/>
25 32
     <meta property="og:image:width" content="<%= ogImageWidth %>"/>
26 33
     <meta property="og:locale" content="<%= appLocale %>"/>
27 34
     <meta property="og:site_name" content="<%= appName %>"/>
28 35
     <meta property="og:title" content="<%= appName %>"/>
29 36
     <meta property="og:type" content="website"/>
30
-    <meta property="og:url" content="/"/>
37
+    <!-- <meta property="og:url" content="/"/> -->
31 38
 
32
-    <link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
33
-    <link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
34
-    <link rel="mask-icon" href="/favicon.svg" color="<%= appColor %>"/>
35
-    <link rel="shortcut icon" href="/favicon.ico"/>
36
-    <link rel="stylesheet" href="/css/bundle.css"/>
39
+    <link rel="apple-touch-icon" href="<%= workingDirectory %>/apple-touch-icon.png"/>
40
+    <link rel="icon" href="<%= workingDirectory %>/favicon.svg" type="image/svg+xml"/>
41
+    <link rel="mask-icon" href="<%= workingDirectory %>/favicon.svg" color="<%= appColor %>"/>
42
+    <link rel="shortcut icon" href="<%= workingDirectory %>/favicon.ico"/>
43
+    <link rel="stylesheet" href="<%= workingDirectory %>/css/bundle.css"/>
37 44
 
38
-    <script src="/plugins/clipboard.js"></script>
45
+    <script src="<%= workingDirectory %>/plugins/clipboard.js"></script>
39 46
   </head>
40 47
 
41 48
   <body>
@@ -45,6 +52,6 @@
45 52
       <span><%= appName %> is an SPA (Single Page Application) and requires JavaScript to work, please enable it if you can.</span>
46 53
     </noscript>
47 54
 
48
-    <script src="/scripts/bundle.js"></script>
55
+    <script src="<%= workingDirectory %>/scripts/bundle.js"></script>
49 56
   </body>
50 57
 </html>

+ 1
- 1
app/index.js View File

@@ -15,5 +15,5 @@ const mountNode = document.querySelector("app");
15 15
 
16 16
 //  P R O G R A M
17 17
 
18
-m.route.prefix(""); // no fugly hash in our URLs
18
+m.route.prefix("#"); // hash is needed for desktop apps, not for web
19 19
 m.route(mountNode, "/", routes);

+ 31
- 0
app/models/sdk-init.js View File

@@ -0,0 +1,31 @@
1
+"use strict";
2
+
3
+
4
+
5
+const { Lbry } = require("lbry-redux");
6
+// let sdkStatus = false;
7
+
8
+Lbry.connect().then(checkSDKStarted);
9
+
10
+// Wait until the sdk is fully started before doing anything else
11
+export default function checkSDKStarted() {
12
+  Lbry.status().then(status => {
13
+    if (status.is_running) {
14
+      // SDK is now running
15
+      // const resolveWrapper = document.getElementById("resolve");
16
+      // const loadingWrapper = document.getElementById("loading");
17
+      // loadingWrapper.style.display = "none";
18
+      // resolveWrapper.style.display = "block";
19
+      console.info("SDK has loaded"); // eslint-disable-line no-console
20
+      // sdkStatus = true;
21
+      // console.log(sdkStatus);
22
+      return;
23
+    }
24
+
25
+    setTimeout(() => {
26
+      checkSDKStarted();
27
+    }, 500);
28
+  });
29
+}
30
+
31
+// export default sdkStatus;

+ 64
- 0
app/sdk.js View File

@@ -0,0 +1,64 @@
1
+"use strict";
2
+
3
+
4
+
5
+//  N A T I V E
6
+
7
+const path = require("path");
8
+
9
+//  U T I L S
10
+
11
+const { execSync, spawn } = require("child_process");
12
+
13
+
14
+
15
+//  P R O G R A M
16
+
17
+class SDK {
18
+  constructor() {
19
+    this.path = process.env.LBRY_DAEMON || path.join(__dirname, "../../dist/sdk/lbrynet");
20
+    this.handlers = [];
21
+    this.subprocess = undefined;
22
+  }
23
+
24
+  launch() {
25
+    this.subprocess = spawn(this.path, ["start"]);
26
+    this.subprocess.stdout.on("data", data => console.log(`SDK: ${data}`)); // eslint-disable-line no-console
27
+    this.subprocess.stderr.on("data", data => console.error(`SDK: ${data}`)); // eslint-disable-line no-console
28
+    this.subprocess.on("exit", () => this.fire("exit"));
29
+    this.subprocess.on("error", error => console.error(`SDK error: ${error}`)); // eslint-disable-line no-console
30
+  }
31
+
32
+  quit() {
33
+    if (process.platform === "win32") {
34
+      try {
35
+        execSync(`taskkill /pid ${this.subprocess.pid} /t /f`);
36
+      } catch (error) {
37
+        console.error(error.message); // eslint-disable-line no-console
38
+      }
39
+    } else {
40
+      this.subprocess.kill();
41
+    }
42
+  }
43
+
44
+  // Follows the publish/subscribe pattern
45
+
46
+  // Subscribe method
47
+  on(event, handler, context = handler) {
48
+    this.handlers.push({ event, handler: handler.bind(context) });
49
+  }
50
+
51
+  // Publish method
52
+  fire(event, args) {
53
+    this.handlers.forEach(topic => {
54
+      if (topic.event === event)
55
+        topic.handler(args);
56
+    });
57
+  }
58
+}
59
+
60
+
61
+
62
+//  E X P O R T
63
+
64
+module.exports = exports = SDK;

+ 147
- 5
app/views/home.js View File

@@ -2,14 +2,22 @@
2 2
 
3 3
 
4 4
 
5
-//  I M P O R T
5
+// import util from "util";
6
+// const util = require("util");
7
+
8
+//  I M P O R T S
6 9
 
7 10
 import m from "mithril";
11
+const { Lbry } = require("lbry-redux");
12
+// import { Lbry } from "lbry-redux";
8 13
 
9 14
 //  U T I L S
10 15
 
11 16
 import Trending from "~model/trending";
12 17
 import Wrapper from "~component/wrapper";
18
+import sdkStatus from "~model/sdk-init";
19
+
20
+// console.log(sdkStatus);
13 21
 
14 22
 
15 23
 
@@ -17,8 +25,9 @@ import Wrapper from "~component/wrapper";
17 25
 
18 26
 export default {
19 27
   onmatch: () => {
28
+    sdkStatus();
20 29
     Trending.loadList();
21
-    console.log("Hello, welcome"); // eslint-disable-line no-console
30
+    // console.log("Hello, welcome"); // eslint-disable-line no-console
22 31
   },
23 32
   render: () => {
24 33
     const { list } = Trending;
@@ -29,8 +38,8 @@ export default {
29 38
         <channels>
30 39
           {
31 40
             channelNames.length ?
32
-              channelNames.map(channelName => renderChannelLink(channelName)) :
33
-              m("div", { style: "text-align: center;" }, "Waiting for content query to finish...")
41
+              channelNames.map(channelName => renderChannelLink(channelName, list)) :
42
+              m("div", { style: "color: white; text-align: center;" }, "Waiting for content query to finish...")
34 43
           }
35 44
         </channels>
36 45
       )
@@ -42,15 +51,148 @@ export default {
42 51
 
43 52
 //  H E L P E R
44 53
 
45
-function renderChannelLink(data) {
54
+// Promise.prototype.isPending = function() {
55
+//   return util.inspect(this).indexOf("<pending>") > -1;
56
+// };
57
+
58
+// daemon.lbry.tech
59
+// b5125d5be5ef2b8f3a2fba18a349c8375a85e613
60
+// https://daemon.lbry.tech/resolve
61
+// body.authorization = b5125d5be5ef2b8f3a2fba18a349c8375a85e613;
62
+// body.method = resolve;
63
+
64
+// claim.value.stream.metadata
65
+// author / thumbnail / title
66
+
67
+// claim.value.stream.source
68
+// contentType (video/mp4)
69
+
70
+function renderChannelLink(data, fullData) {
46 71
   const channelName = data.split("|")[1].trim();
47 72
   const channelNameHash = channelName.split("#")[1];
48 73
   const channelNameSolo = channelName.split("#")[0];
49 74
   const formattedChannelName = `<span class="name">${channelNameSolo.replace("@", "")}</span><span class="hash">#${channelNameHash}</span>`;
50 75
 
76
+  // console.log(fullData[data]);
77
+  // console.log("————————————");
78
+
79
+  // resolve(fullData[data]);
80
+
81
+
82
+
83
+  // check out streaming branch
84
+  // const test = resolve(fullData[data]);
85
+
86
+  // console.log("————————————");
87
+  // console.log(process.binding("util").getPromiseDetails(test));
88
+  // console.log("————————————");
89
+
90
+  // return test.then(result => {
91
+  //   console.log(result);
92
+
93
+  //   return (
94
+  //     <channel data-channel-name={channelName}>
95
+  //       {m.trust(formattedChannelName)}
96
+  //     </channel>
97
+  //   );
98
+  // });
99
+
100
+  // return (
101
+  //   <div>
102
+  //     {
103
+  //       test ?
104
+  //         <channel data-channel-name={channelName}>
105
+  //           {m.trust(formattedChannelName)}
106
+  //         </channel> :
107
+  //         m("div", { style: "color: white; text-align: center;" }, "Waiting for OTHER query to finish...")
108
+  //     }
109
+  //   </div>
110
+  // );
111
+
112
+
113
+
114
+  // const oop = [];
115
+
116
+  // for (const test of fullData[data])
117
+  //   oop.push(resolveII(test));
118
+
119
+  // return (
120
+  //   <div>
121
+  //     {
122
+  //       Promise.all(oop).then(values => {
123
+  //         console.log(values);
124
+
125
+  //         return (
126
+  //           <channel data-channel-name={channelName}>
127
+  //             {m.trust(formattedChannelName)}
128
+  //           </channel>
129
+  //         );
130
+  //       })
131
+  //     }
132
+  //   </div>
133
+  // );
134
+
51 135
   return (
52 136
     <channel data-channel-name={channelName}>
53 137
       {m.trust(formattedChannelName)}
54 138
     </channel>
55 139
   );
56 140
 }
141
+
142
+// https://api.lbry.tv/content/claims/house/a05f51fe630f51e2568c329222ffb5e0bea5fb2d/stream
143
+// /claims/{claim_name}/{claim_id}/stream
144
+// You can use that as a `src` tag on `<audio>` or `<video>` elements
145
+
146
+function resolveII(suppliedUrl) {
147
+  return m.request({
148
+    background: true, // commenting this out is not a good idea...
149
+    data: {
150
+      authorization: "b5125d5be5ef2b8f3a2fba18a349c8375a85e613",
151
+      method: "resolve",
152
+      uri: suppliedUrl
153
+    },
154
+    headers: {
155
+      "Content-Type": "application/json"
156
+    },
157
+    method: "POST",
158
+    // url: "https://daemon.lbry.tech/resolve",
159
+    url: "http://localhost:5200/resolve"
160
+    // useBody: true
161
+  }).then(result => {
162
+    result = result.result[suppliedUrl];
163
+    return result;
164
+  });
165
+}
166
+
167
+function resolve(url) {
168
+  return Lbry.resolve({ urls: url }) // This can be a list of urls
169
+    .then(response => {
170
+      const test = Object.keys(response);
171
+      const cool = [];
172
+
173
+      // console.log(Object.keys(response));
174
+
175
+      for (const uh of test) {
176
+        const thing = response[uh];
177
+        const { metadata } = thing.claim.value.stream;
178
+        // console.log(thing);
179
+
180
+        cool.push(`
181
+          <figure>
182
+            <img alt=${metadata.title} src=${metadata.thumbnail}/>
183
+            <figcaption>${metadata.title} by ${metadata.author}</figcaption>
184
+          </figure>
185
+        `);
186
+      }
187
+
188
+      // console.log("————————————");
189
+      // console.log(cool);
190
+      return cool;
191
+
192
+      // claimData.innerText = JSON.stringify(res[url].claim, null, 2);
193
+    })
194
+    .catch(error => {
195
+      console.error(error);
196
+      // claimData.innerText = JSON.stringify(error, null, 2);
197
+    });
198
+}

+ 22
- 5
package.json View File

@@ -9,8 +9,13 @@
9 9
     "name": "Paul Anthony Webb"
10 10
   },
11 11
   "dependencies": {
12
+    "electron": "^4.1.4",
13
+    "find-process": "^1.4.1",
12 14
     "got": "^9.6.0",
13
-    "mithril": "^1.1.6"
15
+    "lbry-redux": "lbryio/lbry-redux",
16
+    "mithril": "^1.1.6",
17
+    "node-fetch": "^2.3.0",
18
+    "url": "^0.11.0"
14 19
   },
15 20
   "description": "",
16 21
   "devDependencies": {
@@ -26,21 +31,25 @@
26 31
     "@inc/eslint-config": "^1.1.3",
27 32
     "@inc/sasslint-config": "^1.2.0",
28 33
     "@lbry/color": "^1.1.1",
34
+    "babel-preset-electron": "^1.4.15",
29 35
     "cwd": "^0.10.0",
36
+    "decompress": "^4.2.0",
37
+    "del": "^4.1.0",
38
+    "electron-reload": "^1.4.0",
30 39
     "eslint": "^5.16.0",
31 40
     "eslint-plugin-react": "^7.12.4",
32 41
     "husky": "^1.3.1",
33 42
     "link-module-alias": "^1.2.0",
34 43
     "npm-run-all": "^4.1.5",
35 44
     "rimraf": "^2.6.3",
36
-    "rollup": "^1.9.0",
45
+    "rollup": "^1.9.3",
37 46
     "rollup-plugin-babel": "^4.3.2",
38 47
     "rollup-plugin-commonjs": "^9.3.4",
39 48
     "rollup-plugin-eslint": "^5.1.0",
40 49
     "rollup-plugin-livereload": "^1.0.0",
41
-    "rollup-plugin-node-resolve": "^4.2.1",
50
+    "rollup-plugin-node-resolve": "^4.2.2",
42 51
     "rollup-plugin-pathmodify": "^1.0.1",
43
-    "rollup-plugin-replace": "^2.1.1",
52
+    "rollup-plugin-replace": "^2.2.0",
44 53
     "rollup-plugin-serve": "^1.0.1",
45 54
     "rollup-plugin-uglify": "^6.0.2",
46 55
     "rollup-watch": "^4.3.1",
@@ -58,6 +67,12 @@
58 67
     "README.md"
59 68
   ],
60 69
   "keywords": [],
70
+  "lbrySettings": {
71
+    "lbrynetSDKVersion": "0.32.1",
72
+    "lbrynetSDKUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vSDKVER/lbrynet-OSNAME.zip",
73
+    "lbrynetSDKDir": "dist/sdk",
74
+    "lbrynetSDKFileName": "lbrynet"
75
+  },
61 76
   "license": "ISC",
62 77
   "main": "dist/scripts/bundle",
63 78
   "module": "dist/scripts/bundle.mjs",
@@ -71,13 +86,15 @@
71 86
     "postinstall": "link-module-alias",
72 87
     "preinstall": "command -v link-module-alias; link-module-alias clean || true",
73 88
     "rollup": "rollup --config ./scripts/rollup.umd.js --environment DEPS:1; rollup --config ./scripts/rollup.es.js --environment DEPS:1",
74
-    "start": "npm run build; NODE_ENV=production npx here dist index.html 7854",
89
+    "--start": "npm run build; NODE_ENV=production npx here dist index.html 7854",
90
+    "start": "electron ./app/electron",
75 91
     "test": "run-s test:*",
76 92
     "test:dependencies": "updates --update ./",
77 93
     "test:lint": "standardx --verbose | snazzy",
78 94
     "test:sass": "sass-lint --config ./node_modules/@inc/sasslint-config/config.json --verbose",
79 95
     "watch": "npm run init; NODE_ENV=development run-p watch:*",
80 96
     "watch:app": "rollup --config ./scripts/rollup.watch.js --environment DEPS:1,PORT:7854,WATCH_DIR:./dist/ --watch",
97
+    "watch:dev": "electron ./app/electron.js",
81 98
     "watch:sass": "sass --load-path=node_modules --watch sass:dist/css --style compressed"
82 99
   },
83 100
   "standardx": {

+ 3
- 14
scripts/rollup.base.js View File

@@ -10,7 +10,7 @@ import fs from "fs";
10 10
 
11 11
 import babel from "rollup-plugin-babel";
12 12
 import commonjs from "rollup-plugin-commonjs";
13
-// import cwd from "cwd";
13
+import cwd from "cwd";
14 14
 import { eslint } from "rollup-plugin-eslint";
15 15
 import pathmodify from "rollup-plugin-pathmodify";
16 16
 import replace from "rollup-plugin-replace";
@@ -25,19 +25,10 @@ if (!pkg)
25 25
   throw("Could not read package.json");
26 26
 
27 27
 const isProduction = process.env.NODE_ENV !== "development";
28
-
29
-// const apiUrl = isProduction ?
30
-//   "https://api.socii.network" :
31
-//   "http://localhost:2413";
32 28
 const { env } = process;
33 29
 const environment = process.env.NODE_ENV || "development";
34 30
 const external = Object.keys(pkg.dependencies || {});
35 31
 const globals = {};
36
-// const hubUrl = isProduction ?
37
-//   "https://hub.socii.network" :
38
-//   "http://localhost:1881";
39
-// const icons = fs.readFileSync(cwd() + "/dist/media/svg/icons.svg", "utf8")
40
-//   .replace("position:absolute", "position: absolute;");
41 32
 const input = env.INPUT || "index.js";
42 33
 const name = env.NAME || pkg.name;
43 34
 
@@ -58,12 +49,12 @@ indexGenerator({
58 49
   output: "dist/index.html",
59 50
   template: {
60 51
     appColor: "#111",
61
-    // appIcons: icons,
62 52
     appLocale: "en_US",
63 53
     appName: "LBRY",
64 54
     appTagline: "The future of content on the Internet",
65 55
     ogImageHeight: "1024",
66
-    ogImageWidth: "768"
56
+    ogImageWidth: "768",
57
+    workingDirectory: cwd() + "/dist"
67 58
   }
68 59
 });
69 60
 
@@ -105,9 +96,7 @@ export const createConfig = ({ includeDepencies }) => ({
105 96
 
106 97
     replace({
107 98
       exclude: "node_modules/**",
108
-      // API: JSON.stringify(apiUrl),
109 99
       ENV: JSON.stringify(environment),
110
-      // HUB: JSON.stringify(hubUrl),
111 100
       PRODUCTION: JSON.stringify(isProduction)
112 101
     })
113 102
   ]

Loading…
Cancel
Save