Add subtitles in frontend
Update backend to match polochon
This commit is contained in:
parent
e85554fbaa
commit
e71c5bfb84
@ -325,12 +325,23 @@ func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.R
|
|||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.UpdateSubtitles(&papi.Movie{ImdbID: id})
|
movie := &papi.Movie{ImdbID: id}
|
||||||
|
refreshSubs, err := client.UpdateSubtitles(movie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return env.RenderOK(w, "Subtitles refreshed")
|
subs := []subtitles.Subtitle{}
|
||||||
|
for _, lang := range refreshSubs {
|
||||||
|
subtitleURL, _ := client.SubtitleURL(movie, lang)
|
||||||
|
subs = append(subs, subtitles.Subtitle{
|
||||||
|
Language: lang,
|
||||||
|
URL: subtitleURL,
|
||||||
|
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", id, lang),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, subs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/odwrtw/polochon/lib"
|
"github.com/odwrtw/polochon/lib"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/subtitles"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
)
|
)
|
||||||
@ -31,12 +32,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
type Alias Movie
|
type Alias Movie
|
||||||
|
|
||||||
var downloadURL string
|
var downloadURL string
|
||||||
type Subtitle struct {
|
var subs []subtitles.Subtitle
|
||||||
Language string `json:"language"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
VVTFile string `json:"vvt_file"`
|
|
||||||
}
|
|
||||||
var subtitles []Subtitle
|
|
||||||
// If the episode is present, fill the downloadURL
|
// If the episode is present, fill the downloadURL
|
||||||
if m.pMovie != nil {
|
if m.pMovie != nil {
|
||||||
// Get the DownloadURL
|
// Get the DownloadURL
|
||||||
@ -44,7 +40,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
// Append the Subtitles
|
// Append the Subtitles
|
||||||
for _, l := range m.pMovie.Subtitles {
|
for _, l := range m.pMovie.Subtitles {
|
||||||
subtitleURL, _ := m.client.SubtitleURL(m.pMovie, l)
|
subtitleURL, _ := m.client.SubtitleURL(m.pMovie, l)
|
||||||
subtitles = append(subtitles, Subtitle{
|
subs = append(subs, subtitles.Subtitle{
|
||||||
Language: l,
|
Language: l,
|
||||||
URL: subtitleURL,
|
URL: subtitleURL,
|
||||||
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, l),
|
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, l),
|
||||||
@ -57,12 +53,12 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
*Alias
|
*Alias
|
||||||
PolochonURL string `json:"polochon_url"`
|
PolochonURL string `json:"polochon_url"`
|
||||||
PosterURL string `json:"poster_url"`
|
PosterURL string `json:"poster_url"`
|
||||||
Subtitles []Subtitle `json:"subtitles"`
|
Subtitles []subtitles.Subtitle `json:"subtitles"`
|
||||||
}{
|
}{
|
||||||
Alias: (*Alias)(m),
|
Alias: (*Alias)(m),
|
||||||
PolochonURL: downloadURL,
|
PolochonURL: downloadURL,
|
||||||
PosterURL: m.PosterURL(),
|
PosterURL: m.PosterURL(),
|
||||||
Subtitles: subtitles,
|
Subtitles: subs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Marshal(movieToMarshal)
|
return json.Marshal(movieToMarshal)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/subtitles"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,12 +25,7 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
type alias Episode
|
type alias Episode
|
||||||
|
|
||||||
var downloadURL string
|
var downloadURL string
|
||||||
type Subtitle struct {
|
var subs []subtitles.Subtitle
|
||||||
Language string `json:"language"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
VVTFile string `json:"vvt_file"`
|
|
||||||
}
|
|
||||||
var subtitles []Subtitle
|
|
||||||
// If the episode is present, fill the downloadURL
|
// If the episode is present, fill the downloadURL
|
||||||
if e.pShow != nil {
|
if e.pShow != nil {
|
||||||
pEpisode := e.pShow.GetEpisode(e.Season, e.Episode)
|
pEpisode := e.pShow.GetEpisode(e.Season, e.Episode)
|
||||||
@ -45,7 +41,7 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
// Append the Subtitles
|
// Append the Subtitles
|
||||||
for _, l := range pEpisode.Subtitles {
|
for _, l := range pEpisode.Subtitles {
|
||||||
subtitleURL, _ := e.client.SubtitleURL(pEpisode, l)
|
subtitleURL, _ := e.client.SubtitleURL(pEpisode, l)
|
||||||
subtitles = append(subtitles, Subtitle{
|
subs = append(subs, subtitles.Subtitle{
|
||||||
Language: l,
|
Language: l,
|
||||||
URL: subtitleURL,
|
URL: subtitleURL,
|
||||||
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, l),
|
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, l),
|
||||||
@ -58,11 +54,11 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
episodeToMarshal := &struct {
|
episodeToMarshal := &struct {
|
||||||
*alias
|
*alias
|
||||||
PolochonURL string `json:"polochon_url"`
|
PolochonURL string `json:"polochon_url"`
|
||||||
Subtitles []Subtitle `json:"subtitles"`
|
Subtitles []subtitles.Subtitle `json:"subtitles"`
|
||||||
}{
|
}{
|
||||||
alias: (*alias)(e),
|
alias: (*alias)(e),
|
||||||
PolochonURL: downloadURL,
|
PolochonURL: downloadURL,
|
||||||
Subtitles: subtitles,
|
Subtitles: subs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Marshal(episodeToMarshal)
|
return json.Marshal(episodeToMarshal)
|
||||||
|
@ -374,16 +374,28 @@ func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http
|
|||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.UpdateSubtitles(&papi.Episode{
|
e := &papi.Episode{
|
||||||
ShowImdbID: id,
|
ShowImdbID: id,
|
||||||
Season: season,
|
Season: season,
|
||||||
Episode: episode,
|
Episode: episode,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
refreshedSubs, err := client.UpdateSubtitles(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return env.RenderOK(w, "Subtitles refreshed")
|
subs := []subtitles.Subtitle{}
|
||||||
|
for _, lang := range refreshedSubs {
|
||||||
|
subtitleURL, _ := client.SubtitleURL(e, lang)
|
||||||
|
subs = append(subs, subtitles.Subtitle{
|
||||||
|
Language: lang,
|
||||||
|
URL: subtitleURL,
|
||||||
|
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, lang),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, subs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
||||||
|
8
src/internal/subtitles/subtitles.go
Normal file
8
src/internal/subtitles/subtitles.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package subtitles
|
||||||
|
|
||||||
|
// Subtitle represents a Subtitle
|
||||||
|
type Subtitle struct {
|
||||||
|
Language string `json:"language"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
VVTFile string `json:"vvt_file"`
|
||||||
|
}
|
36
src/public/js/actions/subtitles.js
Normal file
36
src/public/js/actions/subtitles.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { configureAxios, request } from '../requests'
|
||||||
|
|
||||||
|
import { addAlertOk } from './alerts'
|
||||||
|
|
||||||
|
export function refreshSubtitles(type, id, season, episode) {
|
||||||
|
switch (type) {
|
||||||
|
case 'movie':
|
||||||
|
var resourceURL = `/movies/${id}`
|
||||||
|
return request(
|
||||||
|
'MOVIE_SUBTITLES_UPDATE',
|
||||||
|
configureAxios().post(`${resourceURL}/subtitles/refresh`),
|
||||||
|
[
|
||||||
|
addAlertOk("Subtitles refreshed"),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
imdb_id: id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
case 'episode':
|
||||||
|
var resourceURL = `/shows/${id}/seasons/${season}/episodes/${episode}`
|
||||||
|
return request(
|
||||||
|
'EPISODE_SUBTITLES_UPDATE',
|
||||||
|
configureAxios().post(`${resourceURL}/subtitles/refresh`),
|
||||||
|
[
|
||||||
|
addAlertOk("Subtitles refreshed"),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
imdb_id: id,
|
||||||
|
season: season,
|
||||||
|
episode: episode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
console.log("refreshSubtitles - Unknown type " + type)
|
||||||
|
}
|
||||||
|
}
|
97
src/public/js/components/buttons/subtitles.js
Normal file
97
src/public/js/components/buttons/subtitles.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { DropdownButton, MenuItem } from 'react-bootstrap'
|
||||||
|
|
||||||
|
export default class SubtitlesButton extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
handleClick(e, url) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.props.fetching) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Refresh the subtitles
|
||||||
|
this.props.refreshSubtitles(this.props.type, this.props.resourceID, this.props.data.season, this.props.data.episode);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
// If there is no URL, the resource is not in polochon, we won't be able to download subtitles
|
||||||
|
if (this.props.url === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the button
|
||||||
|
const entries = buildMenuItems(this.props.subtitles);
|
||||||
|
let btnSize = "small";
|
||||||
|
if (this.props.xs) {
|
||||||
|
btnSize = "xsmall";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownButton bsStyle="success" bsSize={btnSize} title="Subtitles" id="download-subtitles-button" dropup>
|
||||||
|
{entries.map(function(e, index) {
|
||||||
|
switch (e.type) {
|
||||||
|
case 'action':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} onClick={(event) => this.handleClick(event, e.url)}>
|
||||||
|
{this.props.fetchingSubtitles ||
|
||||||
|
<span>
|
||||||
|
<i className="fa fa-refresh">
|
||||||
|
</i> Refresh
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{this.props.fetchingSubtitles &&
|
||||||
|
<span>
|
||||||
|
<i className="fa fa-spin fa-refresh">
|
||||||
|
</i> Refreshing
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
case 'divider':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} divider></MenuItem>
|
||||||
|
);
|
||||||
|
case 'entry':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} href={e.url}>
|
||||||
|
<i className="fa fa-download"></i> {e.lang}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, this)}
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMenuItems(subtitles) {
|
||||||
|
// Build the array of entries
|
||||||
|
let entries = [];
|
||||||
|
|
||||||
|
// Push the refresh button
|
||||||
|
entries.push({
|
||||||
|
type: "action",
|
||||||
|
value: "Refresh",
|
||||||
|
});
|
||||||
|
|
||||||
|
// If there is no subtitles, stop here
|
||||||
|
if (!subtitles) {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the divider
|
||||||
|
entries.push({ type: "divider" });
|
||||||
|
// Push the subtitles
|
||||||
|
for (let sub of subtitles) {
|
||||||
|
entries.push({
|
||||||
|
type: "entry",
|
||||||
|
// Take only the last part of fr_FR
|
||||||
|
lang: sub.language.split("_")[1],
|
||||||
|
url: sub.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
@ -2,10 +2,12 @@ import React from 'react'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import { addTorrent } from '../../actions/torrents'
|
import { addTorrent } from '../../actions/torrents'
|
||||||
|
import { refreshSubtitles } from '../../actions/subtitles'
|
||||||
import { addMovieToWishlist, deleteMovieFromWishlist,
|
import { addMovieToWishlist, deleteMovieFromWishlist,
|
||||||
getMovieDetails, selectMovie } from '../../actions/movies'
|
getMovieDetails, selectMovie } from '../../actions/movies'
|
||||||
|
|
||||||
import DownloadButton from '../buttons/download'
|
import DownloadButton from '../buttons/download'
|
||||||
|
import SubtitlesButton from '../buttons/subtitles'
|
||||||
import TorrentsButton from './torrents'
|
import TorrentsButton from './torrents'
|
||||||
import ActionsButton from './actions'
|
import ActionsButton from './actions'
|
||||||
import ListPosters from '../list/posters'
|
import ListPosters from '../list/posters'
|
||||||
@ -16,7 +18,7 @@ function mapStateToProps(state) {
|
|||||||
}
|
}
|
||||||
const mapDispatchToProps = (dipatch) =>
|
const mapDispatchToProps = (dipatch) =>
|
||||||
bindActionCreators({ selectMovie, getMovieDetails, addTorrent,
|
bindActionCreators({ selectMovie, getMovieDetails, addTorrent,
|
||||||
addMovieToWishlist, deleteMovieFromWishlist }, dipatch)
|
addMovieToWishlist, deleteMovieFromWishlist, refreshSubtitles }, dipatch)
|
||||||
|
|
||||||
function MovieButtons(props) {
|
function MovieButtons(props) {
|
||||||
const imdb_link = `http://www.imdb.com/title/${props.movie.imdb_id}`;
|
const imdb_link = `http://www.imdb.com/title/${props.movie.imdb_id}`;
|
||||||
@ -48,6 +50,15 @@ function MovieButtons(props) {
|
|||||||
subtitles={props.movie.subtitles}
|
subtitles={props.movie.subtitles}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SubtitlesButton
|
||||||
|
url={props.movie.polochon_url}
|
||||||
|
subtitles={props.movie.subtitles}
|
||||||
|
refreshSubtitles={props.refreshSubtitles}
|
||||||
|
resourceID={props.movie.imdb_id}
|
||||||
|
data={props.movie}
|
||||||
|
type="movie"
|
||||||
|
/>
|
||||||
|
|
||||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||||
<i className="fa fa-external-link"></i> IMDB
|
<i className="fa fa-external-link"></i> IMDB
|
||||||
</a>
|
</a>
|
||||||
@ -96,6 +107,7 @@ class MovieList extends React.Component {
|
|||||||
addToWishlist={this.props.addMovieToWishlist}
|
addToWishlist={this.props.addMovieToWishlist}
|
||||||
deleteFromWishlist={this.props.deleteMovieFromWishlist}
|
deleteFromWishlist={this.props.deleteMovieFromWishlist}
|
||||||
lastFetchUrl={this.props.movieStore.lastFetchUrl}
|
lastFetchUrl={this.props.movieStore.lastFetchUrl}
|
||||||
|
refreshSubtitles={this.props.refreshSubtitles}
|
||||||
/>
|
/>
|
||||||
</ListDetails>
|
</ListDetails>
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@ import React from 'react'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import { addTorrent } from '../../actions/torrents'
|
import { addTorrent } from '../../actions/torrents'
|
||||||
|
import { refreshSubtitles } from '../../actions/subtitles'
|
||||||
import { addShowToWishlist, deleteFromWishlist, getEpisodeDetails,
|
import { addShowToWishlist, deleteFromWishlist, getEpisodeDetails,
|
||||||
updateEpisodeDetailsStore, updateShowDetails } from '../../actions/shows'
|
updateEpisodeDetailsStore, updateShowDetails } from '../../actions/shows'
|
||||||
|
|
||||||
import Loader from '../loader/loader'
|
import Loader from '../loader/loader'
|
||||||
import DownloadButton from '../buttons/download'
|
import DownloadButton from '../buttons/download'
|
||||||
|
import SubtitlesButton from '../buttons/subtitles'
|
||||||
|
|
||||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
|
|
||||||
@ -18,7 +20,8 @@ function mapStateToProps(state) {
|
|||||||
}
|
}
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
bindActionCreators({addTorrent, addShowToWishlist, deleteFromWishlist,
|
bindActionCreators({addTorrent, addShowToWishlist, deleteFromWishlist,
|
||||||
updateShowDetails, updateEpisodeDetailsStore, getEpisodeDetails }, dispatch)
|
updateShowDetails, updateEpisodeDetailsStore, getEpisodeDetails,
|
||||||
|
refreshSubtitles }, dispatch)
|
||||||
|
|
||||||
class ShowDetails extends React.Component {
|
class ShowDetails extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
@ -39,6 +42,7 @@ class ShowDetails extends React.Component {
|
|||||||
addToWishlist={this.props.addShowToWishlist}
|
addToWishlist={this.props.addShowToWishlist}
|
||||||
getEpisodeDetails={this.props.getEpisodeDetails}
|
getEpisodeDetails={this.props.getEpisodeDetails}
|
||||||
updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore}
|
updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore}
|
||||||
|
refreshSubtitles={this.props.refreshSubtitles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -113,6 +117,7 @@ function SeasonsList(props){
|
|||||||
addToWishlist={props.addToWishlist}
|
addToWishlist={props.addToWishlist}
|
||||||
getEpisodeDetails={props.getEpisodeDetails}
|
getEpisodeDetails={props.getEpisodeDetails}
|
||||||
updateEpisodeDetailsStore={props.updateEpisodeDetailsStore}
|
updateEpisodeDetailsStore={props.updateEpisodeDetailsStore}
|
||||||
|
refreshSubtitles={props.refreshSubtitles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -159,6 +164,7 @@ class Season extends React.Component {
|
|||||||
addToWishlist={this.props.addToWishlist}
|
addToWishlist={this.props.addToWishlist}
|
||||||
getEpisodeDetails={this.props.getEpisodeDetails}
|
getEpisodeDetails={this.props.getEpisodeDetails}
|
||||||
updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore}
|
updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore}
|
||||||
|
refreshSubtitles={this.props.refreshSubtitles}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, this)}
|
}, this)}
|
||||||
@ -181,8 +187,17 @@ function Episode(props) {
|
|||||||
{props.data.episode}
|
{props.data.episode}
|
||||||
</th>
|
</th>
|
||||||
<td className="col-xs-5">{props.data.title}</td>
|
<td className="col-xs-5">{props.data.title}</td>
|
||||||
<td className="col-xs-6">
|
<td className="col-xs-6 list-details-button">
|
||||||
<span className="pull-right">
|
<span className="pull-right episode-buttons btn-toolbar">
|
||||||
|
<SubtitlesButton
|
||||||
|
url={props.data.polochon_url}
|
||||||
|
subtitles={props.data.subtitles}
|
||||||
|
refreshSubtitles={props.refreshSubtitles}
|
||||||
|
resourceID={props.data.show_imdb_id}
|
||||||
|
data={props.data}
|
||||||
|
type="episode"
|
||||||
|
xs
|
||||||
|
/>
|
||||||
{props.data.torrents && props.data.torrents.map(function(torrent, index) {
|
{props.data.torrents && props.data.torrents.map(function(torrent, index) {
|
||||||
let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`;
|
let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`;
|
||||||
return (
|
return (
|
||||||
@ -196,7 +211,6 @@ function Episode(props) {
|
|||||||
<DownloadButton
|
<DownloadButton
|
||||||
url={props.data.polochon_url}
|
url={props.data.polochon_url}
|
||||||
subtitles={props.data.subtitles}
|
subtitles={props.data.subtitles}
|
||||||
customClassName="episode-button"
|
|
||||||
xs
|
xs
|
||||||
/>
|
/>
|
||||||
<GetDetailsButton
|
<GetDetailsButton
|
||||||
@ -221,7 +235,7 @@ class Torrent extends React.Component {
|
|||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<span className="episode-button">
|
<span>
|
||||||
<a type="button"
|
<a type="button"
|
||||||
className="btn btn-primary btn-xs"
|
className="btn btn-primary btn-xs"
|
||||||
onClick={(e) => this.handleClick(e, this.props.data.url)}
|
onClick={(e) => this.handleClick(e, this.props.data.url)}
|
||||||
@ -306,7 +320,7 @@ class TrackButton extends React.Component {
|
|||||||
<Tooltip id={tooltipId}>Track show from here</Tooltip>
|
<Tooltip id={tooltipId}>Track show from here</Tooltip>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger placement="top" overlay={tooltip} className="episode-button">
|
<OverlayTrigger placement="top" overlay={tooltip}>
|
||||||
<a type="button" className="btn btn-default btn-xs" onClick={(e) => this.handleClick(e)}>
|
<a type="button" className="btn btn-default btn-xs" onClick={(e) => this.handleClick(e)}>
|
||||||
<i className="fa fa-bookmark"></i>
|
<i className="fa fa-bookmark"></i>
|
||||||
</a>
|
</a>
|
||||||
@ -326,11 +340,11 @@ class GetDetailsButton extends React.Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.props.updateEpisodeDetailsStore(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
|
this.props.updateEpisodeDetailsStore(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
|
||||||
|
console.log(this.props.data);
|
||||||
this.props.getEpisodeDetails(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
|
this.props.getEpisodeDetails(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<span className="episode-button">
|
|
||||||
<a type="button" className="btn btn-xs btn-info" onClick={(e) => this.handleClick(e)}>
|
<a type="button" className="btn btn-xs btn-info" onClick={(e) => this.handleClick(e)}>
|
||||||
{this.props.data.fetching ||
|
{this.props.data.fetching ||
|
||||||
<span>
|
<span>
|
||||||
@ -343,7 +357,6 @@ class GetDetailsButton extends React.Component {
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ export default function movieStore(state = defaultState, action) {
|
|||||||
case 'MOVIE_LIST_FETCH_FULFILLED':
|
case 'MOVIE_LIST_FETCH_FULFILLED':
|
||||||
let selectedImdbId = "";
|
let selectedImdbId = "";
|
||||||
// Select the first movie
|
// Select the first movie
|
||||||
if (action.payload.data.length > 0) {
|
if (action.payload.response.data.length > 0) {
|
||||||
// Sort by year
|
// Sort by year
|
||||||
action.payload.data.sort((a,b) => b.year - a.year);
|
action.payload.response.data.sort((a,b) => b.year - a.year);
|
||||||
selectedImdbId = action.payload.data[0].imdb_id;
|
selectedImdbId = action.payload.response.data[0].imdb_id;
|
||||||
}
|
}
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
movies: action.payload.data,
|
movies: action.payload.response.data,
|
||||||
selectedImdbId: selectedImdbId,
|
selectedImdbId: selectedImdbId,
|
||||||
filter: defaultState.filter,
|
filter: defaultState.filter,
|
||||||
perPage: defaultState.perPage,
|
perPage: defaultState.perPage,
|
||||||
@ -36,7 +36,7 @@ export default function movieStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'MOVIE_GET_DETAILS_FULFILLED':
|
case 'MOVIE_GET_DETAILS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
movies: updateMovieDetails(state.movies.slice(), action.payload.data.imdb_id, action.payload.data),
|
movies: updateMovieDetails(state.movies.slice(), action.payload.response.data.imdb_id, action.payload.response.data),
|
||||||
fetchingDetails: false,
|
fetchingDetails: false,
|
||||||
})
|
})
|
||||||
case 'MOVIE_UPDATE_STORE_WISHLIST':
|
case 'MOVIE_UPDATE_STORE_WISHLIST':
|
||||||
@ -45,12 +45,21 @@ export default function movieStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'MOVIE_GET_EXPLORE_OPTIONS_FULFILLED':
|
case 'MOVIE_GET_EXPLORE_OPTIONS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
exploreOptions: action.payload.data,
|
exploreOptions: action.payload.response.data,
|
||||||
})
|
})
|
||||||
case 'UPDATE_LAST_MOVIE_FETCH_URL':
|
case 'UPDATE_LAST_MOVIE_FETCH_URL':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
lastFetchUrl: action.payload.url,
|
lastFetchUrl: action.payload.url,
|
||||||
})
|
})
|
||||||
|
case 'MOVIE_SUBTITLES_UPDATE_PENDING':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, true),
|
||||||
|
})
|
||||||
|
case 'MOVIE_SUBTITLES_UPDATE_FULFILLED':
|
||||||
|
console.log("payload :", action.payload);
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, false, action.payload.response.data),
|
||||||
|
})
|
||||||
case 'SELECT_MOVIE':
|
case 'SELECT_MOVIE':
|
||||||
// Don't select the movie if we're fetching another movie's details
|
// Don't select the movie if we're fetching another movie's details
|
||||||
if (state.fetchingDetails) {
|
if (state.fetchingDetails) {
|
||||||
@ -76,3 +85,12 @@ function updateStoreWishlist(movies, imdbId, wishlisted) {
|
|||||||
movies[index].wishlisted = wishlisted;
|
movies[index].wishlisted = wishlisted;
|
||||||
return movies
|
return movies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateMovieSubtitles(movies, imdbId, fetching, data = null) {
|
||||||
|
let index = movies.map((el) => el.imdb_id).indexOf(imdbId);
|
||||||
|
if (data) {
|
||||||
|
movies[index].subtitles = data;
|
||||||
|
}
|
||||||
|
movies[index].fetchingSubtitles = fetching;
|
||||||
|
return movies
|
||||||
|
}
|
||||||
|
@ -21,11 +21,12 @@ export default function showStore(state = defaultState, action) {
|
|||||||
case 'SHOW_LIST_FETCH_FULFILLED':
|
case 'SHOW_LIST_FETCH_FULFILLED':
|
||||||
let selectedImdbId = "";
|
let selectedImdbId = "";
|
||||||
// Select the first show
|
// Select the first show
|
||||||
if (action.payload.data.length > 0) {
|
console.log("Hey", action.payload);
|
||||||
selectedImdbId = action.payload.data[0].imdb_id;
|
if (action.payload.response.data.length > 0) {
|
||||||
|
selectedImdbId = action.payload.response.data[0].imdb_id;
|
||||||
}
|
}
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
shows: action.payload.data,
|
shows: action.payload.response.data,
|
||||||
selectedImdbId: selectedImdbId,
|
selectedImdbId: selectedImdbId,
|
||||||
filter: defaultState.filter,
|
filter: defaultState.filter,
|
||||||
perPage: defaultState.perPage,
|
perPage: defaultState.perPage,
|
||||||
@ -37,7 +38,7 @@ export default function showStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'SHOW_GET_DETAILS_FULFILLED':
|
case 'SHOW_GET_DETAILS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
shows: updateShowDetails(state.shows.slice(), action.payload.data),
|
shows: updateShowDetails(state.shows.slice(), action.payload.response.data),
|
||||||
getDetails: false,
|
getDetails: false,
|
||||||
})
|
})
|
||||||
case 'SHOW_FETCH_DETAILS_PENDING':
|
case 'SHOW_FETCH_DETAILS_PENDING':
|
||||||
@ -46,7 +47,7 @@ export default function showStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'SHOW_FETCH_DETAILS_FULFILLED':
|
case 'SHOW_FETCH_DETAILS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
show: sortEpisodes(action.payload.data),
|
show: sortEpisodes(action.payload.response.data),
|
||||||
loading: false,
|
loading: false,
|
||||||
})
|
})
|
||||||
case 'EPISODE_GET_DETAILS':
|
case 'EPISODE_GET_DETAILS':
|
||||||
@ -55,7 +56,7 @@ export default function showStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'EPISODE_GET_DETAILS_FULFILLED':
|
case 'EPISODE_GET_DETAILS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
show: updateEpisode(Object.assign({}, state.show), false, action.payload.data),
|
show: updateEpisode(Object.assign({}, state.show), false, action.payload.response.data),
|
||||||
})
|
})
|
||||||
case 'EXPLORE_SHOWS_PENDING':
|
case 'EXPLORE_SHOWS_PENDING':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
@ -63,12 +64,12 @@ export default function showStore(state = defaultState, action) {
|
|||||||
})
|
})
|
||||||
case 'EXPLORE_SHOWS_FULFILLED':
|
case 'EXPLORE_SHOWS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
shows: action.payload.data,
|
shows: action.payload.response.data,
|
||||||
loading: false,
|
loading: false,
|
||||||
})
|
})
|
||||||
case 'SHOW_GET_EXPLORE_OPTIONS_FULFILLED':
|
case 'SHOW_GET_EXPLORE_OPTIONS_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
exploreOptions: action.payload.data,
|
exploreOptions: action.payload.response.data,
|
||||||
})
|
})
|
||||||
case 'SHOW_UPDATE_STORE_WISHLIST':
|
case 'SHOW_UPDATE_STORE_WISHLIST':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
@ -79,6 +80,15 @@ export default function showStore(state = defaultState, action) {
|
|||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
lastShowsFetchUrl: action.payload.url,
|
lastShowsFetchUrl: action.payload.url,
|
||||||
})
|
})
|
||||||
|
case 'EPISODE_SUBTITLES_UPDATE_PENDING':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, true),
|
||||||
|
})
|
||||||
|
case 'EPISODE_SUBTITLES_UPDATE_FULFILLED':
|
||||||
|
console.log("payload :", action.payload);
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, false, action.payload.response.data),
|
||||||
|
})
|
||||||
case 'SELECT_SHOW':
|
case 'SELECT_SHOW':
|
||||||
// Don't select the show if we're fetching another show's details
|
// Don't select the show if we're fetching another show's details
|
||||||
if (state.fetchingDetails) {
|
if (state.fetchingDetails) {
|
||||||
@ -113,6 +123,17 @@ function updateEpisode(show, fetching, data = null) {
|
|||||||
return show
|
return show
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateEpisodeSubtitles(show, season, episode, fetching, data = null) {
|
||||||
|
let seasonIndex = show.seasons.map((el) => el.season).indexOf(season.toString());
|
||||||
|
let episodeIndex = show.seasons[seasonIndex].episodes.map((el) => el.episode).indexOf(episode);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
show.seasons[seasonIndex].episodes[episodeIndex].subtitles = data;
|
||||||
|
}
|
||||||
|
show.seasons[seasonIndex].episodes[episodeIndex].fetchingSubtitles = fetching;
|
||||||
|
return show
|
||||||
|
}
|
||||||
|
|
||||||
function sortEpisodes(show) {
|
function sortEpisodes(show) {
|
||||||
let episodes = show.episodes;
|
let episodes = show.episodes;
|
||||||
delete show["episodes"];
|
delete show["episodes"];
|
||||||
|
@ -12,7 +12,7 @@ export default function torrentStore(state = defaultState, action) {
|
|||||||
case 'TORRENTS_FETCH_FULFILLED':
|
case 'TORRENTS_FETCH_FULFILLED':
|
||||||
return state.merge(fromJS({
|
return state.merge(fromJS({
|
||||||
fetching: false,
|
fetching: false,
|
||||||
torrents: action.payload.data,
|
torrents: action.payload.response.data,
|
||||||
}));
|
}));
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
|
@ -17,18 +17,18 @@ export default function userStore(state = defaultState, action) {
|
|||||||
userLoading: true,
|
userLoading: true,
|
||||||
})
|
})
|
||||||
case 'USER_LOGIN_FULFILLED':
|
case 'USER_LOGIN_FULFILLED':
|
||||||
if (action.payload.status === "error") {
|
if (action.payload.response.status === "error") {
|
||||||
return logoutUser(state)
|
return logoutUser(state)
|
||||||
}
|
}
|
||||||
return updateFromToken(state, action.payload.data.token)
|
return updateFromToken(state, action.payload.response.data.token)
|
||||||
case 'USER_SET_TOKEN':
|
case 'USER_SET_TOKEN':
|
||||||
return updateFromToken(state, action.payload.token)
|
return updateFromToken(state, action.payload.token)
|
||||||
case 'USER_LOGOUT':
|
case 'USER_LOGOUT':
|
||||||
return logoutUser(state)
|
return logoutUser(state)
|
||||||
case 'GET_USER_FULFILLED':
|
case 'GET_USER_FULFILLED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
polochonToken: action.payload.data.token,
|
polochonToken: action.payload.response.data.token,
|
||||||
polochonUrl: action.payload.data.url,
|
polochonUrl: action.payload.response.data.url,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
@ -16,7 +16,7 @@ export function configureAxios(headers = {}) {
|
|||||||
|
|
||||||
// This function takes en event prefix to dispatch evens during the life of the
|
// This function takes en event prefix to dispatch evens during the life of the
|
||||||
// request, it also take a promise (axios request)
|
// request, it also take a promise (axios request)
|
||||||
export function request(eventPrefix, promise, callbackEvents = null) {
|
export function request(eventPrefix, promise, callbackEvents = null, mainPayload = null) {
|
||||||
// Events
|
// Events
|
||||||
const pending = `${eventPrefix}_PENDING`;
|
const pending = `${eventPrefix}_PENDING`;
|
||||||
const fulfilled = `${eventPrefix}_FULFILLED`;
|
const fulfilled = `${eventPrefix}_FULFILLED`;
|
||||||
@ -24,6 +24,9 @@ export function request(eventPrefix, promise, callbackEvents = null) {
|
|||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: pending,
|
type: pending,
|
||||||
|
payload: {
|
||||||
|
main: mainPayload,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
promise
|
promise
|
||||||
.then(response => {
|
.then(response => {
|
||||||
@ -33,12 +36,16 @@ export function request(eventPrefix, promise, callbackEvents = null) {
|
|||||||
type: 'ADD_ALERT_ERROR',
|
type: 'ADD_ALERT_ERROR',
|
||||||
payload: {
|
payload: {
|
||||||
message: response.data.message,
|
message: response.data.message,
|
||||||
|
main: mainPayload,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: fulfilled,
|
type: fulfilled,
|
||||||
payload: response.data,
|
payload: {
|
||||||
|
response: response.data,
|
||||||
|
main: mainPayload,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (callbackEvents) {
|
if (callbackEvents) {
|
||||||
for (let event of callbackEvents) {
|
for (let event of callbackEvents) {
|
||||||
@ -57,6 +64,7 @@ export function request(eventPrefix, promise, callbackEvents = null) {
|
|||||||
type: 'ADD_ALERT_ERROR',
|
type: 'ADD_ALERT_ERROR',
|
||||||
payload: {
|
payload: {
|
||||||
message: error.response.data,
|
message: error.response.data,
|
||||||
|
main: mainPayload,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -46,8 +46,11 @@ body {
|
|||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-button {
|
.episode-buttons {
|
||||||
padding-right: 5px;
|
display: flex;
|
||||||
|
span {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user