Skip to content

Commit

Permalink
Add feature to download directly in browser (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
fxsth committed Oct 20, 2023
1 parent 1810a3c commit e77676a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 23 deletions.
63 changes: 51 additions & 12 deletions Web/ClientApp/src/components/DownloadButton.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import React from 'react';
import {Button} from "reactstrap";
import {
Button,
DropdownItem,
DropdownMenu,
DropdownToggle, UncontrolledDropdown, UncontrolledTooltip
} from "reactstrap";

export default class DownloadButton extends React.Component {
constructor(props) {
super(props);
this.state = {
mediaKey: this.props.mediaKey,
isLoading: false,
mediatype: this.props.mediaType,
mediaFileKey: this.props.mediaFileKey
tooltipOpen: false
};
}

// componentDidMount() {
// console.log('DownloadButton mounted with key' + this.state.mediaKey);
// }


handleClick(event) {
console.log('Handle click is called for key: ' + this.state.mediaKey);
this.setState({
isLoading: true
});
this.SendDownloadRequest();

}

getDownloadLink(){
try {
console.log("building url")
const host = this.props.server
const token = this.props.token
const path = this.props.mediaFileKey
const url = new URL(path, host);
url.searchParams.append("X-Plex-Token", token)
console.log(url.toString());
return url.toString()
}
catch(e)
{
return ""
}
}

async SendDownloadRequest() {
const settings = {
Expand Down Expand Up @@ -60,7 +73,33 @@ export default class DownloadButton extends React.Component {
}

render() {
return (<Button color={this.props.color} disabled={this.state.isLoading} onClick={this.handleClick.bind(this)}>{this.props.children}</Button>);
if (this.props.downloadBrowserPossible)
return (
<span>
<UncontrolledDropdown group id={'download_button_'+ this.props.mediaKey}>
<Button color={this.props.color} disabled={this.state.isLoading}
onClick={this.handleClick.bind(this)}>{this.props.children}</Button>
<DropdownToggle
caret
// color="primary"
/>
<DropdownMenu>
<DropdownItem>
<a href={this.getDownloadLink()} download>Download in browser</a>
</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
<UncontrolledTooltip
target={'download_button_'+ this.props.mediaKey}>
Per default file will be downloaded server-sided.
</UncontrolledTooltip>
</span>
);
else
return (
<Button color={this.props.color} disabled={this.state.isLoading}
onClick={this.handleClick.bind(this)}>{this.props.children}</Button>
);
}
}

26 changes: 17 additions & 9 deletions Web/ClientApp/src/components/Movies.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import Dropdown from "./Dropdown";
import DownloadButton from "./DownloadButton";
import {Table, Button} from "reactstrap";
import {Table} from "reactstrap";

export class Movies extends Component {
static displayName = Movies.name;
Expand All @@ -12,6 +12,7 @@ export class Movies extends Component {
servers: [],
libraries: [],
movies: [],
selectedServer: null,
serverselected: false,
libraryselected: false,
serverloading: true,
Expand All @@ -25,8 +26,11 @@ export class Movies extends Component {
}

handleServerChange = (event) => {
this.setState({serverselected: true, libraryselected: false, libraryloading: true})
if (event.target.value != null) {
const validSelection = event.target.value != null;
if(validSelection)
{
const server = this.state.servers.find(x=>x.id === event.target.value)
this.setState({selectedServer: server, serverselected: true, libraryselected: false, libraryloading: true})
this.populateLibrariesData(event.target.value);
} else {
this.setState({serverselected: false});
Expand Down Expand Up @@ -88,7 +92,7 @@ export class Movies extends Component {
let moviesContent = this.state.libraryselected
? this.state.movieloading
? <p><em>Loading movies...</em></p>
: Movies.renderMoviesTable(this.state.movies)
: Movies.renderMoviesTable(this.state.movies, this.state.selectedServer)
: <p/>;

return (
Expand All @@ -105,7 +109,7 @@ export class Movies extends Component {
);
}

static renderMoviesTable(movies) {
static renderMoviesTable(movies, selectedServer) {
return (
<Table striped>
<thead>
Expand All @@ -119,25 +123,29 @@ export class Movies extends Component {
</tr>
</thead>
<tbody>
{movies.map(movie =>
{movies.map(movie =>
movie.mediaFiles.map(mediaFile =>
<tr key={mediaFile.downloadUri}>
<td>{movie.title}</td>
<td>{movie.year}</td>
<td>{mediaFile.videoCodec}</td>
<td>{mediaFile.videoResolution}</td>
<td>{this.humanizeByteSize(mediaFile.totalBytes)}</td>
<td><DownloadButton mediaType='movie' mediaKey={movie.ratingKey} mediaFileKey={mediaFile.downloadUri}>Download</DownloadButton></td>
<td><DownloadButton
mediaType='movie' mediaKey={movie.ratingKey}
mediaFileKey={mediaFile.downloadUri}
server={selectedServer.lastKnownUri}
token={selectedServer.accessToken}
downloadBrowserPossible={true}>Download</DownloadButton></td>
</tr>)

)}
</tbody>
</Table>
);
}

static humanizeByteSize(size) {
if(!size)
if (!size)
return "--";
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
Expand Down
2 changes: 0 additions & 2 deletions Web/Models/Server.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;

namespace Web.Models;

Expand All @@ -18,7 +17,6 @@ public class Server

public bool IsOnline { get; set; }

[JsonIgnore]
public string? AccessToken { get; set; }

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
Expand Down

0 comments on commit e77676a

Please sign in to comment.