Repository: phobal/ivideo
Branch: master
Commit: e9a26b66177a
Files: 68
Total size: 76.3 KB
Directory structure:
gitextract_9m4e08g_/
├── .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.json
│ ├── reducers/
│ │ ├── index.js
│ │ └── source.js
│ ├── routes.js
│ ├── store/
│ │ ├── configureStore.dev.js
│ │ ├── configureStore.js
│ │ └── configureStore.prod.js
│ └── utils/
│ ├── .gitkeep
│ └── fetch.js
├── appveyor.yml
├── flow-typed/
│ └── module_vx.x.x.js
├── internals/
│ ├── flow/
│ │ ├── CSSModule.js.flow
│ │ └── WebpackAsset.js.flow
│ ├── mocks/
│ │ └── fileMock.js
│ └── scripts/
│ ├── CheckBuiltsExist.js
│ ├── CheckNativeDep.js
│ ├── CheckNodeEnv.js
│ ├── CheckPortInUse.js
│ ├── ElectronRebuild.js
│ └── RunTests.js
├── package.json
├── resources/
│ ├── icon.icns
│ └── 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
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [
["env", {
"targets": { "node": 7 },
"useBuiltIns": true
}],
"stage-0",
"react"
],
"plugins": ["add-module-exports",
["import",
{
"libraryName": "antd",
"style": "css"
}
]
],
"env": {
"production": {
"presets": ["react-optimize"],
"plugins": ["dev-expression"]
},
"development": {
"plugins": [
"transform-class-properties",
"transform-es2015-classes",
["flow-runtime", {
"assert": true,
"annotate": true
}]
]
}
}
}
================================================
FILE: .dockerignore
================================================
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
app/node_modules
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
# App packaged
release
app/main.prod.js
app/main.prod.js.map
app/renderer.prod.js
app/renderer.prod.js.map
app/style.css
app/style.css.map
dist
dll
main.js
main.js.map
.idea
npm-debug.log.*
.*.dockerfile
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .eslintignore
================================================
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
app/node_modules
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
# App packaged
release
app/main.prod.js
app/main.prod.js.map
app/renderer.prod.js
app/renderer.prod.js.map
app/style.css
app/style.css.map
dist
dll
main.js
main.js.map
.idea
npm-debug.log.*
__snapshots__
================================================
FILE: .eslintrc
================================================
{
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": true
},
"extends": "airbnb",
"env": {
"browser": true,
"node": true
},
"rules": {
"arrow-parens": ["off"],
"compat/compat": "error",
"consistent-return": "off",
"comma-dangle": "off",
"generator-star-spacing": "off",
"import/no-unresolved": "error",
"import/no-extraneous-dependencies": "off",
"jsx-a11y/anchor-is-valid": "off",
"no-console": "off",
"no-use-before-define": "off",
"no-multi-assign": "off",
"promise/param-names": "error",
"promise/always-return": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
"react/sort-comp": ["error", {
"order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"]
}],
"react/jsx-no-bind": "off",
"react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
"react/prefer-stateless-function": "off"
},
"plugins": [
"flowtype",
"import",
"promise",
"compat",
"react"
],
"settings": {
"import/resolver": {
"webpack": {
"config": "webpack.config.eslint.js"
}
}
}
}
================================================
FILE: .flowconfig
================================================
[ignore]
<PROJECT_ROOT>/node_modules/*
<PROJECT_ROOT>/app/main.prod.js
<PROJECT_ROOT>/app/main.prod.js.map
<PROJECT_ROOT>/app/dist/.*
<PROJECT_ROOT>/resources/.*
<PROJECT_ROOT>/release/.*
<PROJECT_ROOT>/dll/.*
<PROJECT_ROOT>/release/.*
<PROJECT_ROOT>/git/.*
[include]
[libs]
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.export_star_as=enable
module.name_mapper.extension='css' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='styl' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='scss' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='png' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow'
module.name_mapper.extension='jpg' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow'
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
================================================
FILE: .gitattributes
================================================
* text eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.ico binary
*.icns binary
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
app/node_modules
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
# App packaged
release
app/main.prod.js
app/main.prod.js.map
app/renderer.prod.js
app/renderer.prod.js.map
app/style.css
app/style.css.map
dist
dll
main.js
main.js.map
.idea
npm-debug.log.*
================================================
FILE: .stylelintrc
================================================
{
"extends": "stylelint-config-standard"
}
================================================
FILE: .travis.yml
================================================
sudo: true
language: node_js
node_js:
- 8
- 7
cache:
yarn: true
directories:
- node_modules
- app/node_modules
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- icnsutils
- graphicsmagick
- xz-utils
- xorriso
install:
- export CXX="g++-4.8"
- yarn
- cd app && yarn && cd ..
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start &
- sleep 3
script:
- node --version
- yarn lint
- yarn package
- yarn test
- yarn test-e2e
================================================
FILE: .vscode/settings.json
================================================
{
"javascript.validate.enable": false,
"flow.useNPMPackagedFlow": true,
"search.exclude": {
".git": true,
".eslintcache": true,
"app/dist": true,
"app/main.prod.js": true,
"app/main.prod.js.map": true,
"bower_components": true,
"dll": true,
"flow-typed": true,
"release": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"yarn.lock": true
}
}
================================================
FILE: CHANGELOG.md
================================================
* 2018.5.5
修改页面样式,当全屏时隐藏掉左侧菜单栏和顶部接口切换栏
* 2018.07.13
修改线路下拉框不能出现滚动条的 bug
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015-present C. T. Lin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
### i视频
#### 产品介绍
> 基于 Electron 开发的跨平台客户端版本的视频播放器,该播放器包括国内主流视频平台视频资源,你不用去单独下载各个平台的客户端,只需要使用这一个客户端就能查看所有平台的视频,并且内置了各大视频网站 VIP 资源。
#### 使用方法
1. 下载客户端
* [Mac](https://github.com/phobal/ivideo/releases/download/v1.1.4/ivideo-1.1.4.dmg.zip)
* [Windows](https://github.com/phobal/ivideo/releases/download/v1.1.4/ivideo.Setup.1.1.4.exe.zip)
* [Linux](https://github.com/phobal/ivideo/releases/download/1.0.0/linux-unpacked.v1.0.0.zip)
2. 选择视频资源
比方说看腾讯视频上的 VIP 才能看的《下一站,别离》

点击进去以后提示需要开通VIP才能看

3. 选择资源播放接口

点击【确定】按钮就可以播放了,如果遇到无法播放的情况,请多换几条线路试试

### 技术栈
* Electron
* React
* Redux
### 如何启动
> node version >= 7.6
1. clone 项目到本地
``` bash
git clone https://github.com/phobal/ivideo.git
```
2. 进入项目 ` cd ivideo`
3. 安装依赖 `yarn install`(如果没有的话,请全局安装yarn, `npm i yarn -g`)
4. 打开开发环境 `yarn start`
### 如何编译
* 编译全平台 ` yarn package-all`
* 编译当前平台 `yarn package`
* windows: `yarn package-win`
* Linux `yarn package-linux`
编译出来的包都放在 `release` 目录下
该项目是基于 [electron-react-boilerplate](https://github.com/chentsulin/electron-react-boilerplate) 脚手架 进行创建,感谢 @[chentsulin](https://github.com/chentsulin)
# 最后请大家低调使用,祝大家看得舒心
## 本项目仅作为个人学习用途,如有侵权请联系我删除该仓库
================================================
FILE: app/.eslintrc
================================================
{
"rules": {
"flowtype/boolean-style": ["error", "boolean"],
"flowtype/define-flow-type": "warn",
"flowtype/delimiter-dangle": ["error", "never"],
"flowtype/generic-spacing": ["error", "never"],
"flowtype/no-primitive-constructor-types": "error",
"flowtype/no-weak-types": "warn",
"flowtype/object-type-delimiter": ["error", "comma"],
"flowtype/require-parameter-type": "off",
"flowtype/require-return-type": "off",
"flowtype/require-valid-file-annotation": "off",
"flowtype/semi": ["error", "always"],
"flowtype/space-after-type-colon": ["error", "always"],
"flowtype/space-before-generic-bracket": ["error", "never"],
"flowtype/space-before-type-colon": ["error", "never"],
"flowtype/union-intersection-spacing": ["error", "always"],
"flowtype/use-flow-type": "error",
"flowtype/valid-syntax": "error"
}
}
================================================
FILE: app/actions/source.js
================================================
import * as api from '../utils/fetch';
export function getAllVideoSource() {
return (dispatch) => {
api.source.getAllVideoSource().then((res) =>
dispatch({
type: 'GETALLVIDEOSOURCE',
payload: res.data
}));
};
}
================================================
FILE: app/app.global.css
================================================
/*
* @NOTE: Prepend a `~` to css file paths that are in your node_modules
* See https://github.com/webpack-contrib/sass-loader#imports
*/
@import "~font-awesome/css/font-awesome.css";
@import "~rc-menu/assets/index.css";
/* @import "~antd/lib/style/index.css"; */
@import '~rc-select/assets/index.css';
* {
margin: 0;
padding: 0;
}
body {
position: relative;
color:#000;
height: 100vh;
/* background-color: #232c39; */
/* background-image: linear-gradient(45deg, rgba(0, 216, 255, 0.5) 10%, rgba(0, 1, 127, 0.7)); */
font-family: Arial, Helvetica, Helvetica Neue, serif;
overflow-y: hidden;
}
h2 {
margin: 0;
font-size: 2.25rem;
font-weight: bold;
letter-spacing: -0.025em;
color: #fff;
}
p {
font-size: 24px;
}
li {
list-style: none;
}
a {
color: white;
opacity: 0.75;
text-decoration: none;
}
a:hover {
opacity: 1;
text-decoration: none;
cursor: pointer;
}
.rc-select-dropdown-menu {
max-height: 400px;
}
================================================
FILE: app/app.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>i视频</title>
<script>
(function() {
if (!process.env.HOT) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './dist/style.css';
// HACK: Writing the script path should be done with webpack
document.getElementsByTagName('head')[0].appendChild(link);
}
}());
</script>
</head>
<body>
<div id="root"></div>
<script>
{
const scripts = [];
// Dynamically insert the DLL script in development env in the
// renderer process
if (process.env.NODE_ENV === 'development') {
scripts.push('../dll/renderer.dev.dll.js');
}
// Dynamically insert the bundled app script in the renderer process
const port = process.env.PORT || 1212;
scripts.push(
(process.env.HOT)
? 'http://localhost:' + port + '/dist/renderer.dev.js'
: './dist/renderer.prod.js'
);
document.write(
scripts
.map(script => '<script defer src="' + script + '"><\/script>')
.join('')
);
}
</script>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?5c4119b9e77388039645dd22dd9b5a26";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</body>
</html>
================================================
FILE: app/components/.githook
================================================
================================================
FILE: app/containers/App.js
================================================
// @flow
import * as React from 'react';
type Props = {
children: React.Node
};
export default class App extends React.Component<Props> {
props: Props;
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
================================================
FILE: app/containers/Channel.js
================================================
import React from 'react';
import Menu, { Item as MenuItem } from 'rc-menu';
const Channel = ({ channel, handleSwitchChannel }) => {
const item = channel.map((d) => <MenuItem key={d.url}>{d.name}</MenuItem>);
return <Menu onSelect={handleSwitchChannel}>{item}</Menu>;
};
export default Channel;
================================================
FILE: app/containers/Frame.js
================================================
// @flow
import React from 'react'
import Channel from './Channel'
import ToolBar from './ToolBar'
export const Frame = ({
onComeback,
onSourceSelected,
onSwitchSource,
handleSwitchChannel,
channel,
url,
freeUrl,
title,
isFullScreen,
children
}) => {
const isHiddenStyle = isFullScreen ? { display: 'none' } : { display: 'flex' }
return (
<div style={{ display: 'flex' }}>
<div style={{ minWidth: '150px', ...isHiddenStyle }}>
<Channel channel={channel} handleSwitchChannel={handleSwitchChannel} />
</div>
<div
style={{
height: '100vh',
width: isFullScreen ? '100vw' : 'calc(100vw - 150px)',
display: 'flex',
flexDirection: 'column'
}}
>
<div style={{ height: '60px', ...isHiddenStyle }}>
<ToolBar
onComeback={onComeback}
onSourceSelected={onSourceSelected}
onSwitchSource={onSwitchSource}
freeUrl={freeUrl}
title={title}
/>
</div>
{children}
</div>
</div>
)
}
export default Frame
================================================
FILE: app/containers/Root.js
================================================
// @flow
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import Routes from '../routes';
type Props = {
store: {},
history: {}
};
export default class Root extends Component<Props> {
render() {
return (
<Provider store={this.props.store}>
<ConnectedRouter history={this.props.history}>
<Routes />
</ConnectedRouter>
</Provider>
);
}
}
================================================
FILE: app/containers/ToolBar.js
================================================
import React from 'react';
import Select, { Option } from 'rc-select';
// import { Icon, Select, Button } from 'antd';
const ToolBar = ({ onComeback, onSwitchSource, onSourceSelected, freeUrl, title }) => {
const options = freeUrl.map(d => {
return (
<Option key={d.name} value={d.name}>{d.name}</Option>
)
})
return (
<div style={{ display: 'flex', height: '100%', alignItems: 'center', justifyContent: 'space-between', padding: '0 20px' }}>
<div type="rollback" style={{ fontSize: '18px', cursor: 'pointer' }} onClick={onComeback}>返回</div>
<span style={{ padding: '0 20px', color: 'darkcyan' }}>{title}</span>
<div
style={{ display: 'flex', alignItems: 'center' }}
>
<Select
style={{ width: '200px' }}
placeholder="请选择路线"
onSelect={onSourceSelected}
>
{options}
</Select>
<div
style={{ width: '100px', height: '40px', lineHeight: '40px', cursor: 'pointer', background: '#2196f3', color: '#fff', textAlign: 'center', marginLeft: '20px' }}
onClick={onSwitchSource}
>确定</div>
</div>
</div>
)
}
export default ToolBar;
================================================
FILE: app/containers/Video.js
================================================
// @flow
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { webview, ipcRenderer } from 'electron';
import Channel from './Channel';
import ToolBar from './ToolBar';
import Frame from './Frame';
import * as sourceActions from '../actions/source';
class VideoPlay extends PureComponent<Props> {
constructor(props) {
super(props);
this.handleSwitchChannel = this.handleSwitchChannel.bind(this);
this.onComeback = this.onComeback.bind(this);
this.onSourceSelected = this.onSourceSelected.bind(this);
this.onSwitchSource = this.onSwitchSource.bind(this);
}
state = {
channel: [],
url: 'https://v.qq.com',
freeUrl: [],
selectedUrl: 'http://vip.jlsprh.com/index.php?url=',
isFullScreen: false
}
componentDidMount() {
this.props.actions.getAllVideoSource();
const webView = this.webview;
webView.addEventListener('dom-ready', () => {
this.setTitle();
});
webView.addEventListener('new-window', (obj) => {
this.setState({
url: `${obj.url}`
});
});
webView.addEventListener('will-navigate', (obj) => {
this.setState({
url: `${obj.url}`
});
});
ipcRenderer.on('enter-full-screen', (e, msg) => {
this.setState({
isFullScreen: msg
});
});
}
componentWillReceiveProps(nextProps) {
const { source } = nextProps;
if (source) {
this.setState({
channel: source.platformlist,
freeUrl: source.list
});
}
}
handleSwitchChannel(value) {
this.setState({
url: value.key
});
}
setTitle() {
const title = this.webview.getTitle();
this.setState({
title
});
}
onComeback() {
this.webview.goBack();
}
onSourceSelected(value) {
const selectedUrl = this.state.freeUrl.find((d) => {
if (d.name === value) {
return d.url;
}
});
this.setState({
selectedUrl
});
}
onSwitchSource() {
const { selectedUrl } = this.state;
const currentVideoUrl = this.webview.getURL();
this.setState({
url: `${selectedUrl.url}${currentVideoUrl}`
});
}
render() {
const {
channel, url, freeUrl, title, isFullScreen
} = this.state;
return (
<Frame
onComeback={this.onComeback}
onSourceSelected={this.onSourceSelected}
onSwitchSource={this.onSwitchSource}
handleSwitchChannel={this.handleSwitchChannel}
{...{
channel,
url,
freeUrl,
title,
isFullScreen
}}
>
<webview
ref={(webview) => {
this.webview = webview;
}}
title="腾讯视频"
style={{
height: isFullScreen ? '100vh' : 'calc(100vh - 60px)',
width: '100%'
}}
src={url}
allowpopups="true"
plugins
/>
</Frame>
);
}
}
function mapDispatchToProps(dispatch) {
return {
actions: {
...bindActionCreators(sourceActions, dispatch)
}
};
}
function mapStateToProps(state) {
return {
source: state.source
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(VideoPlay);
================================================
FILE: app/index.js
================================================
import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import Root from './containers/Root';
import { configureStore, history } from './store/configureStore';
import './app.global.css';
const store = configureStore();
render(
<AppContainer>
<Root store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
if (module.hot) {
module.hot.accept('./containers/Root', () => {
const NextRoot = require('./containers/Root'); // eslint-disable-line global-require
render(
<AppContainer>
<NextRoot store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
});
}
================================================
FILE: app/main.dev.js
================================================
/* eslint global-require: 0, flowtype-errors/show-errors: 0 */
/**
* This module executes inside of electron's main process. You can start
* electron renderer process from here and communicate with the other processes
* through IPC.
*
* When running `npm run build` or `npm run build-main`, this file is compiled to
* `./app/main.prod.js` using webpack. This gives us some performance wins.
*
* @flow
*/
import { app, BrowserWindow, ipcMain } from 'electron';
import MenuBuilder from './menu';
let mainWindow = null;
if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
}
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
require('electron-debug')();
const path = require('path');
const p = path.join(__dirname, '..', 'app', 'node_modules');
require('module').globalPaths.push(p);
}
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = [
'REACT_DEVELOPER_TOOLS',
'REDUX_DEVTOOLS'
];
return Promise
.all(extensions.map(name => installer.default(installer[name], forceDownload)))
.catch(console.log);
};
// Try to append Pepper flash. See https://github.com/electron/electron/blob/master/docs/tutorial/using-pepper-flash-plugin.md
if (process.platform === 'darwin' && app.getPath("pepperFlashSystemPlugin")) {
app.commandLine.appendSwitch(
"ppapi-flash-path",
app.getPath("pepperFlashSystemPlugin")
);
}
/**
* Add event listeners...
*/
app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('ready', async () => {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
await installExtensions();
}
mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728,
webPreferences: {
plugins: true
}
});
mainWindow.loadURL(`file://${__dirname}/app.html`);
// @TODO: Use 'ready-to-show' event
// https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
mainWindow.webContents.on('did-finish-load', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
mainWindow.show();
mainWindow.focus();
});
mainWindow.on('closed', () => {
mainWindow = null;
});
mainWindow.on('enter-full-screen', () => {
mainWindow.webContents.send('enter-full-screen', true);
})
mainWindow.on('leave-full-screen', () => {
mainWindow.webContents.send('enter-full-screen', false);
})
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
});
================================================
FILE: app/menu.js
================================================
// @flow
import { app, Menu, shell, BrowserWindow } from 'electron';
export default class MenuBuilder {
mainWindow: BrowserWindow;
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
buildMenu() {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
}
const template = process.platform === 'darwin'
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
return menu;
}
setupDevelopmentEnvironment() {
this.mainWindow.openDevTools();
this.mainWindow.webContents.on('context-menu', (e, props) => {
const { x, y } = props;
Menu
.buildFromTemplate([{
label: 'Inspect element',
click: () => {
this.mainWindow.inspectElement(x, y);
}
}])
.popup(this.mainWindow);
});
}
buildDarwinTemplate() {
const subMenuAbout = {
label: 'Electron',
submenu: [
{ label: 'About ElectronReact', selector: 'orderFrontStandardAboutPanel:' },
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{ label: 'Hide ElectronReact', accelerator: 'Command+H', selector: 'hide:' },
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' },
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{ label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } }
]
};
const subMenuEdit = {
label: 'Edit',
submenu: [
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
{ label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
{ type: 'separator' },
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
{ label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
]
};
const subMenuViewDev = {
label: 'View',
submenu: [
{ label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } },
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } },
{ label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', click: () => { this.mainWindow.toggleDevTools(); } }
]
};
const subMenuViewProd = {
label: 'View',
submenu: [
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } }
]
};
const subMenuWindow = {
label: 'Window',
submenu: [
{ label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' }
]
};
const subMenuHelp = {
label: 'Help',
submenu: [
{ label: 'Learn More', click() { shell.openExternal('http://electron.atom.io'); } },
{ label: 'Documentation', click() { shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); } },
{ label: 'Community Discussions', click() { shell.openExternal('https://discuss.atom.io/c/electron'); } },
{ label: 'Search Issues', click() { shell.openExternal('https://github.com/atom/electron/issues'); } }
]
};
const subMenuView = process.env.NODE_ENV === 'development'
? subMenuViewDev
: subMenuViewProd;
return [
subMenuAbout,
subMenuEdit,
subMenuView,
subMenuWindow,
subMenuHelp
];
}
buildDefaultTemplate() {
const templateDefault = [{
label: '&File',
submenu: [{
label: '&Open',
accelerator: 'Ctrl+O'
}, {
label: '&Close',
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
}
}]
}, {
label: '&View',
submenu: (process.env.NODE_ENV === 'development') ? [{
label: '&Reload',
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
}
}, {
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
}
}, {
label: 'Toggle &Developer Tools',
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.toggleDevTools();
}
}] : [{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
}
}]
}, {
label: 'Help',
submenu: [{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
}
}, {
label: 'Documentation',
click() {
shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
}
}, {
label: 'Community Discussions',
click() {
shell.openExternal('https://discuss.atom.io/c/electron');
}
}, {
label: 'Search Issues',
click() {
shell.openExternal('https://github.com/atom/electron/issues');
}
}]
}];
return templateDefault;
}
}
================================================
FILE: app/package.json
================================================
{
"name": "electron-react-boilerplate",
"productName": "electron-react-boilerplate",
"version": "1.1.4",
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
"main": "./main.prod.js",
"author": {
"name": "C. T. Lin",
"email": "chentsulin@gmail.com",
"url": "https://github.com/chentsulin"
},
"scripts": {
"electron-rebuild": "node -r babel-register ../internals/scripts/ElectronRebuild.js",
"postinstall": "npm run electron-rebuild"
},
"license": "MIT",
"dependencies": {}
}
================================================
FILE: app/reducers/index.js
================================================
// @flow
import { combineReducers } from 'redux';
import { routerReducer as router } from 'react-router-redux';
import source from './source';
const rootReducer = combineReducers({
router,
source,
});
export default rootReducer;
================================================
FILE: app/reducers/source.js
================================================
export default function source(state = null, action) {
switch(action.type) {
case 'GETALLVIDEOSOURCE':
return action.payload;
default:
return state;
}
}
================================================
FILE: app/routes.js
================================================
/* eslint flowtype-errors/show-errors: 0 */
import React from 'react';
import { Switch, Route } from 'react-router';
import App from './containers/App';
import Video from './containers/Video';
export default () => (
<App>
<Switch>
<Route path="/" component={Video} />
</Switch>
</App>
);
================================================
FILE: app/store/configureStore.dev.js
================================================
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { createHashHistory } from 'history';
import { routerMiddleware, routerActions } from 'react-router-redux';
import { createLogger } from 'redux-logger';
import rootReducer from '../reducers';
// import type { counterStateType } from '../reducers/counter';
const history = createHashHistory();
const configureStore = (initialState) => {
// Redux Configuration
const middleware = [];
const enhancers = [];
// Thunk Middleware
middleware.push(thunk);
// Logging Middleware
const logger = createLogger({
level: 'info',
collapsed: true
});
// Skip redux logs in console during the tests
if (process.env.NODE_ENV !== 'test') {
middleware.push(logger);
}
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Redux DevTools Configuration
const actionCreators = {
...routerActions,
};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
actionCreators,
})
: compose;
/* eslint-enable no-underscore-dangle */
// Apply Middleware & Compose Enhancers
enhancers.push(applyMiddleware(...middleware));
const enhancer = composeEnhancers(...enhancers);
// Create Store
const store = createStore(rootReducer, initialState, enhancer);
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers'))); // eslint-disable-line global-require
}
return store;
};
export default { configureStore, history };
================================================
FILE: app/store/configureStore.js
================================================
// @flow
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStore.prod'); // eslint-disable-line global-require
} else {
module.exports = require('./configureStore.dev'); // eslint-disable-line global-require
}
================================================
FILE: app/store/configureStore.prod.js
================================================
// @flow
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { createBrowserHistory } from 'history';
import { routerMiddleware } from 'react-router-redux';
import rootReducer from '../reducers';
import type { counterStateType } from '../reducers/counter';
const history = createBrowserHistory();
const router = routerMiddleware(history);
const enhancer = applyMiddleware(thunk, router);
function configureStore(initialState?: counterStateType) {
return createStore(rootReducer, initialState, enhancer);
}
export default { configureStore, history };
================================================
FILE: app/utils/.gitkeep
================================================
================================================
FILE: app/utils/fetch.js
================================================
import axios from 'axios';
const BASEURL =
'https://raw.githubusercontent.com/phobal/ivideo/master/resources/viplist.json';
const instance = axios.create({
baseURL: BASEURL,
timeout: 10000
});
const createAPI = (url, method, config) => {
config = config || {} // eslint-disable-line
return instance({
url,
method,
...config
});
};
const source = {
getAllVideoSource: (config) => createAPI('', 'GET', config)
};
export { source };
================================================
FILE: appveyor.yml
================================================
os: unstable
environment:
matrix:
- nodejs_version: 8
- nodejs_version: 7
cache:
- "%LOCALAPPDATA%/Yarn"
- node_modules -> package.json
- app/node_modules -> app/package.json
matrix:
fast_finish: true
build: off
version: '{build}'
shallow_clone: true
clone_depth: 1
install:
- ps: Install-Product node $env:nodejs_version
- set CI=true
- yarn
- cd app && yarn
test_script:
- node --version
- yarn lint
- yarn package
- yarn test
- yarn test-e2e
================================================
FILE: flow-typed/module_vx.x.x.js
================================================
declare module 'module' {
declare module.exports: any;
}
================================================
FILE: internals/flow/CSSModule.js.flow
================================================
// @flow
declare export default { [key: string]: string }
================================================
FILE: internals/flow/WebpackAsset.js.flow
================================================
// @flow
declare export default string
================================================
FILE: internals/mocks/fileMock.js
================================================
export default 'test-file-stub';
================================================
FILE: internals/scripts/CheckBuiltsExist.js
================================================
// @flow
// Check if the renderer and main bundles are built
import path from 'path';
import chalk from 'chalk';
import fs from 'fs';
function CheckBuildsExist() {
const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js');
const rendererPath = path.join(__dirname, '..', '..', 'app', 'dist', 'renderer.prod.js');
if (!fs.existsSync(mainPath)) {
throw new Error(chalk.whiteBright.bgRed.bold('The main process is not built yet. Build it by running "npm run build-main"'));
}
if (!fs.existsSync(rendererPath)) {
throw new Error(chalk.whiteBright.bgRed.bold('The renderer process is not built yet. Build it by running "npm run build-renderer"'));
}
}
CheckBuildsExist();
================================================
FILE: internals/scripts/CheckNativeDep.js
================================================
// @flow
import fs from 'fs';
import chalk from 'chalk';
import { execSync } from 'child_process';
import { dependencies } from '../../package.json';
(() => {
if (!dependencies) return;
const dependenciesKeys = Object.keys(dependencies);
const nativeDeps =
fs.readdirSync('node_modules')
.filter(folder => fs.existsSync(`node_modules/${folder}/binding.gyp`));
try {
// Find the reason for why the dependency is installed. If it is installed
// because of a devDependency then that is okay. Warn when it is installed
// because of a dependency
const dependenciesObject = JSON.parse(execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString());
const rootDependencies = Object.keys(dependenciesObject.dependencies);
const filteredRootDependencies = rootDependencies
.filter(rootDependency => dependenciesKeys.includes(rootDependency));
if (filteredRootDependencies.length > 0) {
const plural = filteredRootDependencies.length > 1;
console.log(`
${chalk.whiteBright.bgYellow.bold('Webpack does not work with native dependencies.')}
${chalk.bold(filteredRootDependencies.join(', '))} ${plural ? 'are native dependencies' : 'is a native dependency'} and should be installed inside of the "./app" folder.
First uninstall the packages from "./package.json":
${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')}
${chalk.bold('Then, instead of installing the package to the root "./package.json":')}
${chalk.whiteBright.bgRed.bold('npm install your-package --save')}
${chalk.bold('Install the package to "./app/package.json"')}
${chalk.whiteBright.bgGreen.bold('cd ./app && npm install your-package --save')}
Read more about native dependencies at:
${chalk.bold('https://github.com/chentsulin/electron-react-boilerplate/wiki/Module-Structure----Two-package.json-Structure')}
`);
process.exit(1);
}
} catch (e) {
console.log('Native dependencies could not be checked');
}
})();
================================================
FILE: internals/scripts/CheckNodeEnv.js
================================================
// @flow
import chalk from 'chalk';
export default function CheckNodeEnv(expectedEnv: string) {
if (!expectedEnv) {
throw new Error('"expectedEnv" not set');
}
if (process.env.NODE_ENV !== expectedEnv) {
console.log(chalk.whiteBright.bgRed.bold(`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`));
process.exit(2);
}
}
================================================
FILE: internals/scripts/CheckPortInUse.js
================================================
// @flow
import chalk from 'chalk';
import detectPort from 'detect-port';
(function CheckPortInUse() {
const port: string = process.env.PORT || '1212';
detectPort(port, (err: ?Error, availablePort: number) => {
if (port !== String(availablePort)) {
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`));
} else {
process.exit(0);
}
});
}());
================================================
FILE: internals/scripts/ElectronRebuild.js
================================================
// @flow
import path from 'path';
import { execSync } from 'child_process';
import fs from 'fs';
import dependencies from '../../app/package.json';
const nodeModulesPath =
path.join(__dirname, '..', '..', 'app', 'node_modules');
if (Object.keys(dependencies || {}).length > 0 && fs.existsSync(nodeModulesPath)) {
const electronRebuildCmd =
'../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .';
const cmd = process.platform === 'win32'
? electronRebuildCmd.replace(/\//g, '\\')
: electronRebuildCmd;
execSync(cmd, {
cwd: path.join(__dirname, '..', '..', 'app')
});
}
================================================
FILE: internals/scripts/RunTests.js
================================================
import spawn from 'cross-spawn';
import path from 'path';
const pattern = process.argv[2] === 'e2e'
? 'test/e2e/.+\\.spec\\.js'
: 'test/(?!e2e/)[^/]+/.+\\.spec\\.js$';
const result = spawn.sync(
path.normalize('./node_modules/.bin/jest'),
[pattern, ...process.argv.slice(2)],
{ stdio: 'inherit' }
);
process.exit(result.status);
================================================
FILE: package.json
================================================
{
"name": "ivideo",
"productName": "ivideo",
"version": "1.1.4",
"description": "一个视频播放器观看国内主流视频网站,不用单独下载各个平台客户端",
"scripts": {
"build": "concurrently \"npm run build-main\" \"npm run build-renderer\"",
"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",
"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",
"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",
"dev": "cross-env START_HOT=1 node -r babel-register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 npm run start-renderer-dev",
"electron-rebuild": "electron-rebuild --parallel --force --types prod,dev,optional --module-dir app",
"flow": "flow",
"flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true",
"lint": "cross-env NODE_ENV=development eslint --cache --format=node_modules/eslint-formatter-pretty .",
"lint-fix": "npm run lint -- --fix",
"lint-styles": "stylelint app/*.css app/components/*.css --syntax scss",
"lint-styles-fix": "stylefmt -r app/*.css app/components/*.css",
"package": "npm run build && build --publish never",
"package-all": "npm run build && build -mwl",
"package-linux": "npm run build && build --linux",
"package-win": "npm run build && build --win --x64",
"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",
"prestart": "npm run build",
"start": "cross-env NODE_ENV=production electron ./app/",
"start-main-dev": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main.dev",
"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",
"test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings -r babel-register ./internals/scripts/RunTests.js",
"test-all": "npm run lint && npm run flow && npm run build && npm run test && npm run test-e2e",
"test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings -r babel-register ./internals/scripts/RunTests.js e2e",
"test-watch": "npm test -- --watch"
},
"browserslist": "electron 1.6",
"build": {
"productName": "ivideo",
"appId": "org.phobal.ivideo",
"files": [
"dist/",
"node_modules/",
"app.html",
"main.prod.js",
"main.prod.js.map",
"package.json"
],
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
}
]
},
"linux": {
"target": [
"deb",
"AppImage"
],
"category": "Development"
},
"directories": {
"buildResources": "resources",
"output": "release"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/phobal/ivideo.git"
},
"author": {
"name": "phobal",
"email": "phobal@126.com",
"url": "https://github.com/phobal"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/phobal/ivideo/issues"
},
"keywords": [
"electron",
"boilerplate",
"react",
"redux",
"flow",
"sass",
"webpack",
"hot",
"reload"
],
"homepage": "https://github.com/phobal/ivideo#readme",
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules",
"app/node_modules"
],
"transform": {
"^.+\\.js$": "babel-jest"
},
"setupFiles": [
"./internals/scripts/CheckBuiltsExist.js"
]
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.1",
"babel-jest": "^22.1.0",
"babel-loader": "^7.1.2",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-dev-expression": "^0.2.1",
"babel-plugin-flow-runtime": "^0.15.0",
"babel-plugin-import": "^1.7.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"chalk": "^2.3.0",
"concurrently": "^3.5.1",
"cross-env": "^5.1.3",
"cross-spawn": "^6.0.4",
"css-loader": "^0.28.9",
"detect-port": "^1.2.2",
"electron": "^1.7.11",
"electron-builder": "^19.55.3",
"electron-devtools-installer": "^2.2.3",
"electron-rebuild": "^1.7.3",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.1",
"eslint": "^4.16.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-formatter-pretty": "^1.3.0",
"eslint-import-resolver-webpack": "^0.8.4",
"eslint-plugin-compat": "^2.2.0",
"eslint-plugin-flowtype": "^2.42.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jest": "^21.7.0",
"eslint-plugin-jsx-a11y": "6.0.3",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.6.1",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2",
"fbjs-scripts": "^0.8.1",
"file-loader": "^1.1.6",
"flow-bin": "^0.64.0",
"flow-runtime": "^0.16.0",
"flow-typed": "^2.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^22.1.4",
"less": "^3.0.1",
"less-loader": "^4.1.0",
"minimist": "^1.2.0",
"node-sass": "^4.7.2",
"npm-logical-tree": "^1.2.1",
"react-test-renderer": "^16.2.0",
"redux-logger": "^3.0.6",
"rimraf": "^2.6.2",
"sass-loader": "^6.0.6",
"sinon": "^4.2.2",
"spectron": "^3.8.0",
"style-loader": "^0.20.1",
"stylefmt": "^6.0.0",
"stylelint": "^8.4.0",
"stylelint-config-standard": "^18.0.0",
"uglifyjs-webpack-plugin": "1.1.8",
"url-loader": "^0.6.2",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.2",
"webpack-dev-server": "^2.11.1",
"webpack-merge": "^4.1.1"
},
"dependencies": {
"axios": "^0.18.0",
"devtron": "^1.4.0",
"electron-debug": "^1.5.0",
"font-awesome": "^4.7.0",
"history": "^4.7.2",
"rc-menu": "^6.2.10",
"rc-select": "^7.7.7",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-hot-loader": "^4.0.0-beta.13",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.6",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"source-map-support": "^0.5.3"
},
"devEngines": {
"node": ">=7.x",
"npm": ">=4.x",
"yarn": ">=0.21.3"
}
}
================================================
FILE: resources/viplist.json
================================================
{
"platformlist": [
{
"name": "爱奇艺",
"url": "http://www.iqiyi.com/"
},
{
"name": "腾讯视频",
"url": "https://v.qq.com/"
},
{
"name": "芒果",
"url": "https://www.mgtv.com/"
},
{
"name": "优酷",
"url": "https://www.youku.com/"
},
{
"name": "搜狐视频",
"url": "https://tv.sohu.com/"
},
{
"name": "乐视视频",
"url": "https://www.le.com/"
},
{
"name": "电影天堂",
"url": "http://www.btbtdy.net/"
},
{
"name": "新视觉影院",
"url": "http://www.yy3080.com/vod-type-id-1-pg-1.html/"
}
],
"list": [
{
"name": "5月-21",
"url": "http://jiexi.071811.cc/jx2.php?url="
},
{
"name": "9月-2",
"url": "http://jqaaa.com/jx.php?url="
},
{
"name": "5月-4",
"url": "http://beaacc.com/api.php?url="
},
{
"name": "4.21-4",
"url": "http://www.82190555.com/index.php?url="
},
{
"name": "4.21-6",
"url": "http://www.85105052.com/admin.php?url="
},
{
"name": "5月-23",
"url": "http://api.baiyug.cn/vip/index.php?url="
},
{
"name": "4.21-3-慢",
"url": "https://yooomm.com/index.php?url="
},
{
"name": "5月-24",
"url": "http://www.82190555.com/index/qqvod.php?url="
},
{
"name": "1",
"url": "http://17kyun.com/api.php?url="
},
{
"name": "品优解析-可播但广告",
"url": "http://api.pucms.com/xnflv/?url="
},
{
"name": "5月-1",
"url": "http://www.82190555.com/index/qqvod.php?url="
},
{
"name": "腾讯可用,金桥解析",
"url": "http://jqaaa.com/jx.php?url="
},
{
"name": "速度牛",
"url": "http://api.wlzhan.com/sudu/?url="
},
{
"name": "万能接口6",
"url": "http://wwwhe1.177kdy.cn/4.php?pass=1&url="
},
{
"name": "花园影视(可能无效)",
"url": "http://j.zz22x.com/jx/?url="
},
{
"name": "9月-1",
"url": "http://api.ledboke.com/?url="
}
]
}
================================================
FILE: test/.eslintrc
================================================
{
"env": {
"jest/globals": true
},
"plugins": [
"jest"
],
"rules": {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error"
}
}
================================================
FILE: test/actions/__snapshots__/counter.spec.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`actions should decrement should create decrement action 1`] = `
Object {
"type": "DECREMENT_COUNTER",
}
`;
exports[`actions should increment should create increment action 1`] = `
Object {
"type": "INCREMENT_COUNTER",
}
`;
================================================
FILE: test/actions/counter.spec.js
================================================
import { spy } from 'sinon';
import * as actions from '../../app/actions/counter';
describe('actions', () => {
it('should increment should create increment action', () => {
expect(actions.increment()).toMatchSnapshot();
});
it('should decrement should create decrement action', () => {
expect(actions.decrement()).toMatchSnapshot();
});
it('should incrementIfOdd should create increment action', () => {
const fn = actions.incrementIfOdd();
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
const getState = () => ({ counter: 1 });
fn(dispatch, getState);
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
});
it('should incrementIfOdd shouldnt create increment action if counter is even', () => {
const fn = actions.incrementIfOdd();
const dispatch = spy();
const getState = () => ({ counter: 2 });
fn(dispatch, getState);
expect(dispatch.called).toBe(false);
});
// There's no nice way to test this at the moment...
it('should incrementAsync', done => {
const fn = actions.incrementAsync(1);
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
fn(dispatch);
setTimeout(() => {
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
done();
}, 5);
});
});
================================================
FILE: test/components/Counter.spec.js
================================================
import { spy } from 'sinon';
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { BrowserRouter as Router } from 'react-router-dom';
import renderer from 'react-test-renderer';
import Counter from '../../app/components/Counter';
Enzyme.configure({ adapter: new Adapter() });
function setup() {
const actions = {
increment: spy(),
incrementIfOdd: spy(),
incrementAsync: spy(),
decrement: spy()
};
const component = shallow(<Counter counter={1} {...actions} />);
return {
component,
actions,
buttons: component.find('button'),
p: component.find('.counter')
};
}
describe('Counter component', () => {
it('should should display count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^1$/);
});
it('should first button should call increment', () => {
const { buttons, actions } = setup();
buttons.at(0).simulate('click');
expect(actions.increment.called).toBe(true);
});
it('should match exact snapshot', () => {
const { actions } = setup();
const counter = (
<div>
<Router>
<Counter counter={1} {...actions} />
</Router>
</div>
);
const tree = renderer
.create(counter)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should second button should call decrement', () => {
const { buttons, actions } = setup();
buttons.at(1).simulate('click');
expect(actions.decrement.called).toBe(true);
});
it('should third button should call incrementIfOdd', () => {
const { buttons, actions } = setup();
buttons.at(2).simulate('click');
expect(actions.incrementIfOdd.called).toBe(true);
});
it('should fourth button should call incrementAsync', () => {
const { buttons, actions } = setup();
buttons.at(3).simulate('click');
expect(actions.incrementAsync.called).toBe(true);
});
});
================================================
FILE: test/components/__snapshots__/Counter.spec.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Counter component should match exact snapshot 1`] = `
<div>
<div>
<div
className="backButton"
data-tid="backButton"
>
<a
href="/"
onClick={[Function]}
>
<i
className="fa fa-arrow-left fa-3x"
/>
</a>
</div>
<div
className="counter counter"
data-tid="counter"
>
1
</div>
<div
className="btnGroup"
>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-plus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-minus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
odd
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
async
</button>
</div>
</div>
</div>
`;
================================================
FILE: test/containers/CounterPage.spec.js
================================================
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import CounterPage from '../../app/containers/CounterPage';
import { configureStore } from '../../app/store/configureStore';
Enzyme.configure({ adapter: new Adapter() });
function setup(initialState) {
const store = configureStore(initialState);
const history = createBrowserHistory();
const provider = (
<Provider store={store}>
<ConnectedRouter history={history}>
<CounterPage />
</ConnectedRouter>
</Provider>
);
const app = mount(provider);
return {
app,
buttons: app.find('button'),
p: app.find('.counter')
};
}
describe('containers', () => {
describe('App', () => {
it('should display initial count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^0$/);
});
it('should display updated count after increment button click', () => {
const { buttons, p } = setup();
buttons.at(0).simulate('click');
expect(p.text()).toMatch(/^1$/);
});
it('should display updated count after descrement button click', () => {
const { buttons, p } = setup();
buttons.at(1).simulate('click');
expect(p.text()).toMatch(/^-1$/);
});
it('shouldnt change if even and if odd button clicked', () => {
const { buttons, p } = setup();
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^0$/);
});
it('should change if odd and if odd button clicked', () => {
const { buttons, p } = setup({ counter: 1 });
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^2$/);
});
});
});
================================================
FILE: test/e2e/e2e.spec.js
================================================
import { Application } from 'spectron';
import electronPath from 'electron';
import path from 'path';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
const delay = time => new Promise(resolve => setTimeout(resolve, time));
describe('main window', function spec() {
beforeAll(async () => {
this.app = new Application({
path: electronPath,
args: [path.join(__dirname, '..', '..', 'app')],
});
return this.app.start();
});
afterAll(() => {
if (this.app && this.app.isRunning()) {
return this.app.stop();
}
});
const findCounter = () => this.app.client.element('[data-tid="counter"]');
const findButtons = async () => {
const { value } = await this.app.client.elements('[data-tclass="btn"]');
return value.map(btn => btn.ELEMENT);
};
it('should open window', async () => {
const { client, browserWindow } = this.app;
await client.waitUntilWindowLoaded();
await delay(500);
const title = await browserWindow.getTitle();
expect(title).toBe('Hello Electron React!');
});
it('should haven\'t any logs in console of main window', async () => {
const { client } = this.app;
const logs = await client.getRenderProcessLogs();
// Print renderer process logs
logs.forEach(log => {
console.log(log.message);
console.log(log.source);
console.log(log.level);
});
expect(logs).toHaveLength(0);
});
it('should to Counter with click "to Counter" link', async () => {
const { client } = this.app;
await client.click('[data-tid=container] > a');
expect(await findCounter().getText()).toBe('0');
});
it('should display updated count after increment button click', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[0]); // +
expect(await findCounter().getText()).toBe('1');
});
it('should display updated count after descrement button click', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[1]); // -
expect(await findCounter().getText()).toBe('0');
});
it('shouldnt change if even and if odd button clicked', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[2]); // odd
expect(await findCounter().getText()).toBe('0');
});
it('should change if odd and if odd button clicked', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[0]); // +
await client.elementIdClick(buttons[2]); // odd
expect(await findCounter().getText()).toBe('2');
});
it('should change if async button clicked and a second later', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[3]); // async
expect(await findCounter().getText()).toBe('2');
await delay(1500);
expect(await findCounter().getText()).toBe('3');
});
it('should back to home if back button clicked', async () => {
const { client } = this.app;
await client.element('[data-tid="backButton"] > a').click();
expect(await client.isExisting('[data-tid="container"]')).toBe(true);
});
});
================================================
FILE: test/example.js
================================================
describe('description', () => {
it('should have description', () => {
expect(1 + 2).toBe(3);
});
});
================================================
FILE: test/reducers/__snapshots__/counter.spec.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`;
exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`;
exports[`reducers counter should handle initial state 1`] = `0`;
exports[`reducers counter should handle unknown action type 1`] = `1`;
================================================
FILE: test/reducers/counter.spec.js
================================================
import counter from '../../app/reducers/counter';
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter';
describe('reducers', () => {
describe('counter', () => {
it('should handle initial state', () => {
expect(counter(undefined, {})).toMatchSnapshot();
});
it('should handle INCREMENT_COUNTER', () => {
expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle DECREMENT_COUNTER', () => {
expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle unknown action type', () => {
expect(counter(1, { type: 'unknown' })).toMatchSnapshot();
});
});
});
================================================
FILE: webpack.config.base.js
================================================
/**
* Base webpack config used across other specific configs
*/
import path from 'path';
import webpack from 'webpack';
import { dependencies as externals } from './app/package.json';
export default {
externals: Object.keys(externals || {}),
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}]
},
output: {
path: path.join(__dirname, 'app'),
// https://github.com/webpack/webpack/issues/1114
libraryTarget: 'commonjs2'
},
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json'],
modules: [
path.join(__dirname, 'app'),
'node_modules',
],
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production'
}),
new webpack.NamedModulesPlugin(),
],
};
================================================
FILE: webpack.config.eslint.js
================================================
require('babel-register');
module.exports = require('./webpack.config.renderer.dev');
================================================
FILE: webpack.config.main.prod.js
================================================
/**
* Webpack config for production electron main process
*/
import webpack from 'webpack';
import merge from 'webpack-merge';
import UglifyJSPlugin from 'uglifyjs-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from './internals/scripts/CheckNodeEnv';
CheckNodeEnv('production');
export default merge.smart(baseConfig, {
devtool: 'source-map',
target: 'electron-main',
entry: './app/main.dev',
output: {
path: __dirname,
filename: './app/main.prod.js'
},
plugins: [
new UglifyJSPlugin({
parallel: true,
sourceMap: true
}),
new BundleAnalyzerPlugin({
analyzerMode: process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
openAnalyzer: process.env.OPEN_ANALYZER === 'true'
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: 'false'
})
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false
},
});
================================================
FILE: webpack.config.renderer.dev.dll.js
================================================
/**
* Builds the DLL for development electron renderer process
*/
import webpack from 'webpack';
import path from 'path';
import merge from 'webpack-merge';
import baseConfig from './webpack.config.base';
import { dependencies } from './package.json';
import CheckNodeEnv from './internals/scripts/CheckNodeEnv';
CheckNodeEnv('development');
const dist = path.resolve(process.cwd(), 'dll');
export default merge.smart(baseConfig, {
context: process.cwd(),
devtool: 'eval',
target: 'electron-renderer',
externals: ['fsevents', 'crypto-browserify'],
/**
* Use `module` from `webpack.config.renderer.dev.js`
*/
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
plugins: [
// Here, we include babel plugins that are only required for the
// renderer process. The 'transform-*' plugins must be included
// before react-hot-loader/babel
'transform-class-properties',
'transform-es2015-classes',
'react-hot-loader/babel'
],
}
}
},
{
test: /\.global\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
}
]
},
{
test: /^((?!\.global).)*\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
},
]
},
// SASS support - compile all .global.scss files and pipe it to style.css
{
test: /\.global\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader'
}
]
},
// SASS support - compile all other .scss files and pipe it to style.css
{
test: /^((?!\.global).)*\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
},
{
loader: 'sass-loader'
}
]
},
// WOFF Font
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
},
},
// WOFF2 Font
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
}
},
// TTF Font
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream'
}
}
},
// EOT Font
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader',
},
// SVG Font
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
}
}
},
// Common Image Formats
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
use: 'url-loader',
}
]
},
entry: {
renderer: (
Object
.keys(dependencies || {})
.filter(dependency => dependency !== 'font-awesome')
)
},
output: {
library: 'renderer',
path: dist,
filename: '[name].dev.dll.js',
libraryTarget: 'var'
},
plugins: [
new webpack.DllPlugin({
path: path.join(dist, '[name].json'),
name: '[name]',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development'
}),
new webpack.LoaderOptionsPlugin({
debug: true,
options: {
context: path.resolve(process.cwd(), 'app'),
output: {
path: path.resolve(process.cwd(), 'dll'),
},
},
})
],
});
================================================
FILE: webpack.config.renderer.dev.js
================================================
/* eslint global-require: 0, import/no-dynamic-require: 0 */
/**
* Build config for development electron renderer process that uses
* Hot-Module-Replacement
*
* https://webpack.js.org/concepts/hot-module-replacement/
*/
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import chalk from 'chalk';
import merge from 'webpack-merge';
import { spawn, execSync } from 'child_process';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from './internals/scripts/CheckNodeEnv';
CheckNodeEnv('development');
const port = process.env.PORT || 1212;
const publicPath = `http://localhost:${port}/dist`;
const dll = path.resolve(process.cwd(), 'dll');
const manifest = path.resolve(dll, 'renderer.json');
/**
* Warn if the DLL is not built
*/
if (!(fs.existsSync(dll) && fs.existsSync(manifest))) {
console.log(chalk.black.bgYellow.bold('The DLL files are missing. Sit back while we build them for you with "npm run build-dll"'));
execSync('npm run build-dll');
}
export default merge.smart(baseConfig, {
devtool: 'inline-source-map',
target: 'electron-renderer',
entry: [
'react-hot-loader/patch',
`webpack-dev-server/client?http://localhost:${port}/`,
'webpack/hot/only-dev-server',
path.join(__dirname, 'app/index.js'),
],
output: {
publicPath: `http://localhost:${port}/dist/`,
filename: 'renderer.dev.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
plugins: [
// Here, we include babel plugins that are only required for the
// renderer process. The 'transform-*' plugins must be included
// before react-hot-loader/babel
'transform-class-properties',
'transform-es2015-classes',
'react-hot-loader/babel'
],
}
}
},
{
test: /\.less$/,
loader: `style!css!less`,
include: path.resolve(__dirname, 'node_modules'),
},
{
test: /\.global\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
}
]
},
{
test: /^((?!\.global).)*\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
},
]
},
// SASS support - compile all .global.scss files and pipe it to style.css
{
test: /\.global\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader'
}
]
},
// SASS support - compile all other .scss files and pipe it to style.css
{
test: /^((?!\.global).)*\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
},
{
loader: 'sass-loader'
}
]
},
// WOFF Font
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
},
},
// WOFF2 Font
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
}
},
// TTF Font
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream'
}
}
},
// EOT Font
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader',
},
// SVG Font
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
}
}
},
// Common Image Formats
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
use: 'url-loader',
}
]
},
plugins: [
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(manifest),
sourceType: 'var',
}),
new webpack.HotModuleReplacementPlugin({
multiStep: true
}),
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development'
}),
new webpack.LoaderOptionsPlugin({
debug: true
}),
new ExtractTextPlugin({
filename: '[name].css'
}),
],
node: {
__dirname: false,
__filename: false
},
devServer: {
port,
publicPath,
compress: true,
noInfo: true,
stats: 'errors-only',
inline: true,
lazy: false,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
contentBase: path.join(__dirname, 'dist'),
watchOptions: {
aggregateTimeout: 300,
ignored: /node_modules/,
poll: 100
},
historyApiFallback: {
verbose: true,
disableDotRule: false,
},
before() {
if (process.env.START_HOT) {
console.log('Starting Main Process...');
spawn(
'npm',
['run', 'start-main-dev'],
{ shell: true, env: process.env, stdio: 'inherit' }
)
.on('close', code => process.exit(code))
.on('error', spawnError => console.error(spawnError));
}
}
},
});
================================================
FILE: webpack.config.renderer.prod.js
================================================
/**
* Build config for electron renderer process
*/
import path from 'path';
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import merge from 'webpack-merge';
import UglifyJSPlugin from 'uglifyjs-webpack-plugin';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from './internals/scripts/CheckNodeEnv';
CheckNodeEnv('production');
export default merge.smart(baseConfig, {
devtool: 'source-map',
target: 'electron-renderer',
entry: './app/index',
output: {
path: path.join(__dirname, 'app/dist'),
publicPath: './dist/',
filename: 'renderer.prod.js'
},
module: {
rules: [
// Extract all .global.css to style.css as is
{
test: /\.global\.css$/,
use: ExtractTextPlugin.extract({
publicPath: './',
use: {
loader: 'css-loader',
options: {
minimize: true,
}
},
fallback: 'style-loader',
})
},
// Pipe other styles through css modules and append to style.css
{
test: /^((?!\.global).)*\.css$/,
use: ExtractTextPlugin.extract({
use: {
loader: 'css-loader',
options: {
modules: true,
minimize: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
}
}),
},
// Add SASS support - compile all .global.scss files and pipe it to style.css
{
test: /\.global\.(scss|sass)$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
minimize: true,
}
},
{
loader: 'sass-loader'
}
],
fallback: 'style-loader',
})
},
// Add SASS support - compile all other .scss files and pipe it to style.css
{
test: /^((?!\.global).)*\.(scss|sass)$/,
use: ExtractTextPlugin.extract({
use: [{
loader: 'css-loader',
options: {
modules: true,
minimize: true,
importLoaders: 1,
localIdentName: '[name]__[local]__[hash:base64:5]',
}
},
{
loader: 'sass-loader'
}]
}),
},
// WOFF Font
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
},
},
// WOFF2 Font
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
}
}
},
// TTF Font
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream'
}
}
},
// EOT Font
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader',
},
// SVG Font
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
}
}
},
// Common Image Formats
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
use: 'url-loader',
}
]
},
plugins: [
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production'
}),
new UglifyJSPlugin({
parallel: true,
sourceMap: true
}),
new ExtractTextPlugin('style.css'),
new BundleAnalyzerPlugin({
analyzerMode: process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
openAnalyzer: process.env.OPEN_ANALYZER === 'true'
}),
],
});
gitextract_9m4e08g_/ ├── .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.json │ ├── reducers/ │ │ ├── index.js │ │ └── source.js │ ├── routes.js │ ├── store/ │ │ ├── configureStore.dev.js │ │ ├── configureStore.js │ │ └── configureStore.prod.js │ └── utils/ │ ├── .gitkeep │ └── fetch.js ├── appveyor.yml ├── flow-typed/ │ └── module_vx.x.x.js ├── internals/ │ ├── flow/ │ │ ├── CSSModule.js.flow │ │ └── WebpackAsset.js.flow │ ├── mocks/ │ │ └── fileMock.js │ └── scripts/ │ ├── CheckBuiltsExist.js │ ├── CheckNativeDep.js │ ├── CheckNodeEnv.js │ ├── CheckPortInUse.js │ ├── ElectronRebuild.js │ └── RunTests.js ├── package.json ├── resources/ │ ├── icon.icns │ └── 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
SYMBOL INDEX (20 symbols across 12 files)
FILE: app/actions/source.js
function getAllVideoSource (line 3) | function getAllVideoSource() {
FILE: app/containers/Root.js
method render (line 13) | render() {
FILE: app/containers/Video.js
class VideoPlay (line 12) | class VideoPlay extends PureComponent<Props> {
method constructor (line 13) | constructor(props) {
function mapDispatchToProps (line 125) | function mapDispatchToProps(dispatch) {
function mapStateToProps (line 132) | function mapStateToProps(state) {
FILE: app/menu.js
class MenuBuilder (line 4) | class MenuBuilder {
method constructor (line 7) | constructor(mainWindow: BrowserWindow) {
method buildMenu (line 11) | buildMenu() {
method setupDevelopmentEnvironment (line 26) | setupDevelopmentEnvironment() {
method buildDarwinTemplate (line 42) | buildDarwinTemplate() {
method buildDefaultTemplate (line 115) | buildDefaultTemplate() {
FILE: app/reducers/source.js
function source (line 1) | function source(state = null, action) {
FILE: app/store/configureStore.prod.js
function configureStore (line 13) | function configureStore(initialState?: counterStateType) {
FILE: app/utils/fetch.js
constant BASEURL (line 3) | const BASEURL =
FILE: internals/scripts/CheckBuiltsExist.js
function CheckBuildsExist (line 7) | function CheckBuildsExist() {
FILE: internals/scripts/CheckNodeEnv.js
function CheckNodeEnv (line 4) | function CheckNodeEnv(expectedEnv: string) {
FILE: test/components/Counter.spec.js
function setup (line 11) | function setup() {
FILE: test/containers/CounterPage.spec.js
function setup (line 12) | function setup(initialState) {
FILE: webpack.config.renderer.dev.js
method before (line 267) | before() {
Condensed preview — 68 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (86K chars).
[
{
"path": ".babelrc",
"chars": 597,
"preview": "{\n \"presets\": [\n [\"env\", {\n \"targets\": { \"node\": 7 },\n \"useBuiltIns\": true\n }],\n \"stage-0\",\n \"rea"
},
{
"path": ".dockerignore",
"chars": 842,
"preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
},
{
"path": ".editorconfig",
"chars": 188,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
},
{
"path": ".eslintignore",
"chars": 843,
"preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
},
{
"path": ".eslintrc",
"chars": 1248,
"preview": "{\n \"parser\": \"babel-eslint\",\n \"parserOptions\": {\n \"sourceType\": \"module\",\n \"allowImportExportEverywhere\": true\n "
},
{
"path": ".flowconfig",
"chars": 933,
"preview": "[ignore]\n<PROJECT_ROOT>/node_modules/*\n<PROJECT_ROOT>/app/main.prod.js\n<PROJECT_ROOT>/app/main.prod.js.map\n<PROJECT_ROOT"
},
{
"path": ".gitattributes",
"chars": 81,
"preview": "* text eol=lf\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.ico binary\n*.icns binary\n"
},
{
"path": ".gitignore",
"chars": 829,
"preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
},
{
"path": ".stylelintrc",
"chars": 45,
"preview": "{\n \"extends\": \"stylelint-config-standard\"\n}\n"
},
{
"path": ".travis.yml",
"chars": 709,
"preview": "sudo: true\n\nlanguage: node_js\n\nnode_js:\n - 8\n - 7\n\ncache:\n yarn: true\n directories:\n - node_modules\n - app/nod"
},
{
"path": ".vscode/settings.json",
"chars": 438,
"preview": "{\n \"javascript.validate.enable\": false,\n \"flow.useNPMPackagedFlow\": true,\n \"search.exclude\": {\n \".git\": true,\n "
},
{
"path": "CHANGELOG.md",
"chars": 75,
"preview": "* 2018.5.5\n\n修改页面样式,当全屏时隐藏掉左侧菜单栏和顶部接口切换栏\n\n* 2018.07.13\n\n修改线路下拉框不能出现滚动条的 bug\n"
},
{
"path": "LICENSE",
"chars": 1085,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015-present C. T. Lin\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 1283,
"preview": "### i视频\n\n#### 产品介绍\n\n> 基于 Electron 开发的跨平台客户端版本的视频播放器,该播放器包括国内主流视频平台视频资源,你不用去单独下载各个平台的客户端,只需要使用这一个客户端就能查看所有平台的视频,并且内置了各大视频"
},
{
"path": "app/.eslintrc",
"chars": 880,
"preview": "{\n \"rules\": {\n \"flowtype/boolean-style\": [\"error\", \"boolean\"],\n \"flowtype/define-flow-type\": \"warn\",\n \"flowtyp"
},
{
"path": "app/actions/source.js",
"chars": 248,
"preview": "import * as api from '../utils/fetch';\n\nexport function getAllVideoSource() {\n return (dispatch) => {\n api.source.ge"
},
{
"path": "app/app.global.css",
"chars": 967,
"preview": "/*\n * @NOTE: Prepend a `~` to css file paths that are in your node_modules\n * See https://github.com/webpack-cont"
},
{
"path": "app/app.html",
"chars": 1575,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <title>i视频</title>\n <script>\n (function() {\n "
},
{
"path": "app/components/.githook",
"chars": 0,
"preview": ""
},
{
"path": "app/containers/App.js",
"chars": 253,
"preview": "// @flow\nimport * as React from 'react';\n\ntype Props = {\n children: React.Node\n};\n\nexport default class App extends Rea"
},
{
"path": "app/containers/Channel.js",
"chars": 301,
"preview": "import React from 'react';\nimport Menu, { Item as MenuItem } from 'rc-menu';\n\nconst Channel = ({ channel, handleSwitchCh"
},
{
"path": "app/containers/Frame.js",
"chars": 1117,
"preview": "// @flow\nimport React from 'react'\n\nimport Channel from './Channel'\nimport ToolBar from './ToolBar'\n\nexport const Frame "
},
{
"path": "app/containers/Root.js",
"chars": 479,
"preview": "// @flow\nimport React, { Component } from 'react';\nimport { Provider } from 'react-redux';\nimport { ConnectedRouter } fr"
},
{
"path": "app/containers/ToolBar.js",
"chars": 1186,
"preview": "import React from 'react';\nimport Select, { Option } from 'rc-select';\n// import { Icon, Select, Button } from 'antd';\n\n"
},
{
"path": "app/containers/Video.js",
"chars": 3272,
"preview": "// @flow\nimport React, { PureComponent } from 'react';\nimport { connect } from 'react-redux';\nimport { bindActionCreator"
},
{
"path": "app/index.js",
"chars": 722,
"preview": "import React from 'react';\nimport { render } from 'react-dom';\nimport { AppContainer } from 'react-hot-loader';\nimport R"
},
{
"path": "app/main.dev.js",
"chars": 2906,
"preview": "/* eslint global-require: 0, flowtype-errors/show-errors: 0 */\n\n/**\n * This module executes inside of electron's main pr"
},
{
"path": "app/menu.js",
"chars": 5781,
"preview": "// @flow\nimport { app, Menu, shell, BrowserWindow } from 'electron';\n\nexport default class MenuBuilder {\n mainWindow: B"
},
{
"path": "app/package.json",
"chars": 605,
"preview": "{\n \"name\": \"electron-react-boilerplate\",\n \"productName\": \"electron-react-boilerplate\",\n \"version\": \"1.1.4\",\n \"descri"
},
{
"path": "app/reducers/index.js",
"chars": 235,
"preview": "// @flow\nimport { combineReducers } from 'redux';\nimport { routerReducer as router } from 'react-router-redux';\nimport s"
},
{
"path": "app/reducers/source.js",
"chars": 176,
"preview": "export default function source(state = null, action) {\n switch(action.type) {\n case 'GETALLVIDEOSOURCE':\n retur"
},
{
"path": "app/routes.js",
"chars": 307,
"preview": "/* eslint flowtype-errors/show-errors: 0 */\nimport React from 'react';\nimport { Switch, Route } from 'react-router';\nimp"
},
{
"path": "app/store/configureStore.dev.js",
"chars": 1853,
"preview": "import { createStore, applyMiddleware, compose } from 'redux';\nimport thunk from 'redux-thunk';\nimport { createHashHisto"
},
{
"path": "app/store/configureStore.js",
"chars": 246,
"preview": "// @flow\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./configureStore.prod'); // eslint-dis"
},
{
"path": "app/store/configureStore.prod.js",
"chars": 597,
"preview": "// @flow\nimport { createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\nimport { createBrowserHi"
},
{
"path": "app/utils/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "app/utils/fetch.js",
"chars": 461,
"preview": "import axios from 'axios';\n\nconst BASEURL =\n 'https://raw.githubusercontent.com/phobal/ivideo/master/resources/viplist."
},
{
"path": "appveyor.yml",
"chars": 490,
"preview": "os: unstable\n\nenvironment:\n matrix:\n - nodejs_version: 8\n - nodejs_version: 7\n\ncache:\n - \"%LOCALAPPDATA%/Yarn\"\n "
},
{
"path": "flow-typed/module_vx.x.x.js",
"chars": 59,
"preview": "declare module 'module' {\n declare module.exports: any;\n}\n"
},
{
"path": "internals/flow/CSSModule.js.flow",
"chars": 58,
"preview": "// @flow\n\ndeclare export default { [key: string]: string }"
},
{
"path": "internals/flow/WebpackAsset.js.flow",
"chars": 39,
"preview": "// @flow\ndeclare export default string\n"
},
{
"path": "internals/mocks/fileMock.js",
"chars": 33,
"preview": "export default 'test-file-stub';\n"
},
{
"path": "internals/scripts/CheckBuiltsExist.js",
"chars": 706,
"preview": "// @flow\n// Check if the renderer and main bundles are built\nimport path from 'path';\nimport chalk from 'chalk';\nimport "
},
{
"path": "internals/scripts/CheckNativeDep.js",
"chars": 1976,
"preview": "// @flow\nimport fs from 'fs';\nimport chalk from 'chalk';\nimport { execSync } from 'child_process';\nimport { dependencies"
},
{
"path": "internals/scripts/CheckNodeEnv.js",
"chars": 368,
"preview": "// @flow\nimport chalk from 'chalk';\n\nexport default function CheckNodeEnv(expectedEnv: string) {\n if (!expectedEnv) {\n "
},
{
"path": "internals/scripts/CheckPortInUse.js",
"chars": 469,
"preview": "// @flow\nimport chalk from 'chalk';\nimport detectPort from 'detect-port';\n\n(function CheckPortInUse() {\n const port: st"
},
{
"path": "internals/scripts/ElectronRebuild.js",
"chars": 641,
"preview": "// @flow\nimport path from 'path';\nimport { execSync } from 'child_process';\nimport fs from 'fs';\nimport dependencies fro"
},
{
"path": "internals/scripts/RunTests.js",
"chars": 342,
"preview": "import spawn from 'cross-spawn';\nimport path from 'path';\n\nconst pattern = process.argv[2] === 'e2e'\n ? 'test/e2e/.+\\\\."
},
{
"path": "package.json",
"chars": 7556,
"preview": "{\n \"name\": \"ivideo\",\n \"productName\": \"ivideo\",\n \"version\": \"1.1.4\",\n \"description\": \"一个视频播放器观看国内主流视频网站,不用单独下载各个平台客户端"
},
{
"path": "resources/viplist.json",
"chars": 2023,
"preview": "{\n \"platformlist\": [\n {\n \"name\": \"爱奇艺\",\n \"url\": \"http://www.iqiyi.com/\"\n },\n {\n \"name\": \"腾讯视频\","
},
{
"path": "test/.eslintrc",
"chars": 208,
"preview": "{\n \"env\": {\n \"jest/globals\": true\n },\n \"plugins\": [\n \"jest\"\n ],\n \"rules\": {\n \"jest/no-disabled-tests\": \"wa"
},
{
"path": "test/actions/__snapshots__/counter.spec.js.snap",
"chars": 281,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`actions should decrement should create decrement action 1`] = `\nObj"
},
{
"path": "test/actions/counter.spec.js",
"chars": 1341,
"preview": "import { spy } from 'sinon';\nimport * as actions from '../../app/actions/counter';\n\ndescribe('actions', () => {\n it('sh"
},
{
"path": "test/components/Counter.spec.js",
"chars": 1950,
"preview": "import { spy } from 'sinon';\nimport React from 'react';\nimport Enzyme, { shallow } from 'enzyme';\nimport Adapter from 'e"
},
{
"path": "test/components/__snapshots__/Counter.spec.js.snap",
"chars": 1115,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Counter component should match exact snapshot 1`] = `\n<div>\n <div>"
},
{
"path": "test/containers/CounterPage.spec.js",
"chars": 1821,
"preview": "import React from 'react';\nimport Enzyme, { mount } from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\nimport"
},
{
"path": "test/e2e/e2e.spec.js",
"chars": 3318,
"preview": "import { Application } from 'spectron';\nimport electronPath from 'electron';\nimport path from 'path';\n\njasmine.DEFAULT_T"
},
{
"path": "test/example.js",
"chars": 109,
"preview": "describe('description', () => {\n it('should have description', () => {\n expect(1 + 2).toBe(3);\n });\n});\n"
},
{
"path": "test/reducers/__snapshots__/counter.spec.js.snap",
"chars": 321,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`;\n\nexport"
},
{
"path": "test/reducers/counter.spec.js",
"chars": 702,
"preview": "import counter from '../../app/reducers/counter';\nimport { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/action"
},
{
"path": "webpack.config.base.js",
"chars": 961,
"preview": "/**\n * Base webpack config used across other specific configs\n */\n\nimport path from 'path';\nimport webpack from 'webpack"
},
{
"path": "webpack.config.eslint.js",
"chars": 87,
"preview": "require('babel-register');\n\nmodule.exports = require('./webpack.config.renderer.dev');\n"
},
{
"path": "webpack.config.main.prod.js",
"chars": 1532,
"preview": "/**\n * Webpack config for production electron main process\n */\n\nimport webpack from 'webpack';\nimport merge from 'webpac"
},
{
"path": "webpack.config.renderer.dev.dll.js",
"chars": 5040,
"preview": "/**\n * Builds the DLL for development electron renderer process\n */\n\nimport webpack from 'webpack';\nimport path from 'pa"
},
{
"path": "webpack.config.renderer.dev.js",
"chars": 6835,
"preview": "/* eslint global-require: 0, import/no-dynamic-require: 0 */\n\n/**\n * Build config for development electron renderer proc"
},
{
"path": "webpack.config.renderer.prod.js",
"chars": 4413,
"preview": "/**\n * Build config for electron renderer process\n */\n\nimport path from 'path';\nimport webpack from 'webpack';\nimport Ex"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the phobal/ivideo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 68 files (76.3 KB), approximately 22.1k tokens, and a symbol index with 20 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.