Compare commits
4 Commits
f8db5e1211
...
2f0497ebc6
Author | SHA1 | Date | |
---|---|---|---|
2f0497ebc6 | |||
5b68ddb098 | |||
0aa3b6fc59 | |||
3d7b663f97 |
@ -49,7 +49,11 @@ func run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
backend := &models.Backend{Database: db}
|
backend := &models.Backend{
|
||||||
|
Database: db,
|
||||||
|
PublicDir: cf.PublicDir,
|
||||||
|
ImgURLPrefix: cf.ImgURLPrefix,
|
||||||
|
}
|
||||||
|
|
||||||
// Generate auth params
|
// Generate auth params
|
||||||
authParams := auth.Params{
|
authParams := auth.Params{
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
// Backend represents the data backend
|
// Backend represents the data backend
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
Database *sqlx.DB
|
Database *sqlx.DB
|
||||||
|
PublicDir string
|
||||||
|
ImgURLPrefix string
|
||||||
configured bool
|
configured bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -28,6 +31,15 @@ func (b *Backend) GetMovieDetails(pMovie *polochon.Movie, log *logrus.Entry) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the movie images
|
||||||
|
imgURL := fmt.Sprintf("movies/%s.jpg", pMovie.ImdbID)
|
||||||
|
imgFile := filepath.Join(b.PublicDir, "img", imgURL)
|
||||||
|
posterURL := ""
|
||||||
|
if _, err := os.Stat(imgFile); !os.IsNotExist(err) {
|
||||||
|
posterURL = b.ImgURLPrefix + imgURL
|
||||||
|
}
|
||||||
|
pMovie.Thumb = posterURL
|
||||||
|
|
||||||
log.Debugf("got movie %s from backend", pMovie.ImdbID)
|
log.Debugf("got movie %s from backend", pMovie.ImdbID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -44,7 +43,8 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
Container string `json:"container"`
|
Container string `json:"container"`
|
||||||
}{
|
}{
|
||||||
Alias: (*Alias)(m),
|
Alias: (*Alias)(m),
|
||||||
PosterURL: m.PosterURL(),
|
// TODO: remove this field to use m.Thumb
|
||||||
|
PosterURL: m.Thumb,
|
||||||
Subtitles: []subtitles.Subtitle{},
|
Subtitles: []subtitles.Subtitle{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,15 +244,6 @@ func (m *Movie) imgFile() string {
|
|||||||
return filepath.Join(m.publicDir, "img", m.imgURL())
|
return filepath.Join(m.publicDir, "img", m.imgURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PosterURL returns the image URL or the default image if the poster is not yet downloaded
|
|
||||||
func (m *Movie) PosterURL() string {
|
|
||||||
// Check if the movie image exists
|
|
||||||
if _, err := os.Stat(m.imgFile()); os.IsNotExist(err) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return m.imgURLPrefix + m.imgURL()
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPolochonMovies returns an array of the user's polochon movies
|
// getPolochonMovies returns an array of the user's polochon movies
|
||||||
func getPolochonMovies(user *models.User, env *web.Env) ([]*Movie, error) {
|
func getPolochonMovies(user *models.User, env *web.Env) ([]*Movie, error) {
|
||||||
movies := []*Movie{}
|
movies := []*Movie{}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
|
|
||||||
import { prettySize } from "../../utils";
|
import { AddTorrent } from "./list/addTorrent";
|
||||||
import { addTorrent, removeTorrent } from "../../actions/torrents";
|
import { Torrents } from "./list/torrents";
|
||||||
|
|
||||||
export const TorrentList = () => {
|
export const TorrentList = () => {
|
||||||
return (
|
return (
|
||||||
@ -15,108 +13,3 @@ export const TorrentList = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddTorrent = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (url === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dispatch(
|
|
||||||
addTorrent({
|
|
||||||
result: { url: url },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setUrl("");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control mb-3 w-100"
|
|
||||||
placeholder="Add torrent URL"
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
value={url}
|
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Torrents = () => {
|
|
||||||
const torrents = useSelector((state) => state.torrents.torrents);
|
|
||||||
|
|
||||||
if (torrents.length === 0) {
|
|
||||||
return (
|
|
||||||
<div className="jumbotron">
|
|
||||||
<h2>No torrents</h2>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="d-flex flex-wrap">
|
|
||||||
{torrents.map((torrent, index) => (
|
|
||||||
<Torrent key={index} torrent={torrent} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Torrent = ({ torrent }) => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
var progressStyle = torrent.status.is_finished
|
|
||||||
? "success"
|
|
||||||
: "info progress-bar-striped progress-bar-animated";
|
|
||||||
const progressBarClass = "progress-bar bg-" + progressStyle;
|
|
||||||
|
|
||||||
var percentDone = torrent.status.percent_done;
|
|
||||||
const started = percentDone !== 0;
|
|
||||||
if (started) {
|
|
||||||
percentDone = Number(percentDone).toFixed(1) + "%";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pretty sizes
|
|
||||||
const downloadedSize = prettySize(torrent.status.downloaded_size);
|
|
||||||
const totalSize = prettySize(torrent.status.total_size);
|
|
||||||
const downloadRate = prettySize(torrent.status.download_rate) + "/s";
|
|
||||||
return (
|
|
||||||
<div className="card w-100 mb-3">
|
|
||||||
<h5 className="card-header">
|
|
||||||
<span className="text text-break">{torrent.status.name}</span>
|
|
||||||
<span
|
|
||||||
className="fa fa-trash clickable pull-right"
|
|
||||||
onClick={() => dispatch(removeTorrent(torrent.status.id))}
|
|
||||||
></span>
|
|
||||||
</h5>
|
|
||||||
<div className="card-body pb-0">
|
|
||||||
{started && (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="progress bg-light">
|
|
||||||
<div
|
|
||||||
className={progressBarClass}
|
|
||||||
style={{ width: percentDone }}
|
|
||||||
role="progressbar"
|
|
||||||
aria-valuenow={percentDone}
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}
|
|
||||||
</p>
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
{!started && <p>Download not yet started</p>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Torrent.propTypes = {
|
|
||||||
torrent: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
35
frontend/js/components/torrents/list/addTorrent.js
Normal file
35
frontend/js/components/torrents/list/addTorrent.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
|
||||||
|
import { addTorrent } from "../../../actions/torrents";
|
||||||
|
|
||||||
|
export const AddTorrent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [url, setUrl] = useState("");
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (url === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(
|
||||||
|
addTorrent({
|
||||||
|
result: { url: url },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setUrl("");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={(e) => handleSubmit(e)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control mb-3 w-100"
|
||||||
|
placeholder="Add torrent URL"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
47
frontend/js/components/torrents/list/progress.js
Normal file
47
frontend/js/components/torrents/list/progress.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
import { prettySize } from "../../../utils";
|
||||||
|
|
||||||
|
export const Progress = ({ torrent }) => {
|
||||||
|
var progressStyle = torrent.status.is_finished
|
||||||
|
? "success"
|
||||||
|
: "info progress-bar-striped progress-bar-animated";
|
||||||
|
const progressBarClass = "progress-bar bg-" + progressStyle;
|
||||||
|
|
||||||
|
var percentDone = torrent.status.percent_done;
|
||||||
|
const started = percentDone !== 0;
|
||||||
|
if (started) {
|
||||||
|
percentDone = Number(percentDone).toFixed(1) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty sizes
|
||||||
|
const downloadedSize = prettySize(torrent.status.downloaded_size);
|
||||||
|
const totalSize = prettySize(torrent.status.total_size);
|
||||||
|
const downloadRate = prettySize(torrent.status.download_rate) + "/s";
|
||||||
|
return (
|
||||||
|
<div className="card-body pb-0">
|
||||||
|
{started && (
|
||||||
|
<>
|
||||||
|
<div className="progress bg-light">
|
||||||
|
<div
|
||||||
|
className={progressBarClass}
|
||||||
|
style={{ width: percentDone }}
|
||||||
|
role="progressbar"
|
||||||
|
aria-valuenow={percentDone}
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!started && <p>Download not yet started</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Progress.propTypes = {
|
||||||
|
torrent: PropTypes.object.isRequired,
|
||||||
|
};
|
45
frontend/js/components/torrents/list/torrent.js
Normal file
45
frontend/js/components/torrents/list/torrent.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
|
||||||
|
import { prettyEpisodeName } from "../../../utils";
|
||||||
|
import { removeTorrent } from "../../../actions/torrents";
|
||||||
|
|
||||||
|
import { Progress } from "./progress";
|
||||||
|
|
||||||
|
export const Torrent = ({ torrent }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const torrentTitle = (torrent) => {
|
||||||
|
switch (torrent.type) {
|
||||||
|
case "movie":
|
||||||
|
return torrent.video ? torrent.video.title : torrent.status.name;
|
||||||
|
case "episode":
|
||||||
|
return torrent.video
|
||||||
|
? prettyEpisodeName(
|
||||||
|
torrent.video.show_title,
|
||||||
|
torrent.video.season,
|
||||||
|
torrent.video.episode
|
||||||
|
)
|
||||||
|
: torrent.status.name;
|
||||||
|
default:
|
||||||
|
return torrent.status.name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card w-100 mb-3">
|
||||||
|
<h5 className="card-header">
|
||||||
|
<span className="text text-break">{torrentTitle(torrent)}</span>
|
||||||
|
<span
|
||||||
|
className="fa fa-trash clickable pull-right"
|
||||||
|
onClick={() => dispatch(removeTorrent(torrent.status.id))}
|
||||||
|
></span>
|
||||||
|
</h5>
|
||||||
|
<Progress torrent={torrent} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Torrent.propTypes = {
|
||||||
|
torrent: PropTypes.object.isRequired,
|
||||||
|
};
|
24
frontend/js/components/torrents/list/torrents.js
Normal file
24
frontend/js/components/torrents/list/torrents.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import { Torrent } from "./torrent";
|
||||||
|
|
||||||
|
export const Torrents = () => {
|
||||||
|
const torrents = useSelector((state) => state.torrents.torrents);
|
||||||
|
|
||||||
|
if (torrents.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="jumbotron">
|
||||||
|
<h2>No torrents</h2>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-wrap">
|
||||||
|
{torrents.map((torrent, index) => (
|
||||||
|
<Torrent key={index} torrent={torrent} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user