commit 5bb231e2e6e615a3b6a0528f82b627409ed2a8f9 Author: Zijin Xiao <28256042@qq.com> Date: Sat Feb 11 17:33:27 2017 +0800 Initial commit diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..b98dc73 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,30 @@ +{ + "extends": "airbnb", + "plugins": [ + "react" + ], + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "module" + }, + "rules": { + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "warn", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "constructor-super": "warn", + "valid-typeof": "warn", + "semi": [2, "always"], + "indent": [2,4], + "no-path-concat": 0 + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d38747 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +dist +node_modules +.DS_Store +.vscode \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..384ec9d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zijin Xiao + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..87e0753 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# private-music-react +A front-end impl of [Private Cloud Music](https://github.com/BLumia/Private-Cloud-Music) by React and Antd. + +## Install +``` bash +# Clone the repository once +$ git clone https://github.com/BearKidsTeam/private-music-react + +# Go into the repository (rename it as you wish) +$ cd private-music-react + +# Install the dependencies once +$ npm install +``` + +## Usage +``` bash +$ npm start +``` + +## Build +``` bash +$ npm run build +``` +The files will be in ```./dist``` +## License +[MIT](http://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/index.css b/index.css new file mode 100644 index 0000000..0f178d7 --- /dev/null +++ b/index.css @@ -0,0 +1,4 @@ + +html, body, #root { + height: 100%; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..f65f403 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + Demo + + + + +
+ + + + + + diff --git a/index.jsx b/index.jsx new file mode 100644 index 0000000..d3e376e --- /dev/null +++ b/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import Player from './player'; + +function App() { + return ( +
+ +
+ ); +} + +ReactDOM.render(, document.getElementById('root')); diff --git a/package.json b/package.json new file mode 100644 index 0000000..1dc199b --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "private-music-react", + "version": "1.0.0", + "description": "A front-end impl of Private Cloud Music by React and Antd", + "author": "Zijin Xiao ", + "repository": "BearKidsTeam/private-music-react", + "license": "MIT", + "entry": { + "index": "./index.jsx" + }, + "dependencies": { + "antd": "^2.7.1", + "axios": "^0.15.3", + "react": "^15.1.0", + "react-dom": "^15.1.0" + }, + "devDependencies": { + "atool-build": "^0.9.0", + "atool-test-mocha": "^0.1.4", + "babel-eslint": "^7.0.0", + "babel-plugin-import": "^1.0.1", + "babel-plugin-transform-runtime": "^6.8.0", + "babel-runtime": "^6.9.2", + "dora": "0.4.x", + "dora-plugin-webpack": "^0.8.1", + "eslint": "^3.15.0", + "eslint-config-airbnb": "^12.0.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^2.2.3", + "eslint-plugin-react": "^6.9.0", + "expect": "^1.20.1", + "pre-commit": "1.x", + "redbox-react": "^1.2.6" + }, + "pre-commit": [ + "lint" + ], + "scripts": { + "build": "atool-build", + "lint": "eslint --ext .js,.jsx src/", + "start": "dora --plugins webpack", + "test": "atool-test-mocha ./**/__tests__/*-test.js" + } +} diff --git a/player.jsx b/player.jsx new file mode 100644 index 0000000..689f835 --- /dev/null +++ b/player.jsx @@ -0,0 +1,247 @@ +import React from "react"; +import ReactDOM from 'react-dom'; +import { Row,Col,Button,Checkbox,Layout, Menu, Icon, Table, Slider, Modal, Input } from 'antd'; +import axios from 'axios'; + +const { Header, Content, Footer, Sider } = Layout; +const SubMenu = Menu.SubMenu; +const ButtonGroup = Button.Group; + +const columns = [{ + title: 'File Name', + dataIndex: 'fileName', + key: 'fileName', + render: text => {text}, +}]; + +function formatTime(t) { + const m = Math.floor(t / 60); + const s = Math.round(t - Math.floor(t / 60) * 60); + if (s < 10) { + return m + ":0" + s; + } + else if (s === 60) { + return (m + 1) + ":00"; + } + else { + return m + ":" + s; + } +} + +class Player extends React.Component { + audio = null; + currentFolder = ''; + pagination = { + total: 0, + showSizeChanger: true + }; + state = { + playing:'', + loop: false, + order: false, + playIcon: 'caret-right', + percent: 0, + curTime: 0, + totTime: 0, + data: [], + fdlist: [], + collapsed: false, + source: 'http://direct.blumia.cn/hidden', + newSource: 'http://direct.blumia.cn/hidden', + settingsVisible: false + }; + onCollapse = (collapsed) => { + this.setState({ collapsed }); + }; + init = () => { + this.audio = document.getElementsByTagName('audio')[0]; + this.fetchPlaylist(); + this.audio.ontimeupdate = () => { + this.setState({curTime: this.audio.currentTime, totTime: this.audio.duration, percent: this.audio.currentTime * 1.0 / this.audio.duration * 100.0}); + }; + this.audio.onpause = () => { + this.setState({playIcon: 'caret-right'}); + } + this.audio.onplay = () => { + this.setState({playIcon: 'pause'}); + } + }; + fetchPlaylist = () => { + axios({method: 'POST', url: this.state.source + '/api.php',data:'do=getplaylist&folder=/', headers:{'Content-Type':'application/x-www-form-urlencoded'}}) + .then((response) => { + this.setState({fdlist: response.data.result.data.subFolderList.map(x => decodeURIComponent(x))}); + }).catch(() => {}); + }; + fetchMusic = (folder) => { + axios({method: 'POST', url: this.state.source + '/api.php',data:'do=getplaylist&folder=' + folder, headers:{'Content-Type':'application/x-www-form-urlencoded'}}) + .then((response) => { + this.pagination = { + total: response.data.result.data.musicList.length, + showSizeChanger: true + } + this.setState({data: response.data.result.data.musicList.map(x=> { x.fileName = decodeURIComponent(x.fileName); return x})}); + }).catch(() => {}); + + }; + onMenuClick = ({item, key, keyPath}) => { + if (key === 'settings') { + this.setState({settingsVisible : true, newRandomKey: Math.random()}); + return; + } + if (key !== 'settings' && this.state.collapsed) { + this.setState({collapsed:false}); + } + this.currentFolder = key; + this.fetchMusic(key); + }; + componentDidMount() { + this.init(); + }; + playAtIndex = (i) => { + const filename = this.state.data[i].fileName; + this.audio.pause(); + this.audio.src = this.state.source + this.currentFolder + '/' + filename; + this.audio.load(); + this.audio.play(); + this.setState({playing: filename}); + }; + onRowClick = (record,index) => { + this.audio.pause(); + this.audio.src = this.state.source + this.currentFolder + '/' + record.fileName; + this.audio.load(); + this.audio.play(); + this.setState({playing: record.fileName}); + }; + onPlayClick = () => { + if(this.state.playIcon === 'pause') { + this.audio.pause(); + } else { + this.audio.play(); + } + }; + onPrevClick = () => { + const currentIndex = this.state.data.findIndex(x => x.fileName === this.state.playing); + if (currentIndex === -1) { + this.playAtIndex(0); + } else if (currentIndex === 0) { + this.playAtIndex(this.state.data.length - 1); + } else { + this.playAtIndex(Number(currentIndex) - 1); + } + }; + onNextClick = () => { + const currentIndex = this.state.data.findIndex(x => x.fileName === this.state.playing); + if (currentIndex === -1) { + this.playAtIndex(0); + } else if (currentIndex === (this.state.data.length - 1)) { + this.playAtIndex(0); + } else { + this.playAtIndex(Number(currentIndex) + 1); + } + }; + onLoopChange = (value) => { + if (value.target.checked) { + this.audio.loop = true; + } else { + this.audio.loop = false; + } + }; + onOrderChange = (value) => { + if (value.target.checked) { + this.audio.onended = () => { + if (this.audio.loop === 0) { + this.onNextClick(); + } + }; + } else { + this.audio.onended = undefined; + } + }; + onSettingsOk = () => { + this.setState({source: this.state.newSource,settingsVisible:false}); + this.init(); + }; + onSettingsCancel = () => { + this.setState({settingsVisible: false,newSource: this.state.source}); + }; + onChange = (event) => { + this.setState({newSource: event.target.value}); + }; + onProgressChange = (value) => { + this.audio.currentTime = value; + this.setState({percent: value * 1.0 / this.state.totTime,curTime: value}); + }; + onAfterChange = (value) => { + this.audio.currentTime = value; + this.setState({percent: value * 1.0 / this.state.totTime,curTime: value}); + }; + render() { + return ( +
+ + + + + + { !this.state.collapsed && + Folder List}> + {this.state.fdlist.map(x => {x})} + + } + { this.state.collapsed && + + + + } + + { !this.state.collapsed && 'Settings' } + + + + + + + + + +
+ + + +
+ {this.state.playing} + + + + + + {formatTime(this.state.curTime)} + + + + {formatTime(this.state.totTime)} + + +