Compare commits

..

No commits in common. "2f0497ebc686727d3e0e1b8556c7ca8b111d38c8" and "f8db5e12118892a6f3ce32b5568071e189090b37" have entirely different histories.

9 changed files with 125 additions and 178 deletions

View File

@ -49,11 +49,7 @@ func run() error {
if err != nil { if err != nil {
return err return err
} }
backend := &models.Backend{ backend := &models.Backend{Database: db}
Database: db,
PublicDir: cf.PublicDir,
ImgURLPrefix: cf.ImgURLPrefix,
}
// Generate auth params // Generate auth params
authParams := auth.Params{ authParams := auth.Params{

View File

@ -8,8 +8,6 @@ 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
} }

View File

@ -2,9 +2,6 @@ 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"
@ -31,15 +28,6 @@ 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

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"time" "time"
@ -43,8 +44,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
Container string `json:"container"` Container string `json:"container"`
}{ }{
Alias: (*Alias)(m), Alias: (*Alias)(m),
// TODO: remove this field to use m.Thumb PosterURL: m.PosterURL(),
PosterURL: m.Thumb,
Subtitles: []subtitles.Subtitle{}, Subtitles: []subtitles.Subtitle{},
} }
@ -244,6 +244,15 @@ 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{}

View File

@ -1,7 +1,9 @@
import React from "react"; import React, { useState } from "react";
import PropTypes from "prop-types";
import { useDispatch, useSelector } from "react-redux";
import { AddTorrent } from "./list/addTorrent"; import { prettySize } from "../../utils";
import { Torrents } from "./list/torrents"; import { addTorrent, removeTorrent } from "../../actions/torrents";
export const TorrentList = () => { export const TorrentList = () => {
return ( return (
@ -13,3 +15,108 @@ 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,
};

View File

@ -1,35 +0,0 @@
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>
);
};

View File

@ -1,47 +0,0 @@
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,
};

View File

@ -1,45 +0,0 @@
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,
};

View File

@ -1,24 +0,0 @@
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>
);
};