Merge branch 'torrents' into 'master'
Add torrent page See merge request !69
This commit is contained in:
commit
a22c57e4e5
@ -284,3 +284,10 @@ export function addTorrent(url) {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fetchTorrents() {
|
||||||
|
return request(
|
||||||
|
'TORRENTS_FETCH',
|
||||||
|
configureAxios().get('/torrents')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -43,6 +43,7 @@ import ShowDetails from './components/shows/details'
|
|||||||
import UserLoginForm from './components/users/login'
|
import UserLoginForm from './components/users/login'
|
||||||
import UserEdit from './components/users/edit'
|
import UserEdit from './components/users/edit'
|
||||||
import UserSignUp from './components/users/signup'
|
import UserSignUp from './components/users/signup'
|
||||||
|
import TorrentList from './components/torrents/list'
|
||||||
|
|
||||||
function Main(props) {
|
function Main(props) {
|
||||||
return (
|
return (
|
||||||
@ -61,6 +62,7 @@ function mapStateToProps(state) {
|
|||||||
movieStore: state.movieStore,
|
movieStore: state.movieStore,
|
||||||
showStore: state.showStore,
|
showStore: state.showStore,
|
||||||
userStore: state.userStore,
|
userStore: state.userStore,
|
||||||
|
torrentStore: state.torrentStore,
|
||||||
alerts: state.alerts,
|
alerts: state.alerts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +72,14 @@ function mapDispatchToProps(dispatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
|
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
|
||||||
|
export function startPollingTorrents() {
|
||||||
|
return request(
|
||||||
|
'TORRENTS_FETCH',
|
||||||
|
configureAxios().get('/torrents')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollingTorrentsId;
|
||||||
const loginCheck = function(nextState, replace, next, f) {
|
const loginCheck = function(nextState, replace, next, f) {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const isLogged = state.userStore.isLogged;
|
const isLogged = state.userStore.isLogged;
|
||||||
@ -91,6 +100,13 @@ const loginCheck = function(nextState, replace, next, f) {
|
|||||||
replace('/users/login');
|
replace('/users/login');
|
||||||
} else {
|
} else {
|
||||||
f();
|
f();
|
||||||
|
// Poll torrents once logged
|
||||||
|
if (!pollingTorrentsId) {
|
||||||
|
// Fetch the torrents every 10s
|
||||||
|
pollingTorrentsId = setInterval(function() {
|
||||||
|
store.dispatch(actionCreators.fetchTorrents());
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
@ -208,6 +224,15 @@ const routes = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/torrents',
|
||||||
|
component: TorrentList,
|
||||||
|
onEnter: function(nextState, replace, next) {
|
||||||
|
loginCheck(nextState, replace, next, function() {
|
||||||
|
store.dispatch(actionCreators.fetchTorrents());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ export default function NavBar(props) {
|
|||||||
<MoviesDropdown username={props.userStore.username} />
|
<MoviesDropdown username={props.userStore.username} />
|
||||||
<ShowsDropdown username={props.userStore.username} />
|
<ShowsDropdown username={props.userStore.username} />
|
||||||
<WishlistDropdown username={props.userStore.username} />
|
<WishlistDropdown username={props.userStore.username} />
|
||||||
|
<Torrents
|
||||||
|
username={props.userStore.username}
|
||||||
|
torrentsCount={props.torrentStore.torrents.length}
|
||||||
|
/>
|
||||||
<UserDropdown username={props.userStore.username} />
|
<UserDropdown username={props.userStore.username} />
|
||||||
<Search
|
<Search
|
||||||
placeholder="Search movies"
|
placeholder="Search movies"
|
||||||
@ -148,3 +152,24 @@ function WishlistDropdown(props) {
|
|||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Torrents(props) {
|
||||||
|
if (props.username === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<Nav>
|
||||||
|
<LinkContainer to="/torrents">
|
||||||
|
<NavItem>
|
||||||
|
Torrents
|
||||||
|
{props.torrentsCount > 0 &&
|
||||||
|
<span>
|
||||||
|
|
||||||
|
<span className="label label-info">{props.torrentsCount}</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</NavItem>
|
||||||
|
</LinkContainer>
|
||||||
|
</Nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
133
src/public/js/components/torrents/list.js
Normal file
133
src/public/js/components/torrents/list.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function TorrentList(props){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AddTorrent func={props.addTorrent} />
|
||||||
|
<List torrents={props.torrentStore.torrents} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddTorrent extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { url: '' };
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
}
|
||||||
|
handleChange(event) {
|
||||||
|
this.setState({ url: event.target.value });
|
||||||
|
}
|
||||||
|
handleSubmit() {
|
||||||
|
if (this.state.url === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ url: '' });
|
||||||
|
this.props.func(this.state.url);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12 col-md-12">
|
||||||
|
<form className="input-group" onSubmit={this.handleSubmit}>
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Add torrent URL"
|
||||||
|
onChange={this.handleChange}
|
||||||
|
value={this.state.url}
|
||||||
|
/>
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
onClick={this.handleSubmit}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function List(props){
|
||||||
|
if (props.torrents.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12 col-md-12">
|
||||||
|
<h3>Torrents</h3>
|
||||||
|
<div className="panel panel-default">
|
||||||
|
<div className="panel-heading">No torrents</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12 col-md-12">
|
||||||
|
<h3>Torrents</h3>
|
||||||
|
{props.torrents.map(function(el, index) {
|
||||||
|
return (
|
||||||
|
<Torrent key={index} data={el} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Torrent(props){
|
||||||
|
const done = props.data.is_finished;
|
||||||
|
var progressStyle = 'progress-bar progress-bar-warning';
|
||||||
|
if (done) {
|
||||||
|
progressStyle = 'progress-bar progress-bar-success';
|
||||||
|
}
|
||||||
|
var percentDone = props.data.percent_done;
|
||||||
|
const started = (percentDone !== 0);
|
||||||
|
if (started) {
|
||||||
|
percentDone = Number(percentDone).toFixed(1) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadedSize = prettySize(props.data.downloaded_size);
|
||||||
|
var totalSize = prettySize(props.data.total_size);
|
||||||
|
var downloadRate = prettySize(props.data.download_rate) + "/s";
|
||||||
|
return (
|
||||||
|
<div className="panel panel-default">
|
||||||
|
<div className="panel-heading">{props.data.name}</div>
|
||||||
|
<div className="panel-body">
|
||||||
|
{started &&
|
||||||
|
<div className="progress progress-striped">
|
||||||
|
<div
|
||||||
|
className={progressStyle}
|
||||||
|
style={{width: percentDone}}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{!started &&
|
||||||
|
<p>Download not yet started</p>
|
||||||
|
}
|
||||||
|
{started &&
|
||||||
|
<div>
|
||||||
|
<p>{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prettySize(fileSizeInBytes) {
|
||||||
|
var i = -1;
|
||||||
|
var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
do {
|
||||||
|
fileSizeInBytes = fileSizeInBytes / 1024;
|
||||||
|
i++;
|
||||||
|
} while (fileSizeInBytes > 1024);
|
||||||
|
|
||||||
|
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
|
||||||
|
};
|
@ -5,6 +5,7 @@ import movieStore from './movies'
|
|||||||
import showStore from './shows'
|
import showStore from './shows'
|
||||||
import userStore from './users'
|
import userStore from './users'
|
||||||
import alerts from './alerts'
|
import alerts from './alerts'
|
||||||
|
import torrentStore from './torrents'
|
||||||
|
|
||||||
// Use combine form form react-redux-form, it's a thin wrapper arround the
|
// Use combine form form react-redux-form, it's a thin wrapper arround the
|
||||||
// default combinedReducers provided with React. It allows the forms to be
|
// default combinedReducers provided with React. It allows the forms to be
|
||||||
@ -15,6 +16,7 @@ const rootReducer = combineForms({
|
|||||||
showStore,
|
showStore,
|
||||||
userStore,
|
userStore,
|
||||||
alerts,
|
alerts,
|
||||||
|
torrentStore,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
20
src/public/js/reducers/torrents.js
Normal file
20
src/public/js/reducers/torrents.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const defaultState = {
|
||||||
|
fetching: false,
|
||||||
|
torrents: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function showStore(state = defaultState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'TORRENTS_FETCH_PENDING':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
fetching: true,
|
||||||
|
})
|
||||||
|
case 'TORRENTS_FETCH_FULFILLED':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
fetching: false,
|
||||||
|
torrents: action.payload.data,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user