All Properties working, added usages

This commit is contained in:
Xing Han Lu 2018-07-26 18:00:18 -04:00
parent 2834ccd7c7
commit 0d391fbf98
27 changed files with 13650 additions and 1 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "./node_modules/dash-components-archetype/config/babel/babelrc"
}

3
.builderrc Normal file
View File

@ -0,0 +1,3 @@
---
archetypes:
- dash-components-archetype

2
.eslintrc Normal file
View File

@ -0,0 +1,2 @@
---
extends: ./node_modules/dash-components-archetype/config/eslint/eslintrc-react.json

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
build/
coverage/
dist/
lib/
lib/bundle.js*
node_modules/
dash_player/metadata.json
dash_player/bundle.js*
.npm
vv/
venv/
*.pyc
*.egg-info
*.log
.DS_Store
.idea

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
node_modules/
.npm
.git/
vv/
venv/
*.pyc
*.log
.DS_Store

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
# Change Log for dash-player
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
### Added
- Feature x
### Fixed
- Bug y
## 0.0.1 - 2018-07-26
- Initial release
[Unreleased]: https://github.com/plotly/dash-player/v0.0.1...HEAD

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 plotly
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.

5
MANIFEST.in Normal file
View File

@ -0,0 +1,5 @@
include dash_player/bundle.js
include dash_player/bundle.js.map
include dash_player/metadata.json
include README.md
include LICENSE.md

165
README.md
View File

@ -1 +1,164 @@
# dash-player
# dash-player
A Dash component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia, Mixcloud, and DailyMotion.
## Dash
Go to this link to learn about [Dash][].
## Getting started
```sh
# Install dependencies
$ npm install
# Watch source for changes and build to `lib/`
$ npm start
```
## Development
### Demo server
You can start up a demo development server to see a demo of the rendered
components:
```sh
$ builder run demo
$ open http://localhost:9000
```
You have to maintain the list of components in `demo/Demo.react.js`.
### Code quality and tests
#### To run lint and unit tests:
```sh
$ npm test
```
#### To run unit tests and watch for changes:
```sh
$ npm run test-watch
```
#### To debug unit tests in a browser (Chrome):
```sh
$ npm run test-debug
```
1. Wait until Chrome launches.
2. Click the "DEBUG" button in the top right corner.
3. Open up Chrome Devtools (`Cmd+opt+i`).
4. Click the "Sources" tab.
5. Find source files
- Navigate to `webpack:// -> . -> spec/components` to find your test source files.
- Navigate to `webpack:// -> [your/repo/path]] -> dash-player -> src` to find your component source files.
6. Now you can set breakpoints and reload the page to hit them.
7. The test output is available in the "Console" tab, or in any tab by pressing "Esc".
#### To run a specific test
In your test, append `.only` to a `describe` or `it` statement:
```javascript
describe.only('Foo component', () => {
// ...
})l
```
### Testing your components in Dash
1. Build development bundle to `lib/` and watch for changes
# Once this is started, you can just leave it running.
$ npm start
2. Install module locally (after every change)
# Generate metadata, and build the JavaScript bundle
$ npm run install-local
# Now you're done. For subsequent changes, if you've got `npm start`
# running in a separate process, it's enough to just do:
$ python setup.py install
3. Run the dash layout you want to test
# Import dash-player to your layout, then run it:
$ python my_dash_layout.py
**TODO:** There is a workflow that links your module into `site-packages` which would
make it unnecessary to re-run `2.` on every change: `python setup.py develop`.
Unfortunately, this doesn't seem to work with resources defined in
`package_data`.
See https://github.com/plotly/dash-components-archetype/issues/20
## Installing python package locally
Before publishing to PyPi, you can test installing the module locally:
```sh
# Install in `site-packages` on your machine
$ npm run install-local
```
## Uninstalling python package locally
```sh
$ npm run uninstall-local
```
## Publishing
For now, multiple steps are necessary for publishing to NPM and PyPi,
respectively. **TODO:**
[#5](https://github.com/plotly/dash-components-archetype/issues/5) will roll up
publishing steps into one workflow.
Ask @chriddyp to get NPM / PyPi package publishing accesss.
1. Preparing to publish to NPM
# Bump the package version
$ npm version major|minor|patch
# Push branch and tags to repo
$ git push --follow-tags
2. Preparing to publish to PyPi
# Bump the PyPi package to the same version
$ vi setup.py
# Commit to github
$ git add setup.py
$ git commit -m "Bump pypi package version to vx.x.x"
3. Publish to npm and PyPi
$ npm run publish-all
## Builder / Archetype
We use [Builder][] to centrally manage build configuration, dependencies, and
scripts.
To see all `builder` scripts available:
```sh
$ builder help
```
See the [dash-components-archetype][] repo for more information.
[Builder]: https://github.com/FormidableLabs/builder
[Dash]: https://github.com/plotly/dash2
[dash-components-archetype]: https://github.com/plotly/dash-components-archetype

33
dash_player/__init__.py Normal file
View File

@ -0,0 +1,33 @@
import os as _os
import dash as _dash
import sys as _sys
from .version import __version__
_current_path = _os.path.dirname(_os.path.abspath(__file__))
_components = _dash.development.component_loader.load_components(
_os.path.join(_current_path, 'metadata.json'),
'dash_player'
)
_this_module = _sys.modules[__name__]
_js_dist = [
{
"relative_package_path": "bundle.js",
"external_url": (
"https://unpkg.com/dash-player@{}"
"/dash_player/bundle.js"
).format(__version__),
"namespace": "dash_player"
}
]
_css_dist = []
for _component in _components:
setattr(_this_module, _component.__name__, _component)
setattr(_component, '_js_dist', _js_dist)
setattr(_component, '_css_dist', _css_dist)

1
dash_player/version.py Normal file
View File

@ -0,0 +1 @@
__version__ = '0.0.1'

30
demo/Demo.react.js Normal file
View File

@ -0,0 +1,30 @@
import React, {Component} from 'react';
import {ExampleComponent} from '../src';
class Demo extends Component {
constructor() {
super();
this.state = {
value: ''
}
}
render() {
return (
<div>
<h1>dash-player Demo</h1>
<hr/>
<h2>ExampleComponent</h2>
<ExampleComponent
label="This is an example label"
value={this.state.value}
setProps={newProps => this.setState({value: newProps.value})}
/>
<hr/>
</div>
);
}
}
export default Demo;

13
demo/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="https://unpkg.com/react@15.4.2/dist/react.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/react-dom@15.4.2/dist/react-dom.min.js"></script>
</head>
<body>
<div id="react-demo-entry-point"></div>
</body>
<script type="text/javascript" src="/lib/bundle.js"></script>
</html>

20
demo/index.js Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Demo from './Demo.react';
// Fix for rendering React externally:
// See https://github.com/gaearon/react-hot-loader/tree/v1.3.1/docs#usage-with-external-react
const rootInstance = ReactDOM.render(
<Demo/>,
document.getElementById('react-demo-entry-point')
);
/* eslint-disable */
require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({
/* eslint-enable */
getRootInstances: function () {
// Help React Hot Loader figure out the root component instances on the page:
return [rootInstance];
}
});

7552
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "dash-player",
"version": "0.0.1",
"description": "A Dash component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia, Mixcloud, and DailyMotion.",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "https://github.com/plotly/dash-player.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/plotly/dash-player/issues"
},
"homepage": "https://github.com/plotly/dash-player",
"scripts": {
"copy-lib": "copyfiles -u 1 lib/* dash_player",
"demo": "builder run demo",
"install-local": "npm run copy-lib && python setup.py install",
"prepublish": "npm test && builder run build-dist && npm run copy-lib",
"publish-all": "npm publish && python setup.py sdist upload",
"publish-pypi": "npm run prepublish && python setup.py sdist upload",
"start": "builder run build-dev",
"test": "builder run check",
"test-watch": "builder run test-frontend-watch",
"test-debug": "builder run test-frontend-debug",
"uninstall-local": "pip uninstall dash-player -y"
},
"dependencies": {
"builder": "3.2.2",
"copyfiles": "^1.2.0",
"dash-components-archetype": "^0.2.11",
"prop-types": "^15.5.9",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-player": "^1.6.4"
},
"devDependencies": {
"dash-components-archetype-dev": "^0.2.11",
"enzyme": "^2.8.2",
"react-test-renderer": "^15.5.4"
}
}

10
requirements.txt Normal file
View File

@ -0,0 +1,10 @@
--index-url https://pypi.python.org/simple/
-e .
# Core
plotly==2.7.0
dash==0.21.1
dash-renderer==0.12.1
dash-html-components==0.10.1
dash-core-components==0.22.1.

14
setup.py Normal file
View File

@ -0,0 +1,14 @@
from setuptools import setup
exec (open('dash_player/version.py').read())
setup(
name='dash_player',
version=__version__,
author='plotly',
packages=['dash_player'],
include_package_data=True,
license='MIT',
description='A Dash component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia, Mixcloud, and DailyMotion.',
install_requires=[]
)

View File

@ -0,0 +1,59 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
/**
* ExampleComponent is an example component.
* It takes a property, `label`, and
* displays it.
* It renders an input with the property `value`
* which is editable by the user.
*/
export default class ExampleComponent extends Component {
render() {
const {id, label, setProps, value} = this.props;
return (
<div id={id}>
ExampleComponent: {label}
<input
value={value}
onChange={e => {
/*
* Send the new value to the parent component.
* In a Dash app, this will send the data back to the
* Python Dash app server.
*/
if (setProps) {
setProps({
value: e.target.value
});
}
}}
/>
</div>
);
}
}
ExampleComponent.propTypes = {
/**
* The ID used to identify this compnent in Dash callbacks
*/
id: PropTypes.string,
/**
* A label that will be printed when this component is rendered.
*/
label: PropTypes.string.isRequired,
/**
* The value displayed in the input
*/
value: PropTypes.string,
/**
* Dash-assigned callback that should be called whenever any of the
* properties change
*/
setProps: PropTypes.func
};

View File

@ -0,0 +1,132 @@
import React, {Component} from 'react';
import ReactPlayer from 'react-player';
import PropTypes from 'prop-types';
/**
* ExampleComponent is an example component.
* It takes a property, `label`, and
* displays it.
* It renders an input with the property `value`
* which is editable by the user.
*/
export default class PlayerComponent extends Component {
render() {
const {
url,
playing,
loop,
controls,
volume,
muted,
playbackRate,
width,
height,
progressInterval,
playsinline
} = this.props;
return (
<ReactPlayer
ref="player"
url={url}
playing={playing}
loop={loop}
controls={controls}
volume={volume}
muted={muted}
playbackRate={playbackRate}
width={width}
height={height}
progressInterval={progressInterval}
playsline={playsinline}
/>
);
}
}
PlayerComponent.propTypes = {
/**
* The ID used to identify this compnent in Dash callbacks
*/
id: PropTypes.string,
/**
* Dash-assigned callback that should be called whenever any of the
* properties change
*/
setProps: PropTypes.func,
/**
* The url of a video or song to play
* Can be an array or MediaStream object
*/
url: PropTypes.string,
/**
* Set to true or false to pause or play the media
*/
playing: PropTypes.bool,
/**
* Set to true or false to loop the media
*/
loop: PropTypes.bool,
/**
* Set to true or false to display native player controls
* Vimeo, Twitch and Wistia player will always display controls
*/
controls: PropTypes.bool,
/**
* Set the volume of the player, between 0 and 1
* null uses default volume on all players
*/
volume: PropTypes.number,
/**
* Mutes the player
* Only works if volume is set
*/
muted: PropTypes.bool,
/**
* Set the playback rate of the player
* Only supported by YouTube, Wistia, and file paths
*/
playbackRate: PropTypes.number,
/**
* Set the width of the player
*/
width: PropTypes.string,
/**
* Set the height of the player
*/
height: PropTypes.string,
/**
* The time between onProgress callbacks, in milliseconds
*/
progressInterval: PropTypes.string,
/**
* Applies the playsinline attribute where supported
*/
playsinline: PropTypes.bool
};
PlayerComponent.defaultProps = {
playing: false,
loop: false,
controls: false,
volume: null,
muted: false,
playbackRate: 1,
width: '640px',
height: '360px',
progressInterval: 1000,
playsinline: false
};

View File

@ -0,0 +1,5 @@
---
extends: ../../../node_modules/dash-components-archetype/config/eslint/eslintrc-test.json
globals:
expect: false

View File

@ -0,0 +1,11 @@
import React from 'react';
import {shallow} from 'enzyme';
import ExampleComponent from '../ExampleComponent.react';
describe('ExampleComponent', () => {
it('renders', () => {
const component = shallow(<ExampleComponent label="Test label"/>);
expect(component).to.be.ok;
});
});

8
src/index.js Normal file
View File

@ -0,0 +1,8 @@
/* eslint-disable import/prefer-default-export */
import ExampleComponent from './components/ExampleComponent.react';
import PlayerComponent from './components/PlayerComponent.react';
export {
ExampleComponent,
PlayerComponent
};

11
test/main.js Normal file
View File

@ -0,0 +1,11 @@
/* eslint-disable import/default */
/* global require:false */
import karmaRunner from 'dash-components-archetype-dev/karma-runner';
karmaRunner.setupEnvironment();
// Use webpack to infer and `require` tests automatically.
var testsReq = require.context('../src', true, /\.test.js$/);
testsReq.keys().map(testsReq);
karmaRunner.startKarma();

116
usage-advanced.py Normal file
View File

@ -0,0 +1,116 @@
from textwrap import dedent
import dash_player
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
app = dash.Dash('')
app.scripts.config.serve_locally = True
app.layout = html.Div([
html.Div(style={'width': '40%', 'float': 'left'}, children=[
dash_player.PlayerComponent(
id='video-player',
url='http://media.w3.org/2010/05/bunny/movie.mp4',
controls=True
),
dcc.Markdown(dedent('''
### Video Examples
* mp4: http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
* mp3: https://storage.googleapis.com/media-session/elephants-dream/the-wires.mp3
* webm: http://clips.vorwaerts-gmbh.de/big_buck_bunny.webm
* ogv: http://clips.vorwaerts-gmbh.de/big_buck_bunny.ogv
* Youtube: https://www.youtube.com/watch?v=sea2K4AuPOk
'''))
]),
html.Div(style={'width': '40%', 'float': 'left'}, children=[
dcc.Input(
id='input-url',
value='http://media.w3.org/2010/05/bunny/movie.mp4'
),
html.Button('Change URL', id='button-update-url'),
dcc.Checklist(
id='radio-bool-props',
options=[{'label': val.capitalize(), 'value': val} for val in [
'playing',
'loop',
'controls',
'muted'
]],
values=['controls']
),
html.P("Volume:", style={'margin-top': '10px'}),
dcc.Slider(
id='slider-volume',
min=0,
max=1,
step=0.1,
value=None,
marks={0: '0%', 1: '100%'}
),
html.P("Playback Rate:", style={'margin-top': '25px'}),
dcc.Slider(
id='slider-playback-rate',
min=0,
max=4,
step=None,
marks={i: str(i)+'x' for i in [0, 0.25, 0.5, 0.75, 1, 2, 3, 4]},
value=1
)
]),
])
@app.callback(Output('video-player', 'playing'),
[Input('radio-bool-props', 'values')])
def update_prop_playing(values):
return 'playing' in values
@app.callback(Output('video-player', 'loop'),
[Input('radio-bool-props', 'values')])
def update_prop_loop(values):
return 'loop' in values
@app.callback(Output('video-player', 'controls'),
[Input('radio-bool-props', 'values')])
def update_prop_controls(values):
return 'controls' in values
@app.callback(Output('video-player', 'muted'),
[Input('radio-bool-props', 'values')])
def update_prop_muted(values):
return 'muted' in values
@app.callback(Output('video-player', 'volume'),
[Input('slider-volume', 'value')])
def update_volume(value):
return value
@app.callback(Output('video-player', 'playbackRate'),
[Input('slider-playback-rate', 'value')])
def update_playbackRate(value):
return value
@app.callback(Output('video-player', 'url'),
[Input('button-update-url', 'n_clicks')],
[State('input-url', 'value')])
def update_url(n_clicks, value):
return value
if __name__ == '__main__':
app.run_server(debug=True)

57
usage.py Normal file
View File

@ -0,0 +1,57 @@
import dash_player
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
app = dash.Dash('')
app.scripts.config.serve_locally = True
app.layout = html.Div([
dash_player.PlayerComponent(
id='video-player',
url='http://media.w3.org/2010/05/bunny/movie.mp4',
controls=True
),
dcc.Checklist(
id='radio-bool-props',
options=[{'label': val.capitalize(), 'value': val} for val in [
'playing',
'loop',
'controls',
'muted'
]],
values=['controls']
)
])
@app.callback(Output('video-player', 'playing'),
[Input('radio-bool-props', 'values')])
def update_prop_playing(values):
return 'playing' in values
@app.callback(Output('video-player', 'loop'),
[Input('radio-bool-props', 'values')])
def update_prop_loop(values):
return 'loop' in values
@app.callback(Output('video-player', 'controls'),
[Input('radio-bool-props', 'values')])
def update_prop_controls(values):
return 'controls' in values
@app.callback(Output('video-player', 'muted'),
[Input('radio-bool-props', 'values')])
def update_prop_muted(values):
return 'muted' in values
if __name__ == '__main__':
app.run_server(debug=True)

5297
yarn.lock Normal file

File diff suppressed because it is too large Load Diff