├── static
└── .gitkeep
├── tslint.json
├── src
├── web
│ ├── components
│ │ └── player
│ │ │ ├── player.html
│ │ │ ├── index.ts
│ │ │ ├── player.less
│ │ │ └── player.ts
│ ├── styles
│ │ ├── index.less
│ │ └── variables.less
│ ├── index.ts
│ ├── application
│ │ ├── index.ts
│ │ ├── workspace.ts
│ │ ├── context.ts
│ │ └── workbench.ts
│ ├── routes
│ │ └── index.ts
│ └── views
│ │ ├── dashboard.less
│ │ └── dashboard.vue
├── app
│ ├── index.ts
│ ├── context.ts
│ └── workbench.ts
└── index.ejs
├── .editorconfig
├── .gitignore
├── .postcssrc.js
├── appveyor.yml
├── LICENSE
├── README.md
├── tsconfig.json
├── .babelrc
├── .travis.yml
└── package.json
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint-config-flagwind"
3 | }
--------------------------------------------------------------------------------
/src/web/components/player/player.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/web/styles/index.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | @import "./variables.less";
--------------------------------------------------------------------------------
/src/web/components/player/index.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | export { default } from "./player";
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | /test/unit/coverage/
8 |
9 | # Editor directories and files
10 | .idea
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 | *.mp3
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports =
4 | {
5 | "plugins":
6 | {
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "postcss-import": {},
9 | "autoprefixer": {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/web/styles/variables.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | @white-color: #fff;
9 | @primary-color: #000;
10 | @primary-border: #003f7a;
11 | @body-bg: @primary-color;
12 | @text-color: #727ec5;
13 | @text-highlight-color: #fff700;
14 | @text-size: 16px;
--------------------------------------------------------------------------------
/src/app/index.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import flagwind from "flagwind-core";
9 | import ApplicationContext from "./context";
10 |
11 | // 获取应用上下文
12 | let context = ApplicationContext.current;
13 |
14 | // 启动应用程序
15 | flagwind.Application.start(context);
16 |
--------------------------------------------------------------------------------
/src/web/index.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import flagwind from "flagwind-core";
9 | import ApplicationContext from "./application/context";
10 |
11 | // 获取应用上下文
12 | let context = ApplicationContext.current;
13 |
14 | // 启动应用程序
15 | flagwind.Application.start(context);
16 |
--------------------------------------------------------------------------------
/src/web/application/index.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import ApplicationContext from "./context";
9 | import Workbench from "./workbench";
10 | import Workspace from "./workspace";
11 |
12 | export
13 | {
14 | ApplicationContext,
15 | Workbench,
16 | Workspace
17 | };
18 |
--------------------------------------------------------------------------------
/src/web/routes/index.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | const routes =
9 | [
10 | {
11 | path: "/",
12 | redirect: "/dashboard"
13 | },
14 | {
15 | name: "dashboard",
16 | path: "/dashboard",
17 | component: (resolve: any) => (require)(["src/web/views/dashboard.vue"], resolve)
18 | }
19 | ];
20 |
21 | export default routes;
22 |
--------------------------------------------------------------------------------
/src/web/views/dashboard.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | @import "~styles/variables.less";
9 |
10 | .v-dashboard
11 | {
12 | position: absolute;
13 | top: 0;
14 | bottom: 0;
15 | left: 0;
16 | right: 0;
17 | font-size: @text-size;
18 | background-color: @body-bg;
19 |
20 | .ivu-row
21 | {
22 | margin-top: 20px;
23 | }
24 |
25 | .ivu-col
26 | {
27 | height: 400px;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/web/components/player/player.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | @import "~styles/variables.less";
9 |
10 | .u-player
11 | {
12 | display: table;
13 | width: 100%;
14 | height: 100%;
15 |
16 | &-message
17 | {
18 | display:table-cell;
19 | width: 100%;
20 | height: 100%;
21 | vertical-align:middle;
22 | text-align: center;
23 | font-size: 18px;
24 | color: #fff;
25 | letter-spacing: 2px;
26 | background: #000;
27 | }
28 |
29 | &-video
30 | {
31 | width: 100%;
32 | height: 100%;
33 | }
34 | }
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # Commented sections below can be used to run tests on the CI server
2 | # https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
3 | version: 0.1.{build}
4 |
5 | branches:
6 | only:
7 | - master
8 |
9 | image: Visual Studio 2017
10 | platform:
11 | - x64
12 |
13 | cache:
14 | - node_modules
15 | - '%APPDATA%\npm-cache'
16 | - '%USERPROFILE%\.electron'
17 | - '%USERPROFILE%\AppData\Local\Yarn\cache'
18 |
19 | init:
20 | - git config --global core.autocrlf input
21 |
22 | install:
23 | - ps: Install-Product node 8 x64
24 | - choco install yarn --ignore-dependencies
25 | - git reset --hard HEAD
26 | - yarn
27 | - node --version
28 |
29 | build_script:
30 | #- yarn test
31 | - yarn build
32 |
33 | test: off
34 |
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RTSP Player
6 | <% if(htmlWebpackPlugin.options.nodeModules) { %>
7 |
8 |
11 | <% } %>
12 |
13 |
14 |
15 |
16 |
17 | <% if(!process.env.IS_RAW) { %>
18 |
24 | <% } %>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RTSP Player
2 |
3 | 一个基于 [`Electron`](https://github.com/electron/electron/tree/1-4-x/docs) + [`VLC`](https://github.com/jaruba/wcjs-player/wiki/JavaScript-API) 的 RTSP 视频播放解决方案,可用于播放 `海康`、`大华` 等支持 RTSP 协议的网络摄像头。
4 |
5 | ## 安装调试
6 |
7 | 安装依赖:
8 |
9 | ``` bash
10 | $ npm install
11 | ```
12 |
13 | 调试:
14 |
15 | ``` bash
16 | $ npm run dev
17 | ```
18 |
19 | ## 打包部署
20 |
21 | 打包配置在 `build\dist.config.js` 文件中修改,对应的配置项请浏览
22 | [electron-packager](https://github.com/electron-userland/electron-packager/blob/master/docs/api.md) 文档。
23 |
24 | 打包命令如下:
25 |
26 | ``` bash
27 | $ npm run dist
28 | ```
29 |
30 | 打包后的文件在 `dist\app` 目录中。
31 |
32 | ## RTSP 协议
33 |
34 | 以海康威视IP摄像头为例:
35 |
36 | rtsp://[username]:[passwd]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
37 |
38 | - username:用户名,例如admin
39 | - passwd:密码,例如12345
40 | - ip:设备的ip地址,例如192.0.0.64
41 | - port:端口号默认554,若为默认可以不写
42 | - codec:有h264、MPEG-4、mpeg4这几种
43 | - channel:通道号,起始为1
44 | - subtype:码流类型,主码流为main,子码流为sub
45 |
46 | 获取通道1的主码流,url如下:
47 |
48 | rtsp://admin:admin123@10.10.10.127:554/h264/ch1/main/av_stream
49 | rtsp://admin:admin123@10.10.10.127:554/mpeg4/ch1/main/av_stream
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions":
3 | {
4 | "module": "es6",
5 | "target": "es6",
6 | "moduleResolution": "node",
7 | "allowJs": true,
8 | "sourceMap": true,
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "allowSyntheticDefaultImports": true,
12 | "baseUrl": ".",
13 | "paths": {
14 | "src/*": ["./src/*"],
15 | "app/*": ["./src/app/*"],
16 | "web/*": ["./src/web/*"],
17 | "assets/*": ["./src/web/assets/*"],
18 | "styles/*": ["./src/web/styles/*"],
19 | "config/*": ["./src/web/config/*"],
20 | "components/*": ["./src/web/components/*"],
21 | "models/*": ["./src/web/models/*"],
22 | "services/*": ["./src/web/services/*"],
23 | "views/*": ["./src/web/views/*"],
24 | "utils/*": ["./src/web/utils/*"],
25 | "iview/*": ["./node_modules/flagwind-web/dist/typings/components/iview/typings/*"]
26 | },
27 | "lib":
28 | [
29 | "dom",
30 | "es6"
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/src/web/views/dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
48 |
--------------------------------------------------------------------------------
/src/web/application/workspace.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import Vue from "vue";
9 | import flagwind from "flagwind-core";
10 | import IWorkbench = flagwind.IWorkbench;
11 | import ApplicationContext from "./context";
12 |
13 | /**
14 | * 提供工作空间的常用功能。
15 | * @class
16 | * @version 1.0.0
17 | */
18 | export default class Workspace extends Vue
19 | {
20 | private _workbench: IWorkbench;
21 |
22 | /**
23 | * 获取工作空间所属的工作台实例。
24 | * @property
25 | * @returns IWorkbench
26 | */
27 | public get workbench(): IWorkbench
28 | {
29 | return this._workbench;
30 | }
31 |
32 | /**
33 | * 初始化工作空间的新实例。
34 | * @constructor
35 | * @param {IWorkbench} workbench 工作台实例。
36 | * @param {Router} router 主路由程序。
37 | */
38 | public constructor(workbench: IWorkbench)
39 | {
40 | let options =
41 | {
42 | el: "#workspace",
43 | router: ApplicationContext.current.router,
44 | store: ApplicationContext.current.store,
45 | template: '
'
46 | };
47 |
48 | // 传入配置进行初始化
49 | super(options);
50 |
51 | // 保存工作台
52 | this._workbench = workbench;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "comments": false,
3 | "env": {
4 | "test": {
5 | "presets": [
6 | [
7 | "env",
8 | {
9 | "targets": {
10 | "node": 7
11 | }
12 | }
13 | ],
14 | "stage-0"
15 | ],
16 | "plugins": [
17 | "istanbul"
18 | ]
19 | },
20 | "app": {
21 | "presets": [
22 | [
23 | "env",
24 | {
25 | "targets": {
26 | "node": 7
27 | }
28 | }
29 | ],
30 | "stage-0"
31 | ]
32 | },
33 | "web": {
34 | "presets": [
35 | [
36 | "env",
37 | {
38 | "modules": false
39 | }
40 | ],
41 | "stage-0"
42 | ]
43 | },
44 | "raw": {
45 | "presets": [
46 | [
47 | "env",
48 | {
49 | "modules": false
50 | }
51 | ],
52 | "stage-0"
53 | ]
54 | }
55 | },
56 | "plugins": [
57 | "transform-runtime"
58 | ]
59 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Commented sections below can be used to run tests on the CI server
2 | # https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
3 | osx_image: xcode8.3
4 | sudo: required
5 | dist: trusty
6 | language: c
7 | matrix:
8 | include:
9 | - os: osx
10 | - os: linux
11 | env: CC=clang CXX=clang++ npm_config_clang=1
12 | compiler: clang
13 | cache:
14 | directories:
15 | - node_modules
16 | - "$HOME/.electron"
17 | - "$HOME/.cache"
18 | addons:
19 | apt:
20 | packages:
21 | - libgnome-keyring-dev
22 | - icnsutils
23 | #- xvfb
24 | before_install:
25 | - mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
26 | "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
27 | | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
28 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
29 | install:
30 | #- export DISPLAY=':99.0'
31 | #- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
32 | - nvm install 7
33 | - curl -o- -L https://yarnpkg.com/install.sh | bash
34 | - source ~/.bashrc
35 | - npm install -g xvfb-maybe
36 | - yarn
37 | script:
38 | #- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
39 | #- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
40 | - yarn run build
41 | branches:
42 | only:
43 | - master
44 |
--------------------------------------------------------------------------------
/src/app/context.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import path from "path";
9 | import flagwind from "flagwind-core";
10 | import IWorkbench = flagwind.IWorkbench;
11 | import ApplicationContextBase = flagwind.ApplicationContextBase;
12 | import Workbench from "./workbench";
13 |
14 | /**
15 | * 包含当前应用程序的上下文实例。
16 | * @class
17 | * @version 1.0.0
18 | */
19 | export default class ApplicationContext extends ApplicationContextBase
20 | {
21 | private _mainUrl: string;
22 |
23 | /**
24 | * 获取当前应用的入口地址。
25 | * @property
26 | * @returns string
27 | */
28 | public get mainUrl(): string
29 | {
30 | return this._mainUrl;
31 | }
32 |
33 | /**
34 | * 获取当前应用程序的上下文实例。
35 | * @static
36 | * @member
37 | */
38 | public static readonly current: ApplicationContext = new ApplicationContext();
39 |
40 | /**
41 | * 私有构造函数。
42 | * @private
43 | */
44 | protected constructor()
45 | {
46 | super("wayto-toilets-client");
47 |
48 | // 设置主窗口地址
49 | this._mainUrl = process.env.NODE_ENV === "development" ? "http://localhost:9080" : `file://${__dirname}/index.html`;
50 |
51 | /**
52 | * Set `__static` path to static files in production
53 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
54 | */
55 | if(process.env.NODE_ENV !== "development")
56 | {
57 | global["__static"] = path.join(__dirname, "/static").replace(/\\/g, "\\\\");
58 | }
59 | }
60 |
61 | /**
62 | * 创建一个工作台对象。
63 | * @override
64 | * @returns IWorkbench
65 | */
66 | protected createWorkbench(args: Array): IWorkbench
67 | {
68 | return new Workbench(this);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/web/application/context.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import Router from "vue-router";
9 | import Vuex, { Store } from "vuex";
10 | import flagwind from "flagwind-core";
11 | import IWorkbench = flagwind.IWorkbench;
12 | import ApplicationContextBase = flagwind.ApplicationContextBase;
13 | import InvalidOperationException = flagwind.InvalidOperationException;
14 | import Workbench from "./workbench";
15 |
16 | /**
17 | * 包含当前应用程序的上下文实例。
18 | * @class
19 | * @version 1.0.0
20 | */
21 | export default class ApplicationContext extends ApplicationContextBase
22 | {
23 | private _router: Router;
24 | private _store: Store;
25 |
26 | /**
27 | * 获取或设置当前应用的主路由对象。
28 | * @property
29 | * @returns Router
30 | */
31 | public get router(): Router
32 | {
33 | return this._router;
34 | }
35 |
36 | public set router(value: Router)
37 | {
38 | if(!value)
39 | {
40 | throw new InvalidOperationException();
41 | }
42 |
43 | this._router = value;
44 | }
45 |
46 | /**
47 | * 获取或设置当前应用的状态管理对象。
48 | * @property
49 | * @returns Store
50 | */
51 | public get store(): Store
52 | {
53 | return this._store;
54 | }
55 |
56 | public set store(value: Store)
57 | {
58 | if(!value)
59 | {
60 | throw new InvalidOperationException();
61 | }
62 |
63 | this._store = value;
64 | }
65 |
66 | /**
67 | * 获取当前应用程序的上下文实例。
68 | * @static
69 | * @member
70 | */
71 | public static readonly current: ApplicationContext = new ApplicationContext();
72 |
73 | /**
74 | * 私有构造函数。
75 | * @private
76 | */
77 | protected constructor()
78 | {
79 | super("flagwind-rtsp-player");
80 | }
81 |
82 | /**
83 | * 创建一个工作台对象。
84 | * @override
85 | * @returns IWorkbench
86 | */
87 | protected createWorkbench(args: Array): IWorkbench
88 | {
89 | return new Workbench(this);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flagwind-rtsp-player",
3 | "version": "1.0.0",
4 | "author": "jason ",
5 | "description": "A RTSP Player.",
6 | "private": true,
7 | "main": "./dist/web/app.js",
8 | "scripts": {
9 | "dev": "node build/dev.js",
10 | "dist": "node build/dist.js"
11 | },
12 | "dependencies": {
13 | "flagwind-core": "^1.1.0",
14 | "flagwind-web": "^1.0.7",
15 | "vue": "^2.5.7",
16 | "vue-router": "^3.0.1",
17 | "vuex": "^3.0.1",
18 | "wcjs-player": "^6.0.0",
19 | "wcjs-prebuilt": "^3.0.0"
20 | },
21 | "devDependencies": {
22 | "@types/node": "^8.5.2",
23 | "babel-core": "^6.25.0",
24 | "babel-loader": "^7.1.1",
25 | "babel-plugin-istanbul": "^4.1.1",
26 | "babel-plugin-transform-runtime": "^6.23.0",
27 | "babel-preset-env": "^1.6.0",
28 | "babel-preset-stage-0": "^6.24.1",
29 | "babel-register": "^6.24.1",
30 | "babili-webpack-plugin": "^0.1.2",
31 | "cfonts": "^1.1.3",
32 | "chalk": "^2.1.0",
33 | "copy-webpack-plugin": "^4.0.1",
34 | "cross-env": "^5.0.5",
35 | "css-loader": "^0.28.4",
36 | "del": "^3.0.0",
37 | "devtron": "^1.4.0",
38 | "electron-packager": "8.7.2",
39 | "electron": "1.4.13",
40 | "extract-text-webpack-plugin": "^3.0.0",
41 | "file-loader": "^0.11.2",
42 | "html-webpack-plugin": "^2.30.1",
43 | "inject-loader": "^3.0.0",
44 | "less": "^2.7.3",
45 | "less-loader": "^4.0.5",
46 | "multispinner": "^0.2.1",
47 | "node-loader": "^0.6.0",
48 | "postcss-import": "^11.0.0",
49 | "postcss-loader": "^2.0.9",
50 | "require-dir": "^0.3.0",
51 | "style-loader": "^0.18.2",
52 | "ts-loader": "^3.2.0",
53 | "tslint": "^5.8.0",
54 | "tslint-config-flagwind": "^1.0.1",
55 | "tslint-loader": "^3.5.3",
56 | "typescript": "^2.6.2",
57 | "url-loader": "^0.5.9",
58 | "vue-html-loader": "^1.2.4",
59 | "vue-loader": "^13.6.0",
60 | "vue-style-loader": "^3.0.3",
61 | "vue-template-compiler": "^2.5.13",
62 | "webpack": "^3.10.0",
63 | "webpack-dev-server": "^2.9.7",
64 | "webpack-hot-middleware": "^2.21.0",
65 | "webpack-merge": "^4.1.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/app/workbench.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import path from "path";
9 | import flagwind from "flagwind-core";
10 | import WorkbenchBase = flagwind.WorkbenchBase;
11 | import ApplicationContextBase = flagwind.ApplicationContextBase;
12 | import ApplicationContext from "./context";
13 |
14 | // tslint:disable-next-line:no-var-requires
15 | const electron = require("electron");
16 | const app = electron.app;
17 | const BrowserWindow = electron.BrowserWindow;
18 | const globalShortcut = electron.globalShortcut;
19 |
20 | // Electron 1.4.x 文档
21 | // https://github.com/electron/electron/tree/1-4-x/docs
22 |
23 | export default class Workbench extends WorkbenchBase
24 | {
25 | private _mainWindow: any;
26 |
27 | /**
28 | * 获取当前应用的主窗口。
29 | * @returns BrowserWindow
30 | */
31 | public get mainWindow(): any
32 | {
33 | return this._mainWindow;
34 | }
35 |
36 | /**
37 | * 初始化工作台的新实例。
38 | * @param {ApplicationContextBase} applicationContext
39 | */
40 | public constructor(context: ApplicationContextBase)
41 | {
42 | super(context);
43 | }
44 |
45 | /**
46 | * 当工作台打开时调用。
47 | * @async
48 | * @protected
49 | * @virtual
50 | * @param {Array} args
51 | * @returns void
52 | */
53 | protected async onOpen(args: Array): Promise
54 | {
55 | // 设置 VLC 的路径
56 | process.env["VLC_PLUGIN_PATH"] = path.join(__dirname, "../../node_modules/wcjs-prebuilt/bin/plugins");
57 |
58 | app.on("ready", async() =>
59 | {
60 | this.createMainWindow();
61 |
62 | // 注册全局快捷键
63 | globalShortcut.register("f6", () =>
64 | {
65 | this._mainWindow.openDevTools({detach: true});
66 | });
67 |
68 | // 注册全局快捷键
69 | globalShortcut.register("f11", () =>
70 | {
71 | this._mainWindow.setFullScreen(!this._mainWindow.isFullScreen());
72 | });
73 | });
74 |
75 | app.on("window-all-closed", () =>
76 | {
77 | app.quit();
78 | });
79 |
80 | app.on("activate", () =>
81 | {
82 | if(this._mainWindow === null)
83 | {
84 | this.createMainWindow();
85 | }
86 | });
87 | }
88 |
89 | /**
90 | * 创建主窗口。
91 | * @returns void
92 | */
93 | private createMainWindow(): void
94 | {
95 | let context = this.applicationContext as ApplicationContext;
96 |
97 | // 测试版默认打开为最大化
98 | this._mainWindow = new BrowserWindow
99 | ({
100 | width: 1460,
101 | height: 900,
102 | useContentSize: false
103 | });
104 |
105 | if(process.env.NODE_ENV === "development")
106 | {
107 | // 隐藏菜单栏
108 | this._mainWindow.setMenuBarVisibility(false);
109 | }
110 |
111 | // 打开主地址
112 | this._mainWindow.loadURL(context.mainUrl);
113 |
114 | // 绑定关闭事件
115 | this._mainWindow.on("closed", () =>
116 | {
117 | // 释放主窗口
118 | this._mainWindow = null;
119 | });
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/web/application/workbench.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import flagwind from "flagwind-core";
9 | import WorkbenchBase = flagwind.WorkbenchBase;
10 | import ApplicationContextBase = flagwind.ApplicationContextBase;
11 | import ApplicationContext from "./context";
12 | import Workspace from "./workspace";
13 |
14 | import Vue from "vue";
15 | import Router from "vue-router";
16 | import routes from "../routes";
17 |
18 | // 导入系统组件
19 | import { components } from "flagwind-web";
20 |
21 | // 倒入全局样式
22 | import "flagwind-web/dist/styles/flagwind.css";
23 | import "wcjs-player/css/general.css";
24 | import "src/web/styles/index.less";
25 |
26 | /**
27 | * 提供工作台的基本封装。
28 | * @class
29 | * @version 1.0.0
30 | */
31 | export default class Workbench extends WorkbenchBase
32 | {
33 | private _workspace: Workspace;
34 |
35 | /**
36 | * 获取当前应用的主工作空间。
37 | * @property
38 | * @returns Workspace
39 | */
40 | public get workspace(): Workspace
41 | {
42 | return this._workspace;
43 | }
44 |
45 | /**
46 | * 初始化工作台的新实例。
47 | * @param {ApplicationContextBase} applicationContext
48 | */
49 | public constructor(context: ApplicationContextBase)
50 | {
51 | super(context);
52 | }
53 |
54 | /**
55 | * 当工作台打开时调用。
56 | * @async
57 | * @protected
58 | * @virtual
59 | * @param {Array} args
60 | * @returns void
61 | */
62 | protected async onOpen(args: Array): Promise
63 | {
64 | let context = this.applicationContext as ApplicationContext;
65 |
66 | Vue.config.productionTip = false;
67 |
68 | Vue.config.errorHandler = (err, vm, info) =>
69 | {
70 | console.error(err, vm, info);
71 | };
72 |
73 | Vue.config.warnHandler = (msg, vm, trace) =>
74 | {
75 | console.warn(msg, vm, trace);
76 | };
77 |
78 | // 初始化组件
79 | this.initializeComponent(context);
80 |
81 | // 初始化路由程序
82 | this.initializeRouter(context);
83 |
84 | // 初始化状态管理程序
85 | this.initializeStore(context);
86 |
87 | // 初始化快捷键
88 | this.initializeShortcut(context);
89 |
90 | // 初始化工作空间
91 | this._workspace = this.createWorkspace();
92 | }
93 |
94 | /**
95 | * 创建一个工作空间对象。
96 | * @override
97 | * @returns IWorkspace
98 | */
99 | protected createWorkspace(): Workspace
100 | {
101 | return new Workspace(this);
102 | }
103 |
104 | /**
105 | * 初始化全局组件。
106 | * @param {ApplicationContext} context 应用程序上下文实例。
107 | * @returns void
108 | */
109 | private initializeComponent(context: ApplicationContext): void
110 | {
111 | // 注册系统组件
112 | Vue.use(components);
113 |
114 | // 注册应用组件
115 |
116 | // 注册布局母版
117 | }
118 |
119 | /**
120 | * 初始化路由程序。
121 | * @param {ApplicationContext} context 应用程序上下文实例。
122 | * @returns void
123 | */
124 | private initializeRouter(context: ApplicationContext): void
125 | {
126 | // 注册路由组件
127 | Vue.use(Router);
128 |
129 | // 初始化路由程序
130 | let router = new Router({routes});
131 |
132 | // 设置路由程序
133 | context.router = router;
134 | }
135 |
136 | /**
137 | * 初始化状态管理程序。
138 | * @param {ApplicationContext} context 应用程序上下文实例。
139 | * @returns void
140 | */
141 | private initializeStore(context: ApplicationContext): void
142 | {
143 | // 注册状态管理程序
144 | // Vue.use(Vuex);
145 |
146 | // 初始化状态容器
147 | // let store = new Vuex.Store
148 | // ({
149 | // modules
150 | // });
151 |
152 | // 设置状态容器
153 | // context.store = store;
154 | }
155 |
156 | /**
157 | * 初始化状态管理程序。
158 | * @param {ApplicationContext} context 应用程序上下文实例。
159 | * @returns void
160 | */
161 | private initializeShortcut(context: ApplicationContext): void
162 | {
163 | window.addEventListener("keyup", (e: KeyboardEvent) =>
164 | {
165 | // 绑定 F2 快捷键,跳转至设置视图
166 | if(e.keyCode === 113)
167 | {
168 | context.router.push("/settings");
169 | }
170 |
171 | // 绑定 F8 快捷键,跳转至引导视图
172 | if(e.keyCode === 119)
173 | {
174 | context.router.push("/guide");
175 | }
176 |
177 | }, true);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/web/components/player/player.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Authors:
3 | * jason
4 | *
5 | * Copyright (C) 2010-present Flagwind Inc. All rights reserved.
6 | */
7 |
8 | import { component, config, watch, Component } from "flagwind-web";
9 | import "./player.less";
10 |
11 | // tslint:disable-next-line:variable-name no-var-requires
12 | const WebChimera = require("wcjs-player");
13 | // tslint:disable-next-line:no-var-requires
14 | const wcjs = require("wcjs-prebuilt");
15 |
16 | const seed = new Array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "p", "Q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "2", "3", "4", "5", "6", "7", "8", "9");
17 | const seedlength = seed.length;
18 |
19 | const getRandomCode = (size: number) =>
20 | {
21 | let result = "";
22 |
23 | for(let i = 0; i < size; i++)
24 | {
25 | const j = Math.floor(Math.random() * seedlength);
26 |
27 | result += seed[j];
28 | }
29 |
30 | return result;
31 | };
32 |
33 | /**
34 | * 播放器。
35 | * @class
36 | * @version 1.0.0
37 | */
38 | @component
39 | ({
40 | template: require("./player.html")
41 | })
42 | export default class Player extends Component
43 | {
44 | private _videoId: string; // 播放器编号
45 | private _video: any; // 播放器实例
46 |
47 | /**
48 | * 获取视频编号。
49 | * @protected
50 | * @property
51 | * @returns string
52 | */
53 | protected get videoId(): string
54 | {
55 | if(!this._videoId)
56 | {
57 | this._videoId = `player-${getRandomCode(6)}`;
58 | }
59 |
60 | return this._videoId;
61 | }
62 |
63 | public get video(): object
64 | {
65 | return this._video;
66 | }
67 |
68 | /**
69 | * 获取或设置需要显示的消息。
70 | * @public
71 | * @config {string}
72 | * @description 动态配置,支持响应式。
73 | */
74 | @config({type: String})
75 | public message: string;
76 |
77 | /**
78 | * 获取或设置播放地址。
79 | * @public
80 | * @config {string}
81 | * @description 动态配置,支持响应式。
82 | */
83 | @config({required: true, type: String})
84 | public src: string;
85 |
86 | /**
87 | * 获取或设置是否显示播放器UI。
88 | * @public
89 | * @config {boolean}
90 | * @default false
91 | * @description 动态配置,支持响应式。
92 | */
93 | @config({type: Boolean, default: false})
94 | public ui: boolean;
95 |
96 | /**
97 | * 获取或设置是否自动播放。
98 | * @public
99 | * @config {boolean}
100 | * @default true
101 | * @description 静态配置,不支持响应式。
102 | */
103 | @config({type: Boolean, default: true})
104 | public autoplay: boolean;
105 |
106 | /**
107 | * 获取或设置是否以静音方式播放。
108 | * @public
109 | * @config {boolean}
110 | * @default false
111 | * @description 静态配置,不支持响应式。
112 | */
113 | @config({type: Boolean, default: false})
114 | public mute: boolean;
115 |
116 | /**
117 | * 获取或设置是否自动循环播放。
118 | * @public
119 | * @config {boolean}
120 | * @default false
121 | * @description 静态配置,不支持响应式。
122 | */
123 | @config({type: Boolean, default: false})
124 | public loop: boolean;
125 |
126 | /**
127 | * 获取或设置是否允许全屏播放。
128 | * @public
129 | * @config {boolean}
130 | * @default true
131 | * @description 静态配置,不支持响应式。
132 | */
133 | @config({type: Boolean, default: true})
134 | public allowFullscreen: boolean;
135 |
136 | /**
137 | * 获取或设置网络资源缓存值(单位:毫秒)。
138 | * @public
139 | * @config {number}
140 | * @default 10000
141 | * @description 静态配置,不支持响应式。
142 | */
143 | @config({type: Number, default: 10000})
144 | public buffer: number;
145 |
146 | /**
147 | * 获取或设置标题栏何时可见。
148 | * @public
149 | * @config {string}
150 | * @default "none"
151 | * @description 取值范围:"fullscreen"、"minimized"、"both"、"none" 静态配置,不支持响应式。
152 | */
153 | @config({type: String, default: "none"})
154 | public titleBar: string;
155 |
156 | /**
157 | * 获取或设置VLC参数。
158 | * @public
159 | * @config {Array}
160 | * @description 静态配置,不支持响应式。
161 | * @see https://wiki.videolan.org/VLC_command-line_help
162 | */
163 | @config({type: Array})
164 | public vlcArgs: Array;
165 |
166 | /**
167 | * 获取或设置WCJS选项。
168 | * @public
169 | * @config {Object}
170 | * @description 静态配置,不支持响应式。
171 | * @see https://github.com/Magics-Group/wcjs-renderer
172 | */
173 | @config({type: Object})
174 | public wcjsRendererOptions: object;
175 |
176 | /**
177 | * 创建组件时调用的钩子方法。
178 | * @protected
179 | * @override
180 | * @returns void
181 | */
182 | protected mounted(): void
183 | {
184 | try
185 | {
186 | const options: any =
187 | {
188 | wcjs: wcjs,
189 | autoplay: this.autoplay,
190 | mute: this.mute,
191 | loop: this.loop,
192 | allowFullscreen: this.allowFullscreen,
193 | buffer: this.buffer,
194 | titleBar: this.titleBar
195 | };
196 |
197 | if(this.vlcArgs)
198 | {
199 | options.vlcArgs = this.vlcArgs;
200 | }
201 |
202 | if(this.wcjsRendererOptions)
203 | {
204 | options.wcjsRendererOptions = this.wcjsRendererOptions;
205 | }
206 |
207 | // 初始化播放器
208 | this._video = new WebChimera(`#${this.videoId}`).addPlayer(options);
209 |
210 | this._video.onError = (error: any) =>
211 | {
212 | this.onPlayError(error);
213 | };
214 |
215 | // 设置播放器UI
216 | this.setUserInterface(this.ui);
217 |
218 | // 设置视频源
219 | this.setSource(this.src);
220 | }
221 | catch(ex)
222 | {
223 | this.onPlayError(ex);
224 | }
225 | }
226 |
227 | /**
228 | * 当 "src" 发生变动时调用。
229 | * @protected
230 | * @param {String} src 视频源。
231 | * @returns void
232 | */
233 | @watch("src")
234 | protected onSourceChange(src: string): void
235 | {
236 | this.setSource(src);
237 | }
238 |
239 | /**
240 | * 当 "ui" 发生变动时调用。
241 | * @protected
242 | * @param {boolean} ui 是否显示UI界面。
243 | * @returns void
244 | */
245 | @watch("ui")
246 | protected onUIChange(ui: boolean): void
247 | {
248 | this.setUserInterface(ui);
249 | }
250 |
251 | /**
252 | * 设置播放器的视频源。
253 | * @private
254 | * @param {string} src 视频源。
255 | * @returns void
256 | */
257 | private setSource(src: string): void
258 | {
259 | if(src)
260 | {
261 | this._video.pause();
262 | this._video.clearPlaylist();
263 | this._video.addPlaylist(src);
264 | this._video.play();
265 | }
266 | }
267 |
268 | /**
269 | * 设置播放器的UI界面是否显示。
270 | * @param {boolean} show
271 | * @returns void
272 | */
273 | private setUserInterface(show: boolean): void
274 | {
275 | this._video.ui(show);
276 | }
277 |
278 | private onPlayError(error: any): void
279 | {
280 | this.message = "播放失败";
281 |
282 | console.error(error);
283 | }
284 | }
285 |
--------------------------------------------------------------------------------