Add support for magnet links

This commit is contained in:
Drew DeVault 2017-09-07 17:37:11 +09:00
parent 51395f9349
commit 12c4936b68
3 changed files with 96 additions and 39 deletions

View File

@ -33,3 +33,7 @@ ws_init(() => {
</ConnectedRouter> </ConnectedRouter>
</Provider>, root); </Provider>, root);
}); });
navigator.registerProtocolHandler("magnet",
window.location.origin + "/add-torrent/%s",
"Open magnet link with receptor");

View File

@ -144,6 +144,8 @@ class AddTorrent extends Component {
loading: false, loading: false,
customize: false, customize: false,
file: null, file: null,
magnet: null,
useMagnet: false,
torrent: null, torrent: null,
files: [], files: [],
startImmediately: true, startImmediately: true,
@ -153,6 +155,14 @@ class AddTorrent extends Component {
}; };
} }
componentDidMount() {
let { magnet } = this.props.match.params;
if (magnet) {
magnet = decodeURIComponent(magnet);
this.setState({ magnet, useMagnet: true });
}
}
async handleTransferOffer(offer, file) { async handleTransferOffer(offer, file) {
const headers = new Headers(); const headers = new Headers();
headers.append("Authorization", "Bearer " + offer.token); headers.append("Authorization", "Bearer " + offer.token);
@ -200,16 +210,13 @@ class AddTorrent extends Component {
uploadFile() { uploadFile() {
this.setState({ loading: true }); this.setState({ loading: true });
const { file, startImmediately } = this.state; const { magnet, file, startImmediately } = this.state;
const { dispatch } = this.props; const { dispatch } = this.props;
const customize = // TODO: File options const customize = // TODO: File options
this.state.priority !== 3 || this.state.priority !== 3 ||
this.state.uploadThrottle !== -1 || this.state.uploadThrottle !== -1 ||
this.state.downloadThrottle !== -1; this.state.downloadThrottle !== -1;
ws_send("UPLOAD_TORRENT", { const handleOffer = async offer => {
size: file.size,
start: startImmediately && !customize
}, async offer => {
switch (offer.type) { switch (offer.type) {
case "TRANSFER_OFFER": case "TRANSFER_OFFER":
return await this.handleTransferOffer(offer, file); return await this.handleTransferOffer(offer, file);
@ -218,7 +225,18 @@ class AddTorrent extends Component {
this.applyOptions.bind(this)(id); this.applyOptions.bind(this)(id);
break; break;
} }
}); };
if (magnet) {
ws_send("UPLOAD_MAGNET", {
uri: magnet,
start: startImmediately && !customize
}, handleOffer);
} else {
ws_send("UPLOAD_TORRENT", {
size: file.size,
start: startImmediately && !customize
}, handleOffer);
}
} }
processTorrent(torrent) { processTorrent(torrent) {
@ -303,7 +321,7 @@ class AddTorrent extends Component {
} }
renderTorrent() { renderTorrent() {
const { torrent, file, files, loading } = this.state; const { magnet, torrent, file, files, loading } = this.state;
const details = { const details = {
"comment": d => d, "comment": d => d,
@ -313,35 +331,39 @@ class AddTorrent extends Component {
return ( return (
<Card style={{marginBottom: "1rem"}}> <Card style={{marginBottom: "1rem"}}>
<CardBlock> {torrent &&
<CardTitle>{file.name}</CardTitle> <CardBlock>
<CardText> <CardTitle>{magnet && "Magnet link" || file.name}</CardTitle>
<dl style={{marginBottom: "0"}}> <CardText>
{Object.keys(details).map(key => <dl style={{marginBottom: "0"}}>
torrent[key] && ( {Object.keys(details).map(key =>
<div> torrent[key] && (
<dt>{key}</dt> <div>
<dd>{details[key](torrent[key])}</dd> <dt>{key}</dt>
</div> <dd>{details[key](torrent[key])}</dd>
) </div>
)} )
</dl> )}
</CardText> </dl>
</CardBlock> </CardText>
</CardBlock>
}
<ToggleContainer className="form-group" title="Options"> <ToggleContainer className="form-group" title="Options">
{this.renderOptions.bind(this)()} {this.renderOptions.bind(this)()}
</ToggleContainer> </ToggleContainer>
<ToggleContainer className="form-group" title="Files"> {torrent &&
<Card> <ToggleContainer className="form-group" title="Files">
<CardBlock> <Card>
TODO <CardBlock>
</CardBlock> TODO
</Card> </CardBlock>
</ToggleContainer> </Card>
</ToggleContainer>
}
<button <button
type="button" type="button"
className="btn btn-primary btn-block" className="btn btn-primary btn-block"
disabled={file === null || loading} disabled={(file || magnet) && loading}
onClick={this.uploadFile.bind(this)} onClick={this.uploadFile.bind(this)}
>Add torrent</button> >Add torrent</button>
{loading ? {loading ?
@ -360,7 +382,14 @@ class AddTorrent extends Component {
return ( return (
<div> <div>
<h3>Add torrent</h3> <h3>Add torrent</h3>
{this.state.torrent && this.renderTorrent.bind(this)()} {this.state.magnet && this.state.useMagnet &&
<p style={{
textOverflow: "ellipsis",
overflowX: "hidden",
whiteSpace: "nowrap"
}}>{this.state.magnet}</p>
}
{(this.state.torrent || this.state.magnet) && this.renderTorrent.bind(this)()}
<div className="form-group"> <div className="form-group">
<input <input
style={{display: "none"}} style={{display: "none"}}
@ -368,7 +397,7 @@ class AddTorrent extends Component {
accept=".torrent" accept=".torrent"
onChange={this.handleFile.bind(this)} onChange={this.handleFile.bind(this)}
/> />
{this.state.torrent ? {!this.state.useMagnet && (this.state.torrent ?
<div style={{ <div style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
@ -381,12 +410,35 @@ class AddTorrent extends Component {
>Select a different torrent?</button> >Select a different torrent?</button>
</div> </div>
: :
<button <div>
type="button" <button
className="btn btn-default btn-block" type="button"
onClick={() => findDOMNode(this).querySelector("input[type='file']").click()} className="btn btn-default btn-block"
>Select torrent</button> onClick={() => findDOMNode(this).querySelector("input[type='file']").click()}
} >Select torrent</button>
<div className="text-centered" style={{margin: "1rem auto"}}>
- or -
</div>
<FormGroup>
<Input
type="text"
placeholder="Magnet link"
value={this.state.magnet}
onChange={e => this.setState({ magnet: e.target.value })}
/>
</FormGroup>
<button
type="button"
className="btn btn-default btn-block"
onClick={() => this.setState({ useMagnet: true })}
disabled={(() => {
const a = document.createElement("a");
a.href = this.state.magnet;
return a.protocol !== "magnet:";
})()}
>Add magnet</button>
</div>
)}
</div> </div>
</div> </div>
); );

View File

@ -13,7 +13,8 @@ export default class Main extends Component {
<TorrentTable /> <TorrentTable />
</div> </div>
<div className="col-md-4"> <div className="col-md-4">
<Route path="/add-torrent" component={AddTorrent} /> <Route exact path="/add-torrent" component={AddTorrent} />
<Route path="/add-torrent/:magnet" component={AddTorrent} />
<Route path="/torrents/:ids" component={TorrentDetails} /> <Route path="/torrents/:ids" component={TorrentDetails} />
<Route exact path="/" component={Server} /> <Route exact path="/" component={Server} />
</div> </div>