├── .babelrc ├── .gitignore ├── .gitmodules ├── .travis.yml ├── .vscode └── launch.json ├── LICENSE ├── NOTICE ├── README.md ├── app ├── .eslintrc ├── app.html ├── main.dev.js ├── menu.js ├── package.json └── preload.js ├── appveyor.yml ├── build-frontend.js ├── build-macos.sh ├── build-wr.sh ├── build ├── background.tiff ├── icon.icns ├── icon.ico └── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 512x512.png │ └── 64x64.png ├── internals └── scripts │ └── CheckNodeEnv.js ├── package.json ├── plugins └── README.md ├── python-binaries └── .gitignore ├── walkthrough.md ├── webpack └── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "useBuiltIns": true 5 | }], 6 | "stage-0", 7 | "react" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *~ 3 | 4 | node_modules 5 | python-binaries/* 6 | dll 7 | app/static/ 8 | 9 | app/main.prod.js 10 | app/main.prod.js.map 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "webrecorder"] 2 | path = webrecorder 3 | url = https://github.com/webrecorder/webrecorder 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: linux 4 | language: node_js 5 | node_js: 6 | - "10" 7 | - os: osx 8 | osx_image: xcode10.2 9 | language: node_js 10 | node_js: 11 | - "10" 12 | 13 | env: 14 | global: 15 | - ARTIFACTS_TARGET_PATHS="electron-player/$TRAVIS_BRANCH" 16 | 17 | addons: 18 | artifacts: 19 | paths: 20 | - $(ls webrecorder-player-*{dmg,AppImage}) 21 | 22 | before_install: 23 | - | 24 | if [[ $TRAVIS_OS_NAME == 'linux' ]]; then 25 | sed -i'' -E 's/"name": "(.*)"/"name": "\1-linux"/' ./package.json 26 | apt-get -qq update; 27 | sudo apt-get install -y icnsutils graphicsmagick; 28 | FLASH_PLUGIN="https://s3.amazonaws.com/webrecorder-builds/flashplugin/libpepflashplayer.so"; 29 | cd plugins; 30 | wget $FLASH_PLUGIN; 31 | cd -; 32 | fi 33 | - | 34 | if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 35 | sed -i '' -E 's/"name": "(.*)"/"name": "\1-osx"/' ./package.json 36 | FLASH_PLUGIN="https://s3.amazonaws.com/webrecorder-builds/flashplugin/PepperFlashPlayer.plugin.zip"; 37 | cd plugins; 38 | wget $FLASH_PLUGIN; 39 | unzip PepperFlashPlayer.plugin.zip; 40 | rm PepperFlashPlayer.plugin.zip; 41 | cd -; 42 | fi 43 | 44 | install: 45 | - BUCKET="https://s3.amazonaws.com/webrecorder-builds/webrecorder-player" 46 | - CURRENT_BRANCH="$BUCKET/$TRAVIS_BRANCH/webrecorder-player-$TRAVIS_OS_NAME" 47 | - DEVELOP_BRANCH="$BUCKET/develop/webrecorder-player-$TRAVIS_OS_NAME" 48 | - | 49 | if curl -o /dev/null -s -I -f "$CURRENT_BRANCH"; then 50 | echo "downloading webrecorder artifact: $CURRENT_BRANCH"; 51 | curl -o python-binaries/webrecorder_player $CURRENT_BRANCH; 52 | else 53 | echo "downloading webrecorder artifact: $DEVELOP_BRANCH"; 54 | curl -o python-binaries/webrecorder_player $DEVELOP_BRANCH; 55 | fi 56 | - chmod +x python-binaries/webrecorder_player 57 | - python-binaries/webrecorder_player -v 58 | - yarn install 59 | - yarn run build 60 | 61 | after_failure: 62 | - cat ~/.npm/_logs/*.log 63 | 64 | script: 65 | - | 66 | if [[ $TRAVIS_BRANCH == 'master' || $TRAVIS_BRANCH == 'prerelease' ]]; then 67 | yarn run release 68 | else 69 | yarn run dist 70 | fi 71 | - mv ./dist/webrecorder-player-* $TRAVIS_BUILD_DIR/ 72 | 73 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Main Process", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", 10 | "program": "${workspaceRoot}/main.js" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Version 2.0, January 2004 2 | http://www.apache.org/licenses/ 3 | 4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 5 | 6 | 1. Definitions. 7 | 8 | "License" shall mean the terms and conditions for use, reproduction, 9 | and distribution as defined by Sections 1 through 9 of this document. 10 | 11 | "Licensor" shall mean the copyright owner or entity authorized by 12 | the copyright owner that is granting the License. 13 | 14 | "Legal Entity" shall mean the union of the acting entity and all 15 | other entities that control, are controlled by, or are under common 16 | control with that entity. For the purposes of this definition, 17 | "control" means (i) the power, direct or indirect, to cause the 18 | direction or management of such entity, whether by contract or 19 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity 23 | exercising permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, 26 | including but not limited to software source code, documentation 27 | source, and configuration files. 28 | 29 | "Object" form shall mean any form resulting from mechanical 30 | transformation or translation of a Source form, including but 31 | not limited to compiled object code, generated documentation, 32 | and conversions to other media types. 33 | 34 | "Work" shall mean the work of authorship, whether in Source or 35 | Object form, made available under the License, as indicated by a 36 | copyright notice that is included in or attached to the work 37 | (an example is provided in the Appendix below). 38 | 39 | "Derivative Works" shall mean any work, whether in Source or Object 40 | form, that is based on (or derived from) the Work and for which the 41 | editorial revisions, annotations, elaborations, or other modifications 42 | represent, as a whole, an original work of authorship. For the purposes 43 | of this License, Derivative Works shall not include works that remain 44 | separable from, or merely link (or bind by name) to the interfaces of, 45 | the Work and Derivative Works thereof. 46 | 47 | "Contribution" shall mean any work of authorship, including 48 | the original version of the Work and any modifications or additions 49 | to that Work or Derivative Works thereof, that is intentionally 50 | submitted to Licensor for inclusion in the Work by the copyright owner 51 | or by an individual or Legal Entity authorized to submit on behalf of 52 | the copyright owner. For the purposes of this definition, "submitted" 53 | means any form of electronic, verbal, or written communication sent 54 | to the Licensor or its representatives, including but not limited to 55 | communication on electronic mailing lists, source code control systems, 56 | and issue tracking systems that are managed by, or on behalf of, the 57 | Licensor for the purpose of discussing and improving the Work, but 58 | excluding communication that is conspicuously marked or otherwise 59 | designated in writing by the copyright owner as "Not a Contribution." 60 | 61 | "Contributor" shall mean Licensor and any individual or Legal Entity 62 | on behalf of whom a Contribution has been received by Licensor and 63 | subsequently incorporated within the Work. 64 | 65 | 2. Grant of Copyright License. Subject to the terms and conditions of 66 | this License, each Contributor hereby grants to You a perpetual, 67 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 68 | copyright license to reproduce, prepare Derivative Works of, 69 | publicly display, publicly perform, sublicense, and distribute the 70 | Work and such Derivative Works in Source or Object form. 71 | 72 | 3. Grant of Patent License. Subject to the terms and conditions of 73 | this License, each Contributor hereby grants to You a perpetual, 74 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 75 | (except as stated in this section) patent license to make, have made, 76 | use, offer to sell, sell, import, and otherwise transfer the Work, 77 | where such license applies only to those patent claims licensable 78 | by such Contributor that are necessarily infringed by their 79 | Contribution(s) alone or by combination of their Contribution(s) 80 | with the Work to which such Contribution(s) was submitted. If You 81 | institute patent litigation against any entity (including a 82 | cross-claim or counterclaim in a lawsuit) alleging that the Work 83 | or a Contribution incorporated within the Work constitutes direct 84 | or contributory patent infringement, then any patent licenses 85 | granted to You under this License for that Work shall terminate 86 | as of the date such litigation is filed. 87 | 88 | 4. Redistribution. You may reproduce and distribute copies of the 89 | Work or Derivative Works thereof in any medium, with or without 90 | modifications, and in Source or Object form, provided that You 91 | meet the following conditions: 92 | 93 | (a) You must give any other recipients of the Work or 94 | Derivative Works a copy of this License; and 95 | 96 | (b) You must cause any modified files to carry prominent notices 97 | stating that You changed the files; and 98 | 99 | (c) You must retain, in the Source form of any Derivative Works 100 | that You distribute, all copyright, patent, trademark, and 101 | attribution notices from the Source form of the Work, 102 | excluding those notices that do not pertain to any part of 103 | the Derivative Works; and 104 | 105 | (d) If the Work includes a "NOTICE" text file as part of its 106 | distribution, then any Derivative Works that You distribute must 107 | include a readable copy of the attribution notices contained 108 | within such NOTICE file, excluding those notices that do not 109 | pertain to any part of the Derivative Works, in at least one 110 | of the following places: within a NOTICE text file distributed 111 | as part of the Derivative Works; within the Source form or 112 | documentation, if provided along with the Derivative Works; or, 113 | within a display generated by the Derivative Works, if and 114 | wherever such third-party notices normally appear. The contents 115 | of the NOTICE file are for informational purposes only and 116 | do not modify the License. You may add Your own attribution 117 | notices within Derivative Works that You distribute, alongside 118 | or as an addendum to the NOTICE text from the Work, provided 119 | that such additional attribution notices cannot be construed 120 | as modifying the License. 121 | 122 | You may add Your own copyright statement to Your modifications and 123 | may provide additional or different license terms and conditions 124 | for use, reproduction, or distribution of Your modifications, or 125 | for any such Derivative Works as a whole, provided Your use, 126 | reproduction, and distribution of the Work otherwise complies with 127 | the conditions stated in this License. 128 | 129 | 5. Submission of Contributions. Unless You explicitly state otherwise, 130 | any Contribution intentionally submitted for inclusion in the Work 131 | by You to the Licensor shall be under the terms and conditions of 132 | this License, without any additional terms or conditions. 133 | Notwithstanding the above, nothing herein shall supersede or modify 134 | the terms of any separate license agreement you may have executed 135 | with Licensor regarding such Contributions. 136 | 137 | 6. Trademarks. This License does not grant permission to use the trade 138 | names, trademarks, service marks, or product names of the Licensor, 139 | except as required for reasonable and customary use in describing the 140 | origin of the Work and reproducing the content of the NOTICE file. 141 | 142 | 7. Disclaimer of Warranty. Unless required by applicable law or 143 | agreed to in writing, Licensor provides the Work (and each 144 | Contributor provides its Contributions) on an "AS IS" BASIS, 145 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 146 | implied, including, without limitation, any warranties or conditions 147 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 148 | PARTICULAR PURPOSE. You are solely responsible for determining the 149 | appropriateness of using or redistributing the Work and assume any 150 | risks associated with Your exercise of permissions under this License. 151 | 152 | 8. Limitation of Liability. In no event and under no legal theory, 153 | whether in tort (including negligence), contract, or otherwise, 154 | unless required by applicable law (such as deliberate and grossly 155 | negligent acts) or agreed to in writing, shall any Contributor be 156 | liable to You for damages, including any direct, indirect, special, 157 | incidental, or consequential damages of any character arising as a 158 | result of this License or out of the use or inability to use the 159 | Work (including but not limited to damages for loss of goodwill, 160 | work stoppage, computer failure or malfunction, or any and all 161 | other commercial damages or losses), even if such Contributor 162 | has been advised of the possibility of such damages. 163 | 164 | 9. Accepting Warranty or Additional Liability. While redistributing 165 | the Work or Derivative Works thereof, You may choose to offer, 166 | and charge a fee for, acceptance of support, warranty, indemnity, 167 | or other liability obligations and/or rights consistent with this 168 | License. However, in accepting such obligations, You may act only 169 | on Your own behalf and on Your sole responsibility, not on behalf 170 | of any other Contributor, and only if You agree to indemnify, 171 | defend, and hold each Contributor harmless for any liability 172 | incurred by, or claims asserted against, such Contributor by reason 173 | of your accepting any such warranty or additional liability. 174 | 175 | END OF TERMS AND CONDITIONS 176 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Webrecorder 2 | Copyright 2015 Rhizome Inc. 3 | 4 | Distributed under the Apache License 2.0. 5 | See LICENSE for details. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *NOTE: Webrecorder Player has been superceded by [ReplayWeb.page](https://github.com/webrecorder/replayweb.page).* 2 | 3 | *Users of Webrecorder Player are encouraged to switch to [the latest ReplayWeb.page App](https://github.com/webrecorder/replayweb.page/releases/latest).* 4 | 5 |
6 | 7 | # Webrecorder Player 8 | 9 | Webrecorder Player is a desktop application for viewing high-fidelity web archives on your local machine, no internet connection required. Webrecorder Player is available for download for OSX, Windows and Linux (experimental). 10 | 11 | ### [Download Latest Release](https://github.com/webrecorder/webrecorderplayer-electron/releases/latest) 12 | 13 | ## Usage 14 | 15 | 1) Download the latest version for your platform. On OSX, you can move the extracted Webrecorder Player app into your Applications directory. 16 | 17 | 2) Start the application. (Note: At this time, Webrecorder Player is not yet signed through official app store, so you may have to accept launching unsigned apps). 18 | 19 | 3) Click **Load Web Archive** to select a web archive file and start browsing. 20 | 21 | See [Visual Walkthrough](walkthrough.md) 22 | 23 | 24 | ## What are Web Archives? 25 | 26 | A web archive is a record of web resources. It may include HTML and images, scripts, stylesheets, as well as video, audio and other elements that web pages and web apps are made of, all in one file. 27 | 28 | Webrecorder Player currently supports browsing web archives in the following formats: 29 | 30 | - [WARC format](https://en.wikipedia.org/wiki/Web_ARChive) **(.warc, warc.gz)** — the most commonly used one 31 | - [HAR format](https://en.wikipedia.org/wiki/.har) **(.har)** 32 | - [ARC format](http://archive.org/web/researcher/ArcFileFormat.php) **(.arc, .arg.gz)** 33 | 34 | 35 | ## How do I Create Web Archives? 36 | You can use free service [https://webrecorder.io](https://webrecorder.io) to create, view, share and save your web archives online. 37 | 38 | To view your web archives offline, you can download them from [https://webrecorder.io](https://webrecorder.io) and use this app to browse your archives. 39 | 40 | 41 | ------ 42 | 43 | ## Build locally 44 | 45 | Most of the code for Webrecorder, both backend and frontend is found in our main [webrecorder/webrecorder](https://github.com/webrecorder/webrecorder) repository. 46 | 47 | This repository includes the Electron wrapper and includes `webrecorder/webrecorder` as a submodule. 48 | Building locally requires Node and Python. 49 | 50 | 51 | 1) Clone this repository with submodules: 52 | 53 | $ git clone --recurse-submodules https://github.com/webrecorder/webrecorderplayer-electron 54 | or for older versions of git: 55 | 56 | $ git clone https://github.com/webrecorder/webrecorderplayer-electron.git 57 | 58 | $ git submodule update --init --recursive 59 | 60 | 61 | 2) Switch to the directory and install node modules (`yarn` or `npm` should work): 62 | 63 | $ cd webrecorderplayer-electron; yarn install 64 | 65 | 3) Build the Webrecorder python binary by running the build script. This requires Python 3.5+: 66 | 67 | $ bash ./build-wr.sh 68 | 69 | Alternatively, you can manually download the latest python binary, placing it into ``python-binaries/webrecorder_player``. You can obtain pre-built python binaries here: 70 | - [OS X](https://s3.amazonaws.com/webrecorder-builds/webrecorder-player/master/webrecorder-player-osx) 71 | - [Windows 32-bit](https://s3.amazonaws.com/webrecorder-builds/webrecorder-player/master/webrecorder-player-win-x32.exe) 72 | - [Windows 64-bit](https://s3.amazonaws.com/webrecorder-builds/webrecorder-player/master/webrecorder-player-win-x64.exe) 73 | - [Linux](https://s3.amazonaws.com/webrecorder-builds/webrecorder-player/master/webrecorder-player-linux) 74 | 75 | 76 | 4) (Optional) if you want to enable Flash you have to copy a flash plugin into `./plugins`. 77 | 78 | Follow these [instructions](plugins/README.md) to find a local copy of PepperFlashPlugin in Chrome/Chromium 79 | 80 | 5) Build the electron and webrecorder packages: 81 | 82 | $ yarn run build 83 | 84 | 6) To start in Dev mode run: 85 | 86 | $ yarn run start-dev 87 | 88 | 7) A final packaged binary version of Webrecorder Player can be created for your environment (Linux, OSX, Windows) and placed in `./dist` by running: 89 | 90 | $ yarn run dist 91 | 92 | ## Contact 93 | 94 | Webrecorder Player is a project of [Rhizome](https://rhizome.org) 95 | 96 | For issues with the project, you can open an issue or contact us at [support@webrecorder.io](mailto:support@webrecorder.io) 97 | 98 | 99 | ## License 100 | 101 | Webrecorder is Licensed under the Apache 2.0 License. See [NOTICE](NOTICE) and [LICENSE](LICENSE) for details. 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb", 3 | "parser": "babel-eslint", 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "mocha": true, 8 | }, 9 | "rules": { 10 | "camelcase": 1, 11 | "max-len": [0, 800, 2], 12 | "react/no-multi-comp": 0, 13 | "import/default": 0, 14 | "import/no-duplicates": 0, 15 | "import/named": 0, 16 | "import/namespace": 0, 17 | "import/no-unresolved": 0, 18 | "import/no-named-as-default": 2, 19 | "comma-dangle": 0, 20 | "comma-spacing": 1, 21 | "indent": [2, 2, {"SwitchCase": 1}], 22 | "no-console": 0, 23 | "no-alert": 0, 24 | "global-require": 0, 25 | "import/no-extraneous-dependencies": 0, 26 | "import/extensions": 0, 27 | "react/no-unescaped-entities": 0, 28 | "react/jsx-no-target-blank": 0, 29 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 30 | "react/jsx-closing-bracket-location": 0, 31 | "react/forbid-prop-types": 0, 32 | "react/prefer-stateless-function": 0, 33 | "react/prop-types": 1, 34 | "react/no-unused-prop-types": 0, 35 | "react/no-did-mount-set-state": 1, 36 | "react/require-default-props": 0, 37 | "spaced-comment": 0, 38 | "no-unused-vars": 1, 39 | "keyword-spacing": 0, 40 | "curly": 0, 41 | "arrow-body-style": 0, 42 | "semi": 1, 43 | "no-useless-escape": 0, 44 | "no-param-reassign": 1, 45 | "vars-on-top": 1, 46 | "no-shadow": 1, 47 | "class-methods-use-this": 1, 48 | "prefer-template": 1, 49 | "no-bitwise": 0, 50 | "no-plusplus": 1, 51 | "jsx-a11y/no-static-element-interactions": 1, 52 | "object-curly-spacing":1, 53 | "import/prefer-default-export": 1, 54 | "new-cap": 0, 55 | "quote-props": 1, 56 | "quotes": 1, 57 | "no-underscore-dangle": 0 58 | }, 59 | "plugins": [ 60 | "react", "import" 61 | ], 62 | "settings": { 63 | "import/parser": "babel-eslint", 64 | "import/resolver": { 65 | "webpack": { 66 | "config": "webpack/webpack.config.eslint.js", 67 | "moduleDirectory": ["node_modules", "app"] 68 | } 69 | } 70 | }, 71 | "globals": { 72 | "__DEVELOPMENT__": true, 73 | "__CLIENT__": true, 74 | "__SERVER__": true, 75 | "__PLAYER__": true, 76 | "__DEVTOOLS__": true, 77 | "socket": true, 78 | "webpackIsomorphicTools": true 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webrecorder Player 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/main.dev.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 1 */ 2 | 3 | /** 4 | * This module executes inside of electron's main process. You can start 5 | * electron renderer process from here and communicate with the other processes 6 | * through IPC. 7 | * 8 | * When running `npm run build` or `npm run build-main`, this file is compiled to 9 | * `./app/main.prod.js` using webpack. This gives us some performance wins. 10 | * 11 | */ 12 | require('babel-polyfill'); 13 | 14 | 15 | import { app, BrowserWindow, ipcMain, session } from 'electron'; 16 | import child_process from 'child_process'; 17 | import Dat from 'dat-node'; 18 | import fs from 'fs'; 19 | import path from 'path'; 20 | import url from 'url'; 21 | import windowStateKeeper from 'electron-window-state'; 22 | 23 | import MenuBuilder from './menu'; 24 | import packageInfo from '../package'; 25 | 26 | 27 | let debugOutput = []; 28 | let mainWindow = null; 29 | let pluginName; 30 | let port; 31 | let spawnOptions; 32 | let webrecorderProcess; 33 | let openNextFile = null; 34 | 35 | 36 | const projectDir = path.join(__dirname, '../'); 37 | const EXE_NAME = 'webrecorder_player'; 38 | const webrecorderBin = path.join(projectDir, 'python-binaries', EXE_NAME); 39 | const stdio = ['ignore', 'pipe', 'pipe']; 40 | const wrConfig = {}; 41 | const pluginDir = 'plugins'; 42 | 43 | 44 | switch (process.platform) { 45 | case 'win32': 46 | pluginName = 'pepflashplayer.dll'; 47 | spawnOptions = { detached: true, stdio }; 48 | break; 49 | case 'darwin': 50 | pluginName = 'PepperFlashPlayer.plugin'; 51 | spawnOptions = { detached: true, stdio }; 52 | break; 53 | case 'linux': 54 | pluginName = 'libpepflashplayer.so'; 55 | spawnOptions = { detached: true, stdio }; 56 | break; 57 | default: 58 | console.log('platform unsupported'); 59 | break; 60 | } 61 | 62 | app.commandLine.appendSwitch('ignore-certificate-errors'); 63 | app.commandLine.appendSwitch( 64 | 'ppapi-flash-path', 65 | path.join(projectDir, pluginDir, pluginName) 66 | ); 67 | app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); 68 | 69 | const findPort = function (rawBuff, source) { 70 | const buff = rawBuff.toString(); 71 | 72 | debugOutput.push(buff); 73 | 74 | // clip line buffer 75 | if (debugOutput.length > 500) { 76 | debugOutput.shift(); 77 | } 78 | 79 | if (!buff || port) { 80 | return; 81 | } 82 | 83 | console.log(buff); 84 | 85 | const parts = buff.split('APP_HOST=http://localhost:'); 86 | if (parts.length !== 2) { 87 | return; 88 | } 89 | 90 | port = parts[1].trim(); 91 | 92 | if (process.platform !== 'win32') { 93 | webrecorderProcess.unref(); 94 | } 95 | 96 | const appUrl = `http://localhost:${port}/`; 97 | 98 | console.log( 99 | `webrecorder is listening on: ${appUrl} (pid ${webrecorderProcess.pid}) ` 100 | ); 101 | Object.assign(wrConfig, { host: appUrl }); 102 | 103 | const sesh = session.fromPartition('persist:local-replay', { cache: true }); 104 | const proxy = `localhost:${port}`; 105 | sesh.setProxy({ proxyRules: proxy }, () => { 106 | mainWindow.webContents.send('indexing', { host: appUrl, source }); 107 | }); 108 | 109 | // Ensure Brotli Support 110 | sesh.webRequest.onBeforeSendHeaders((details, callback) => { 111 | details.requestHeaders['Accept-Encoding'] = 'gzip, deflate, br'; 112 | callback({cancel: false, requestHeaders: details.requestHeaders}); 113 | }); 114 | 115 | }; 116 | 117 | 118 | function killProcess() { 119 | console.log('killing webrecorder?', Boolean(webrecorderProcess)); 120 | // if a previous webrecorder player is running, kill it 121 | if (webrecorderProcess) { 122 | port = 0; 123 | 124 | if (process.platform === 'win32') { 125 | child_process.execSync( 126 | `taskkill /F /PID ${webrecorderProcess.pid} /T` 127 | ); 128 | } else { 129 | webrecorderProcess.kill('SIGTERM'); 130 | } 131 | } 132 | } 133 | 134 | 135 | const registerOpenWarc = function () { 136 | // get versions for stack 137 | child_process.execFile(webrecorderBin, ['--version'], (err, stdout, stderr) => { 138 | const electronVersion = `electron ${process.versions.electron}
139 | chrome ${process.versions.chrome}`; 140 | Object.assign(wrConfig, { 141 | version: `webrecorder player ${packageInfo.version}
142 | ${stdout.replace(/\n/g, '
').replace(EXE_NAME, 'webrecorder')}
${electronVersion}` 143 | }); 144 | }); 145 | 146 | ipcMain.on('open-warc', (event, argument) => { 147 | openWarc(argument); 148 | }); 149 | }; 150 | 151 | const openWarc = (warc) => { 152 | debugOutput = []; 153 | openNextFile = null; 154 | console.log(`warc file: ${warc}`); 155 | 156 | // move to homepage 157 | mainWindow.webContents.send('change-location', '/'); 158 | 159 | // notify renderer that we are initializing webrecorder binary 160 | mainWindow.webContents.send('initializing', { src: 'warc' }); 161 | 162 | killProcess(); 163 | 164 | webrecorderProcess = child_process.spawn( 165 | webrecorderBin, 166 | ['--no-browser', '--loglevel', 'error', '--cache-dir', '_warc_cache', '--port', 0, warc], 167 | spawnOptions 168 | ); 169 | 170 | // catch any errors spawning webrecorder binary and add to debug info 171 | webrecorderProcess.on('error', (err) => { 172 | debugOutput.push(`Error spawning ${webrecorderBin} binary:\n ${err}\n\n`); 173 | }); 174 | 175 | // log any stderr notices 176 | webrecorderProcess.stderr.on('data', (buff) => { 177 | debugOutput.push(`stderr: ${buff.toString()}`); 178 | 179 | // clip line buffer 180 | if (debugOutput.length > 500) { 181 | debugOutput.shift(); 182 | } 183 | }); 184 | 185 | webrecorderProcess.stdout.on('data', (buff) => findPort(buff, warc)); 186 | }; 187 | 188 | const createWindow = function () { 189 | // keep track of window state 190 | const mainWindowState = windowStateKeeper({ 191 | defaultWidth: 1000, 192 | defaultHeight: 800 193 | }); 194 | 195 | mainWindow = new BrowserWindow({ 196 | webPreferences: { plugins: true, 197 | webviewTag: true, 198 | nodeIntegration: true }, 199 | 200 | // start with state from windowStateKeeper 201 | x: mainWindowState.x, 202 | y: mainWindowState.y, 203 | width: mainWindowState.width, 204 | height: mainWindowState.height, 205 | isMaximized: mainWindowState.isMaximized, 206 | isFullScreen: mainWindowState.isFullScreen, 207 | 208 | // hide the window until the content is loaded 209 | show: false 210 | }); 211 | 212 | // have windowStateKeeper subscribe to window state changes 213 | mainWindowState.manage(mainWindow); 214 | 215 | // show the window once its content is ready to go 216 | mainWindow.once('ready-to-show', () => { 217 | // init session earlier 218 | const sesh = session.fromPartition('persist:wr', { cache: true }); 219 | 220 | if (openNextFile) { 221 | openWarc(openNextFile); 222 | } else if (process.argv.length == 2 && !process.argv[1].startsWith('-psn')) { 223 | openWarc(process.argv[1]); 224 | } 225 | 226 | mainWindow.show() 227 | 228 | }); 229 | 230 | // load the application into the main window 231 | mainWindow.loadURL(`file://${__dirname}/app.html`); 232 | 233 | mainWindow.on('closed', () => { 234 | mainWindow = null; 235 | if (webrecorderProcess) { 236 | if (process.platform === 'win32') { 237 | child_process.execSync( 238 | `taskkill /F /PID ${webrecorderProcess.pid} /T` 239 | ); 240 | } else { 241 | webrecorderProcess.kill('SIGINT'); 242 | } 243 | } 244 | }); 245 | }; 246 | 247 | if (process.env.NODE_ENV === 'production') { 248 | const sourceMapSupport = require('source-map-support'); 249 | sourceMapSupport.install(); 250 | } 251 | 252 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 253 | require('electron-debug')(); 254 | 255 | const p = path.join(__dirname, '..', 'app', 'node_modules'); 256 | require('module').globalPaths.push(p); 257 | } 258 | 259 | const installExtensions = async () => { 260 | const installer = require('electron-devtools-installer'); 261 | const forceDownload = !!process.env.UPGRADE_EXTENSIONS; 262 | const extensions = [ 263 | 'REACT_DEVELOPER_TOOLS', 264 | 'REDUX_DEVTOOLS' 265 | ]; 266 | 267 | return Promise 268 | .all(extensions.map(name => installer.default(installer[name], forceDownload))) 269 | .catch(console.log); 270 | }; 271 | 272 | 273 | /** 274 | * Add event listeners... 275 | */ 276 | 277 | app.on('window-all-closed', () => { 278 | app.quit(); 279 | }); 280 | 281 | // Ensure new-window urls are just opened directly in the webview 282 | app.on('web-contents-created', (e, contents) => { 283 | if (contents.getType() == 'webview') { 284 | // Listen for any new window events on the webview 285 | contents.on('new-window', (e, url) => { 286 | e.preventDefault(); 287 | contents.loadURL(url); 288 | }) 289 | } 290 | }) 291 | 292 | 293 | app.on('will-finish-launching', function() { 294 | app.on('open-file', function(event, filePath) { 295 | event.preventDefault(); 296 | openNextFile = filePath; 297 | if (mainWindow) { 298 | openWarc(openNextFile); 299 | } 300 | }); 301 | }); 302 | 303 | 304 | app.on('ready', async () => { 305 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 306 | await installExtensions(); 307 | } 308 | 309 | createWindow(); 310 | registerOpenWarc(); 311 | 312 | const menuBuilder = new MenuBuilder(mainWindow); 313 | menuBuilder.buildMenu(); 314 | 315 | }); 316 | 317 | // renderer process communication 318 | ipcMain.on('async-call', (evt, arg) => { 319 | evt.sender.send('async-response', { 320 | config: wrConfig, 321 | stdout: debugOutput.join('
').replace(/\n/g, '
')}); 322 | }); 323 | 324 | 325 | ipcMain.on('sync-dat', (evt, datKey) => { 326 | console.log('launching dat with key:', datKey) 327 | const baseFolder = path.join(app.getPath('downloads'), 'webrecorder-dat'); 328 | 329 | fs.mkdir(baseFolder, (err) => { 330 | if (err === null || err.code === 'EEXIST') { 331 | const dlDir = path.join(baseFolder, datKey.replace('dat://', '')); 332 | const openWarcDir = () => { 333 | killProcess(); 334 | 335 | webrecorderProcess = child_process.spawn( 336 | webrecorderBin, 337 | ['--no-browser', '--loglevel', 'error', '--cache-dir', '_warc_cache', '--port', 0, '--coll-dir', dlDir], 338 | spawnOptions 339 | ); 340 | 341 | // catch any errors spawning webrecorder binary and add to debug info 342 | webrecorderProcess.on('error', (err) => { 343 | debugOutput.push(`Error spawning ${webrecorderBin} binary:\n ${err}\n\n`); 344 | }); 345 | 346 | // log any stderr notices 347 | webrecorderProcess.stderr.on('data', (buff) => { 348 | debugOutput.push(`stderr: ${buff.toString()}`); 349 | 350 | // clip line buffer 351 | if (debugOutput.length > 500) { 352 | debugOutput.shift(); 353 | } 354 | }); 355 | 356 | webrecorderProcess.stdout.on('data', (buff) => findPort(buff, datKey)); 357 | } 358 | 359 | Dat(dlDir, {key: datKey}, (err, dat) => { 360 | dat.joinNetwork((err) => { 361 | if (err) { 362 | throw err; 363 | } 364 | 365 | if (!dat.network.connected || !dat.network.connecting) { 366 | console.log('dat key not found'); 367 | //mainWindow.loadURL(`file://${__dirname}/app.html`); 368 | } 369 | }); 370 | 371 | mainWindow.webContents.send('initializing', { src: 'dat' }); 372 | 373 | const stats = dat.trackStats(); 374 | let handle = null; 375 | 376 | const up = () => { 377 | const s = stats.get(); 378 | clearTimeout(handle); 379 | 380 | mainWindow.webContents.send('indexProgress', { perct: Math.round((s.downloaded / s.length) * 100) }); 381 | 382 | if (s.length > 0 && s.downloaded === s.length) { 383 | handle = setTimeout(openWarcDir, 750); 384 | } 385 | } 386 | stats.on('update', up); 387 | }); 388 | } 389 | }); 390 | }); 391 | -------------------------------------------------------------------------------- /app/menu.js: -------------------------------------------------------------------------------- 1 | import { app, ipcMain, Menu, shell, BrowserWindow } from 'electron'; 2 | 3 | export default class MenuBuilder { 4 | mainWindow: BrowserWindow; 5 | 6 | constructor(mainWindow: BrowserWindow) { 7 | this.mainWindow = mainWindow; 8 | } 9 | 10 | buildMenu() { 11 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 12 | this.setupDevelopmentEnvironment(); 13 | } 14 | 15 | let template; 16 | 17 | if (process.platform === 'darwin') { 18 | template = this.buildDarwinTemplate(); 19 | } else { 20 | template = this.buildDefaultTemplate(); 21 | } 22 | 23 | const menu = Menu.buildFromTemplate(template); 24 | Menu.setApplicationMenu(menu); 25 | 26 | return menu; 27 | } 28 | 29 | setupDevelopmentEnvironment() { 30 | this.mainWindow.openDevTools(); 31 | this.mainWindow.webContents.on('context-menu', (e, props) => { 32 | const { x, y } = props; 33 | 34 | Menu 35 | .buildFromTemplate([{ 36 | label: 'Inspect element', 37 | click: () => { 38 | this.mainWindow.inspectElement(x, y); 39 | } 40 | }]) 41 | .popup(this.mainWindow); 42 | }); 43 | } 44 | 45 | buildDarwinTemplate() { 46 | const subMenuAbout = { 47 | label: 'Webrecorder Player', 48 | submenu: [ 49 | { label: 'About Webrecorder Player', click: () => { this.mainWindow.webContents.send('change-location', '/help'); } }, 50 | { type: 'separator' }, 51 | { label: 'Services', submenu: [] }, 52 | { type: 'separator' }, 53 | { label: 'Hide Webrecorder Player', accelerator: 'Command+H', selector: 'hide:' }, 54 | { label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }, 55 | { label: 'Show All', selector: 'unhideAllApplications:' }, 56 | { type: 'separator' }, 57 | { label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } } 58 | ] 59 | }; 60 | 61 | const subMenuFile = { 62 | label: 'File', 63 | submenu: [ 64 | { label: 'Open...', accelerator: 'Command+O', click: () => { this.mainWindow.webContents.send('open-warc-dialog'); }} 65 | ] 66 | }; 67 | const subMenuEdit = { 68 | label: 'Edit', 69 | submenu: [ 70 | { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, 71 | { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, 72 | { type: 'separator' }, 73 | { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, 74 | { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, 75 | { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, 76 | { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' } 77 | ] 78 | }; 79 | const subMenuViewDev = { 80 | label: 'View', 81 | submenu: [ 82 | { label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } }, 83 | { label: 'Toggle App Developer Tools', accelerator: 'Alt+Ctrl+J', click: () => { this.mainWindow.toggleDevTools(); }}, 84 | { label: 'Toggle Page Developer Tools', accelerator: 'Alt+Ctrl+I', click: () => { this.mainWindow.webContents.send('toggle-devtools'); }}, 85 | { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } } 86 | ] 87 | }; 88 | const subMenuViewProd = { 89 | label: 'View', 90 | submenu: [ 91 | { label: 'Toggle Page Developer Tools', accelerator: 'Alt+Ctrl+I', click: () => { this.mainWindow.webContents.send('toggle-devtools'); }}, 92 | { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } } 93 | ] 94 | }; 95 | const subMenuWindow = { 96 | label: 'Window', 97 | submenu: [ 98 | { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' }, 99 | { type: 'separator' }, 100 | { label: 'Bring All to Front', selector: 'arrangeInFront:' } 101 | ] 102 | }; 103 | const subMenuHelp = { 104 | label: 'Help', 105 | submenu: [ 106 | { label: 'More Info', click: () => { this.mainWindow.webContents.send('change-location', '/help'); } }, 107 | { label: 'Webrecorder.io', click() { shell.openExternal('https://webrecorder.io'); } }, 108 | ] 109 | }; 110 | 111 | const subMenuView = process.env.NODE_ENV === 'development' 112 | ? subMenuViewDev 113 | : subMenuViewProd; 114 | 115 | return [ 116 | subMenuAbout, 117 | subMenuFile, 118 | subMenuEdit, 119 | subMenuView, 120 | subMenuWindow, 121 | subMenuHelp 122 | ]; 123 | } 124 | 125 | buildDefaultTemplate() { 126 | const templateDefault = [ 127 | { 128 | label: '&File', 129 | submenu: [ 130 | { label: 'Open', accelerator: 'Ctrl+O', click: () => { this.mainWindow.webContents.send('open-warc-dialog'); } } 131 | ] 132 | }, 133 | { 134 | label: '&View', 135 | submenu: 136 | (process.env.NODE_ENV === 'development') ? [ 137 | { label: 'Reload', accelerator: 'Ctrl+R', click: () => { this.mainWindow.webContents.reload(); } }, 138 | { label: 'Toggle Page Developer Tools', accelerator: 'Alt+Ctrl+I', click: () => { this.mainWindow.webContents.send('toggle-devtools'); }}, 139 | { label: 'Toggle App Developer Tools', accelerator: 'Alt+Ctrl+J', click: () => { this.mainWindow.toggleDevTools(); } }, 140 | { label: 'Toggle Full Screen', accelerator: 'F11', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());} } 141 | ] : 142 | [ 143 | { label: 'Toggle Page Developer Tools', accelerator: 'Alt+Ctrl+I', click: () => { this.mainWindow.webContents.send('toggle-devtools'); } }, 144 | { label: 'Toggle Full Screen', accelerator: 'F11', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } } 145 | ] 146 | }, 147 | { 148 | label: '&Help', 149 | submenu: [ 150 | { label: 'Learn More', click: () => { this.mainWindow.webContents.send('change-location', '/help'); } }, 151 | { label: 'Webrecorder.io', click() { shell.openExternal('https://webrecorder.io'); } 152 | }] 153 | } 154 | ]; 155 | 156 | return templateDefault; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrecorder-player", 3 | "productName": "Webrecorder Player", 4 | "version": "1.8.0", 5 | "description": "Webrecorder Player -- Standalone Web Archive Browser App", 6 | "main": "./main.prod.js", 7 | "scripts": { 8 | "postinstall": "npm rebuild --runtime=electron --target=1.6.6 --disturl=https://atom.io/download/atom-shell --build-from-source" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/preload.js: -------------------------------------------------------------------------------- 1 | const { ipcRenderer } = require('electron'); 2 | 3 | const wr_msg_handler = '___$wr_msg_handler___$$'; 4 | 5 | class IPCHandler { 6 | constructor() { 7 | this.on_message = null; 8 | 9 | ipcRenderer.on('wr-message', (event) => { 10 | if (this.on_message) { 11 | this.on_message(event.data); 12 | } 13 | }); 14 | } 15 | 16 | send(msg) { 17 | ipcRenderer.sendToHost('wr-message', msg); 18 | } 19 | } 20 | 21 | window[wr_msg_handler] = new IPCHandler(); 22 | 23 | 24 | document.addEventListener('drop', (evt) => { 25 | evt.preventDefault(); 26 | 27 | const filename = evt.dataTransfer.files[0].path; 28 | const state = { 29 | wb_type: 'open', 30 | filename 31 | }; 32 | 33 | ipcRenderer.sendToHost('open', state); 34 | }); 35 | 36 | document.addEventListener('dragover', (evt) => { 37 | evt.preventDefault(); 38 | evt.stopPropagation(); 39 | }); 40 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "10" 3 | matrix: 4 | - platform: x32 5 | ARCH_SUFFIX: '-x86' 6 | ARCH_ARG: '--ia32' 7 | 8 | - platform: x64 9 | ARCH_SUFFIX: '-x86_64' 10 | ARCH_ARG: '--x64' 11 | 12 | install: 13 | - git submodule -q update --init 14 | - ps: Install-Product node $env:nodejs_version 15 | - yarn install 16 | 17 | - ps: | 18 | $replace_name = '"name": "$1' + "-win$env:ARCH_SUFFIX" + '"' 19 | (Get-Content package.json) -replace '"name": "(.*)"', $replace_name | Set-Content package.json 20 | (Get-Content .\app\package.json) -replace '"name": "(.*)"', $replace_name | Set-Content .\app\package.json 21 | $bucket = "https://s3.amazonaws.com/webrecorder-builds/webrecorder-player" 22 | $destination = "$env:APPVEYOR_BUILD_FOLDER\python-binaries\webrecorder_player.exe" 23 | $current_branch = "$bucket/$env:APPVEYOR_REPO_BRANCH/webrecorder-player-win-$env:platform.exe" 24 | $develop_branch = "$bucket/develop/webrecorder-player-win-$env:platform.exe" 25 | 26 | $status = try { (invoke-webrequest -Uri $current_branch -Method Head).statuscode } catch { 27 | $_.Exception.Response.StatusCode.Value__ 28 | } 29 | 30 | if ($status -eq "200") { 31 | Write-Host "downloading webrecorder artifact: $current_branch" 32 | Invoke-WebRequest -Uri $current_branch -OutFile $destination 33 | 34 | } 35 | else { 36 | Write-Host "downloading webrecorder artifact: $develop_branch" 37 | Invoke-WebRequest -Uri $develop_branch -OutFile $destination 38 | } 39 | - ps: | 40 | $flash_plugin = "https://s3.amazonaws.com/webrecorder-builds/flashplugin/pepflashplayer$env:ARCH_SUFFIX.dll" 41 | Invoke-WebRequest -Uri $flash_plugin -OutFile "$env:APPVEYOR_BUILD_FOLDER\plugins\pepflashplayer.dll" 42 | - ps: "& $destination '-v'" 43 | 44 | build_script: 45 | - ps: | 46 | if ($env:APPVEYOR_REPO_BRANCH -eq "master" -or $env:APPVEYOR_REPO_BRANCH -eq "prerelease") { 47 | npm run release -- $env:ARCH_ARG 48 | } 49 | else { 50 | npm run dist -- $env:ARCH_ARG 51 | } 52 | - ps: "move dist/webrecorder-player*.* $env:APPVEYOR_BUILD_FOLDER/" 53 | 54 | test: off 55 | 56 | artifacts: 57 | - path: "webrecorder-player*.*" 58 | name: webrecorder-player 59 | 60 | deploy: 61 | provider: S3 62 | region: $(aws_s3_region) 63 | bucket: $(aws_s3_bucket) 64 | access_key_id: $(aws_access_key_id) 65 | secret_access_key: $(aws_secret_access_key) 66 | folder: electron-player/$(APPVEYOR_REPO_BRANCH) 67 | artifact: webrecorder-player 68 | -------------------------------------------------------------------------------- /build-frontend.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process'); 2 | const path = require('path'); 3 | 4 | const options = {cwd: path.join('webrecorder', 'frontend'), stdio: 'inherit', shell: true}; 5 | 6 | spawn('yarn install', [], options).on('exit', (code) => { 7 | if (code != 0) { 8 | return process.exit(code); 9 | } 10 | 11 | spawn('yarn run build-player', [], options).on('exit', (code2) => { 12 | return process.exit(code2); 13 | }); 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /build-macos.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir build.standalone 4 | cd build.standalone 5 | 6 | printf "\e[91m# create virtualenv\e[0m\n" 7 | virtualenv -p python3.5 venv && source venv/bin/activate 8 | 9 | printf "\e[91m# clone webrecorder@develop\e[0m\n" 10 | git clone https://github.com/webrecorder/webrecorder.git 11 | cd webrecorder 12 | git fetch 13 | git checkout origin/develop 14 | 15 | printf "\e[91m# build\e[0m\n" 16 | cd webrecorder 17 | pip install Jinja2==2.8 # latest Jinja breaks Pyinstaller! 18 | python setup.py install 19 | ./webrecorder/standalone/build_player.sh 20 | cp webrecorder/standalone/dist/webrecorder_player ../../../python-binaries/webrecorder 21 | # cd ../../ 22 | # rm -fr build.standalone 23 | -------------------------------------------------------------------------------- /build-wr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | pushd webrecorder/webrecorder 5 | python3 setup.py install 6 | bash ./webrecorder/standalone/build_player.sh 7 | popd 8 | 9 | mv ./webrecorder/webrecorder/webrecorder/standalone/dist/webrecorder_player ./python-binaries/ 10 | chmod a+x ./python-binaries/webrecorder_player 11 | 12 | -------------------------------------------------------------------------------- /build/background.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/background.tiff -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icon.ico -------------------------------------------------------------------------------- /build/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/1024x1024.png -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/32x32.png -------------------------------------------------------------------------------- /build/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/512x512.png -------------------------------------------------------------------------------- /build/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/build/icons/64x64.png -------------------------------------------------------------------------------- /internals/scripts/CheckNodeEnv.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function CheckNodeEnv(expectedEnv: string) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log(chalk.whiteBright.bgRed.bold( 10 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 11 | )); 12 | process.exit(2); 13 | } 14 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrecorderplayer", 3 | "productName": "Webrecorder Player", 4 | "version": "1.8.0", 5 | "description": "Webrecorder Player -- Standalone Web Archive Browser App", 6 | "scripts": { 7 | "build": "concurrently \"npm run build-main\" \"npm run build-frontend\"", 8 | "build-frontend": "node build-frontend.js", 9 | "build-frontend-dev": "cd webrecorder/frontend && yarn install; yarn run build-player-dev", 10 | "build-main": "cross-env NODE_ENV=production DEBUG_PROD=false node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack/webpack.config.js --display-error-details --colors", 11 | "start": "cross-env NODE_ENV=production electron ./app", 12 | "start-dev": "cross-env NODE_ENV=development electron -r babel-register ./app/main.dev", 13 | "pack": "electron-builder --dir", 14 | "release": "npm run build && electron-builder", 15 | "dist": "npm run build && electron-builder --publish never", 16 | "postinstall": "electron-builder install-app-deps" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/webrecorder/webrecorder-player.git" 21 | }, 22 | "keywords": [ 23 | "warc", 24 | "webarchives", 25 | "electron", 26 | "pywb", 27 | "webrecorder" 28 | ], 29 | "author": "webrecorder/rhizome", 30 | "license": "Apache", 31 | "bugs": { 32 | "url": "https://github.com/webrecorder/webrecorder-player/issues" 33 | }, 34 | "homepage": "https://github.com/webrecorder/webrecorder-player#readme", 35 | "build": { 36 | "productName": "Webrecorder Player", 37 | "asar": true, 38 | "appId": "org.webrecorder.webrecorderplayer", 39 | "artifactName": "${name}-${version}.${ext}", 40 | "files": [ 41 | "dist/", 42 | "app.html", 43 | "static/", 44 | "main.prod.js", 45 | "package.json", 46 | "preload.js" 47 | ], 48 | "extraResources": [ 49 | "python-binaries/", 50 | "plugins" 51 | ], 52 | "mac": { 53 | "category": "public.app-category.utilities", 54 | "target": "dmg" 55 | }, 56 | "linux": { 57 | "category": "Archiving;Utility;", 58 | "target": [ 59 | "AppImage" 60 | ] 61 | }, 62 | "win": { 63 | "target": "portable" 64 | }, 65 | "directories": { 66 | "buildResources": "build", 67 | "output": "dist" 68 | }, 69 | "fileAssociations": [ 70 | { 71 | "ext": "warc", 72 | "name": "WARC File", 73 | "description": "WARC File", 74 | "role": "Viewer" 75 | }, 76 | { 77 | "ext": "warc.gz", 78 | "name": "WARC File", 79 | "description": "WARC File", 80 | "role": "Viewer" 81 | }, 82 | { 83 | "ext": "arc", 84 | "name": "ARC File", 85 | "description": "ARC File", 86 | "role": "Viewer" 87 | }, 88 | { 89 | "ext": "arc.gz", 90 | "name": "ARC File", 91 | "description": "ARC File", 92 | "role": "Viewer" 93 | }, 94 | { 95 | "ext": "har", 96 | "name": "HAR File", 97 | "description": "HAR File", 98 | "role": "Viewer" 99 | } 100 | ] 101 | }, 102 | "dependencies": { 103 | "babel-core": "^6.24.1", 104 | "babel-loader": "^7.1.0", 105 | "babel-preset-env": "^1.7.0", 106 | "babel-preset-react": "^6.24.1", 107 | "babel-preset-stage-0": "^6.24.1", 108 | "babel-register": "^6.24.1", 109 | "babili-webpack-plugin": "^0.1.2", 110 | "concurrently": "^4.1.0", 111 | "cross-env": "^5.2.0", 112 | "dat-node": "^3.5.13", 113 | "electron": "6.1.4", 114 | "electron-builder": "21.2.0", 115 | "electron-debug": "^2.0.0", 116 | "electron-devtools-installer": "^2.2.4", 117 | "electron-publisher-s3": "^20.17.2", 118 | "electron-window-state": "^5.0.3", 119 | "node-sass": "^4.10.0", 120 | "webpack": "3.10.0" 121 | }, 122 | "devEngines": { 123 | "node": ">==8.12.0", 124 | "npm": ">=4.x", 125 | "yarn": ">=0.21.3" 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # External Plugins 2 | 3 | Copy external plugins into this directory. 4 | 5 | ## PepperFlash 6 | To enable flash, locate the plugin and copy it into this folder (`\plugins`). 7 | 8 | #### linux 9 | 10 | get the tar.gz from [https://get.adobe.com/flashplayer](https://get.adobe.com/flashplayer) and copy here `libpepflashplayer.so` 11 | 12 | #### windows 13 | 14 | C:\Users\${USER}\AppData\Local\Google\Chrome\User Data\PepperFlash\${VERSION}/pepflashplayer.dll 15 | 16 | #### macos 17 | 18 | /Users/${USER}/Library/Application Support/Google/Chrome Canary/PepperFlash/${VERSION}/PepperFlashPlayer.plugin 19 | -------------------------------------------------------------------------------- /python-binaries/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrecorder/webrecorder-player/520f82e586917d1dd9e0ca8b06d015edae1c6bf1/python-binaries/.gitignore -------------------------------------------------------------------------------- /walkthrough.md: -------------------------------------------------------------------------------- 1 | ## Webrecorder Player Walkthrough 2 | 3 | 4 | Once Webrecorder Player launches, you can choose a local web archive file (WARC, ARC, or HAR) after clicking **Load Web Archive** 5 | 6 | ![Loading a WARC](https://s3.amazonaws.com/wr-demo-assets/gif_01_Load.gif "Webrecorder Player Loading") 7 | 8 | 9 | Once the archive is fully loaded (it may take a bit if it is a large file), you will be taken to the collection page, listing key *bookmarks* in the archive. If the WARC was downloaded from Webrecorder, the bookmarks should appear the same as they did online. 10 | 11 | From the collection page, you may select any of the bookmarks to start browsing the archive. The navigation bar at the top center can be used to switch to page through available bookmarks, same as when browsing on https://webrecorder.io/ 12 | 13 | The back/forward/refresh buttons at the top left provide same functionality as in a regular browser. 14 | 15 | ![Browsing a WARC](https://s3.amazonaws.com/wr-demo-assets/gif_02_open.gif "Browsing WARC") 16 | 17 | 18 | You can even disconnect from the internet and continue browsing to verify that your browsing is entirely offline! 19 | 20 | 21 | -------------------------------------------------------------------------------- /webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import BabiliPlugin from 'babili-webpack-plugin'; 8 | 9 | const projectDir = path.resolve(__dirname, '../'); 10 | 11 | 12 | export default { 13 | target: 'electron-main', 14 | 15 | entry: 'main.dev', 16 | 17 | module: { 18 | rules: [{ 19 | test: /\.jsx?$/, 20 | exclude: /node_modules/, 21 | use: { 22 | loader: 'babel-loader', 23 | options: { 24 | cacheDirectory: true 25 | } 26 | } 27 | }] 28 | }, 29 | 30 | output: { 31 | path: path.join(projectDir, 'app'), 32 | filename: 'main.prod.js' 33 | }, 34 | 35 | resolve: { 36 | extensions: ['.js', '.jsx', '.json'], 37 | modules: [ 38 | path.join(projectDir, 'app'), 39 | 'node_modules', 40 | ], 41 | }, 42 | 43 | plugins: [ 44 | /** 45 | * Babli is an ES6+ aware minifier based on the Babel toolchain (beta) 46 | */ 47 | new BabiliPlugin(), 48 | 49 | /** 50 | * Create global constants which can be configured at compile time. 51 | * 52 | * Useful for allowing different behaviour between development builds and 53 | * release builds 54 | * 55 | * NODE_ENV should be production so that modules do not perform certain 56 | * development checks 57 | */ 58 | new webpack.DefinePlugin({ 59 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'), 60 | 'process.env.DEBUG_PROD': JSON.stringify(process.env.DEBUG_PROD || 'false') 61 | }) 62 | ], 63 | 64 | /** 65 | * Disables webpack processing of __dirname and __filename. 66 | * If you run the bundle in node.js it falls back to these values of node.js. 67 | * https://github.com/webpack/webpack/issues/2010 68 | */ 69 | node: { 70 | __dirname: false, 71 | __filename: false 72 | }, 73 | }; 74 | --------------------------------------------------------------------------------