├── .babelrc ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .stylelintrc ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app ├── .eslintrc ├── actions │ └── source.js ├── app.global.css ├── app.html ├── app.icns ├── components │ └── .githook ├── containers │ ├── App.js │ ├── Channel.js │ ├── Frame.js │ ├── Root.js │ ├── ToolBar.js │ └── Video.js ├── index.js ├── main.dev.js ├── menu.js ├── package-lock.json ├── package.json ├── reducers │ ├── index.js │ └── source.js ├── routes.js ├── store │ ├── configureStore.dev.js │ ├── configureStore.js │ └── configureStore.prod.js ├── utils │ ├── .gitkeep │ └── fetch.js └── yarn.lock ├── appveyor.yml ├── flow-typed └── module_vx.x.x.js ├── internals ├── flow │ ├── CSSModule.js.flow │ └── WebpackAsset.js.flow ├── img │ ├── eslint-padded-90.png │ ├── eslint-padded.png │ ├── eslint.png │ ├── flow-padded-90.png │ ├── flow-padded.png │ ├── flow.png │ ├── jest-padded-90.png │ ├── jest-padded.png │ ├── jest.png │ ├── js-padded.png │ ├── js.png │ ├── npm.png │ ├── react-padded-90.png │ ├── react-padded.png │ ├── react-router-padded-90.png │ ├── react-router-padded.png │ ├── react-router.png │ ├── react.png │ ├── redux-padded-90.png │ ├── redux-padded.png │ ├── redux.png │ ├── webpack-padded-90.png │ ├── webpack-padded.png │ ├── webpack.png │ ├── yarn-padded-90.png │ ├── yarn-padded.png │ └── yarn.png ├── mocks │ └── fileMock.js └── scripts │ ├── CheckBuiltsExist.js │ ├── CheckNativeDep.js │ ├── CheckNodeEnv.js │ ├── CheckPortInUse.js │ ├── ElectronRebuild.js │ └── RunTests.js ├── package-lock.json ├── package.json ├── resources ├── icon.icns ├── icon.ico ├── icon.png ├── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ └── 96x96.png ├── showcase01.jpg ├── showcase02.jpg ├── showcase03.jpg ├── showcase04.jpg └── viplist.json ├── test ├── .eslintrc ├── actions │ ├── __snapshots__ │ │ └── counter.spec.js.snap │ └── counter.spec.js ├── components │ ├── Counter.spec.js │ └── __snapshots__ │ │ └── Counter.spec.js.snap ├── containers │ └── CounterPage.spec.js ├── e2e │ └── e2e.spec.js ├── example.js └── reducers │ ├── __snapshots__ │ └── counter.spec.js.snap │ └── counter.spec.js ├── webpack.config.base.js ├── webpack.config.eslint.js ├── webpack.config.main.prod.js ├── webpack.config.renderer.dev.dll.js ├── webpack.config.renderer.dev.js ├── webpack.config.renderer.prod.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { "node": 7 }, 5 | "useBuiltIns": true 6 | }], 7 | "stage-0", 8 | "react" 9 | ], 10 | "plugins": ["add-module-exports", 11 | ["import", 12 | { 13 | "libraryName": "antd", 14 | "style": "css" 15 | } 16 | ] 17 | ], 18 | "env": { 19 | "production": { 20 | "presets": ["react-optimize"], 21 | "plugins": ["dev-expression"] 22 | }, 23 | "development": { 24 | "plugins": [ 25 | "transform-class-properties", 26 | "transform-es2015-classes", 27 | ["flow-runtime", { 28 | "assert": true, 29 | "annotate": true 30 | }] 31 | ] 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | app/node_modules 30 | 31 | # OSX 32 | .DS_Store 33 | 34 | # flow-typed 35 | flow-typed/npm/* 36 | !flow-typed/npm/module_vx.x.x.js 37 | 38 | # App packaged 39 | release 40 | app/main.prod.js 41 | app/main.prod.js.map 42 | app/renderer.prod.js 43 | app/renderer.prod.js.map 44 | app/style.css 45 | app/style.css.map 46 | dist 47 | dll 48 | main.js 49 | main.js.map 50 | 51 | .idea 52 | npm-debug.log.* 53 | .*.dockerfile -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | app/node_modules 30 | 31 | # OSX 32 | .DS_Store 33 | 34 | # flow-typed 35 | flow-typed/npm/* 36 | !flow-typed/npm/module_vx.x.x.js 37 | 38 | # App packaged 39 | release 40 | app/main.prod.js 41 | app/main.prod.js.map 42 | app/renderer.prod.js 43 | app/renderer.prod.js.map 44 | app/style.css 45 | app/style.css.map 46 | dist 47 | dll 48 | main.js 49 | main.js.map 50 | 51 | .idea 52 | npm-debug.log.* 53 | __snapshots__ 54 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "sourceType": "module", 5 | "allowImportExportEverywhere": true 6 | }, 7 | "extends": "airbnb", 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "arrow-parens": ["off"], 14 | "compat/compat": "error", 15 | "consistent-return": "off", 16 | "comma-dangle": "off", 17 | "generator-star-spacing": "off", 18 | "import/no-unresolved": "error", 19 | "import/no-extraneous-dependencies": "off", 20 | "jsx-a11y/anchor-is-valid": "off", 21 | "no-console": "off", 22 | "no-use-before-define": "off", 23 | "no-multi-assign": "off", 24 | "promise/param-names": "error", 25 | "promise/always-return": "error", 26 | "promise/catch-or-return": "error", 27 | "promise/no-native": "off", 28 | "react/sort-comp": ["error", { 29 | "order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"] 30 | }], 31 | "react/jsx-no-bind": "off", 32 | "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }], 33 | "react/prefer-stateless-function": "off" 34 | }, 35 | "plugins": [ 36 | "flowtype", 37 | "import", 38 | "promise", 39 | "compat", 40 | "react" 41 | ], 42 | "settings": { 43 | "import/resolver": { 44 | "webpack": { 45 | "config": "webpack.config.eslint.js" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/* 3 | /app/main.prod.js 4 | /app/main.prod.js.map 5 | /app/dist/.* 6 | /resources/.* 7 | /release/.* 8 | /dll/.* 9 | /release/.* 10 | /git/.* 11 | 12 | [include] 13 | 14 | [libs] 15 | 16 | [options] 17 | esproposal.class_static_fields=enable 18 | esproposal.class_instance_fields=enable 19 | esproposal.export_star_as=enable 20 | module.name_mapper.extension='css' -> '/internals/flow/CSSModule.js.flow' 21 | module.name_mapper.extension='styl' -> '/internals/flow/CSSModule.js.flow' 22 | module.name_mapper.extension='scss' -> '/internals/flow/CSSModule.js.flow' 23 | module.name_mapper.extension='png' -> '/internals/flow/WebpackAsset.js.flow' 24 | module.name_mapper.extension='jpg' -> '/internals/flow/WebpackAsset.js.flow' 25 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe 26 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jpg binary 4 | *.jpeg binary 5 | *.ico binary 6 | *.icns binary 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | app/node_modules 30 | 31 | # OSX 32 | .DS_Store 33 | 34 | # flow-typed 35 | flow-typed/npm/* 36 | !flow-typed/npm/module_vx.x.x.js 37 | 38 | # App packaged 39 | release 40 | app/main.prod.js 41 | app/main.prod.js.map 42 | app/renderer.prod.js 43 | app/renderer.prod.js.map 44 | app/style.css 45 | app/style.css.map 46 | dist 47 | dll 48 | main.js 49 | main.js.map 50 | 51 | .idea 52 | npm-debug.log.* 53 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 8 7 | - 7 8 | 9 | cache: 10 | yarn: true 11 | directories: 12 | - node_modules 13 | - app/node_modules 14 | 15 | addons: 16 | apt: 17 | sources: 18 | - ubuntu-toolchain-r-test 19 | packages: 20 | - g++-4.8 21 | - icnsutils 22 | - graphicsmagick 23 | - xz-utils 24 | - xorriso 25 | 26 | install: 27 | - export CXX="g++-4.8" 28 | - yarn 29 | - cd app && yarn && cd .. 30 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" 31 | 32 | before_script: 33 | - export DISPLAY=:99.0 34 | - sh -e /etc/init.d/xvfb start & 35 | - sleep 3 36 | 37 | script: 38 | - node --version 39 | - yarn lint 40 | - yarn package 41 | - yarn test 42 | - yarn test-e2e 43 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "javascript.validate.enable": false, 3 | "flow.useNPMPackagedFlow": true, 4 | "search.exclude": { 5 | ".git": true, 6 | ".eslintcache": true, 7 | "app/dist": true, 8 | "app/main.prod.js": true, 9 | "app/main.prod.js.map": true, 10 | "bower_components": true, 11 | "dll": true, 12 | "flow-typed": true, 13 | "release": true, 14 | "node_modules": true, 15 | "npm-debug.log.*": true, 16 | "test/**/__snapshots__": true, 17 | "yarn.lock": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * 2018.5.5 2 | 3 | 修改页面样式,当全屏时隐藏掉左侧菜单栏和顶部接口切换栏 4 | 5 | * 2018.07.13 6 | 7 | 修改线路下拉框不能出现滚动条的 bug 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present C. T. Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### i视频 2 | 3 | #### 产品介绍 4 | 5 | > 基于 Electron 开发的跨平台客户端版本的视频播放器,该播放器包括国内主流视频平台视频资源,你不用去单独下载各个平台的客户端,只需要使用这一个客户端就能查看所有平台的视频,并且内置了各大视频网站 VIP 资源。 6 | 7 | #### 使用方法 8 | 9 | 1. 下载客户端 10 | * [Mac](https://github.com/phobal/ivideo/releases/download/v1.1.4/ivideo-1.1.4.dmg.zip) 11 | * [Windows](https://github.com/phobal/ivideo/releases/download/v1.1.4/ivideo.Setup.1.1.4.exe.zip) 12 | * [Linux](https://github.com/phobal/ivideo/releases/download/1.0.0/linux-unpacked.v1.0.0.zip) 13 | 14 | 2. 选择视频资源 15 | 16 | 比方说看腾讯视频上的 VIP 才能看的《下一站,别离》 17 | 18 | ![](./resources/showcase01.jpg) 19 | 20 | 点击进去以后提示需要开通VIP才能看 21 | 22 | ![](./resources/showcase02.jpg) 23 | 24 | 3. 选择资源播放接口 25 | 26 | ![](./resources/showcase03.jpg) 27 | 28 | 点击【确定】按钮就可以播放了,如果遇到无法播放的情况,请多换几条线路试试 29 | 30 | ![](./resources/showcase04.jpg) 31 | 32 | ### 技术栈 33 | 34 | * Electron 35 | * React 36 | * Redux 37 | 38 | ### 如何启动 39 | 40 | > node version >= 7.6 41 | 42 | 1. clone 项目到本地 43 | 44 | ``` bash 45 | 46 | git clone https://github.com/phobal/ivideo.git 47 | 48 | ``` 49 | 50 | 2. 进入项目 ` cd ivideo` 51 | 3. 安装依赖 `yarn install`(如果没有的话,请全局安装yarn, `npm i yarn -g`) 52 | 4. 打开开发环境 `yarn start` 53 | 54 | ### 如何编译 55 | 56 | * 编译全平台 ` yarn package-all` 57 | * 编译当前平台 `yarn package` 58 | * windows: `yarn package-win` 59 | * Linux `yarn package-linux` 60 | 61 | 编译出来的包都放在 `release` 目录下 62 | 63 | 该项目是基于 [electron-react-boilerplate](https://github.com/chentsulin/electron-react-boilerplate) 脚手架 进行创建,感谢 @[chentsulin](https://github.com/chentsulin) 64 | 65 | 66 | # 最后请大家低调使用,祝大家看得舒心 67 | ## 本项目仅作为个人学习用途,如有侵权请联系我删除该仓库 68 | -------------------------------------------------------------------------------- /app/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "flowtype/boolean-style": ["error", "boolean"], 4 | "flowtype/define-flow-type": "warn", 5 | "flowtype/delimiter-dangle": ["error", "never"], 6 | "flowtype/generic-spacing": ["error", "never"], 7 | "flowtype/no-primitive-constructor-types": "error", 8 | "flowtype/no-weak-types": "warn", 9 | "flowtype/object-type-delimiter": ["error", "comma"], 10 | "flowtype/require-parameter-type": "off", 11 | "flowtype/require-return-type": "off", 12 | "flowtype/require-valid-file-annotation": "off", 13 | "flowtype/semi": ["error", "always"], 14 | "flowtype/space-after-type-colon": ["error", "always"], 15 | "flowtype/space-before-generic-bracket": ["error", "never"], 16 | "flowtype/space-before-type-colon": ["error", "never"], 17 | "flowtype/union-intersection-spacing": ["error", "always"], 18 | "flowtype/use-flow-type": "error", 19 | "flowtype/valid-syntax": "error" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/actions/source.js: -------------------------------------------------------------------------------- 1 | import * as api from '../utils/fetch'; 2 | 3 | export function getAllVideoSource() { 4 | return (dispatch) => { 5 | api.source.getAllVideoSource().then((res) => 6 | dispatch({ 7 | type: 'GETALLVIDEOSOURCE', 8 | payload: res.data 9 | })); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /app/app.global.css: -------------------------------------------------------------------------------- 1 | /* 2 | * @NOTE: Prepend a `~` to css file paths that are in your node_modules 3 | * See https://github.com/webpack-contrib/sass-loader#imports 4 | */ 5 | @import "~font-awesome/css/font-awesome.css"; 6 | @import "~rc-menu/assets/index.css"; 7 | /* @import "~antd/lib/style/index.css"; */ 8 | @import '~rc-select/assets/index.css'; 9 | * { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | body { 14 | position: relative; 15 | color:#000; 16 | height: 100vh; 17 | /* background-color: #232c39; */ 18 | /* background-image: linear-gradient(45deg, rgba(0, 216, 255, 0.5) 10%, rgba(0, 1, 127, 0.7)); */ 19 | font-family: Arial, Helvetica, Helvetica Neue, serif; 20 | overflow-y: hidden; 21 | } 22 | 23 | h2 { 24 | margin: 0; 25 | font-size: 2.25rem; 26 | font-weight: bold; 27 | letter-spacing: -0.025em; 28 | color: #fff; 29 | } 30 | 31 | p { 32 | font-size: 24px; 33 | } 34 | 35 | li { 36 | list-style: none; 37 | } 38 | 39 | a { 40 | color: white; 41 | opacity: 0.75; 42 | text-decoration: none; 43 | } 44 | 45 | a:hover { 46 | opacity: 1; 47 | text-decoration: none; 48 | cursor: pointer; 49 | } 50 | .rc-select-dropdown-menu { 51 | max-height: 400px; 52 | } -------------------------------------------------------------------------------- /app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | i视频 6 | 17 | 18 | 19 |
20 | 45 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/app/app.icns -------------------------------------------------------------------------------- /app/components/.githook: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/app/components/.githook -------------------------------------------------------------------------------- /app/containers/App.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import * as React from 'react'; 3 | 4 | type Props = { 5 | children: React.Node 6 | }; 7 | 8 | export default class App extends React.Component { 9 | props: Props; 10 | 11 | render() { 12 | return ( 13 |
14 | {this.props.children} 15 |
16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/containers/Channel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu, { Item as MenuItem } from 'rc-menu'; 3 | 4 | const Channel = ({ channel, handleSwitchChannel }) => { 5 | const item = channel.map((d) => {d.name}); 6 | return {item}; 7 | }; 8 | 9 | export default Channel; 10 | -------------------------------------------------------------------------------- /app/containers/Frame.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react' 3 | 4 | import Channel from './Channel' 5 | import ToolBar from './ToolBar' 6 | 7 | export const Frame = ({ 8 | onComeback, 9 | onSourceSelected, 10 | onSwitchSource, 11 | handleSwitchChannel, 12 | channel, 13 | url, 14 | freeUrl, 15 | title, 16 | isFullScreen, 17 | children 18 | }) => { 19 | const isHiddenStyle = isFullScreen ? { display: 'none' } : { display: 'flex' } 20 | return ( 21 |
22 |
23 | 24 |
25 |
33 |
34 | 41 |
42 | {children} 43 |
44 |
45 | ) 46 | } 47 | 48 | export default Frame 49 | -------------------------------------------------------------------------------- /app/containers/Root.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React, { Component } from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { ConnectedRouter } from 'react-router-redux'; 5 | import Routes from '../routes'; 6 | 7 | type Props = { 8 | store: {}, 9 | history: {} 10 | }; 11 | 12 | export default class Root extends Component { 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/containers/ToolBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Select, { Option } from 'rc-select'; 3 | // import { Icon, Select, Button } from 'antd'; 4 | 5 | const ToolBar = ({ onComeback, onSwitchSource, onSourceSelected, freeUrl, title }) => { 6 | const options = freeUrl.map(d => { 7 | return ( 8 | 9 | ) 10 | }) 11 | return ( 12 |
13 |
返回
14 | {title} 15 |
18 | 25 |
确定
29 |
30 |
31 | ) 32 | } 33 | 34 | export default ToolBar; -------------------------------------------------------------------------------- /app/containers/Video.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React, { PureComponent } from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { bindActionCreators } from 'redux'; 5 | import { webview, ipcRenderer } from 'electron'; 6 | 7 | import Channel from './Channel'; 8 | import ToolBar from './ToolBar'; 9 | import Frame from './Frame'; 10 | import * as sourceActions from '../actions/source'; 11 | 12 | class VideoPlay extends PureComponent { 13 | constructor(props) { 14 | super(props); 15 | this.handleSwitchChannel = this.handleSwitchChannel.bind(this); 16 | this.onComeback = this.onComeback.bind(this); 17 | this.onSourceSelected = this.onSourceSelected.bind(this); 18 | this.onSwitchSource = this.onSwitchSource.bind(this); 19 | } 20 | state = { 21 | channel: [], 22 | url: 'https://v.qq.com', 23 | freeUrl: [], 24 | selectedUrl: 'http://vip.jlsprh.com/index.php?url=', 25 | isFullScreen: false 26 | } 27 | componentDidMount() { 28 | this.props.actions.getAllVideoSource(); 29 | const webView = this.webview; 30 | webView.addEventListener('dom-ready', () => { 31 | this.setTitle(); 32 | }); 33 | webView.addEventListener('new-window', (obj) => { 34 | this.setState({ 35 | url: `${obj.url}` 36 | }); 37 | }); 38 | webView.addEventListener('will-navigate', (obj) => { 39 | this.setState({ 40 | url: `${obj.url}` 41 | }); 42 | }); 43 | ipcRenderer.on('enter-full-screen', (e, msg) => { 44 | this.setState({ 45 | isFullScreen: msg 46 | }); 47 | }); 48 | } 49 | componentWillReceiveProps(nextProps) { 50 | const { source } = nextProps; 51 | if (source) { 52 | this.setState({ 53 | channel: source.platformlist, 54 | freeUrl: source.list 55 | }); 56 | } 57 | } 58 | handleSwitchChannel(value) { 59 | this.setState({ 60 | url: value.key 61 | }); 62 | } 63 | setTitle() { 64 | const title = this.webview.getTitle(); 65 | this.setState({ 66 | title 67 | }); 68 | } 69 | onComeback() { 70 | this.webview.goBack(); 71 | } 72 | onSourceSelected(value) { 73 | const selectedUrl = this.state.freeUrl.find((d) => { 74 | if (d.name === value) { 75 | return d.url; 76 | } 77 | }); 78 | this.setState({ 79 | selectedUrl 80 | }); 81 | } 82 | onSwitchSource() { 83 | const { selectedUrl } = this.state; 84 | const currentVideoUrl = this.webview.getURL(); 85 | this.setState({ 86 | url: `${selectedUrl.url}${currentVideoUrl}` 87 | }); 88 | } 89 | render() { 90 | const { 91 | channel, url, freeUrl, title, isFullScreen 92 | } = this.state; 93 | return ( 94 | 107 | { 109 | this.webview = webview; 110 | }} 111 | title="腾讯视频" 112 | style={{ 113 | height: isFullScreen ? '100vh' : 'calc(100vh - 60px)', 114 | width: '100%' 115 | }} 116 | src={url} 117 | allowpopups="true" 118 | plugins 119 | /> 120 | 121 | ); 122 | } 123 | } 124 | 125 | function mapDispatchToProps(dispatch) { 126 | return { 127 | actions: { 128 | ...bindActionCreators(sourceActions, dispatch) 129 | } 130 | }; 131 | } 132 | function mapStateToProps(state) { 133 | return { 134 | source: state.source 135 | }; 136 | } 137 | 138 | export default connect( 139 | mapStateToProps, 140 | mapDispatchToProps 141 | )(VideoPlay); 142 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { AppContainer } from 'react-hot-loader'; 4 | import Root from './containers/Root'; 5 | import { configureStore, history } from './store/configureStore'; 6 | import './app.global.css'; 7 | 8 | const store = configureStore(); 9 | 10 | render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | 17 | if (module.hot) { 18 | module.hot.accept('./containers/Root', () => { 19 | const NextRoot = require('./containers/Root'); // eslint-disable-line global-require 20 | render( 21 | 22 | 23 | , 24 | document.getElementById('root') 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /app/main.dev.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0, flowtype-errors/show-errors: 0 */ 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 | * @flow 12 | */ 13 | import { app, BrowserWindow, ipcMain } from 'electron'; 14 | import MenuBuilder from './menu'; 15 | 16 | let mainWindow = null; 17 | 18 | if (process.env.NODE_ENV === 'production') { 19 | const sourceMapSupport = require('source-map-support'); 20 | sourceMapSupport.install(); 21 | } 22 | 23 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 24 | require('electron-debug')(); 25 | const path = require('path'); 26 | const p = path.join(__dirname, '..', 'app', 'node_modules'); 27 | require('module').globalPaths.push(p); 28 | } 29 | 30 | const installExtensions = async () => { 31 | const installer = require('electron-devtools-installer'); 32 | const forceDownload = !!process.env.UPGRADE_EXTENSIONS; 33 | const extensions = [ 34 | 'REACT_DEVELOPER_TOOLS', 35 | 'REDUX_DEVTOOLS' 36 | ]; 37 | 38 | return Promise 39 | .all(extensions.map(name => installer.default(installer[name], forceDownload))) 40 | .catch(console.log); 41 | }; 42 | 43 | // Try to append Pepper flash. See https://github.com/electron/electron/blob/master/docs/tutorial/using-pepper-flash-plugin.md 44 | if (process.platform === 'darwin' && app.getPath("pepperFlashSystemPlugin")) { 45 | app.commandLine.appendSwitch( 46 | "ppapi-flash-path", 47 | app.getPath("pepperFlashSystemPlugin") 48 | ); 49 | } 50 | 51 | 52 | /** 53 | * Add event listeners... 54 | */ 55 | 56 | app.on('window-all-closed', () => { 57 | // Respect the OSX convention of having the application in memory even 58 | // after all windows have been closed 59 | if (process.platform !== 'darwin') { 60 | app.quit(); 61 | } 62 | }); 63 | 64 | 65 | app.on('ready', async () => { 66 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 67 | await installExtensions(); 68 | } 69 | 70 | mainWindow = new BrowserWindow({ 71 | show: false, 72 | width: 1024, 73 | height: 728, 74 | webPreferences: { 75 | plugins: true 76 | } 77 | }); 78 | 79 | mainWindow.loadURL(`file://${__dirname}/app.html`); 80 | 81 | // @TODO: Use 'ready-to-show' event 82 | // https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event 83 | mainWindow.webContents.on('did-finish-load', () => { 84 | if (!mainWindow) { 85 | throw new Error('"mainWindow" is not defined'); 86 | } 87 | mainWindow.show(); 88 | mainWindow.focus(); 89 | }); 90 | 91 | mainWindow.on('closed', () => { 92 | mainWindow = null; 93 | }); 94 | 95 | mainWindow.on('enter-full-screen', () => { 96 | mainWindow.webContents.send('enter-full-screen', true); 97 | }) 98 | mainWindow.on('leave-full-screen', () => { 99 | mainWindow.webContents.send('enter-full-screen', false); 100 | }) 101 | const menuBuilder = new MenuBuilder(mainWindow); 102 | menuBuilder.buildMenu(); 103 | }); 104 | -------------------------------------------------------------------------------- /app/menu.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { app, Menu, shell, BrowserWindow } from 'electron'; 3 | 4 | export default class MenuBuilder { 5 | mainWindow: BrowserWindow; 6 | 7 | constructor(mainWindow: BrowserWindow) { 8 | this.mainWindow = mainWindow; 9 | } 10 | 11 | buildMenu() { 12 | if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { 13 | this.setupDevelopmentEnvironment(); 14 | } 15 | 16 | const template = process.platform === 'darwin' 17 | ? this.buildDarwinTemplate() 18 | : this.buildDefaultTemplate(); 19 | 20 | const menu = Menu.buildFromTemplate(template); 21 | Menu.setApplicationMenu(menu); 22 | 23 | return menu; 24 | } 25 | 26 | setupDevelopmentEnvironment() { 27 | this.mainWindow.openDevTools(); 28 | this.mainWindow.webContents.on('context-menu', (e, props) => { 29 | const { x, y } = props; 30 | 31 | Menu 32 | .buildFromTemplate([{ 33 | label: 'Inspect element', 34 | click: () => { 35 | this.mainWindow.inspectElement(x, y); 36 | } 37 | }]) 38 | .popup(this.mainWindow); 39 | }); 40 | } 41 | 42 | buildDarwinTemplate() { 43 | const subMenuAbout = { 44 | label: 'Electron', 45 | submenu: [ 46 | { label: 'About ElectronReact', selector: 'orderFrontStandardAboutPanel:' }, 47 | { type: 'separator' }, 48 | { label: 'Services', submenu: [] }, 49 | { type: 'separator' }, 50 | { label: 'Hide ElectronReact', accelerator: 'Command+H', selector: 'hide:' }, 51 | { label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }, 52 | { label: 'Show All', selector: 'unhideAllApplications:' }, 53 | { type: 'separator' }, 54 | { label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } } 55 | ] 56 | }; 57 | const subMenuEdit = { 58 | label: 'Edit', 59 | submenu: [ 60 | { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, 61 | { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, 62 | { type: 'separator' }, 63 | { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, 64 | { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, 65 | { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, 66 | { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' } 67 | ] 68 | }; 69 | const subMenuViewDev = { 70 | label: 'View', 71 | submenu: [ 72 | { label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } }, 73 | { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } }, 74 | { label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', click: () => { this.mainWindow.toggleDevTools(); } } 75 | ] 76 | }; 77 | const subMenuViewProd = { 78 | label: 'View', 79 | submenu: [ 80 | { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } } 81 | ] 82 | }; 83 | const subMenuWindow = { 84 | label: 'Window', 85 | submenu: [ 86 | { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' }, 87 | { label: 'Close', accelerator: 'Command+W', selector: 'performClose:' }, 88 | { type: 'separator' }, 89 | { label: 'Bring All to Front', selector: 'arrangeInFront:' } 90 | ] 91 | }; 92 | const subMenuHelp = { 93 | label: 'Help', 94 | submenu: [ 95 | { label: 'Learn More', click() { shell.openExternal('http://electron.atom.io'); } }, 96 | { label: 'Documentation', click() { shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); } }, 97 | { label: 'Community Discussions', click() { shell.openExternal('https://discuss.atom.io/c/electron'); } }, 98 | { label: 'Search Issues', click() { shell.openExternal('https://github.com/atom/electron/issues'); } } 99 | ] 100 | }; 101 | 102 | const subMenuView = process.env.NODE_ENV === 'development' 103 | ? subMenuViewDev 104 | : subMenuViewProd; 105 | 106 | return [ 107 | subMenuAbout, 108 | subMenuEdit, 109 | subMenuView, 110 | subMenuWindow, 111 | subMenuHelp 112 | ]; 113 | } 114 | 115 | buildDefaultTemplate() { 116 | const templateDefault = [{ 117 | label: '&File', 118 | submenu: [{ 119 | label: '&Open', 120 | accelerator: 'Ctrl+O' 121 | }, { 122 | label: '&Close', 123 | accelerator: 'Ctrl+W', 124 | click: () => { 125 | this.mainWindow.close(); 126 | } 127 | }] 128 | }, { 129 | label: '&View', 130 | submenu: (process.env.NODE_ENV === 'development') ? [{ 131 | label: '&Reload', 132 | accelerator: 'Ctrl+R', 133 | click: () => { 134 | this.mainWindow.webContents.reload(); 135 | } 136 | }, { 137 | label: 'Toggle &Full Screen', 138 | accelerator: 'F11', 139 | click: () => { 140 | this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); 141 | } 142 | }, { 143 | label: 'Toggle &Developer Tools', 144 | accelerator: 'Alt+Ctrl+I', 145 | click: () => { 146 | this.mainWindow.toggleDevTools(); 147 | } 148 | }] : [{ 149 | label: 'Toggle &Full Screen', 150 | accelerator: 'F11', 151 | click: () => { 152 | this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); 153 | } 154 | }] 155 | }, { 156 | label: 'Help', 157 | submenu: [{ 158 | label: 'Learn More', 159 | click() { 160 | shell.openExternal('http://electron.atom.io'); 161 | } 162 | }, { 163 | label: 'Documentation', 164 | click() { 165 | shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); 166 | } 167 | }, { 168 | label: 'Community Discussions', 169 | click() { 170 | shell.openExternal('https://discuss.atom.io/c/electron'); 171 | } 172 | }, { 173 | label: 'Search Issues', 174 | click() { 175 | shell.openExternal('https://github.com/atom/electron/issues'); 176 | } 177 | }] 178 | }]; 179 | 180 | return templateDefault; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-react-boilerplate", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-react-boilerplate", 3 | "productName": "electron-react-boilerplate", 4 | "version": "1.1.4", 5 | "description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development", 6 | "main": "./main.prod.js", 7 | "author": { 8 | "name": "C. T. Lin", 9 | "email": "chentsulin@gmail.com", 10 | "url": "https://github.com/chentsulin" 11 | }, 12 | "scripts": { 13 | "electron-rebuild": "node -r babel-register ../internals/scripts/ElectronRebuild.js", 14 | "postinstall": "npm run electron-rebuild" 15 | }, 16 | "license": "MIT", 17 | "dependencies": {} 18 | } 19 | -------------------------------------------------------------------------------- /app/reducers/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { combineReducers } from 'redux'; 3 | import { routerReducer as router } from 'react-router-redux'; 4 | import source from './source'; 5 | 6 | const rootReducer = combineReducers({ 7 | router, 8 | source, 9 | }); 10 | 11 | export default rootReducer; 12 | -------------------------------------------------------------------------------- /app/reducers/source.js: -------------------------------------------------------------------------------- 1 | export default function source(state = null, action) { 2 | switch(action.type) { 3 | case 'GETALLVIDEOSOURCE': 4 | return action.payload; 5 | default: 6 | return state; 7 | } 8 | } -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | /* eslint flowtype-errors/show-errors: 0 */ 2 | import React from 'react'; 3 | import { Switch, Route } from 'react-router'; 4 | import App from './containers/App'; 5 | import Video from './containers/Video'; 6 | 7 | export default () => ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /app/store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { createHashHistory } from 'history'; 4 | import { routerMiddleware, routerActions } from 'react-router-redux'; 5 | import { createLogger } from 'redux-logger'; 6 | import rootReducer from '../reducers'; 7 | // import type { counterStateType } from '../reducers/counter'; 8 | 9 | const history = createHashHistory(); 10 | 11 | const configureStore = (initialState) => { 12 | // Redux Configuration 13 | const middleware = []; 14 | const enhancers = []; 15 | 16 | // Thunk Middleware 17 | middleware.push(thunk); 18 | 19 | // Logging Middleware 20 | const logger = createLogger({ 21 | level: 'info', 22 | collapsed: true 23 | }); 24 | 25 | // Skip redux logs in console during the tests 26 | if (process.env.NODE_ENV !== 'test') { 27 | middleware.push(logger); 28 | } 29 | 30 | // Router Middleware 31 | const router = routerMiddleware(history); 32 | middleware.push(router); 33 | 34 | // Redux DevTools Configuration 35 | const actionCreators = { 36 | ...routerActions, 37 | }; 38 | // If Redux DevTools Extension is installed use it, otherwise use Redux compose 39 | /* eslint-disable no-underscore-dangle */ 40 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ 41 | ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ 42 | // Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html 43 | actionCreators, 44 | }) 45 | : compose; 46 | /* eslint-enable no-underscore-dangle */ 47 | 48 | // Apply Middleware & Compose Enhancers 49 | enhancers.push(applyMiddleware(...middleware)); 50 | const enhancer = composeEnhancers(...enhancers); 51 | 52 | // Create Store 53 | const store = createStore(rootReducer, initialState, enhancer); 54 | 55 | if (module.hot) { 56 | module.hot.accept('../reducers', () => 57 | store.replaceReducer(require('../reducers'))); // eslint-disable-line global-require 58 | } 59 | 60 | return store; 61 | }; 62 | 63 | export default { configureStore, history }; 64 | -------------------------------------------------------------------------------- /app/store/configureStore.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | if (process.env.NODE_ENV === 'production') { 3 | module.exports = require('./configureStore.prod'); // eslint-disable-line global-require 4 | } else { 5 | module.exports = require('./configureStore.dev'); // eslint-disable-line global-require 6 | } 7 | -------------------------------------------------------------------------------- /app/store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { createStore, applyMiddleware } from 'redux'; 3 | import thunk from 'redux-thunk'; 4 | import { createBrowserHistory } from 'history'; 5 | import { routerMiddleware } from 'react-router-redux'; 6 | import rootReducer from '../reducers'; 7 | import type { counterStateType } from '../reducers/counter'; 8 | 9 | const history = createBrowserHistory(); 10 | const router = routerMiddleware(history); 11 | const enhancer = applyMiddleware(thunk, router); 12 | 13 | function configureStore(initialState?: counterStateType) { 14 | return createStore(rootReducer, initialState, enhancer); 15 | } 16 | 17 | export default { configureStore, history }; 18 | -------------------------------------------------------------------------------- /app/utils/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/app/utils/.gitkeep -------------------------------------------------------------------------------- /app/utils/fetch.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const BASEURL = 4 | 'https://raw.githubusercontent.com/phobal/ivideo/master/resources/viplist.json'; 5 | 6 | const instance = axios.create({ 7 | baseURL: BASEURL, 8 | timeout: 10000 9 | }); 10 | 11 | const createAPI = (url, method, config) => { 12 | config = config || {} // eslint-disable-line 13 | return instance({ 14 | url, 15 | method, 16 | ...config 17 | }); 18 | }; 19 | 20 | const source = { 21 | getAllVideoSource: (config) => createAPI('', 'GET', config) 22 | }; 23 | 24 | export { source }; 25 | -------------------------------------------------------------------------------- /app/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | os: unstable 2 | 3 | environment: 4 | matrix: 5 | - nodejs_version: 8 6 | - nodejs_version: 7 7 | 8 | cache: 9 | - "%LOCALAPPDATA%/Yarn" 10 | - node_modules -> package.json 11 | - app/node_modules -> app/package.json 12 | 13 | matrix: 14 | fast_finish: true 15 | 16 | build: off 17 | 18 | version: '{build}' 19 | 20 | shallow_clone: true 21 | 22 | clone_depth: 1 23 | 24 | install: 25 | - ps: Install-Product node $env:nodejs_version 26 | - set CI=true 27 | - yarn 28 | - cd app && yarn 29 | 30 | test_script: 31 | - node --version 32 | - yarn lint 33 | - yarn package 34 | - yarn test 35 | - yarn test-e2e 36 | -------------------------------------------------------------------------------- /flow-typed/module_vx.x.x.js: -------------------------------------------------------------------------------- 1 | declare module 'module' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /internals/flow/CSSModule.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare export default { [key: string]: string } -------------------------------------------------------------------------------- /internals/flow/WebpackAsset.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | declare export default string 3 | -------------------------------------------------------------------------------- /internals/img/eslint-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/eslint-padded-90.png -------------------------------------------------------------------------------- /internals/img/eslint-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/eslint-padded.png -------------------------------------------------------------------------------- /internals/img/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/eslint.png -------------------------------------------------------------------------------- /internals/img/flow-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/flow-padded-90.png -------------------------------------------------------------------------------- /internals/img/flow-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/flow-padded.png -------------------------------------------------------------------------------- /internals/img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/flow.png -------------------------------------------------------------------------------- /internals/img/jest-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/jest-padded-90.png -------------------------------------------------------------------------------- /internals/img/jest-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/jest-padded.png -------------------------------------------------------------------------------- /internals/img/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/jest.png -------------------------------------------------------------------------------- /internals/img/js-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/js-padded.png -------------------------------------------------------------------------------- /internals/img/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/js.png -------------------------------------------------------------------------------- /internals/img/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/npm.png -------------------------------------------------------------------------------- /internals/img/react-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react-padded-90.png -------------------------------------------------------------------------------- /internals/img/react-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react-padded.png -------------------------------------------------------------------------------- /internals/img/react-router-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react-router-padded-90.png -------------------------------------------------------------------------------- /internals/img/react-router-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react-router-padded.png -------------------------------------------------------------------------------- /internals/img/react-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react-router.png -------------------------------------------------------------------------------- /internals/img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/react.png -------------------------------------------------------------------------------- /internals/img/redux-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/redux-padded-90.png -------------------------------------------------------------------------------- /internals/img/redux-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/redux-padded.png -------------------------------------------------------------------------------- /internals/img/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/redux.png -------------------------------------------------------------------------------- /internals/img/webpack-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/webpack-padded-90.png -------------------------------------------------------------------------------- /internals/img/webpack-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/webpack-padded.png -------------------------------------------------------------------------------- /internals/img/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/webpack.png -------------------------------------------------------------------------------- /internals/img/yarn-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/yarn-padded-90.png -------------------------------------------------------------------------------- /internals/img/yarn-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/yarn-padded.png -------------------------------------------------------------------------------- /internals/img/yarn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/internals/img/yarn.png -------------------------------------------------------------------------------- /internals/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /internals/scripts/CheckBuiltsExist.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // Check if the renderer and main bundles are built 3 | import path from 'path'; 4 | import chalk from 'chalk'; 5 | import fs from 'fs'; 6 | 7 | function CheckBuildsExist() { 8 | const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js'); 9 | const rendererPath = path.join(__dirname, '..', '..', 'app', 'dist', 'renderer.prod.js'); 10 | 11 | if (!fs.existsSync(mainPath)) { 12 | throw new Error(chalk.whiteBright.bgRed.bold('The main process is not built yet. Build it by running "npm run build-main"')); 13 | } 14 | 15 | if (!fs.existsSync(rendererPath)) { 16 | throw new Error(chalk.whiteBright.bgRed.bold('The renderer process is not built yet. Build it by running "npm run build-renderer"')); 17 | } 18 | } 19 | 20 | CheckBuildsExist(); 21 | -------------------------------------------------------------------------------- /internals/scripts/CheckNativeDep.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import fs from 'fs'; 3 | import chalk from 'chalk'; 4 | import { execSync } from 'child_process'; 5 | import { dependencies } from '../../package.json'; 6 | 7 | (() => { 8 | if (!dependencies) return; 9 | 10 | const dependenciesKeys = Object.keys(dependencies); 11 | const nativeDeps = 12 | fs.readdirSync('node_modules') 13 | .filter(folder => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 14 | 15 | try { 16 | // Find the reason for why the dependency is installed. If it is installed 17 | // because of a devDependency then that is okay. Warn when it is installed 18 | // because of a dependency 19 | const dependenciesObject = JSON.parse(execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString()); 20 | const rootDependencies = Object.keys(dependenciesObject.dependencies); 21 | const filteredRootDependencies = rootDependencies 22 | .filter(rootDependency => dependenciesKeys.includes(rootDependency)); 23 | 24 | if (filteredRootDependencies.length > 0) { 25 | const plural = filteredRootDependencies.length > 1; 26 | console.log(` 27 | 28 | ${chalk.whiteBright.bgYellow.bold('Webpack does not work with native dependencies.')} 29 | ${chalk.bold(filteredRootDependencies.join(', '))} ${plural ? 'are native dependencies' : 'is a native dependency'} and should be installed inside of the "./app" folder. 30 | 31 | 32 | First uninstall the packages from "./package.json": 33 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 34 | 35 | ${chalk.bold('Then, instead of installing the package to the root "./package.json":')} 36 | ${chalk.whiteBright.bgRed.bold('npm install your-package --save')} 37 | 38 | ${chalk.bold('Install the package to "./app/package.json"')} 39 | ${chalk.whiteBright.bgGreen.bold('cd ./app && npm install your-package --save')} 40 | 41 | 42 | Read more about native dependencies at: 43 | ${chalk.bold('https://github.com/chentsulin/electron-react-boilerplate/wiki/Module-Structure----Two-package.json-Structure')} 44 | 45 | 46 | `); 47 | 48 | process.exit(1); 49 | } 50 | } catch (e) { 51 | console.log('Native dependencies could not be checked'); 52 | } 53 | })(); 54 | -------------------------------------------------------------------------------- /internals/scripts/CheckNodeEnv.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | 4 | export default function CheckNodeEnv(expectedEnv: string) { 5 | if (!expectedEnv) { 6 | throw new Error('"expectedEnv" not set'); 7 | } 8 | 9 | if (process.env.NODE_ENV !== expectedEnv) { 10 | console.log(chalk.whiteBright.bgRed.bold(`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`)); 11 | process.exit(2); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /internals/scripts/CheckPortInUse.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | import detectPort from 'detect-port'; 4 | 5 | (function CheckPortInUse() { 6 | const port: string = process.env.PORT || '1212'; 7 | 8 | detectPort(port, (err: ?Error, availablePort: number) => { 9 | if (port !== String(availablePort)) { 10 | throw new Error(chalk.whiteBright.bgRed.bold(`Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm run dev`)); 11 | } else { 12 | process.exit(0); 13 | } 14 | }); 15 | }()); 16 | -------------------------------------------------------------------------------- /internals/scripts/ElectronRebuild.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import path from 'path'; 3 | import { execSync } from 'child_process'; 4 | import fs from 'fs'; 5 | import dependencies from '../../app/package.json'; 6 | 7 | const nodeModulesPath = 8 | path.join(__dirname, '..', '..', 'app', 'node_modules'); 9 | 10 | if (Object.keys(dependencies || {}).length > 0 && fs.existsSync(nodeModulesPath)) { 11 | const electronRebuildCmd = 12 | '../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; 13 | 14 | const cmd = process.platform === 'win32' 15 | ? electronRebuildCmd.replace(/\//g, '\\') 16 | : electronRebuildCmd; 17 | 18 | execSync(cmd, { 19 | cwd: path.join(__dirname, '..', '..', 'app') 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /internals/scripts/RunTests.js: -------------------------------------------------------------------------------- 1 | import spawn from 'cross-spawn'; 2 | import path from 'path'; 3 | 4 | const pattern = process.argv[2] === 'e2e' 5 | ? 'test/e2e/.+\\.spec\\.js' 6 | : 'test/(?!e2e/)[^/]+/.+\\.spec\\.js$'; 7 | 8 | const result = spawn.sync( 9 | path.normalize('./node_modules/.bin/jest'), 10 | [pattern, ...process.argv.slice(2)], 11 | { stdio: 'inherit' } 12 | ); 13 | 14 | process.exit(result.status); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ivideo", 3 | "productName": "ivideo", 4 | "version": "1.1.4", 5 | "description": "一个视频播放器观看国内主流视频网站,不用单独下载各个平台客户端", 6 | "scripts": { 7 | "build": "concurrently \"npm run build-main\" \"npm run build-renderer\"", 8 | "build-dll": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --colors", 9 | "build-main": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.main.prod.js --colors", 10 | "build-renderer": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.prod.js --colors", 11 | "dev": "cross-env START_HOT=1 node -r babel-register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 npm run start-renderer-dev", 12 | "electron-rebuild": "electron-rebuild --parallel --force --types prod,dev,optional --module-dir app", 13 | "flow": "flow", 14 | "flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true", 15 | "lint": "cross-env NODE_ENV=development eslint --cache --format=node_modules/eslint-formatter-pretty .", 16 | "lint-fix": "npm run lint -- --fix", 17 | "lint-styles": "stylelint app/*.css app/components/*.css --syntax scss", 18 | "lint-styles-fix": "stylefmt -r app/*.css app/components/*.css", 19 | "package": "npm run build && build --publish never", 20 | "package-all": "npm run build && build -mwl", 21 | "package-linux": "npm run build && build --linux", 22 | "package-win": "npm run build && build --win --x64", 23 | "postinstall": "node -r babel-register internals/scripts/CheckNativeDep.js && npm run flow-typed && npm run build-dll && electron-builder install-app-deps && node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", 24 | "prestart": "npm run build", 25 | "start": "cross-env NODE_ENV=production electron ./app/", 26 | "start-main-dev": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main.dev", 27 | "start-renderer-dev": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.renderer.dev.js", 28 | "test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings -r babel-register ./internals/scripts/RunTests.js", 29 | "test-all": "npm run lint && npm run flow && npm run build && npm run test && npm run test-e2e", 30 | "test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings -r babel-register ./internals/scripts/RunTests.js e2e", 31 | "test-watch": "npm test -- --watch" 32 | }, 33 | "browserslist": "electron 1.6", 34 | "build": { 35 | "productName": "ivideo", 36 | "appId": "org.phobal.ivideo", 37 | "files": [ 38 | "dist/", 39 | "node_modules/", 40 | "app.html", 41 | "main.prod.js", 42 | "main.prod.js.map", 43 | "package.json" 44 | ], 45 | "dmg": { 46 | "contents": [ 47 | { 48 | "x": 130, 49 | "y": 220 50 | }, 51 | { 52 | "x": 410, 53 | "y": 220, 54 | "type": "link", 55 | "path": "/Applications" 56 | } 57 | ] 58 | }, 59 | "win": { 60 | "target": [ 61 | { 62 | "target": "nsis", 63 | "arch": [ 64 | "x64", 65 | "ia32" 66 | ] 67 | } 68 | ] 69 | }, 70 | "linux": { 71 | "target": [ 72 | "deb", 73 | "AppImage" 74 | ], 75 | "category": "Development" 76 | }, 77 | "directories": { 78 | "buildResources": "resources", 79 | "output": "release" 80 | } 81 | }, 82 | "repository": { 83 | "type": "git", 84 | "url": "git+https://github.com/phobal/ivideo.git" 85 | }, 86 | "author": { 87 | "name": "phobal", 88 | "email": "phobal@126.com", 89 | "url": "https://github.com/phobal" 90 | }, 91 | "license": "MIT", 92 | "bugs": { 93 | "url": "https://github.com/phobal/ivideo/issues" 94 | }, 95 | "keywords": [ 96 | "electron", 97 | "boilerplate", 98 | "react", 99 | "redux", 100 | "flow", 101 | "sass", 102 | "webpack", 103 | "hot", 104 | "reload" 105 | ], 106 | "homepage": "https://github.com/phobal/ivideo#readme", 107 | "jest": { 108 | "moduleNameMapper": { 109 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/internals/mocks/fileMock.js", 110 | "\\.(css|less|sass|scss)$": "identity-obj-proxy" 111 | }, 112 | "moduleFileExtensions": [ 113 | "js" 114 | ], 115 | "moduleDirectories": [ 116 | "node_modules", 117 | "app/node_modules" 118 | ], 119 | "transform": { 120 | "^.+\\.js$": "babel-jest" 121 | }, 122 | "setupFiles": [ 123 | "./internals/scripts/CheckBuiltsExist.js" 124 | ] 125 | }, 126 | "devDependencies": { 127 | "babel-core": "^6.26.0", 128 | "babel-eslint": "^8.2.1", 129 | "babel-jest": "^22.1.0", 130 | "babel-loader": "^7.1.2", 131 | "babel-plugin-add-module-exports": "^0.2.1", 132 | "babel-plugin-dev-expression": "^0.2.1", 133 | "babel-plugin-flow-runtime": "^0.15.0", 134 | "babel-plugin-import": "^1.7.0", 135 | "babel-plugin-transform-class-properties": "^6.24.1", 136 | "babel-plugin-transform-es2015-classes": "^6.24.1", 137 | "babel-preset-env": "^1.6.1", 138 | "babel-preset-react": "^6.24.1", 139 | "babel-preset-react-hmre": "^1.1.1", 140 | "babel-preset-react-optimize": "^1.0.1", 141 | "babel-preset-stage-0": "^6.24.1", 142 | "babel-register": "^6.26.0", 143 | "chalk": "^2.3.0", 144 | "concurrently": "^3.5.1", 145 | "cross-env": "^5.1.3", 146 | "cross-spawn": "^6.0.4", 147 | "css-loader": "^0.28.9", 148 | "detect-port": "^1.2.2", 149 | "electron": "^1.7.11", 150 | "electron-builder": "^19.55.3", 151 | "electron-devtools-installer": "^2.2.3", 152 | "electron-rebuild": "^1.7.3", 153 | "enzyme": "^3.3.0", 154 | "enzyme-adapter-react-16": "^1.1.1", 155 | "enzyme-to-json": "^3.3.1", 156 | "eslint": "^4.16.0", 157 | "eslint-config-airbnb": "^16.1.0", 158 | "eslint-formatter-pretty": "^1.3.0", 159 | "eslint-import-resolver-webpack": "^0.8.4", 160 | "eslint-plugin-compat": "^2.2.0", 161 | "eslint-plugin-flowtype": "^2.42.0", 162 | "eslint-plugin-import": "^2.8.0", 163 | "eslint-plugin-jest": "^21.7.0", 164 | "eslint-plugin-jsx-a11y": "6.0.3", 165 | "eslint-plugin-promise": "^3.6.0", 166 | "eslint-plugin-react": "^7.6.1", 167 | "express": "^4.16.2", 168 | "extract-text-webpack-plugin": "^3.0.2", 169 | "fbjs-scripts": "^0.8.1", 170 | "file-loader": "^1.1.6", 171 | "flow-bin": "^0.64.0", 172 | "flow-runtime": "^0.16.0", 173 | "flow-typed": "^2.3.0", 174 | "identity-obj-proxy": "^3.0.0", 175 | "jest": "^22.1.4", 176 | "less": "^3.0.1", 177 | "less-loader": "^4.1.0", 178 | "minimist": "^1.2.0", 179 | "node-sass": "^4.7.2", 180 | "npm-logical-tree": "^1.2.1", 181 | "react-test-renderer": "^16.2.0", 182 | "redux-logger": "^3.0.6", 183 | "rimraf": "^2.6.2", 184 | "sass-loader": "^6.0.6", 185 | "sinon": "^4.2.2", 186 | "spectron": "^3.8.0", 187 | "style-loader": "^0.20.1", 188 | "stylefmt": "^6.0.0", 189 | "stylelint": "^8.4.0", 190 | "stylelint-config-standard": "^18.0.0", 191 | "uglifyjs-webpack-plugin": "1.1.8", 192 | "url-loader": "^0.6.2", 193 | "webpack": "^3.10.0", 194 | "webpack-bundle-analyzer": "^2.9.2", 195 | "webpack-dev-server": "^2.11.1", 196 | "webpack-merge": "^4.1.1" 197 | }, 198 | "dependencies": { 199 | "axios": "^0.18.0", 200 | "devtron": "^1.4.0", 201 | "electron-debug": "^1.5.0", 202 | "font-awesome": "^4.7.0", 203 | "history": "^4.7.2", 204 | "rc-menu": "^6.2.10", 205 | "rc-select": "^7.7.7", 206 | "react": "^16.2.0", 207 | "react-dom": "^16.2.0", 208 | "react-hot-loader": "^4.0.0-beta.13", 209 | "react-redux": "^5.0.6", 210 | "react-router": "^4.2.0", 211 | "react-router-dom": "^4.2.2", 212 | "react-router-redux": "^5.0.0-alpha.6", 213 | "redux": "^3.7.2", 214 | "redux-thunk": "^2.2.0", 215 | "source-map-support": "^0.5.3" 216 | }, 217 | "devEngines": { 218 | "node": ">=7.x", 219 | "npm": ">=4.x", 220 | "yarn": ">=0.21.3" 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icon.icns -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icon.ico -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icon.png -------------------------------------------------------------------------------- /resources/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/1024x1024.png -------------------------------------------------------------------------------- /resources/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/128x128.png -------------------------------------------------------------------------------- /resources/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/16x16.png -------------------------------------------------------------------------------- /resources/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/24x24.png -------------------------------------------------------------------------------- /resources/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/256x256.png -------------------------------------------------------------------------------- /resources/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/32x32.png -------------------------------------------------------------------------------- /resources/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/48x48.png -------------------------------------------------------------------------------- /resources/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/512x512.png -------------------------------------------------------------------------------- /resources/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/64x64.png -------------------------------------------------------------------------------- /resources/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/icons/96x96.png -------------------------------------------------------------------------------- /resources/showcase01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/showcase01.jpg -------------------------------------------------------------------------------- /resources/showcase02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/showcase02.jpg -------------------------------------------------------------------------------- /resources/showcase03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/showcase03.jpg -------------------------------------------------------------------------------- /resources/showcase04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phobal/ivideo/e9a26b66177a643f53bbadeb89da45dfee821abc/resources/showcase04.jpg -------------------------------------------------------------------------------- /resources/viplist.json: -------------------------------------------------------------------------------- 1 | { 2 | "platformlist": [ 3 | { 4 | "name": "爱奇艺", 5 | "url": "http://www.iqiyi.com/" 6 | }, 7 | { 8 | "name": "腾讯视频", 9 | "url": "https://v.qq.com/" 10 | }, 11 | { 12 | "name": "芒果", 13 | "url": "https://www.mgtv.com/" 14 | }, 15 | { 16 | "name": "优酷", 17 | "url": "https://www.youku.com/" 18 | }, 19 | { 20 | "name": "搜狐视频", 21 | "url": "https://tv.sohu.com/" 22 | }, 23 | { 24 | "name": "乐视视频", 25 | "url": "https://www.le.com/" 26 | }, 27 | { 28 | "name": "电影天堂", 29 | "url": "http://www.btbtdy.net/" 30 | }, 31 | { 32 | "name": "新视觉影院", 33 | "url": "http://www.yy3080.com/vod-type-id-1-pg-1.html/" 34 | } 35 | ], 36 | "list": [ 37 | { 38 | "name": "5月-21", 39 | "url": "http://jiexi.071811.cc/jx2.php?url=" 40 | }, 41 | { 42 | "name": "9月-2", 43 | "url": "http://jqaaa.com/jx.php?url=" 44 | }, 45 | { 46 | "name": "5月-4", 47 | "url": "http://beaacc.com/api.php?url=" 48 | }, 49 | { 50 | "name": "4.21-4", 51 | "url": "http://www.82190555.com/index.php?url=" 52 | }, 53 | { 54 | "name": "4.21-6", 55 | "url": "http://www.85105052.com/admin.php?url=" 56 | }, 57 | { 58 | "name": "5月-23", 59 | "url": "http://api.baiyug.cn/vip/index.php?url=" 60 | }, 61 | { 62 | "name": "4.21-3-慢", 63 | "url": "https://yooomm.com/index.php?url=" 64 | }, 65 | { 66 | "name": "5月-24", 67 | "url": "http://www.82190555.com/index/qqvod.php?url=" 68 | }, 69 | { 70 | "name": "1", 71 | "url": "http://17kyun.com/api.php?url=" 72 | }, 73 | { 74 | "name": "品优解析-可播但广告", 75 | "url": "http://api.pucms.com/xnflv/?url=" 76 | }, 77 | { 78 | "name": "5月-1", 79 | "url": "http://www.82190555.com/index/qqvod.php?url=" 80 | }, 81 | { 82 | "name": "腾讯可用,金桥解析", 83 | "url": "http://jqaaa.com/jx.php?url=" 84 | }, 85 | { 86 | "name": "速度牛", 87 | "url": "http://api.wlzhan.com/sudu/?url=" 88 | }, 89 | { 90 | "name": "万能接口6", 91 | "url": "http://wwwhe1.177kdy.cn/4.php?pass=1&url=" 92 | }, 93 | { 94 | "name": "花园影视(可能无效)", 95 | "url": "http://j.zz22x.com/jx/?url=" 96 | }, 97 | { 98 | "name": "9月-1", 99 | "url": "http://api.ledboke.com/?url=" 100 | } 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest/globals": true 4 | }, 5 | "plugins": [ 6 | "jest" 7 | ], 8 | "rules": { 9 | "jest/no-disabled-tests": "warn", 10 | "jest/no-focused-tests": "error", 11 | "jest/no-identical-title": "error" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/actions/__snapshots__/counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`actions should decrement should create decrement action 1`] = ` 4 | Object { 5 | "type": "DECREMENT_COUNTER", 6 | } 7 | `; 8 | 9 | exports[`actions should increment should create increment action 1`] = ` 10 | Object { 11 | "type": "INCREMENT_COUNTER", 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /test/actions/counter.spec.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | import * as actions from '../../app/actions/counter'; 3 | 4 | describe('actions', () => { 5 | it('should increment should create increment action', () => { 6 | expect(actions.increment()).toMatchSnapshot(); 7 | }); 8 | 9 | it('should decrement should create decrement action', () => { 10 | expect(actions.decrement()).toMatchSnapshot(); 11 | }); 12 | 13 | it('should incrementIfOdd should create increment action', () => { 14 | const fn = actions.incrementIfOdd(); 15 | expect(fn).toBeInstanceOf(Function); 16 | const dispatch = spy(); 17 | const getState = () => ({ counter: 1 }); 18 | fn(dispatch, getState); 19 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true); 20 | }); 21 | 22 | it('should incrementIfOdd shouldnt create increment action if counter is even', () => { 23 | const fn = actions.incrementIfOdd(); 24 | const dispatch = spy(); 25 | const getState = () => ({ counter: 2 }); 26 | fn(dispatch, getState); 27 | expect(dispatch.called).toBe(false); 28 | }); 29 | 30 | // There's no nice way to test this at the moment... 31 | it('should incrementAsync', done => { 32 | const fn = actions.incrementAsync(1); 33 | expect(fn).toBeInstanceOf(Function); 34 | const dispatch = spy(); 35 | fn(dispatch); 36 | setTimeout(() => { 37 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true); 38 | done(); 39 | }, 5); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/components/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | import React from 'react'; 3 | import Enzyme, { shallow } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | import { BrowserRouter as Router } from 'react-router-dom'; 6 | import renderer from 'react-test-renderer'; 7 | import Counter from '../../app/components/Counter'; 8 | 9 | Enzyme.configure({ adapter: new Adapter() }); 10 | 11 | function setup() { 12 | const actions = { 13 | increment: spy(), 14 | incrementIfOdd: spy(), 15 | incrementAsync: spy(), 16 | decrement: spy() 17 | }; 18 | const component = shallow(); 19 | return { 20 | component, 21 | actions, 22 | buttons: component.find('button'), 23 | p: component.find('.counter') 24 | }; 25 | } 26 | 27 | describe('Counter component', () => { 28 | it('should should display count', () => { 29 | const { p } = setup(); 30 | expect(p.text()).toMatch(/^1$/); 31 | }); 32 | 33 | it('should first button should call increment', () => { 34 | const { buttons, actions } = setup(); 35 | buttons.at(0).simulate('click'); 36 | expect(actions.increment.called).toBe(true); 37 | }); 38 | 39 | it('should match exact snapshot', () => { 40 | const { actions } = setup(); 41 | const counter = ( 42 |
43 | 44 | 45 | 46 |
47 | ); 48 | const tree = renderer 49 | .create(counter) 50 | .toJSON(); 51 | 52 | expect(tree).toMatchSnapshot(); 53 | }); 54 | 55 | it('should second button should call decrement', () => { 56 | const { buttons, actions } = setup(); 57 | buttons.at(1).simulate('click'); 58 | expect(actions.decrement.called).toBe(true); 59 | }); 60 | 61 | it('should third button should call incrementIfOdd', () => { 62 | const { buttons, actions } = setup(); 63 | buttons.at(2).simulate('click'); 64 | expect(actions.incrementIfOdd.called).toBe(true); 65 | }); 66 | 67 | it('should fourth button should call incrementAsync', () => { 68 | const { buttons, actions } = setup(); 69 | buttons.at(3).simulate('click'); 70 | expect(actions.incrementAsync.called).toBe(true); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/components/__snapshots__/Counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Counter component should match exact snapshot 1`] = ` 4 |
5 |
6 |
10 | 14 | 17 | 18 |
19 |
23 | 1 24 |
25 |
28 | 37 | 46 | 53 | 60 |
61 |
62 |
63 | `; 64 | -------------------------------------------------------------------------------- /test/containers/CounterPage.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Enzyme, { mount } from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-16'; 4 | import { Provider } from 'react-redux'; 5 | import { createBrowserHistory } from 'history'; 6 | import { ConnectedRouter } from 'react-router-redux'; 7 | import CounterPage from '../../app/containers/CounterPage'; 8 | import { configureStore } from '../../app/store/configureStore'; 9 | 10 | Enzyme.configure({ adapter: new Adapter() }); 11 | 12 | function setup(initialState) { 13 | const store = configureStore(initialState); 14 | const history = createBrowserHistory(); 15 | const provider = ( 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | const app = mount(provider); 23 | return { 24 | app, 25 | buttons: app.find('button'), 26 | p: app.find('.counter') 27 | }; 28 | } 29 | 30 | describe('containers', () => { 31 | describe('App', () => { 32 | it('should display initial count', () => { 33 | const { p } = setup(); 34 | expect(p.text()).toMatch(/^0$/); 35 | }); 36 | 37 | it('should display updated count after increment button click', () => { 38 | const { buttons, p } = setup(); 39 | buttons.at(0).simulate('click'); 40 | expect(p.text()).toMatch(/^1$/); 41 | }); 42 | 43 | it('should display updated count after descrement button click', () => { 44 | const { buttons, p } = setup(); 45 | buttons.at(1).simulate('click'); 46 | expect(p.text()).toMatch(/^-1$/); 47 | }); 48 | 49 | it('shouldnt change if even and if odd button clicked', () => { 50 | const { buttons, p } = setup(); 51 | buttons.at(2).simulate('click'); 52 | expect(p.text()).toMatch(/^0$/); 53 | }); 54 | 55 | it('should change if odd and if odd button clicked', () => { 56 | const { buttons, p } = setup({ counter: 1 }); 57 | buttons.at(2).simulate('click'); 58 | expect(p.text()).toMatch(/^2$/); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/e2e/e2e.spec.js: -------------------------------------------------------------------------------- 1 | import { Application } from 'spectron'; 2 | import electronPath from 'electron'; 3 | import path from 'path'; 4 | 5 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; 6 | 7 | const delay = time => new Promise(resolve => setTimeout(resolve, time)); 8 | 9 | describe('main window', function spec() { 10 | beforeAll(async () => { 11 | this.app = new Application({ 12 | path: electronPath, 13 | args: [path.join(__dirname, '..', '..', 'app')], 14 | }); 15 | 16 | return this.app.start(); 17 | }); 18 | 19 | afterAll(() => { 20 | if (this.app && this.app.isRunning()) { 21 | return this.app.stop(); 22 | } 23 | }); 24 | 25 | const findCounter = () => this.app.client.element('[data-tid="counter"]'); 26 | 27 | const findButtons = async () => { 28 | const { value } = await this.app.client.elements('[data-tclass="btn"]'); 29 | return value.map(btn => btn.ELEMENT); 30 | }; 31 | 32 | it('should open window', async () => { 33 | const { client, browserWindow } = this.app; 34 | 35 | await client.waitUntilWindowLoaded(); 36 | await delay(500); 37 | const title = await browserWindow.getTitle(); 38 | expect(title).toBe('Hello Electron React!'); 39 | }); 40 | 41 | it('should haven\'t any logs in console of main window', async () => { 42 | const { client } = this.app; 43 | const logs = await client.getRenderProcessLogs(); 44 | // Print renderer process logs 45 | logs.forEach(log => { 46 | console.log(log.message); 47 | console.log(log.source); 48 | console.log(log.level); 49 | }); 50 | expect(logs).toHaveLength(0); 51 | }); 52 | 53 | it('should to Counter with click "to Counter" link', async () => { 54 | const { client } = this.app; 55 | 56 | await client.click('[data-tid=container] > a'); 57 | expect(await findCounter().getText()).toBe('0'); 58 | }); 59 | 60 | it('should display updated count after increment button click', async () => { 61 | const { client } = this.app; 62 | 63 | const buttons = await findButtons(); 64 | await client.elementIdClick(buttons[0]); // + 65 | expect(await findCounter().getText()).toBe('1'); 66 | }); 67 | 68 | it('should display updated count after descrement button click', async () => { 69 | const { client } = this.app; 70 | 71 | const buttons = await findButtons(); 72 | await client.elementIdClick(buttons[1]); // - 73 | expect(await findCounter().getText()).toBe('0'); 74 | }); 75 | 76 | it('shouldnt change if even and if odd button clicked', async () => { 77 | const { client } = this.app; 78 | 79 | const buttons = await findButtons(); 80 | await client.elementIdClick(buttons[2]); // odd 81 | expect(await findCounter().getText()).toBe('0'); 82 | }); 83 | 84 | it('should change if odd and if odd button clicked', async () => { 85 | const { client } = this.app; 86 | 87 | const buttons = await findButtons(); 88 | await client.elementIdClick(buttons[0]); // + 89 | await client.elementIdClick(buttons[2]); // odd 90 | expect(await findCounter().getText()).toBe('2'); 91 | }); 92 | 93 | it('should change if async button clicked and a second later', async () => { 94 | const { client } = this.app; 95 | 96 | const buttons = await findButtons(); 97 | await client.elementIdClick(buttons[3]); // async 98 | expect(await findCounter().getText()).toBe('2'); 99 | await delay(1500); 100 | expect(await findCounter().getText()).toBe('3'); 101 | }); 102 | 103 | it('should back to home if back button clicked', async () => { 104 | const { client } = this.app; 105 | await client.element('[data-tid="backButton"] > a').click(); 106 | 107 | expect(await client.isExisting('[data-tid="container"]')).toBe(true); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/example.js: -------------------------------------------------------------------------------- 1 | describe('description', () => { 2 | it('should have description', () => { 3 | expect(1 + 2).toBe(3); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /test/reducers/__snapshots__/counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`; 4 | 5 | exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`; 6 | 7 | exports[`reducers counter should handle initial state 1`] = `0`; 8 | 9 | exports[`reducers counter should handle unknown action type 1`] = `1`; 10 | -------------------------------------------------------------------------------- /test/reducers/counter.spec.js: -------------------------------------------------------------------------------- 1 | import counter from '../../app/reducers/counter'; 2 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter'; 3 | 4 | describe('reducers', () => { 5 | describe('counter', () => { 6 | it('should handle initial state', () => { 7 | expect(counter(undefined, {})).toMatchSnapshot(); 8 | }); 9 | 10 | it('should handle INCREMENT_COUNTER', () => { 11 | expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot(); 12 | }); 13 | 14 | it('should handle DECREMENT_COUNTER', () => { 15 | expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot(); 16 | }); 17 | 18 | it('should handle unknown action type', () => { 19 | expect(counter(1, { type: 'unknown' })).toMatchSnapshot(); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /webpack.config.base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { dependencies as externals } from './app/package.json'; 8 | 9 | export default { 10 | externals: Object.keys(externals || {}), 11 | 12 | module: { 13 | rules: [{ 14 | test: /\.jsx?$/, 15 | exclude: /node_modules/, 16 | use: { 17 | loader: 'babel-loader', 18 | options: { 19 | cacheDirectory: true 20 | } 21 | } 22 | }] 23 | }, 24 | 25 | output: { 26 | path: path.join(__dirname, 'app'), 27 | // https://github.com/webpack/webpack/issues/1114 28 | libraryTarget: 'commonjs2' 29 | }, 30 | 31 | /** 32 | * Determine the array of extensions that should be used to resolve modules. 33 | */ 34 | resolve: { 35 | extensions: ['.js', '.jsx', '.json'], 36 | modules: [ 37 | path.join(__dirname, 'app'), 38 | 'node_modules', 39 | ], 40 | }, 41 | 42 | plugins: [ 43 | new webpack.EnvironmentPlugin({ 44 | NODE_ENV: 'production' 45 | }), 46 | 47 | new webpack.NamedModulesPlugin(), 48 | ], 49 | }; 50 | -------------------------------------------------------------------------------- /webpack.config.eslint.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | 3 | module.exports = require('./webpack.config.renderer.dev'); 4 | -------------------------------------------------------------------------------- /webpack.config.main.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import merge from 'webpack-merge'; 7 | import UglifyJSPlugin from 'uglifyjs-webpack-plugin'; 8 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 9 | import baseConfig from './webpack.config.base'; 10 | import CheckNodeEnv from './internals/scripts/CheckNodeEnv'; 11 | 12 | CheckNodeEnv('production'); 13 | 14 | export default merge.smart(baseConfig, { 15 | devtool: 'source-map', 16 | 17 | target: 'electron-main', 18 | 19 | entry: './app/main.dev', 20 | 21 | output: { 22 | path: __dirname, 23 | filename: './app/main.prod.js' 24 | }, 25 | 26 | plugins: [ 27 | new UglifyJSPlugin({ 28 | parallel: true, 29 | sourceMap: true 30 | }), 31 | 32 | new BundleAnalyzerPlugin({ 33 | analyzerMode: process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 34 | openAnalyzer: process.env.OPEN_ANALYZER === 'true' 35 | }), 36 | 37 | /** 38 | * Create global constants which can be configured at compile time. 39 | * 40 | * Useful for allowing different behaviour between development builds and 41 | * release builds 42 | * 43 | * NODE_ENV should be production so that modules do not perform certain 44 | * development checks 45 | */ 46 | new webpack.EnvironmentPlugin({ 47 | NODE_ENV: 'production', 48 | DEBUG_PROD: 'false' 49 | }) 50 | ], 51 | 52 | /** 53 | * Disables webpack processing of __dirname and __filename. 54 | * If you run the bundle in node.js it falls back to these values of node.js. 55 | * https://github.com/webpack/webpack/issues/2010 56 | */ 57 | node: { 58 | __dirname: false, 59 | __filename: false 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /webpack.config.renderer.dev.dll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Builds the DLL for development electron renderer process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import merge from 'webpack-merge'; 8 | import baseConfig from './webpack.config.base'; 9 | import { dependencies } from './package.json'; 10 | import CheckNodeEnv from './internals/scripts/CheckNodeEnv'; 11 | 12 | CheckNodeEnv('development'); 13 | 14 | const dist = path.resolve(process.cwd(), 'dll'); 15 | 16 | export default merge.smart(baseConfig, { 17 | context: process.cwd(), 18 | 19 | devtool: 'eval', 20 | 21 | target: 'electron-renderer', 22 | 23 | externals: ['fsevents', 'crypto-browserify'], 24 | 25 | /** 26 | * Use `module` from `webpack.config.renderer.dev.js` 27 | */ 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.jsx?$/, 32 | exclude: /node_modules/, 33 | use: { 34 | loader: 'babel-loader', 35 | options: { 36 | cacheDirectory: true, 37 | plugins: [ 38 | // Here, we include babel plugins that are only required for the 39 | // renderer process. The 'transform-*' plugins must be included 40 | // before react-hot-loader/babel 41 | 'transform-class-properties', 42 | 'transform-es2015-classes', 43 | 'react-hot-loader/babel' 44 | ], 45 | } 46 | } 47 | }, 48 | { 49 | test: /\.global\.css$/, 50 | use: [ 51 | { 52 | loader: 'style-loader' 53 | }, 54 | { 55 | loader: 'css-loader', 56 | options: { 57 | sourceMap: true, 58 | }, 59 | } 60 | ] 61 | }, 62 | { 63 | test: /^((?!\.global).)*\.css$/, 64 | use: [ 65 | { 66 | loader: 'style-loader' 67 | }, 68 | { 69 | loader: 'css-loader', 70 | options: { 71 | modules: true, 72 | sourceMap: true, 73 | importLoaders: 1, 74 | localIdentName: '[name]__[local]__[hash:base64:5]', 75 | } 76 | }, 77 | ] 78 | }, 79 | // SASS support - compile all .global.scss files and pipe it to style.css 80 | { 81 | test: /\.global\.(scss|sass)$/, 82 | use: [ 83 | { 84 | loader: 'style-loader' 85 | }, 86 | { 87 | loader: 'css-loader', 88 | options: { 89 | sourceMap: true, 90 | }, 91 | }, 92 | { 93 | loader: 'sass-loader' 94 | } 95 | ] 96 | }, 97 | // SASS support - compile all other .scss files and pipe it to style.css 98 | { 99 | test: /^((?!\.global).)*\.(scss|sass)$/, 100 | use: [ 101 | { 102 | loader: 'style-loader' 103 | }, 104 | { 105 | loader: 'css-loader', 106 | options: { 107 | modules: true, 108 | sourceMap: true, 109 | importLoaders: 1, 110 | localIdentName: '[name]__[local]__[hash:base64:5]', 111 | } 112 | }, 113 | { 114 | loader: 'sass-loader' 115 | } 116 | ] 117 | }, 118 | // WOFF Font 119 | { 120 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 121 | use: { 122 | loader: 'url-loader', 123 | options: { 124 | limit: 10000, 125 | mimetype: 'application/font-woff', 126 | } 127 | }, 128 | }, 129 | // WOFF2 Font 130 | { 131 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 132 | use: { 133 | loader: 'url-loader', 134 | options: { 135 | limit: 10000, 136 | mimetype: 'application/font-woff', 137 | } 138 | } 139 | }, 140 | // TTF Font 141 | { 142 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 143 | use: { 144 | loader: 'url-loader', 145 | options: { 146 | limit: 10000, 147 | mimetype: 'application/octet-stream' 148 | } 149 | } 150 | }, 151 | // EOT Font 152 | { 153 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 154 | use: 'file-loader', 155 | }, 156 | // SVG Font 157 | { 158 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 159 | use: { 160 | loader: 'url-loader', 161 | options: { 162 | limit: 10000, 163 | mimetype: 'image/svg+xml', 164 | } 165 | } 166 | }, 167 | // Common Image Formats 168 | { 169 | test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, 170 | use: 'url-loader', 171 | } 172 | ] 173 | }, 174 | 175 | entry: { 176 | renderer: ( 177 | Object 178 | .keys(dependencies || {}) 179 | .filter(dependency => dependency !== 'font-awesome') 180 | ) 181 | }, 182 | 183 | output: { 184 | library: 'renderer', 185 | path: dist, 186 | filename: '[name].dev.dll.js', 187 | libraryTarget: 'var' 188 | }, 189 | 190 | plugins: [ 191 | new webpack.DllPlugin({ 192 | path: path.join(dist, '[name].json'), 193 | name: '[name]', 194 | }), 195 | 196 | /** 197 | * Create global constants which can be configured at compile time. 198 | * 199 | * Useful for allowing different behaviour between development builds and 200 | * release builds 201 | * 202 | * NODE_ENV should be production so that modules do not perform certain 203 | * development checks 204 | */ 205 | new webpack.EnvironmentPlugin({ 206 | NODE_ENV: 'development' 207 | }), 208 | 209 | new webpack.LoaderOptionsPlugin({ 210 | debug: true, 211 | options: { 212 | context: path.resolve(process.cwd(), 'app'), 213 | output: { 214 | path: path.resolve(process.cwd(), 'dll'), 215 | }, 216 | }, 217 | }) 218 | ], 219 | }); 220 | -------------------------------------------------------------------------------- /webpack.config.renderer.dev.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0, import/no-dynamic-require: 0 */ 2 | 3 | /** 4 | * Build config for development electron renderer process that uses 5 | * Hot-Module-Replacement 6 | * 7 | * https://webpack.js.org/concepts/hot-module-replacement/ 8 | */ 9 | 10 | import path from 'path'; 11 | import fs from 'fs'; 12 | import webpack from 'webpack'; 13 | import chalk from 'chalk'; 14 | import merge from 'webpack-merge'; 15 | import { spawn, execSync } from 'child_process'; 16 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 17 | import baseConfig from './webpack.config.base'; 18 | import CheckNodeEnv from './internals/scripts/CheckNodeEnv'; 19 | 20 | CheckNodeEnv('development'); 21 | 22 | const port = process.env.PORT || 1212; 23 | const publicPath = `http://localhost:${port}/dist`; 24 | const dll = path.resolve(process.cwd(), 'dll'); 25 | const manifest = path.resolve(dll, 'renderer.json'); 26 | 27 | /** 28 | * Warn if the DLL is not built 29 | */ 30 | if (!(fs.existsSync(dll) && fs.existsSync(manifest))) { 31 | console.log(chalk.black.bgYellow.bold('The DLL files are missing. Sit back while we build them for you with "npm run build-dll"')); 32 | execSync('npm run build-dll'); 33 | } 34 | 35 | export default merge.smart(baseConfig, { 36 | devtool: 'inline-source-map', 37 | 38 | target: 'electron-renderer', 39 | 40 | entry: [ 41 | 'react-hot-loader/patch', 42 | `webpack-dev-server/client?http://localhost:${port}/`, 43 | 'webpack/hot/only-dev-server', 44 | path.join(__dirname, 'app/index.js'), 45 | ], 46 | 47 | output: { 48 | publicPath: `http://localhost:${port}/dist/`, 49 | filename: 'renderer.dev.js' 50 | }, 51 | 52 | module: { 53 | rules: [ 54 | { 55 | test: /\.jsx?$/, 56 | exclude: /node_modules/, 57 | use: { 58 | loader: 'babel-loader', 59 | options: { 60 | cacheDirectory: true, 61 | plugins: [ 62 | // Here, we include babel plugins that are only required for the 63 | // renderer process. The 'transform-*' plugins must be included 64 | // before react-hot-loader/babel 65 | 'transform-class-properties', 66 | 'transform-es2015-classes', 67 | 'react-hot-loader/babel' 68 | ], 69 | } 70 | } 71 | }, 72 | { 73 | test: /\.less$/, 74 | loader: `style!css!less`, 75 | include: path.resolve(__dirname, 'node_modules'), 76 | }, 77 | { 78 | test: /\.global\.css$/, 79 | use: [ 80 | { 81 | loader: 'style-loader' 82 | }, 83 | { 84 | loader: 'css-loader', 85 | options: { 86 | sourceMap: true, 87 | }, 88 | } 89 | ] 90 | }, 91 | { 92 | test: /^((?!\.global).)*\.css$/, 93 | use: [ 94 | { 95 | loader: 'style-loader' 96 | }, 97 | { 98 | loader: 'css-loader', 99 | options: { 100 | modules: true, 101 | sourceMap: true, 102 | importLoaders: 1, 103 | localIdentName: '[name]__[local]__[hash:base64:5]', 104 | } 105 | }, 106 | ] 107 | }, 108 | // SASS support - compile all .global.scss files and pipe it to style.css 109 | { 110 | test: /\.global\.(scss|sass)$/, 111 | use: [ 112 | { 113 | loader: 'style-loader' 114 | }, 115 | { 116 | loader: 'css-loader', 117 | options: { 118 | sourceMap: true, 119 | }, 120 | }, 121 | { 122 | loader: 'sass-loader' 123 | } 124 | ] 125 | }, 126 | // SASS support - compile all other .scss files and pipe it to style.css 127 | { 128 | test: /^((?!\.global).)*\.(scss|sass)$/, 129 | use: [ 130 | { 131 | loader: 'style-loader' 132 | }, 133 | { 134 | loader: 'css-loader', 135 | options: { 136 | modules: true, 137 | sourceMap: true, 138 | importLoaders: 1, 139 | localIdentName: '[name]__[local]__[hash:base64:5]', 140 | } 141 | }, 142 | { 143 | loader: 'sass-loader' 144 | } 145 | ] 146 | }, 147 | // WOFF Font 148 | { 149 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 150 | use: { 151 | loader: 'url-loader', 152 | options: { 153 | limit: 10000, 154 | mimetype: 'application/font-woff', 155 | } 156 | }, 157 | }, 158 | // WOFF2 Font 159 | { 160 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 161 | use: { 162 | loader: 'url-loader', 163 | options: { 164 | limit: 10000, 165 | mimetype: 'application/font-woff', 166 | } 167 | } 168 | }, 169 | // TTF Font 170 | { 171 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 172 | use: { 173 | loader: 'url-loader', 174 | options: { 175 | limit: 10000, 176 | mimetype: 'application/octet-stream' 177 | } 178 | } 179 | }, 180 | // EOT Font 181 | { 182 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 183 | use: 'file-loader', 184 | }, 185 | // SVG Font 186 | { 187 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 188 | use: { 189 | loader: 'url-loader', 190 | options: { 191 | limit: 10000, 192 | mimetype: 'image/svg+xml', 193 | } 194 | } 195 | }, 196 | // Common Image Formats 197 | { 198 | test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, 199 | use: 'url-loader', 200 | } 201 | ] 202 | }, 203 | 204 | plugins: [ 205 | new webpack.DllReferencePlugin({ 206 | context: process.cwd(), 207 | manifest: require(manifest), 208 | sourceType: 'var', 209 | }), 210 | 211 | new webpack.HotModuleReplacementPlugin({ 212 | multiStep: true 213 | }), 214 | 215 | new webpack.NoEmitOnErrorsPlugin(), 216 | 217 | /** 218 | * Create global constants which can be configured at compile time. 219 | * 220 | * Useful for allowing different behaviour between development builds and 221 | * release builds 222 | * 223 | * NODE_ENV should be production so that modules do not perform certain 224 | * development checks 225 | * 226 | * By default, use 'development' as NODE_ENV. This can be overriden with 227 | * 'staging', for example, by changing the ENV variables in the npm scripts 228 | */ 229 | new webpack.EnvironmentPlugin({ 230 | NODE_ENV: 'development' 231 | }), 232 | 233 | new webpack.LoaderOptionsPlugin({ 234 | debug: true 235 | }), 236 | 237 | new ExtractTextPlugin({ 238 | filename: '[name].css' 239 | }), 240 | ], 241 | 242 | node: { 243 | __dirname: false, 244 | __filename: false 245 | }, 246 | 247 | devServer: { 248 | port, 249 | publicPath, 250 | compress: true, 251 | noInfo: true, 252 | stats: 'errors-only', 253 | inline: true, 254 | lazy: false, 255 | hot: true, 256 | headers: { 'Access-Control-Allow-Origin': '*' }, 257 | contentBase: path.join(__dirname, 'dist'), 258 | watchOptions: { 259 | aggregateTimeout: 300, 260 | ignored: /node_modules/, 261 | poll: 100 262 | }, 263 | historyApiFallback: { 264 | verbose: true, 265 | disableDotRule: false, 266 | }, 267 | before() { 268 | if (process.env.START_HOT) { 269 | console.log('Starting Main Process...'); 270 | spawn( 271 | 'npm', 272 | ['run', 'start-main-dev'], 273 | { shell: true, env: process.env, stdio: 'inherit' } 274 | ) 275 | .on('close', code => process.exit(code)) 276 | .on('error', spawnError => console.error(spawnError)); 277 | } 278 | } 279 | }, 280 | }); 281 | -------------------------------------------------------------------------------- /webpack.config.renderer.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Build config for electron renderer process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 8 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 9 | import merge from 'webpack-merge'; 10 | import UglifyJSPlugin from 'uglifyjs-webpack-plugin'; 11 | import baseConfig from './webpack.config.base'; 12 | import CheckNodeEnv from './internals/scripts/CheckNodeEnv'; 13 | 14 | CheckNodeEnv('production'); 15 | 16 | export default merge.smart(baseConfig, { 17 | devtool: 'source-map', 18 | 19 | target: 'electron-renderer', 20 | 21 | entry: './app/index', 22 | 23 | output: { 24 | path: path.join(__dirname, 'app/dist'), 25 | publicPath: './dist/', 26 | filename: 'renderer.prod.js' 27 | }, 28 | 29 | module: { 30 | rules: [ 31 | // Extract all .global.css to style.css as is 32 | { 33 | test: /\.global\.css$/, 34 | use: ExtractTextPlugin.extract({ 35 | publicPath: './', 36 | use: { 37 | loader: 'css-loader', 38 | options: { 39 | minimize: true, 40 | } 41 | }, 42 | fallback: 'style-loader', 43 | }) 44 | }, 45 | // Pipe other styles through css modules and append to style.css 46 | { 47 | test: /^((?!\.global).)*\.css$/, 48 | use: ExtractTextPlugin.extract({ 49 | use: { 50 | loader: 'css-loader', 51 | options: { 52 | modules: true, 53 | minimize: true, 54 | importLoaders: 1, 55 | localIdentName: '[name]__[local]__[hash:base64:5]', 56 | } 57 | } 58 | }), 59 | }, 60 | // Add SASS support - compile all .global.scss files and pipe it to style.css 61 | { 62 | test: /\.global\.(scss|sass)$/, 63 | use: ExtractTextPlugin.extract({ 64 | use: [ 65 | { 66 | loader: 'css-loader', 67 | options: { 68 | minimize: true, 69 | } 70 | }, 71 | { 72 | loader: 'sass-loader' 73 | } 74 | ], 75 | fallback: 'style-loader', 76 | }) 77 | }, 78 | // Add SASS support - compile all other .scss files and pipe it to style.css 79 | { 80 | test: /^((?!\.global).)*\.(scss|sass)$/, 81 | use: ExtractTextPlugin.extract({ 82 | use: [{ 83 | loader: 'css-loader', 84 | options: { 85 | modules: true, 86 | minimize: true, 87 | importLoaders: 1, 88 | localIdentName: '[name]__[local]__[hash:base64:5]', 89 | } 90 | }, 91 | { 92 | loader: 'sass-loader' 93 | }] 94 | }), 95 | }, 96 | // WOFF Font 97 | { 98 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 99 | use: { 100 | loader: 'url-loader', 101 | options: { 102 | limit: 10000, 103 | mimetype: 'application/font-woff', 104 | } 105 | }, 106 | }, 107 | // WOFF2 Font 108 | { 109 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 110 | use: { 111 | loader: 'url-loader', 112 | options: { 113 | limit: 10000, 114 | mimetype: 'application/font-woff', 115 | } 116 | } 117 | }, 118 | // TTF Font 119 | { 120 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 121 | use: { 122 | loader: 'url-loader', 123 | options: { 124 | limit: 10000, 125 | mimetype: 'application/octet-stream' 126 | } 127 | } 128 | }, 129 | // EOT Font 130 | { 131 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 132 | use: 'file-loader', 133 | }, 134 | // SVG Font 135 | { 136 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 137 | use: { 138 | loader: 'url-loader', 139 | options: { 140 | limit: 10000, 141 | mimetype: 'image/svg+xml', 142 | } 143 | } 144 | }, 145 | // Common Image Formats 146 | { 147 | test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, 148 | use: 'url-loader', 149 | } 150 | ] 151 | }, 152 | 153 | plugins: [ 154 | /** 155 | * Create global constants which can be configured at compile time. 156 | * 157 | * Useful for allowing different behaviour between development builds and 158 | * release builds 159 | * 160 | * NODE_ENV should be production so that modules do not perform certain 161 | * development checks 162 | */ 163 | new webpack.EnvironmentPlugin({ 164 | NODE_ENV: 'production' 165 | }), 166 | 167 | new UglifyJSPlugin({ 168 | parallel: true, 169 | sourceMap: true 170 | }), 171 | 172 | new ExtractTextPlugin('style.css'), 173 | 174 | new BundleAnalyzerPlugin({ 175 | analyzerMode: process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 176 | openAnalyzer: process.env.OPEN_ANALYZER === 'true' 177 | }), 178 | ], 179 | }); 180 | --------------------------------------------------------------------------------