From 935f656add7836a18c0beb9e87a672de48bd620e Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Wed, 18 Jul 2018 17:23:26 -0400 Subject: [PATCH 01/21] check if daemon is already installed before installing again --- .gitignore | 5 ++- build/downloadDaemon.js | 92 +++++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 661998b1d..08c9c7fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ /node_modules /dist -/static/daemon/lbrynet* +/static/daemon/lbrynet-daemon /static/locales yarn-error.log package-lock.json -.idea/ \ No newline at end of file +.idea/ +/build/daemon.ver \ No newline at end of file diff --git a/build/downloadDaemon.js b/build/downloadDaemon.js index 11b0b5ba2..3b1955215 100644 --- a/build/downloadDaemon.js +++ b/build/downloadDaemon.js @@ -1,6 +1,6 @@ /* eslint-disable no-console,import/no-extraneous-dependencies,import/no-commonjs */ const path = require('path'); -const fs = require('fs-path'); +const fs = require('fs'); const packageJSON = require('../package.json'); const axios = require('axios'); const decompress = require('decompress'); @@ -13,54 +13,74 @@ const downloadDaemon = targetPlatform => const daemonVersion = packageJSON.lbrySettings.lbrynetDaemonVersion; const daemonDir = packageJSON.lbrySettings.lbrynetDaemonDir; const daemonFileName = packageJSON.lbrySettings.lbrynetDaemonFileName; + const daemonFilePath = `${__dirname}/../${daemonDir}/${daemonFileName}`; let currentPlatform = os.platform(); if (currentPlatform === 'darwin') currentPlatform = 'macos'; if (currentPlatform === 'win32') currentPlatform = 'windows'; + const daemonVersionPath = __dirname + '/daemon.ver'; const daemonPlatform = targetPlatform || currentPlatform; - + const tmpZipPath = __dirname + '/../dist/daemon.zip'; const daemonURL = daemonURLTemplate .replace(/DAEMONVER/g, daemonVersion) .replace(/OSNAME/g, daemonPlatform); - const tmpZipPath = 'dist/daemon.zip'; + + + // If a daemon and daemon.ver exists, check to see if it matches the current daemon version + const hasDaemonDownloaded = fs.existsSync(daemonFilePath); + const hasDaemonVersion = fs.existsSync(daemonVersionPath); + let downloadedDaemonVersion; + if (hasDaemonVersion) { + downloadedDaemonVersion = fs.readFileSync(daemonVersionPath, "utf8"); + } + + if (hasDaemonDownloaded && hasDaemonVersion && downloadedDaemonVersion === daemonVersion) { + console.log('\x1b[34minfo\x1b[0m Daemon already downloaded'); + resolve('Done'); + return; + } else { + console.log('\x1b[34minfo\x1b[0m Downloading daemon...'); + axios + .request({ + responseType: 'arraybuffer', + url: daemonURL, + method: 'get', + headers: { + 'Content-Type': 'application/zip', + }, + }) + .then( + result => + new Promise((newResolve, newReject) => { + fs.writeFile(tmpZipPath, result.data, error => { - console.log('\x1b[34minfo\x1b[0m Downloading daemon...'); - axios - .request({ - responseType: 'arraybuffer', - url: daemonURL, - method: 'get', - headers: { - 'Content-Type': 'application/zip', - }, - }) - .then( - result => - new Promise((newResolve, newReject) => { - fs.writeFile(tmpZipPath, result.data, error => { - if (error) return newReject(error); - return newResolve(); - }); - }) - ) - .then(() => del(`${daemonDir}/${daemonFileName}*`)) - .then(() => - decompress(tmpZipPath, daemonDir, { + if (error) return newReject(error); + return newResolve(); + }); + }) + ) + .then(() => del(`${daemonDir}/${daemonFileName}*`)) + .then(() => decompress(tmpZipPath, daemonDir, { filter: file => path.basename(file.path).replace(path.extname(file.path), '') === daemonFileName, + })) + .then(() => { + console.log('\x1b[32msuccess\x1b[0m Daemon downloaded!'); + if (hasDaemonVersion) { + del(daemonVersionPath); + } + + fs.writeFileSync(daemonVersionPath, daemonVersion, "utf8") + resolve('Done'); }) - ) - .then(() => { - console.log('\x1b[32msuccess\x1b[0m Daemon downloaded!'); - resolve(true); - }) - .catch(error => { - console.error( - `\x1b[31merror\x1b[0m Daemon download failed due to: \x1b[35m${error}\x1b[0m` - ); - reject(error); - }); + .catch(error => { + console.error( + `\x1b[31merror\x1b[0m Daemon download failed due to: \x1b[35m${error}\x1b[0m` + ); + reject(error); + }); + } }); module.exports = downloadDaemon; From d999dfa52834c5ae07a6153bb712cc73b4ea4cdc Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Thu, 19 Jul 2018 09:42:57 -0400 Subject: [PATCH 02/21] fix for windows --- .gitignore | 4 ++-- build/downloadDaemon.js | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 08c9c7fe2..420cf5a97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ /node_modules /dist -/static/daemon/lbrynet-daemon +/static/daemon/lbrynet-daemon* /static/locales yarn-error.log package-lock.json .idea/ -/build/daemon.ver \ No newline at end of file +/build/daemon.ver diff --git a/build/downloadDaemon.js b/build/downloadDaemon.js index 3b1955215..691a536aa 100644 --- a/build/downloadDaemon.js +++ b/build/downloadDaemon.js @@ -11,22 +11,25 @@ const downloadDaemon = targetPlatform => new Promise((resolve, reject) => { const daemonURLTemplate = packageJSON.lbrySettings.lbrynetDaemonUrlTemplate; const daemonVersion = packageJSON.lbrySettings.lbrynetDaemonVersion; - const daemonDir = packageJSON.lbrySettings.lbrynetDaemonDir; - const daemonFileName = packageJSON.lbrySettings.lbrynetDaemonFileName; - const daemonFilePath = `${__dirname}/../${daemonDir}/${daemonFileName}`; + const daemonDir = path.join(__dirname,'..',packageJSON.lbrySettings.lbrynetDaemonDir); + let daemonFileName = packageJSON.lbrySettings.lbrynetDaemonFileName; let currentPlatform = os.platform(); if (currentPlatform === 'darwin') currentPlatform = 'macos'; - if (currentPlatform === 'win32') currentPlatform = 'windows'; + if (currentPlatform === 'win32') { + currentPlatform = 'windows'; + daemonFileName = daemonFileName + '.exe'; + } - const daemonVersionPath = __dirname + '/daemon.ver'; + const daemonFilePath = path.join(daemonDir, daemonFileName); + const daemonVersionPath = path.join(__dirname, 'daemon.ver'); const daemonPlatform = targetPlatform || currentPlatform; - const tmpZipPath = __dirname + '/../dist/daemon.zip'; + const tmpZipPath = path.join(__dirname, '../','dist','daemon.zip'); const daemonURL = daemonURLTemplate .replace(/DAEMONVER/g, daemonVersion) .replace(/OSNAME/g, daemonPlatform); - - + + // If a daemon and daemon.ver exists, check to see if it matches the current daemon version const hasDaemonDownloaded = fs.existsSync(daemonFilePath); const hasDaemonVersion = fs.existsSync(daemonVersionPath); @@ -34,8 +37,8 @@ const downloadDaemon = targetPlatform => if (hasDaemonVersion) { downloadedDaemonVersion = fs.readFileSync(daemonVersionPath, "utf8"); } - - if (hasDaemonDownloaded && hasDaemonVersion && downloadedDaemonVersion === daemonVersion) { + + if (hasDaemonDownloaded && hasDaemonVersion && downloadedDaemonVersion === daemonVersion) { console.log('\x1b[34minfo\x1b[0m Daemon already downloaded'); resolve('Done'); return; @@ -60,17 +63,18 @@ const downloadDaemon = targetPlatform => }); }) ) - .then(() => del(`${daemonDir}/${daemonFileName}*`)) + .then(() => del(`${daemonFilePath}*`)) .then(() => decompress(tmpZipPath, daemonDir, { filter: file => - path.basename(file.path).replace(path.extname(file.path), '') === daemonFileName, + path.basename(file.path) === daemonFileName, })) + .then(() => del(`${tmpZipPath }*`)) .then(() => { console.log('\x1b[32msuccess\x1b[0m Daemon downloaded!'); if (hasDaemonVersion) { del(daemonVersionPath); } - + fs.writeFileSync(daemonVersionPath, daemonVersion, "utf8") resolve('Done'); }) From f0035fda0a700b6fb2e8780c76a3b51c7fd109ad Mon Sep 17 00:00:00 2001 From: btzr-io Date: Thu, 19 Jul 2018 19:31:00 -0600 Subject: [PATCH 03/21] update three-viewer fix model position and scale fix obj loader --- .../component/viewers/threeViewer/index.jsx | 112 +++++++++++++----- .../viewers/threeViewer/internal/grid.js | 13 ++ .../viewers/threeViewer/internal/loader.js | 4 +- .../viewers/threeViewer/internal/scene.js | 24 +--- .../viewers/threeViewer/internal/three.js | 3 +- 5 files changed, 98 insertions(+), 58 deletions(-) create mode 100644 src/renderer/component/viewers/threeViewer/internal/grid.js diff --git a/src/renderer/component/viewers/threeViewer/index.jsx b/src/renderer/component/viewers/threeViewer/index.jsx index 63dc38622..0ceea09d5 100644 --- a/src/renderer/component/viewers/threeViewer/index.jsx +++ b/src/renderer/component/viewers/threeViewer/index.jsx @@ -4,6 +4,7 @@ import LoadingScreen from 'component/common/loading-screen'; // ThreeJS import * as THREE from './internal/three'; import detectWebGL from './internal/detector'; +import ThreeGrid from './internal/grid'; import ThreeScene from './internal/scene'; import ThreeLoader from './internal/loader'; import ThreeRenderer from './internal/renderer'; @@ -77,6 +78,12 @@ class ThreeViewer extends React.PureComponent { window.removeEventListener('resize', this.handleResize, false); } + transformGroup(group) { + this.fitMeshToCamera(group); + this.createWireFrame(group); + this.updateControlsTarget(group.position); + } + createOrbitControls(camera, canvas) { const { autoRotate } = this.props; const controls = new THREE.OrbitControls(camera, canvas); @@ -87,6 +94,7 @@ class ThreeViewer extends React.PureComponent { controls.minDistance = 1; controls.maxDistance = 50; controls.autoRotate = autoRotate; + controls.enablePan = false; return controls; } @@ -114,32 +122,6 @@ class ThreeViewer extends React.PureComponent { group.add(this.wireframe); } - createMesh(geometry) { - const material = new THREE.MeshPhongMaterial({ - opacity: 1, - transparent: true, - depthWrite: true, - vertexColors: THREE.FaceColors, - // Positive value pushes polygon further away - polygonOffsetFactor: 1, - polygonOffsetUnits: 1, - }); - - // Set material color - material.color.set(this.materialColors.green); - - const mesh = new THREE.Mesh(geometry, material); - - // Assign name - mesh.name = 'objectGroup'; - - this.scene.add(mesh); - this.fitMeshToCamera(mesh); - this.createWireFrame(mesh); - this.updateControlsTarget(mesh.position); - return mesh; - } - toggleWireFrame(show = false) { this.wireframe.opacity = show ? 1 : 0; this.mesh.material.opacity = show ? 0 : 1; @@ -151,7 +133,7 @@ class ThreeViewer extends React.PureComponent { group.traverse(child => { if (child instanceof THREE.Mesh) { - const box = new THREE.Box3().setFromObject(group); + const box = new THREE.Box3().setFromObject(child); // Max max.x = box.max.x > max.x ? box.max.x : max.x; max.y = box.max.y > max.y ? box.max.y : max.y; @@ -165,12 +147,18 @@ class ThreeViewer extends React.PureComponent { const meshY = Math.abs(max.y - min.y); const meshX = Math.abs(max.x - min.x); - const scaleFactor = 15 / Math.max(meshX, meshY); + + const scaleFactor = 10 / Math.max(meshX, meshY); group.scale.set(scaleFactor, scaleFactor, scaleFactor); group.position.setY((meshY / 2) * scaleFactor); + + // Reset object position + const box = new THREE.Box3().setFromObject(group); + box.getCenter(group.position); + group.position.multiplyScalar(-1); - group.position.setY((meshY * scaleFactor) / 2); + group.position.setY(group.position.y + meshY * scaleFactor); } startLoader() { @@ -217,12 +205,52 @@ class ThreeViewer extends React.PureComponent { this.controls.update(); } - renderModel(fileType, data) { + renderStl(data) { const geometry = this.createGeometry(data); - this.mesh = this.createMesh(geometry); + const group = new THREE.Mesh(geometry, this.material); + // Assign name + group.name = 'objectGroup'; + this.scene.add(group); + this.transformGroup(group); + this.mesh = group; + } + + renderObj(event) { + const mesh = event.detail.loaderRootNode; + const group = new THREE.Group(); + group.name = 'objGroup'; + + // Assign new material + mesh.traverse(child => { + if (child instanceof THREE.Mesh) { + // Get geometry from child + const geometry = new THREE.Geometry(); + geometry.fromBufferGeometry(child.geometry); + // Create and regroup inner objects + const innerObj = new THREE.Mesh(geometry, this.material); + group.add(innerObj); + } + }); + + this.scene.add(group); + this.transformGroup(group); + this.mesh = group; + } + + renderModel(fileType, parsedData) { + const renderTypes = { + stl: data => this.renderStl(data), + obj: data => this.renderObj(data), + }; + + if (renderTypes[fileType]) { + renderTypes[fileType](parsedData); + } } renderScene() { + const { gridColor, centerLineColor } = this.theme; + this.renderer = ThreeRenderer({ antialias: true, shadowMap: true, @@ -230,20 +258,40 @@ class ThreeViewer extends React.PureComponent { this.scene = ThreeScene({ showFog: true, - showGrid: true, ...this.theme, }); const viewer = this.viewer.current; const canvas = this.renderer.domElement; const { offsetWidth: width, offsetHeight: height } = viewer; + + // Grid + this.grid = ThreeGrid({ size: 100, gridColor, centerLineColor }); + this.scene.add(this.grid); + // Camera this.camera = new THREE.PerspectiveCamera(80, width / height, 0.1, 1000); this.camera.position.set(-9.5, 14, 11); + // Controls this.controls = this.createOrbitControls(this.camera, canvas); + // Set viewer size this.renderer.setSize(width, height); + + // Create model material + this.material = new THREE.MeshPhongMaterial({ + opacity: 1, + transparent: true, + // depthWrite: true, + vertexColors: THREE.FaceColors, + // Positive value pushes polygon further away + // polygonOffsetFactor: 1, + // polygonOffsetUnits: 1, + }); + // Set material color + this.material.color.set(this.materialColors.green); + // Load file and render mesh this.startLoader(); diff --git a/src/renderer/component/viewers/threeViewer/internal/grid.js b/src/renderer/component/viewers/threeViewer/internal/grid.js new file mode 100644 index 000000000..0793c6913 --- /dev/null +++ b/src/renderer/component/viewers/threeViewer/internal/grid.js @@ -0,0 +1,13 @@ +import { GridHelper, Color } from './three'; + +const ThreeGrid = ({ size, gridColor, centerLineColor }) => { + const divisions = size / 2; + const grid = new GridHelper(size, divisions, new Color(centerLineColor), new Color(gridColor)); + + grid.material.opacity = 0.4; + grid.material.transparent = true; + + return grid; +}; + +export default ThreeGrid; diff --git a/src/renderer/component/viewers/threeViewer/internal/loader.js b/src/renderer/component/viewers/threeViewer/internal/loader.js index dc4ce7482..d69a93344 100644 --- a/src/renderer/component/viewers/threeViewer/internal/loader.js +++ b/src/renderer/component/viewers/threeViewer/internal/loader.js @@ -1,4 +1,4 @@ -import { LoadingManager, STLLoader, OBJLoader } from './three'; +import { LoadingManager, STLLoader, OBJLoader2 } from './three'; const Manager = ({ onLoad, onStart, onError }) => { const manager = new LoadingManager(); @@ -12,7 +12,7 @@ const Manager = ({ onLoad, onStart, onError }) => { const Loader = (fileType, manager) => { const fileTypes = { stl: () => new STLLoader(manager), - obj: () => new OBJLoader(manager), + obj: () => new OBJLoader2(manager), }; return fileTypes[fileType] ? fileTypes[fileType]() : null; }; diff --git a/src/renderer/component/viewers/threeViewer/internal/scene.js b/src/renderer/component/viewers/threeViewer/internal/scene.js index f858c8832..a22c4a996 100644 --- a/src/renderer/component/viewers/threeViewer/internal/scene.js +++ b/src/renderer/component/viewers/threeViewer/internal/scene.js @@ -1,18 +1,5 @@ import * as THREE from './three'; -const addGrid = (scene, { gridColor, centerLineColor, size }) => { - const divisions = size / 2; - const grid = new THREE.GridHelper( - size, - divisions, - new THREE.Color(centerLineColor), - new THREE.Color(gridColor) - ); - grid.material.opacity = 0.4; - grid.material.transparent = true; - scene.add(grid); -}; - const addLights = (scene, color, groundColor) => { // Light color const lightColor = new THREE.Color(color); @@ -30,7 +17,7 @@ const addLights = (scene, color, groundColor) => { scene.add(shadowLight); }; -const Scene = ({ backgroundColor, groundColor, showFog, showGrid, gridColor, centerLineColor }) => { +const Scene = ({ backgroundColor, groundColor, showFog }) => { // Convert color const bgColor = new THREE.Color(backgroundColor); // New scene @@ -39,17 +26,8 @@ const Scene = ({ backgroundColor, groundColor, showFog, showGrid, gridColor, cen scene.background = bgColor; // Fog effect scene.fog = showFog === true ? new THREE.Fog(bgColor, 1, 95) : null; - // Add grid - if (showGrid) { - addGrid(scene, { - size: 100, - gridColor, - centerLineColor, - }); - } // Add basic lights addLights(scene, '#FFFFFF', groundColor); - // Return new three scene return scene; }; diff --git a/src/renderer/component/viewers/threeViewer/internal/three.js b/src/renderer/component/viewers/threeViewer/internal/three.js index 6cb0051d0..4e9f9bdb1 100644 --- a/src/renderer/component/viewers/threeViewer/internal/three.js +++ b/src/renderer/component/viewers/threeViewer/internal/three.js @@ -4,7 +4,8 @@ import * as THREE from 'three'; // Fix: https://github.com/mrdoob/three.js/issues/9562#issuecomment-383390251 global.THREE = THREE; require('three/examples/js/controls/OrbitControls'); -require('three/examples/js/loaders/OBJLoader'); +require('three/examples/js/loaders/LoaderSupport'); +require('three/examples/js/loaders/OBJLoader2'); require('three/examples/js/loaders/STLLoader'); module.exports = global.THREE; From 9128a61051e2c545bb95374f8877a7111b4d924d Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 20 Jul 2018 11:32:44 -0400 Subject: [PATCH 04/21] fix community spacing at the bottom of the page --- src/renderer/scss/_gui.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/scss/_gui.scss b/src/renderer/scss/_gui.scss index 582448d59..8ea4b0907 100644 --- a/src/renderer/scss/_gui.scss +++ b/src/renderer/scss/_gui.scss @@ -184,6 +184,7 @@ p { .main { padding: $spacing-width $spacing-width; margin: auto; + overflow: hidden; } .main--contained { From ffab70fd9c7237569db1553ea3f694ca878108d9 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 20 Jul 2018 12:35:58 -0400 Subject: [PATCH 05/21] fix double navigation issue on channels with more than one page --- src/renderer/component/uriIndicator/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/component/uriIndicator/view.jsx b/src/renderer/component/uriIndicator/view.jsx index 80f2f9ec2..d1b1d90d4 100644 --- a/src/renderer/component/uriIndicator/view.jsx +++ b/src/renderer/component/uriIndicator/view.jsx @@ -80,7 +80,7 @@ class UriIndicator extends React.PureComponent { noPadding className="btn--uri-indicator" navigate="/show" - navigateParams={{ uri: channelLink }} + navigateParams={{ uri: channelLink, page: 1 }} > {inner} From 90655a1dd5e7f3077547f4cab7912b3dbdfe79ae Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 20 Jul 2018 13:48:31 -0400 Subject: [PATCH 06/21] stop loader timeout if not loading --- src/renderer/component/page/view.jsx | 2 ++ src/renderer/page/subscriptions/index.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/component/page/view.jsx b/src/renderer/component/page/view.jsx index ce21e27d7..c8dbca313 100644 --- a/src/renderer/component/page/view.jsx +++ b/src/renderer/component/page/view.jsx @@ -41,6 +41,8 @@ class Page extends React.PureComponent { const { loading } = this.props; if (!this.loaderTimeout && !prevProps.loading && loading) { this.beginLoadingTimeout(); + } else if (!loading && this.loaderTimeout) { + clearTimeout(this.loaderTimeout); } } diff --git a/src/renderer/page/subscriptions/index.js b/src/renderer/page/subscriptions/index.js index 7ac70adbd..9963b26e0 100644 --- a/src/renderer/page/subscriptions/index.js +++ b/src/renderer/page/subscriptions/index.js @@ -13,7 +13,7 @@ import SubscriptionsPage from './view'; const select = state => ({ loading: selectIsFetchingSubscriptions(state) || - Object.keys(selectSubscriptionsBeingFetched(state)).length, + Boolean(Object.keys(selectSubscriptionsBeingFetched(state)).length), subscriptionsBeingFetched: selectSubscriptionsBeingFetched(state), subscriptions: selectSubscriptions(state), subscriptionClaims: selectSubscriptionClaims(state), From dd8e3ba414a32cf8d1133ab877d24cd36f1b537c Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 20 Jul 2018 14:59:21 -0400 Subject: [PATCH 07/21] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cba6ffa40..aceda5a07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Fixed - * Edit option missing from certain published claims ([#175](https://github.com/lbryio/lbry-desktop/issues/1756)) + * Edit option missing from certain published claims ([#1756](https://github.com/lbryio/lbry-desktop/issues/1756)) + * Fix navigation issue with channels that have more than one page ([#1797](https://github.com/lbryio/lbry-desktop/pull/1797)) ### Added From 2d7cf5163080d44fbce7d505e8748cd7e106d363 Mon Sep 17 00:00:00 2001 From: Thomas Zarebczan Date: Fri, 20 Jul 2018 16:54:10 -0400 Subject: [PATCH 08/21] formatting for protocol message --- src/renderer/modal/modalIncompatibleDaemon/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/modal/modalIncompatibleDaemon/view.jsx b/src/renderer/modal/modalIncompatibleDaemon/view.jsx index bf015aec0..b7b7100fd 100644 --- a/src/renderer/modal/modalIncompatibleDaemon/view.jsx +++ b/src/renderer/modal/modalIncompatibleDaemon/view.jsx @@ -23,7 +23,7 @@ class ModalIncompatibleDaemon extends React.PureComponent { onAborted={quit} > {__( - 'This browser is running with an incompatible version of the LBRY protocol, please close the LBRY app and rerun the installation package to repair it' + 'This browser is running with an incompatible version of the LBRY protocol, please close the LBRY app and rerun the installation package to repair it. ' )}