Compare commits
No commits in common. "master" and "refreshShow" have entirely different histories.
master
...
refreshSho
14
.drone.yml
14
.drone.yml
@ -3,12 +3,12 @@ name: default
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: prepare-workdir
|
- name: prepare-workdir
|
||||||
image: alpine:3.16.2
|
image: alpine:3.13.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir canapeapp
|
- mkdir canapeapp
|
||||||
|
|
||||||
- name: frontend
|
- name: frontend
|
||||||
image: node:16.6.2
|
image: node:13.8.0
|
||||||
commands:
|
commands:
|
||||||
- cd frontend
|
- cd frontend
|
||||||
- npm install
|
- npm install
|
||||||
@ -16,15 +16,15 @@ steps:
|
|||||||
- npm run-script build
|
- npm run-script build
|
||||||
|
|
||||||
- name: backend
|
- name: backend
|
||||||
image: golang:1.22-alpine3.19
|
image: golang:1.15.7-alpine3.13
|
||||||
commands:
|
commands:
|
||||||
- apk --no-cache add git
|
- apk --no-cache add git
|
||||||
- go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
- GO111MODULE=off go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate
|
||||||
- cp $$GOPATH/bin/migrate migrate
|
- cp $$GOPATH/bin/migrate migrate
|
||||||
- CGO_ENABLED=0 go build -ldflags '-extldflags "-static"' -trimpath -v -o canapeapp/app backend/*.go
|
- CGO_ENABLED=0 go build -ldflags '-extldflags "-static"' -trimpath -v -o canapeapp/app backend/*.go
|
||||||
|
|
||||||
- name: prepare-docker
|
- name: prepare-docker
|
||||||
image: alpine:3.16.2
|
image: alpine:3.13.1
|
||||||
commands:
|
commands:
|
||||||
- cp docker/run.sh canapeapp/run.sh
|
- cp docker/run.sh canapeapp/run.sh
|
||||||
- cp migrate canapeapp/migrate
|
- cp migrate canapeapp/migrate
|
||||||
@ -44,6 +44,4 @@ steps:
|
|||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
event:
|
event:
|
||||||
include:
|
- push
|
||||||
- push
|
|
||||||
- promote
|
|
||||||
|
@ -39,7 +39,7 @@ DB_DSN_DEV="postgres://test:test@127.0.0.1:5432/dev?sslmode=disable" ./dev.sh ba
|
|||||||
## Connect to the database
|
## Connect to the database
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./dev.sh db-shell
|
docker run -it --rm -e PGPASSWORD=test --link canape_postgresql_dev:postgres postgres:13.1 psql -h postgres -U test -d dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setup the dev users
|
## Setup the dev users
|
||||||
|
@ -141,7 +141,7 @@ func GetShows(env *web.Env, user *models.User, source string, category string, f
|
|||||||
for _, id := range media.IDs {
|
for _, id := range media.IDs {
|
||||||
pShow, _ := pShows.Has(id)
|
pShow, _ := pShows.Has(id)
|
||||||
wShow, _ := wShows.IsShowInWishlist(id)
|
wShow, _ := wShows.IsShowInWishlist(id)
|
||||||
show := shows.NewWithClient(id, client, pShow, wShow)
|
show := shows.NewWithClient(&polochon.Show{ImdbID: id}, client, pShow, wShow)
|
||||||
|
|
||||||
// First check in the DB
|
// First check in the DB
|
||||||
before := []polochon.Detailer{env.Backend.Detailer}
|
before := []polochon.Detailer{env.Backend.Detailer}
|
||||||
|
6
backend/fresh.conf
Normal file
6
backend/fresh.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
root: .
|
||||||
|
valid_ext: .go
|
||||||
|
colors: 1
|
||||||
|
build_name: dev-build
|
||||||
|
build_log: dev-build.log
|
||||||
|
tmp_path: ../build
|
@ -84,6 +84,7 @@ func FillEpisodeFromDB(eDB *episodeDB, pEpisode *polochon.ShowEpisode) {
|
|||||||
pEpisode.Title = eDB.Title
|
pEpisode.Title = eDB.Title
|
||||||
pEpisode.Rating = eDB.Rating
|
pEpisode.Rating = eDB.Rating
|
||||||
pEpisode.Plot = eDB.Plot
|
pEpisode.Plot = eDB.Plot
|
||||||
|
pEpisode.Thumb = eDB.Thumb
|
||||||
pEpisode.Runtime = eDB.Runtime
|
pEpisode.Runtime = eDB.Runtime
|
||||||
pEpisode.Aired = eDB.Aired
|
pEpisode.Aired = eDB.Aired
|
||||||
pEpisode.Thumb = imageURL(fmt.Sprintf(
|
pEpisode.Thumb = imageURL(fmt.Sprintf(
|
||||||
|
@ -90,7 +90,6 @@ func FillMovieFromDB(mDB *movieDB, pMovie *polochon.Movie) {
|
|||||||
pMovie.SortTitle = mDB.SortTitle
|
pMovie.SortTitle = mDB.SortTitle
|
||||||
pMovie.Tagline = mDB.Tagline
|
pMovie.Tagline = mDB.Tagline
|
||||||
pMovie.Thumb = imageURL("movies/" + mDB.ImdbID + ".jpg")
|
pMovie.Thumb = imageURL("movies/" + mDB.ImdbID + ".jpg")
|
||||||
pMovie.Fanart = imageURL("movies/" + mDB.ImdbID + "-fanart.jpg")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateFromMovie will update the movieDB from a Movie
|
// updateFromMovie will update the movieDB from a Movie
|
||||||
|
@ -9,9 +9,8 @@ import (
|
|||||||
// TorrentVideo reprensents a torrent embeding the video inforamtions
|
// TorrentVideo reprensents a torrent embeding the video inforamtions
|
||||||
type TorrentVideo struct {
|
type TorrentVideo struct {
|
||||||
*polochon.Torrent
|
*polochon.Torrent
|
||||||
Thumb string `json:"thumb"`
|
Img string `json:"img"`
|
||||||
Fanart string `json:"fanart"`
|
Video polochon.Video `json:"video,omitempty"`
|
||||||
Video polochon.Video `json:"video,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTorrentVideo returns a new TorrentVideo
|
// NewTorrentVideo returns a new TorrentVideo
|
||||||
@ -48,13 +47,11 @@ func (t *TorrentVideo) Update(detailer polochon.Detailer, db *sqlx.DB, log *logr
|
|||||||
if err := GetShow(db, v.Show); err != nil {
|
if err := GetShow(db, v.Show); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Fanart = v.Show.Fanart
|
t.Img = v.Show.Poster
|
||||||
t.Thumb = v.Show.Poster
|
|
||||||
v.Show = nil
|
v.Show = nil
|
||||||
}
|
}
|
||||||
case *polochon.Movie:
|
case *polochon.Movie:
|
||||||
t.Thumb = v.Thumb
|
t.Img = v.Thumb
|
||||||
t.Fanart = v.Fanart
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,20 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// Modules
|
// Modules
|
||||||
|
_ "github.com/odwrtw/polochon/modules/addicted"
|
||||||
_ "github.com/odwrtw/polochon/modules/canape"
|
_ "github.com/odwrtw/polochon/modules/canape"
|
||||||
_ "github.com/odwrtw/polochon/modules/eztv"
|
_ "github.com/odwrtw/polochon/modules/eztv"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/fsnotify"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/imdb"
|
||||||
_ "github.com/odwrtw/polochon/modules/mock"
|
_ "github.com/odwrtw/polochon/modules/mock"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/openguessit"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/opensubtitles"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/pushover"
|
||||||
_ "github.com/odwrtw/polochon/modules/tmdb"
|
_ "github.com/odwrtw/polochon/modules/tmdb"
|
||||||
_ "github.com/odwrtw/polochon/modules/tpb"
|
_ "github.com/odwrtw/polochon/modules/tpb"
|
||||||
_ "github.com/odwrtw/polochon/modules/trakttv"
|
_ "github.com/odwrtw/polochon/modules/trakttv"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/transmission"
|
||||||
_ "github.com/odwrtw/polochon/modules/tvdb"
|
_ "github.com/odwrtw/polochon/modules/tvdb"
|
||||||
|
_ "github.com/odwrtw/polochon/modules/yifysubtitles"
|
||||||
_ "github.com/odwrtw/polochon/modules/yts"
|
_ "github.com/odwrtw/polochon/modules/yts"
|
||||||
)
|
)
|
||||||
|
@ -49,51 +49,6 @@ func PolochonMoviesHandler(env *web.Env, w http.ResponseWriter, r *http.Request)
|
|||||||
return env.RenderJSON(w, movies)
|
return env.RenderJSON(w, movies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMovieHandler will return a single movie
|
|
||||||
func GetMovieHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
id := vars["id"]
|
|
||||||
|
|
||||||
user := auth.GetCurrentUser(r, env.Log)
|
|
||||||
|
|
||||||
client, err := user.NewPapiClient(env.Database)
|
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
movies, err := client.GetMovies()
|
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
moviesWishlist, err := models.GetMovieWishlist(env.Database, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pMovie, _ := movies.Has(id)
|
|
||||||
movie := New(
|
|
||||||
id,
|
|
||||||
client,
|
|
||||||
pMovie,
|
|
||||||
moviesWishlist.IsMovieInWishlist(id),
|
|
||||||
)
|
|
||||||
|
|
||||||
detailers := []polochon.Detailer{env.Backend.Detailer}
|
|
||||||
err = movie.GetDetails(env, detailers)
|
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
torrenters := []polochon.Torrenter{env.Backend.Torrenter}
|
|
||||||
err = movie.GetTorrents(env, torrenters)
|
|
||||||
if err != nil {
|
|
||||||
env.Log.Errorf("error while getting movie torrents : %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.RenderJSON(w, movie)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefreshMovieHandler refreshes details for a movie
|
// RefreshMovieHandler refreshes details for a movie
|
||||||
func RefreshMovieHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func RefreshMovieHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
@ -328,7 +283,6 @@ func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er
|
|||||||
func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
lang := polochon.Language(vars["lang"])
|
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
user := auth.GetCurrentUser(r, env.Log)
|
user := auth.GetCurrentUser(r, env.Log)
|
||||||
@ -340,35 +294,29 @@ func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
movie := &papi.Movie{Movie: &polochon.Movie{ImdbID: id}}
|
movie := &papi.Movie{Movie: &polochon.Movie{ImdbID: id}}
|
||||||
sub, err := client.UpdateSubtitle(movie, lang)
|
refreshSubs, err := client.UpdateSubtitles(movie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle this with a better error
|
subs := []subtitles.Subtitle{}
|
||||||
if sub == nil {
|
for _, lang := range refreshSubs {
|
||||||
return env.RenderJSON(w, nil)
|
subtitleURL, _ := client.SubtitleURL(movie, lang)
|
||||||
|
subs = append(subs, subtitles.Subtitle{
|
||||||
|
Language: lang,
|
||||||
|
URL: subtitleURL,
|
||||||
|
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", id, lang),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := client.DownloadURLWithToken(sub)
|
return env.RenderJSON(w, subs)
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &subtitles.Subtitle{
|
|
||||||
Subtitle: sub.Subtitle,
|
|
||||||
URL: url,
|
|
||||||
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", id, sub.Lang),
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.RenderJSON(w, s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
||||||
func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
lang := polochon.Language(vars["lang"])
|
lang := vars["lang"]
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
user := auth.GetCurrentUser(r, env.Log)
|
user := auth.GetCurrentUser(r, env.Log)
|
||||||
@ -379,14 +327,7 @@ func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) e
|
|||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &papi.Subtitle{
|
url, err := client.SubtitleURL(&papi.Movie{Movie: &polochon.Movie{ImdbID: id}}, lang)
|
||||||
Subtitle: &polochon.Subtitle{
|
|
||||||
Video: &papi.Movie{Movie: &polochon.Movie{ImdbID: id}},
|
|
||||||
Lang: lang,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := client.DownloadURLWithToken(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
if m.pMovie != nil {
|
if m.pMovie != nil {
|
||||||
// Get the DownloadURL
|
// Get the DownloadURL
|
||||||
movieToMarshal.PolochonURL, _ = m.client.DownloadURLWithToken(m.pMovie)
|
movieToMarshal.PolochonURL, _ = m.client.DownloadURL(m.pMovie)
|
||||||
|
|
||||||
// Get the metadata
|
// Get the metadata
|
||||||
movieToMarshal.DateAdded = m.pMovie.DateAdded
|
movieToMarshal.DateAdded = m.pMovie.DateAdded
|
||||||
@ -58,14 +58,13 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
movieToMarshal.Container = m.pMovie.Container
|
movieToMarshal.Container = m.pMovie.Container
|
||||||
|
|
||||||
// Append the Subtitles
|
// Append the Subtitles
|
||||||
for _, s := range m.pMovie.Subtitles {
|
for _, l := range m.pMovie.Subtitles {
|
||||||
sub := subtitles.Subtitle{Subtitle: s.Subtitle}
|
subtitleURL, _ := m.client.SubtitleURL(m.pMovie, l)
|
||||||
if !sub.Embedded {
|
movieToMarshal.Subtitles = append(movieToMarshal.Subtitles, subtitles.Subtitle{
|
||||||
subtitleURL, _ := m.client.DownloadURLWithToken(s)
|
Language: l,
|
||||||
sub.URL = subtitleURL
|
URL: subtitleURL,
|
||||||
sub.VVTFile = fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, s.Lang)
|
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, l),
|
||||||
}
|
})
|
||||||
movieToMarshal.Subtitles = append(movieToMarshal.Subtitles, sub)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,18 +73,13 @@ func (m *Movie) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
// New returns a new Movie with all the needed infos
|
// New returns a new Movie with all the needed infos
|
||||||
func New(imdbID string, client *papi.Client, pMovie *papi.Movie, isWishlisted bool) *Movie {
|
func New(imdbID string, client *papi.Client, pMovie *papi.Movie, isWishlisted bool) *Movie {
|
||||||
var m *polochon.Movie
|
|
||||||
if pMovie != nil && pMovie.Movie != nil {
|
|
||||||
m = pMovie.Movie
|
|
||||||
} else {
|
|
||||||
m = &polochon.Movie{ImdbID: imdbID}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Movie{
|
return &Movie{
|
||||||
client: client,
|
client: client,
|
||||||
pMovie: pMovie,
|
pMovie: pMovie,
|
||||||
Wishlisted: isWishlisted,
|
Wishlisted: isWishlisted,
|
||||||
Movie: m,
|
Movie: &polochon.Movie{
|
||||||
|
ImdbID: imdbID,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +144,7 @@ func (m *Movie) Refresh(env *web.Env, detailers []polochon.Detailer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Download poster
|
// Download poster
|
||||||
err = web.Download(m.Thumb, m.imgFile("thumb"), 300)
|
err = web.Download(m.Thumb, m.imgFile(), true)
|
||||||
if err != nil {
|
|
||||||
log.Errorf("got error trying to download the poster %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download fanart
|
|
||||||
err = web.Download(m.Fanart, m.imgFile("fanart"), 960)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("got error trying to download the poster %q", err)
|
log.Errorf("got error trying to download the poster %q", err)
|
||||||
}
|
}
|
||||||
@ -243,19 +231,13 @@ func (m *Movie) RefreshTorrents(env *web.Env, torrenters []polochon.Torrenter) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// imgURL returns the default image url
|
// imgURL returns the default image url
|
||||||
func (m *Movie) imgURL(imgType string) string {
|
func (m *Movie) imgURL() string {
|
||||||
var location string
|
return fmt.Sprintf("movies/%s.jpg", m.ImdbID)
|
||||||
if imgType == "thumb" {
|
|
||||||
location = m.ImdbID
|
|
||||||
} else {
|
|
||||||
location = m.ImdbID + "-" + imgType
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("movies/%s.jpg", location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// imgFile returns the image location on disk
|
// imgFile returns the image location on disk
|
||||||
func (m *Movie) imgFile(imgType string) string {
|
func (m *Movie) imgFile() string {
|
||||||
return filepath.Join(models.PublicDir, "img", m.imgURL(imgType))
|
return filepath.Join(models.PublicDir, "img", m.imgURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPolochonMovies returns an array of the user's polochon movies
|
// getPolochonMovies returns an array of the user's polochon movies
|
||||||
|
@ -41,10 +41,9 @@ func setupRoutes(env *web.Env) {
|
|||||||
env.Handle("/movies/explore/options", extmedias.MovieExplorerOptions).WithRole(models.UserRole).Methods("GET")
|
env.Handle("/movies/explore/options", extmedias.MovieExplorerOptions).WithRole(models.UserRole).Methods("GET")
|
||||||
env.Handle("/movies/search/{search}", movies.SearchMovie).WithRole(models.UserRole).Methods("GET")
|
env.Handle("/movies/search/{search}", movies.SearchMovie).WithRole(models.UserRole).Methods("GET")
|
||||||
env.Handle("/movies/{id:tt[0-9]+}", movies.PolochonDeleteHandler).WithRole(models.UserRole).Methods("DELETE")
|
env.Handle("/movies/{id:tt[0-9]+}", movies.PolochonDeleteHandler).WithRole(models.UserRole).Methods("DELETE")
|
||||||
env.Handle("/movies/{id:tt[0-9]+}", movies.GetMovieHandler).WithRole(models.UserRole).Methods("GET")
|
|
||||||
env.Handle("/movies/{id:tt[0-9]+}/refresh", movies.RefreshMovieHandler).WithRole(models.UserRole).Methods("POST")
|
env.Handle("/movies/{id:tt[0-9]+}/refresh", movies.RefreshMovieHandler).WithRole(models.UserRole).Methods("POST")
|
||||||
env.Handle("/movies/{id:tt[0-9]+}/subtitles/{lang}", movies.DownloadVVTSubtitle).WithRole(models.UserRole).Methods("GET")
|
env.Handle("/movies/{id:tt[0-9]+}/subtitles/{lang}", movies.DownloadVVTSubtitle).WithRole(models.UserRole).Methods("GET")
|
||||||
env.Handle("/movies/{id:tt[0-9]+}/subtitles/{lang}", movies.RefreshMovieSubtitlesHandler).WithRole(models.UserRole).Methods("POST")
|
env.Handle("/movies/{id:tt[0-9]+}/subtitles/refresh", movies.RefreshMovieSubtitlesHandler).WithRole(models.UserRole).Methods("POST")
|
||||||
env.Handle("/movies/refresh", extmedias.RefreshMoviesHandler).WithRole(models.AdminRole).Methods("POST")
|
env.Handle("/movies/refresh", extmedias.RefreshMoviesHandler).WithRole(models.AdminRole).Methods("POST")
|
||||||
|
|
||||||
// Shows routes
|
// Shows routes
|
||||||
@ -55,7 +54,7 @@ func setupRoutes(env *web.Env) {
|
|||||||
env.Handle("/shows/{id:tt[0-9]+}", shows.GetDetailsHandler).WithRole(models.UserRole).Methods("GET")
|
env.Handle("/shows/{id:tt[0-9]+}", shows.GetDetailsHandler).WithRole(models.UserRole).Methods("GET")
|
||||||
env.Handle("/shows/{id:tt[0-9]+}/refresh", shows.RefreshShowHandler).WithRole(models.UserRole).Methods("POST")
|
env.Handle("/shows/{id:tt[0-9]+}/refresh", shows.RefreshShowHandler).WithRole(models.UserRole).Methods("POST")
|
||||||
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}", shows.RefreshEpisodeHandler).WithRole(models.UserRole).Methods("POST")
|
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}", shows.RefreshEpisodeHandler).WithRole(models.UserRole).Methods("POST")
|
||||||
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}/subtitles/{lang}", shows.RefreshEpisodeSubtitlesHandler).WithRole(models.UserRole).Methods("POST")
|
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}/subtitles/refresh", shows.RefreshEpisodeSubtitlesHandler).WithRole(models.UserRole).Methods("POST")
|
||||||
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}/subtitles/{lang}", shows.DownloadVVTSubtitle).WithRole(models.UserRole).Methods("GET")
|
env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}/subtitles/{lang}", shows.DownloadVVTSubtitle).WithRole(models.UserRole).Methods("GET")
|
||||||
env.Handle("/shows/refresh", extmedias.RefreshShowsHandler).WithRole(models.AdminRole).Methods("POST")
|
env.Handle("/shows/refresh", extmedias.RefreshShowsHandler).WithRole(models.AdminRole).Methods("POST")
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package shows
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.quimbo.fr/odwrtw/canape/backend/models"
|
"git.quimbo.fr/odwrtw/canape/backend/models"
|
||||||
"git.quimbo.fr/odwrtw/canape/backend/subtitles"
|
"git.quimbo.fr/odwrtw/canape/backend/subtitles"
|
||||||
@ -25,13 +26,18 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
var downloadURL string
|
var downloadURL string
|
||||||
var subs []subtitles.Subtitle
|
var subs []subtitles.Subtitle
|
||||||
|
var dateAdded time.Time
|
||||||
|
var quality string
|
||||||
|
var audioCodec string
|
||||||
|
var videoCodec string
|
||||||
|
var container string
|
||||||
|
|
||||||
// If the episode is present, fill the downloadURL
|
// If the episode is present, fill the downloadURL
|
||||||
if e.show.pShow != nil {
|
if e.show.pShow != nil {
|
||||||
pEpisode := e.show.pShow.GetEpisode(e.Season, e.Episode)
|
pEpisode := e.show.pShow.GetEpisode(e.Season, e.Episode)
|
||||||
if pEpisode != nil {
|
if pEpisode != nil {
|
||||||
// Get the DownloadURL
|
// Get the DownloadURL
|
||||||
downloadURL, _ = e.show.client.DownloadURLWithToken(
|
downloadURL, _ = e.show.client.DownloadURL(
|
||||||
&papi.Episode{
|
&papi.Episode{
|
||||||
ShowEpisode: &polochon.ShowEpisode{
|
ShowEpisode: &polochon.ShowEpisode{
|
||||||
ShowImdbID: e.ShowImdbID,
|
ShowImdbID: e.ShowImdbID,
|
||||||
@ -40,21 +46,20 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
dateAdded = pEpisode.DateAdded
|
||||||
e.ShowEpisode.VideoMetadata = pEpisode.ShowEpisode.VideoMetadata
|
quality = string(pEpisode.Quality)
|
||||||
e.ShowEpisode.File = pEpisode.ShowEpisode.File
|
audioCodec = pEpisode.AudioCodec
|
||||||
|
videoCodec = pEpisode.VideoCodec
|
||||||
|
container = pEpisode.Container
|
||||||
|
|
||||||
// Append the Subtitles
|
// Append the Subtitles
|
||||||
for _, s := range pEpisode.Subtitles {
|
for _, l := range pEpisode.Subtitles {
|
||||||
sub := subtitles.Subtitle{Subtitle: s.Subtitle}
|
subtitleURL, _ := e.show.client.SubtitleURL(pEpisode, l)
|
||||||
if !sub.Embedded {
|
subs = append(subs, subtitles.Subtitle{
|
||||||
subtitleURL, _ := e.show.client.DownloadURLWithToken(s)
|
Language: l,
|
||||||
sub.URL = subtitleURL
|
URL: subtitleURL,
|
||||||
sub.VVTFile = fmt.Sprintf(
|
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, l),
|
||||||
"/shows/%s/seasons/%d/episodes/%d/subtitles/%s",
|
})
|
||||||
e.ShowImdbID, e.Season, e.Episode, s.Lang)
|
|
||||||
}
|
|
||||||
subs = append(subs, sub)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,11 +69,21 @@ func (e *Episode) MarshalJSON() ([]byte, error) {
|
|||||||
*alias
|
*alias
|
||||||
PolochonURL string `json:"polochon_url"`
|
PolochonURL string `json:"polochon_url"`
|
||||||
Subtitles []subtitles.Subtitle `json:"subtitles"`
|
Subtitles []subtitles.Subtitle `json:"subtitles"`
|
||||||
|
DateAdded time.Time `json:"date_added"`
|
||||||
|
Quality string `json:"quality"`
|
||||||
|
AudioCodec string `json:"audio_codec"`
|
||||||
|
VideoCodec string `json:"video_codec"`
|
||||||
|
Container string `json:"container"`
|
||||||
Thumb string `json:"thumb"`
|
Thumb string `json:"thumb"`
|
||||||
}{
|
}{
|
||||||
alias: (*alias)(e),
|
alias: (*alias)(e),
|
||||||
PolochonURL: downloadURL,
|
PolochonURL: downloadURL,
|
||||||
Subtitles: subs,
|
Subtitles: subs,
|
||||||
|
DateAdded: dateAdded,
|
||||||
|
Quality: quality,
|
||||||
|
AudioCodec: audioCodec,
|
||||||
|
VideoCodec: videoCodec,
|
||||||
|
Container: container,
|
||||||
Thumb: e.Thumb,
|
Thumb: e.Thumb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -34,15 +35,15 @@ func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
|
|||||||
|
|
||||||
pShow, err := client.GetShow(id)
|
pShow, err := client.GetShow(id)
|
||||||
if err != nil && err != papi.ErrResourceNotFound {
|
if err != nil && err != papi.ErrResourceNotFound {
|
||||||
env.Log.Println("Got error getting show ", err)
|
log.Println("Got error getting show ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
|
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
|
||||||
if err != nil && err != papi.ErrResourceNotFound {
|
if err != nil && err != papi.ErrResourceNotFound {
|
||||||
env.Log.Println("Got error getting wishlisted show ", err)
|
log.Println("Got error getting wishlisted show ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewWithClient(id, client, pShow, wShow)
|
s := NewWithClient(&polochon.Show{ImdbID: id}, client, pShow, wShow)
|
||||||
// First try from the db
|
// First try from the db
|
||||||
first := []polochon.Detailer{env.Backend.Detailer}
|
first := []polochon.Detailer{env.Backend.Detailer}
|
||||||
// Then try from the polochon detailers
|
// Then try from the polochon detailers
|
||||||
@ -80,15 +81,15 @@ func RefreshShowHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er
|
|||||||
|
|
||||||
pShow, err := client.GetShow(id)
|
pShow, err := client.GetShow(id)
|
||||||
if err != nil && err != papi.ErrResourceNotFound {
|
if err != nil && err != papi.ErrResourceNotFound {
|
||||||
env.Log.Println("Got error getting show ", err)
|
log.Println("Got error getting show ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
|
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
|
||||||
if err != nil && err != papi.ErrResourceNotFound {
|
if err != nil && err != papi.ErrResourceNotFound {
|
||||||
env.Log.Println("Got error getting wishlisted show ", err)
|
log.Println("Got error getting wishlisted show ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewWithClient(id, client, pShow, wShow)
|
s := NewWithClient(&polochon.Show{ImdbID: id}, client, pShow, wShow)
|
||||||
// Refresh the polochon detailers
|
// Refresh the polochon detailers
|
||||||
detailers := env.Config.Show.Detailers
|
detailers := env.Config.Show.Detailers
|
||||||
err = s.Refresh(env, detailers)
|
err = s.Refresh(env, detailers)
|
||||||
@ -162,7 +163,7 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
for _, s := range shows {
|
for _, s := range shows {
|
||||||
pShow, _ := pShows.Has(s.ImdbID)
|
pShow, _ := pShows.Has(s.ImdbID)
|
||||||
wShow, _ := wShows.IsShowInWishlist(s.ImdbID)
|
wShow, _ := wShows.IsShowInWishlist(s.ImdbID)
|
||||||
show := NewWithClient(s.ImdbID, client, pShow, wShow)
|
show := NewWithClient(s, client, pShow, wShow)
|
||||||
|
|
||||||
// First try from the db
|
// First try from the db
|
||||||
first := []polochon.Detailer{env.Backend.Detailer}
|
first := []polochon.Detailer{env.Backend.Detailer}
|
||||||
@ -241,7 +242,8 @@ func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er
|
|||||||
showList := []*Show{}
|
showList := []*Show{}
|
||||||
for _, wishedShow := range wShows.List() {
|
for _, wishedShow := range wShows.List() {
|
||||||
pShow, _ := pShows.Has(wishedShow.ImdbID)
|
pShow, _ := pShows.Has(wishedShow.ImdbID)
|
||||||
show := NewWithClient(wishedShow.ImdbID, client, pShow, wishedShow)
|
poloShow := &polochon.Show{ImdbID: wishedShow.ImdbID}
|
||||||
|
show := NewWithClient(poloShow, client, pShow, wishedShow)
|
||||||
|
|
||||||
// First check in the DB
|
// First check in the DB
|
||||||
before := []polochon.Detailer{env.Backend.Detailer}
|
before := []polochon.Detailer{env.Backend.Detailer}
|
||||||
@ -306,12 +308,8 @@ func RefreshEpisodeHandler(env *web.Env, w http.ResponseWriter, r *http.Request)
|
|||||||
env.Log.Warnf("Error getting show %q", err)
|
env.Log.Warnf("Error getting show %q", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pShow == nil {
|
|
||||||
pShow = &papi.Show{Show: &polochon.Show{ImdbID: id}}
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &Show{
|
s := &Show{
|
||||||
Show: pShow.Show,
|
Show: &polochon.Show{ImdbID: id},
|
||||||
client: client,
|
client: client,
|
||||||
pShow: pShow,
|
pShow: pShow,
|
||||||
}
|
}
|
||||||
@ -345,7 +343,6 @@ func RefreshEpisodeHandler(env *web.Env, w http.ResponseWriter, r *http.Request)
|
|||||||
func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
lang := polochon.Language(vars["lang"])
|
|
||||||
|
|
||||||
// No need to check errors here as the router is making sure that season
|
// No need to check errors here as the router is making sure that season
|
||||||
// and episode are numbers
|
// and episode are numbers
|
||||||
@ -369,35 +366,29 @@ func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sub, err := client.UpdateSubtitle(e, lang)
|
refreshedSubs, err := client.UpdateSubtitles(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle this with a better error
|
subs := []subtitles.Subtitle{}
|
||||||
if sub == nil {
|
for _, lang := range refreshedSubs {
|
||||||
return env.RenderJSON(w, nil)
|
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),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := client.DownloadURL(sub)
|
return env.RenderJSON(w, subs)
|
||||||
if err != nil {
|
|
||||||
return env.RenderError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &subtitles.Subtitle{
|
|
||||||
Subtitle: sub.Subtitle,
|
|
||||||
URL: url,
|
|
||||||
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, lang),
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.RenderJSON(w, s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
// DownloadVVTSubtitle returns a vvt subtitle for the movie
|
||||||
func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
lang := polochon.Language(vars["lang"])
|
lang := vars["lang"]
|
||||||
season, _ := strconv.Atoi(vars["season"])
|
season, _ := strconv.Atoi(vars["season"])
|
||||||
episode, _ := strconv.Atoi(vars["episode"])
|
episode, _ := strconv.Atoi(vars["episode"])
|
||||||
|
|
||||||
@ -410,20 +401,13 @@ func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) e
|
|||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &papi.Subtitle{
|
url, err := client.SubtitleURL(&papi.Episode{
|
||||||
Subtitle: &polochon.Subtitle{
|
ShowEpisode: &polochon.ShowEpisode{
|
||||||
Video: &papi.Episode{
|
ShowImdbID: id,
|
||||||
ShowEpisode: &polochon.ShowEpisode{
|
Season: season,
|
||||||
ShowImdbID: id,
|
Episode: episode,
|
||||||
Season: season,
|
|
||||||
Episode: episode,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Lang: lang,
|
|
||||||
},
|
},
|
||||||
}
|
}, lang)
|
||||||
|
|
||||||
url, err := client.DownloadURLWithToken(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env.RenderError(w, err)
|
return env.RenderError(w, err)
|
||||||
}
|
}
|
||||||
|
@ -62,14 +62,7 @@ func New(imdbID string) *Show {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewWithClient returns a new Show with a polochon ShowConfig
|
// NewWithClient returns a new Show with a polochon ShowConfig
|
||||||
func NewWithClient(imdbID string, client *papi.Client, pShow *papi.Show, wShow *models.WishedShow) *Show {
|
func NewWithClient(show *polochon.Show, client *papi.Client, pShow *papi.Show, wShow *models.WishedShow) *Show {
|
||||||
var show *polochon.Show
|
|
||||||
if pShow == nil || pShow.Show == nil {
|
|
||||||
show = &polochon.Show{ImdbID: imdbID}
|
|
||||||
} else {
|
|
||||||
show = pShow.Show
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &Show{
|
s := &Show{
|
||||||
Show: show,
|
Show: show,
|
||||||
client: client,
|
client: client,
|
||||||
@ -170,24 +163,24 @@ func (s *Show) downloadImages(env *web.Env) {
|
|||||||
}
|
}
|
||||||
// Download the show images
|
// Download the show images
|
||||||
for _, img := range []struct {
|
for _, img := range []struct {
|
||||||
url string
|
url string
|
||||||
urlType string
|
urlType string
|
||||||
maxWidth uint
|
scale bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
url: s.Show.Banner,
|
url: s.Show.Banner,
|
||||||
urlType: "banner",
|
urlType: "banner",
|
||||||
maxWidth: 0,
|
scale: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: s.Show.Fanart,
|
url: s.Show.Fanart,
|
||||||
urlType: "fanart",
|
urlType: "fanart",
|
||||||
maxWidth: 960,
|
scale: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: s.Show.Poster,
|
url: s.Show.Poster,
|
||||||
urlType: "poster",
|
urlType: "poster",
|
||||||
maxWidth: 300,
|
scale: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
if img.url == "" {
|
if img.url == "" {
|
||||||
@ -197,7 +190,7 @@ func (s *Show) downloadImages(env *web.Env) {
|
|||||||
if _, err := os.Stat(s.imgFile(img.urlType)); err == nil {
|
if _, err := os.Stat(s.imgFile(img.urlType)); err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := web.Download(img.url, s.imgFile(img.urlType), img.maxWidth); err != nil {
|
if err := web.Download(img.url, s.imgFile(img.urlType), img.scale); err != nil {
|
||||||
env.Log.Errorf("failed to dowload %s: %s", img.urlType, err)
|
env.Log.Errorf("failed to dowload %s: %s", img.urlType, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +207,7 @@ func (s *Show) downloadImages(env *web.Env) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := web.Download(e.Thumb, fileName, 0)
|
err := web.Download(e.Thumb, fileName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.Log.Errorf("failed to dowload the thumb for season %d episode %d ( %s ) : %s", e.Season, e.Episode, e.Thumb, err)
|
env.Log.Errorf("failed to dowload the thumb for season %d episode %d ( %s ) : %s", e.Season, e.Episode, e.Thumb, err)
|
||||||
}
|
}
|
||||||
@ -259,7 +252,7 @@ func getPolochonShows(env *web.Env, user *models.User) ([]*Show, error) {
|
|||||||
// Create Shows objects from the shows retrieved
|
// Create Shows objects from the shows retrieved
|
||||||
for _, pShow := range pshows.List() {
|
for _, pShow := range pshows.List() {
|
||||||
wShow, _ := wShows.IsShowInWishlist(pShow.ImdbID)
|
wShow, _ := wShows.IsShowInWishlist(pShow.ImdbID)
|
||||||
show := NewWithClient(pShow.ImdbID, client, pShow, wShow)
|
show := NewWithClient(&polochon.Show{ImdbID: pShow.ImdbID}, client, pShow, wShow)
|
||||||
shows = append(shows, show)
|
shows = append(shows, show)
|
||||||
}
|
}
|
||||||
return shows, nil
|
return shows, nil
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package subtitles
|
package subtitles
|
||||||
|
|
||||||
import polochon "github.com/odwrtw/polochon/lib"
|
|
||||||
|
|
||||||
// Subtitle represents a Subtitle
|
// Subtitle represents a Subtitle
|
||||||
type Subtitle struct {
|
type Subtitle struct {
|
||||||
*polochon.Subtitle
|
Language string `json:"language"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
VVTFile string `json:"vvt_file"`
|
VVTFile string `json:"vvt_file"`
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Download used for downloading file
|
// Download used for downloading file
|
||||||
var Download = func(srcURL, dest string, maxWidth uint) error {
|
var Download = func(srcURL, dest string, scale bool) error {
|
||||||
if err := createDirectory(dest); err != nil {
|
if err := createDirectory(dest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -36,8 +36,8 @@ var Download = func(srcURL, dest string, maxWidth uint) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if maxWidth != 0 {
|
if scale {
|
||||||
image = resize.Resize(maxWidth, 0, image, resize.Lanczos3)
|
image = resize.Resize(300, 0, image, resize.Lanczos3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the file
|
// Create the file
|
||||||
|
114
dev.sh
114
dev.sh
@ -18,35 +18,35 @@ MIGRATION_SCHEMA=./migrations
|
|||||||
DOCKER_COMPOSE_FILE=./docker/docker-compose.yml
|
DOCKER_COMPOSE_FILE=./docker/docker-compose.yml
|
||||||
|
|
||||||
_usage() {
|
_usage() {
|
||||||
prog=$(basename "$0")
|
prog=$(basename "$0")
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo " $prog back"
|
echo " $prog back"
|
||||||
echo " Apply the migrations, build and run the backend"
|
echo " Apply the migrations, build and run the backend"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $prog front"
|
echo " $prog front"
|
||||||
echo " Install the JS packages and run the frontend"
|
echo " Install the JS packages and run the frontend"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $prog migrate [args]"
|
echo " $prog migrate [args]"
|
||||||
echo " Runs the migrate command with the given parameters"
|
echo " Runs the migrate command with the given parameters"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $prog db-shell"
|
echo " $prog db-shell"
|
||||||
echo " Get a psql shell on the database"
|
echo " Get a psql shell on the database"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $prog db-init"
|
echo " $prog db-init"
|
||||||
echo " Refresh the informations (imdb / movies / shows) needed"
|
echo " Refresh the informations (imdb / movies / shows) needed"
|
||||||
echo " before the first run"
|
echo " before the first run"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $prog docker-db [up|up -d|down|other docker-compose options...]"
|
echo " $prog docker-db [up|up -d|down|other docker-compose options...]"
|
||||||
echo " Setup the database in a docker"
|
echo " Setup the database in a docker"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_log_info() {
|
_log_info() {
|
||||||
printf "$(tput setaf 5)-->$(tput setaf 2) %s$(tput sgr0)\n" "$@"
|
printf "$(tput setaf 5)-->$(tput setaf 2) %s$(tput sgr0)\n" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
_log_error() {
|
_log_error() {
|
||||||
printf "$(tput setaf 6)-->$(tput setaf 9) %s$(tput sgr0)\n" "$@"
|
printf "$(tput setaf 6)-->$(tput setaf 9) %s$(tput sgr0)\n" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
_check_command() {
|
_check_command() {
|
||||||
@ -69,9 +69,9 @@ _ensure_command npm
|
|||||||
|
|
||||||
_check_command migrate || {
|
_check_command migrate || {
|
||||||
_log_info "Installing migrate"
|
_log_info "Installing migrate"
|
||||||
go install \
|
GO111MODULE=off \
|
||||||
-tags 'postgres' \
|
go get -tags 'postgres' \
|
||||||
github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
-u -v github.com/golang-migrate/migrate/cmd/migrate
|
||||||
}
|
}
|
||||||
|
|
||||||
_check_command fresh || {
|
_check_command fresh || {
|
||||||
@ -86,37 +86,37 @@ _check_command fresh || {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canape_call() {
|
canape_call() {
|
||||||
method=$1
|
method=$1
|
||||||
resource=$2
|
resource=$2
|
||||||
_log_info "Calling: $method $resource ..."
|
_log_info "Calling: $method $resource ..."
|
||||||
response=$(curl --silent --show-error \
|
response=$(curl --silent --show-error \
|
||||||
-X "$method" \
|
-X "$method" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: $TOKEN" \
|
-H "Authorization: $TOKEN" \
|
||||||
"http://localhost:3000/$resource")
|
"http://localhost:3000/$resource")
|
||||||
[ -n "$response" ] && echo "$response"
|
[ -n "$response" ] && echo "$response"
|
||||||
_log_info "Done"
|
_log_info "Done"
|
||||||
}
|
}
|
||||||
|
|
||||||
canape_login() {
|
canape_login() {
|
||||||
# Skip if the user defined its own token
|
# Skip if the user defined its own token
|
||||||
[ -n "$CANAPE_TOKEN" ] && return
|
[ -n "$CANAPE_TOKEN" ] && return
|
||||||
_log_info "Logging in ..."
|
_log_info "Logging in ..."
|
||||||
json_data=$(jq -n -c --arg username "$CANAPE_USERNAME" \
|
json_data=$(jq -n -c --arg username "$CANAPE_USERNAME" \
|
||||||
--arg password "$CANAPE_PASS" \
|
--arg password "$CANAPE_PASS" \
|
||||||
'{username: $username,password: $password}')
|
'{username: $username,password: $password}')
|
||||||
TOKEN=$(curl --silent --show-error -X POST \
|
TOKEN=$(curl --silent --show-error -X POST \
|
||||||
-d "$json_data" \
|
-d "$json_data" \
|
||||||
http://localhost:3000/users/login | jq -r .data.token )
|
http://localhost:3000/users/login | jq -r .data.token )
|
||||||
_log_info "Logged in"
|
_log_info "Logged in"
|
||||||
}
|
}
|
||||||
|
|
||||||
canape_logout() {
|
canape_logout() {
|
||||||
# Skip if the user defined its own token
|
# Skip if the user defined its own token
|
||||||
[ -n "$CANAPE_TOKEN" ] && return
|
[ -n "$CANAPE_TOKEN" ] && return
|
||||||
_log_info "Disconnecting ..."
|
_log_info "Disconnecting ..."
|
||||||
canape_call DELETE "users/tokens/$TOKEN"
|
canape_call DELETE "users/tokens/$TOKEN"
|
||||||
_log_info "Disconnected"
|
_log_info "Disconnected"
|
||||||
}
|
}
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
@ -135,7 +135,7 @@ case $1 in
|
|||||||
# Apply the migrations
|
# Apply the migrations
|
||||||
_migrate -path "$MIGRATION_SCHEMA" up
|
_migrate -path "$MIGRATION_SCHEMA" up
|
||||||
|
|
||||||
(cd backend && CONFIG_FILE="../config.yml" go run ./*.go)
|
(cd backend && CONFIG_FILE="../config.yml" fresh -c fresh.conf)
|
||||||
;;
|
;;
|
||||||
docker-db)
|
docker-db)
|
||||||
_ensure_command docker
|
_ensure_command docker
|
||||||
@ -147,15 +147,15 @@ case $1 in
|
|||||||
(cd frontend && npm install && npm run-script start)
|
(cd frontend && npm install && npm run-script start)
|
||||||
;;
|
;;
|
||||||
db-init)
|
db-init)
|
||||||
_ensure_command jq
|
_ensure_command jq
|
||||||
canape_login
|
canape_login
|
||||||
canape_call POST ratings/refresh
|
canape_call POST ratings/refresh
|
||||||
canape_call POST movies/refresh
|
canape_call POST movies/refresh
|
||||||
canape_call POST shows/refresh
|
canape_call POST shows/refresh
|
||||||
canape_logout
|
canape_logout
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_log_error "Unknown command $1"
|
_log_error "Unknown command $1"
|
||||||
_usage
|
_usage
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.16.2
|
FROM alpine:3.13.1
|
||||||
RUN apk --no-cache add ca-certificates
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
COPY canapeapp /opt/canapeapp
|
COPY canapeapp /opt/canapeapp
|
||||||
|
@ -6,7 +6,7 @@ networks:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
image: postgres:13.8
|
image: postgres:13.1
|
||||||
container_name: canapeapp_database
|
container_name: canapeapp_database
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
|
BIN
frontend/img/favicon-16x16.png
Normal file
BIN
frontend/img/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 622 B |
BIN
frontend/img/favicon-32x32.png
Normal file
BIN
frontend/img/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 850 B |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
46
frontend/img/safari-pinned-tab.svg
Normal file
46
frontend/img/safari-pinned-tab.svg
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,16.000000) scale(0.002286,-0.002286)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M3220 6574 c-154 -16 -224 -25 -230 -29 -3 -2 -26 -6 -50 -10 -130
|
||||||
|
-18 -361 -83 -545 -152 -418 -159 -812 -431 -1075 -743 -30 -36 -60 -69 -66
|
||||||
|
-75 -18 -17 -111 -150 -156 -222 -42 -69 -138 -248 -138 -257 0 -3 35 -22 78
|
||||||
|
-43 42 -20 102 -55 132 -77 162 -118 604 -587 765 -810 11 -16 36 -49 56 -75
|
||||||
|
124 -161 287 -425 391 -634 28 -56 54 -104 58 -107 6 -4 2072 -5 2116 -1 6 1
|
||||||
|
21 25 35 54 39 84 161 304 227 408 218 344 467 647 809 982 156 153 220 203
|
||||||
|
330 258 94 46 92 34 26 159 -217 413 -559 756 -1009 1011 -57 32 -106 59 -109
|
||||||
|
59 -2 0 -33 14 -67 31 -181 89 -548 207 -723 233 -22 4 -65 11 -95 16 -30 5
|
||||||
|
-77 11 -105 14 -27 3 -77 8 -110 12 -98 11 -432 10 -545 -2z"/>
|
||||||
|
<path d="M578 4828 c-182 -24 -427 -197 -515 -363 -45 -87 -62 -162 -59 -264
|
||||||
|
6 -159 63 -291 211 -491 109 -146 231 -350 317 -529 l59 -125 41 47 c93 109
|
||||||
|
276 207 418 224 30 4 281 7 557 8 l502 0 -27 50 c-137 263 -367 594 -587 845
|
||||||
|
-113 129 -355 376 -454 463 -121 107 -296 158 -463 135z"/>
|
||||||
|
<path d="M6270 4832 c-43 -6 -133 -33 -180 -54 -78 -35 -124 -73 -287 -236
|
||||||
|
-212 -210 -283 -288 -438 -482 -139 -175 -273 -366 -338 -483 -14 -25 -37 -63
|
||||||
|
-51 -86 -14 -22 -26 -43 -26 -45 0 -3 -13 -28 -30 -56 -16 -27 -26 -50 -22
|
||||||
|
-51 4 0 243 -2 532 -4 504 -2 528 -3 598 -24 132 -39 263 -118 340 -207 l41
|
||||||
|
-48 60 125 c108 223 174 334 317 530 153 210 216 369 209 528 -6 144 -49 242
|
||||||
|
-154 354 -65 68 -196 162 -261 187 -19 7 -39 17 -45 21 -27 19 -202 40 -265
|
||||||
|
31z"/>
|
||||||
|
<path d="M1135 3039 c-13 -4 -26 -5 -28 -2 -7 7 -98 -22 -147 -47 -109 -56
|
||||||
|
-195 -177 -217 -305 -7 -45 -7 -1013 1 -1067 23 -164 130 -288 296 -344 53
|
||||||
|
-17 155 -18 2460 -18 1970 0 2412 2 2445 13 180 60 288 180 311 349 8 55 9
|
||||||
|
1035 1 1072 -14 70 -61 166 -104 215 -55 62 -168 121 -253 131 -70 9 -4732 11
|
||||||
|
-4765 3z"/>
|
||||||
|
<path d="M1222 962 c-18 -2 -31 -6 -28 -10 2 -4 7 -34 10 -67 10 -108 56 -458
|
||||||
|
61 -465 6 -9 344 -10 350 -1 2 4 7 32 10 62 3 30 8 65 10 79 2 14 7 48 10 75
|
||||||
|
5 50 10 91 20 163 3 20 7 59 10 85 3 27 7 53 11 58 3 5 -2 12 -10 15 -16 6
|
||||||
|
-399 11 -454 6z"/>
|
||||||
|
<path d="M5367 962 c-32 -2 -56 -6 -53 -10 2 -4 7 -32 10 -62 7 -63 13 -110
|
||||||
|
21 -160 2 -19 7 -57 10 -85 3 -27 7 -61 10 -75 2 -14 7 -52 10 -85 4 -32 9
|
||||||
|
-63 11 -67 6 -9 343 -8 349 1 3 6 17 100 31 216 2 22 7 58 10 80 11 80 13 91
|
||||||
|
19 150 3 33 8 68 11 78 4 14 -1 17 -28 18 -230 3 -363 3 -411 1z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
@ -5,7 +5,10 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="Description" content="Canapé">
|
<meta name="Description" content="Canapé">
|
||||||
<meta name="msapplication-navbutton-color" content="#4e5d6c">
|
<link rel="icon" type="image/png" href="<%= require("./img/favicon-16x16.png").default %>" sizes="16x16">
|
||||||
|
<link rel="icon" type="image/png" href="<%= require("./img/favicon-32x32.png").default %>" sizes="32x32">
|
||||||
|
<meta name="msapplication-navbutton-color" content="#4E5D6C">
|
||||||
|
<link rel="mask-icon" href="<%= require("./img/safari-pinned-tab.svg").default %>" color="#5bbad5">
|
||||||
<title>Canapé</title>
|
<title>Canapé</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -20,14 +20,10 @@ export function updateUser(data) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteUser(username, userId) {
|
export function deleteUser(username) {
|
||||||
return request(
|
return request(
|
||||||
"ADMIN_DELETE_USER",
|
"ADMIN_DELETE_USER",
|
||||||
configureAxios().delete("/admins/users/" + username),
|
configureAxios().delete("/admins/users/" + username),
|
||||||
null,
|
[() => getUsers()]
|
||||||
{
|
|
||||||
username,
|
|
||||||
id: userId,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,17 +48,6 @@ export function getMovieDetails(imdbId) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchMovieDetails(imdbId) {
|
|
||||||
return request(
|
|
||||||
"MOVIE_FETCH_DETAILS",
|
|
||||||
configureAxios().get(`/movies/${imdbId}`),
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
imdbId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteMovie(imdbId, lastFetchUrl) {
|
export function deleteMovie(imdbId, lastFetchUrl) {
|
||||||
return request("MOVIE_DELETE", configureAxios().delete(`/movies/${imdbId}`), [
|
return request("MOVIE_DELETE", configureAxios().delete(`/movies/${imdbId}`), [
|
||||||
fetchMovies(lastFetchUrl),
|
fetchMovies(lastFetchUrl),
|
||||||
|
@ -127,7 +127,7 @@ export const newEpisodeEvent = (data) => {
|
|||||||
data.season,
|
data.season,
|
||||||
data.episode
|
data.episode
|
||||||
)} added to the library`,
|
)} added to the library`,
|
||||||
imageUrl: `img/shows/${data.show_imdb_id}/poster.jpg`,
|
imageUrl: `img/shows/${data.show_imdb_id}-poster.jpg`,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
import { configureAxios, request } from "../requests";
|
import { configureAxios, request } from "../requests";
|
||||||
|
|
||||||
export const searchMovieSubtitle = (imdbId, lang) => {
|
export const searchMovieSubtitles = (imdbId) => {
|
||||||
return request(
|
return request(
|
||||||
"MOVIE_SUBTITLES_UPDATE",
|
"MOVIE_SUBTITLES_UPDATE",
|
||||||
configureAxios().post(`/movies/${imdbId}/subtitles/${lang}`),
|
configureAxios().post(`/movies/${imdbId}/subtitles/refresh`),
|
||||||
null,
|
null,
|
||||||
{ imdbId, lang }
|
{ imdbId: imdbId }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const searchEpisodeSubtitle = (imdbId, season, episode, lang) => {
|
export const searchEpisodeSubtitles = (imdbId, season, episode) => {
|
||||||
const url = `/shows/${imdbId}/seasons/${season}/episodes/${episode}`;
|
const url = `/shows/${imdbId}/seasons/${season}/episodes/${episode}`;
|
||||||
return request(
|
return request(
|
||||||
"EPISODE_SUBTITLES_UPDATE",
|
"EPISODE_SUBTITLES_UPDATE",
|
||||||
configureAxios().post(`${url}/subtitles/${lang}`),
|
configureAxios().post(`${url}/subtitles/refresh`),
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
imdbId,
|
imdbId: imdbId,
|
||||||
season,
|
season: season,
|
||||||
episode,
|
episode: episode,
|
||||||
lang,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
// Import default image
|
// Import default image
|
||||||
import "../img/noimage.png";
|
import "../img/noimage.png";
|
||||||
|
|
||||||
|
// Import favicon settings
|
||||||
|
import "../img/favicon-16x16.png";
|
||||||
|
import "../img/favicon-32x32.png";
|
||||||
|
import "../img/favicon.ico";
|
||||||
|
import "../img/safari-pinned-tab.svg";
|
||||||
|
|
||||||
// Styles
|
// Styles
|
||||||
import "../scss/app.scss";
|
import "../scss/app.scss";
|
||||||
|
|
||||||
@ -8,12 +14,7 @@ import "../scss/app.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import {
|
import { Router, Route, Switch, Redirect } from "react-router-dom";
|
||||||
HashRouter as Router,
|
|
||||||
Route,
|
|
||||||
Switch,
|
|
||||||
Redirect,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import Container from "react-bootstrap/Container";
|
import Container from "react-bootstrap/Container";
|
||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
@ -27,7 +28,6 @@ import { AdminPanel } from "./components/admins/panel";
|
|||||||
import { Notifications } from "./components/notifications/notifications";
|
import { Notifications } from "./components/notifications/notifications";
|
||||||
import { Alert } from "./components/alerts/alert";
|
import { Alert } from "./components/alerts/alert";
|
||||||
import MovieList from "./components/movies/list";
|
import MovieList from "./components/movies/list";
|
||||||
import { MovieDetails } from "./components/movies/details";
|
|
||||||
import { AppNavBar } from "./components/navbar";
|
import { AppNavBar } from "./components/navbar";
|
||||||
import { WsHandler } from "./components/websocket";
|
import { WsHandler } from "./components/websocket";
|
||||||
import { ShowDetails } from "./components/shows/details";
|
import { ShowDetails } from "./components/shows/details";
|
||||||
@ -61,7 +61,6 @@ const App = () => (
|
|||||||
/>
|
/>
|
||||||
<Route path="/movies/polochon" exact component={MovieList} />
|
<Route path="/movies/polochon" exact component={MovieList} />
|
||||||
<Route path="/movies/wishlist" exact component={MovieList} />
|
<Route path="/movies/wishlist" exact component={MovieList} />
|
||||||
<Route path="/movies/details/:imdbId" exact component={MovieDetails} />
|
|
||||||
<Route path="/movies/search/:search" exact component={MovieList} />
|
<Route path="/movies/search/:search" exact component={MovieList} />
|
||||||
<Route
|
<Route
|
||||||
path="/movies/explore/:source/:category"
|
path="/movies/explore/:source/:category"
|
||||||
@ -77,20 +76,12 @@ const App = () => (
|
|||||||
component={ShowList}
|
component={ShowList}
|
||||||
/>
|
/>
|
||||||
<Route path="/shows/details/:imdbId" exact component={ShowDetails} />
|
<Route path="/shows/details/:imdbId" exact component={ShowDetails} />
|
||||||
<Route
|
<Route render={() => <Redirect to="/movies/explore/yts/seeds" />} />
|
||||||
render={() => <Redirect to="/movies/explore/trakttv/trending" />}
|
|
||||||
/>
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
|
||||||
window.addEventListener("load", () => {
|
|
||||||
navigator.serviceWorker.register("/service-worker.js");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { getAdminModules } from "../../actions/admins";
|
import { getAdminModules } from "../../actions/admins";
|
||||||
|
|
||||||
@ -10,15 +10,9 @@ export const AdminModules = () => {
|
|||||||
const loading = useSelector((state) => state.admin.fetchingModules);
|
const loading = useSelector((state) => state.admin.fetchingModules);
|
||||||
const modules = useSelector((state) => state.admin.modules);
|
const modules = useSelector((state) => state.admin.modules);
|
||||||
|
|
||||||
const fetchModules = () => {
|
useEffect(() => {
|
||||||
dispatch(getAdminModules());
|
dispatch(getAdminModules());
|
||||||
};
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return <Modules modules={modules} isLoading={loading} />;
|
||||||
<Modules
|
|
||||||
modules={modules}
|
|
||||||
isLoading={loading}
|
|
||||||
fetchModules={fetchModules}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import moment from "moment";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { format } from "timeago.js";
|
|
||||||
|
|
||||||
import { UserEdit } from "./userEdit";
|
import { UserEdit } from "./userEdit";
|
||||||
|
|
||||||
export const User = ({ id }) => {
|
export const User = ({ id }) => {
|
||||||
const user = useSelector((state) => state.admin.users.get(id));
|
const user = useSelector((state) => state.admin.users.get(id));
|
||||||
const polochon = user.polochon;
|
const polochon = user.polochon;
|
||||||
const lastSeen = new Date(user.last_seen);
|
const lastSeen = moment(user.last_seen, "YYYY-MM-DDTHH:mm:ss.SZ");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
@ -33,7 +33,7 @@ export const User = ({ id }) => {
|
|||||||
}
|
}
|
||||||
></span>
|
></span>
|
||||||
</td>
|
</td>
|
||||||
<td>{isNaN(lastSeen) ? "-" : format(lastSeen)}</td>
|
<td>{lastSeen.isValid() ? lastSeen.fromNow() : "-"}</td>
|
||||||
<td>
|
<td>
|
||||||
<UserEdit id={id} />
|
<UserEdit id={id} />
|
||||||
</td>
|
</td>
|
||||||
|
@ -51,7 +51,7 @@ export const UserEdit = ({ id }) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
if (confirmDelete) {
|
if (confirmDelete) {
|
||||||
dispatch(deleteUser(user.name, id));
|
dispatch(deleteUser(name));
|
||||||
setModal(false);
|
setModal(false);
|
||||||
} else {
|
} else {
|
||||||
setConfirmDelete(true);
|
setConfirmDelete(true);
|
||||||
|
@ -18,7 +18,7 @@ export const DownloadAndStream = ({ url, name, subtitles }) => {
|
|||||||
DownloadAndStream.propTypes = {
|
DownloadAndStream.propTypes = {
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
subtitles: PropTypes.object,
|
subtitles: PropTypes.array,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DownloadButton = ({ url }) => (
|
const DownloadButton = ({ url }) => (
|
||||||
@ -67,35 +67,30 @@ const StreamButton = ({ name, url, subtitles }) => {
|
|||||||
StreamButton.propTypes = {
|
StreamButton.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
url: PropTypes.string.isRequired,
|
url: PropTypes.string.isRequired,
|
||||||
subtitles: PropTypes.object,
|
subtitles: PropTypes.array,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Player = ({ url, subtitles }) => {
|
const Player = ({ url, subtitles }) => {
|
||||||
|
const subs = subtitles || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="embed-responsive embed-responsive-16by9">
|
<div className="embed-responsive embed-responsive-16by9">
|
||||||
<video className="embed-responsive-item" controls>
|
<video className="embed-responsive-item" controls>
|
||||||
<source src={url} type="video/mp4" />
|
<source src={url} type="video/mp4" />
|
||||||
{subtitles &&
|
{subs.map((sub, index) => (
|
||||||
[...subtitles.entries()].map(([lang, sub]) => {
|
<track
|
||||||
if (sub.embedded) {
|
key={index}
|
||||||
return null;
|
kind="subtitles"
|
||||||
}
|
label={sub.language}
|
||||||
|
src={sub.vvt_file}
|
||||||
return (
|
srcLang={sub.language}
|
||||||
<track
|
/>
|
||||||
key={lang}
|
))}
|
||||||
kind="subtitles"
|
|
||||||
label={sub.lang}
|
|
||||||
src={sub.vvt_file}
|
|
||||||
srcLang={sub.lang}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Player.propTypes = {
|
Player.propTypes = {
|
||||||
subtitles: PropTypes.object,
|
subtitles: PropTypes.array,
|
||||||
url: PropTypes.string.isRequired,
|
url: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -3,22 +3,25 @@ import PropTypes from "prop-types";
|
|||||||
|
|
||||||
import Dropdown from "react-bootstrap/Dropdown";
|
import Dropdown from "react-bootstrap/Dropdown";
|
||||||
|
|
||||||
import { prettySize, upperCaseFirst } from "../../utils";
|
|
||||||
|
|
||||||
export const SubtitlesButton = ({
|
export const SubtitlesButton = ({
|
||||||
subtitles,
|
subtitles,
|
||||||
inLibrary,
|
inLibrary,
|
||||||
|
searching,
|
||||||
search,
|
search,
|
||||||
fetchingSubtitles,
|
|
||||||
}) => {
|
}) => {
|
||||||
if (inLibrary === false) {
|
if (inLibrary === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
|
const onSelect = (eventKey) => {
|
||||||
|
if (eventKey === null || eventKey != 1) {
|
||||||
|
setShow(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onToggle = (isOpen, event, metadata) => {
|
const onToggle = (isOpen, event, metadata) => {
|
||||||
// Don't close on select
|
// Don't close on select
|
||||||
if (metadata && metadata.source !== "select") {
|
if (metadata && metadata.source !== "select") {
|
||||||
@ -26,20 +29,10 @@ export const SubtitlesButton = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchAll = () => {
|
const count = subtitles && subtitles.length !== 0 ? subtitles.length : 0;
|
||||||
const langs = ["fr_FR", "en_US"];
|
|
||||||
for (const lang of langs) {
|
|
||||||
search(lang);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const count = subtitles && subtitles.size !== 0 ? subtitles.size : 0;
|
|
||||||
|
|
||||||
const searching = fetchingSubtitles.length > 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="mr-1 mb-1">
|
<span className="mr-1 mb-1">
|
||||||
<Dropdown drop="up" show={show} onToggle={onToggle}>
|
<Dropdown drop="up" show={show} onToggle={onToggle} onSelect={onSelect}>
|
||||||
<Dropdown.Toggle variant="secondary" bsPrefix="btn-sm w-md-100">
|
<Dropdown.Toggle variant="secondary" bsPrefix="btn-sm w-md-100">
|
||||||
<i className="fa fa-commenting mr-1" />
|
<i className="fa fa-commenting mr-1" />
|
||||||
Subtitles
|
Subtitles
|
||||||
@ -47,13 +40,9 @@ export const SubtitlesButton = ({
|
|||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
|
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
<Dropdown.Item eventKey={1} onClick={searchAll}>
|
<Dropdown.Item eventKey={1} onClick={search}>
|
||||||
<div className="d-flex justify-content-between align-items-center">
|
<i className={`fa ${searching ? "fa-spin" : ""} fa-refresh mr-1`} />
|
||||||
<span>Automatic search</span>
|
Automatic search
|
||||||
<div
|
|
||||||
className={`fa ${searching ? "fa-spin" : ""} fa-refresh ml-1`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
{count > 0 && (
|
{count > 0 && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@ -64,13 +53,10 @@ export const SubtitlesButton = ({
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
{count > 0 &&
|
{count > 0 &&
|
||||||
[...subtitles.entries()].map(([lang, subtitle]) => (
|
subtitles.map((subtitle, index) => (
|
||||||
<SubtitleEntry
|
<Dropdown.Item href={subtitle.url} key={index}>
|
||||||
key={lang}
|
{subtitle.language.split("_")[1]}
|
||||||
subtitle={subtitle}
|
</Dropdown.Item>
|
||||||
searching={searching}
|
|
||||||
search={search}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@ -78,42 +64,8 @@ export const SubtitlesButton = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
SubtitlesButton.propTypes = {
|
SubtitlesButton.propTypes = {
|
||||||
subtitles: PropTypes.object,
|
subtitles: PropTypes.array,
|
||||||
inLibrary: PropTypes.bool.isRequired,
|
inLibrary: PropTypes.bool.isRequired,
|
||||||
fetchingSubtitles: PropTypes.array.isRequired,
|
searching: PropTypes.bool.isRequired,
|
||||||
search: PropTypes.func.isRequired,
|
search: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SubtitleEntry = ({ subtitle, search }) => {
|
|
||||||
const lang = upperCaseFirst(subtitle.lang.split("_")[0]);
|
|
||||||
const size = subtitle.size ? subtitle.size : 0;
|
|
||||||
const embedded = subtitle.embedded ? subtitle.embedded : false;
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
|
||||||
search(subtitle.lang);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown.Item as="span" disabled={embedded}>
|
|
||||||
<div className="d-flex justify-content-between align-items-center">
|
|
||||||
<a href={subtitle.url ? subtitle.url : ""} className="link-unstyled">
|
|
||||||
{lang}
|
|
||||||
{embedded && <small className="ml-2">(Inside the video)</small>}
|
|
||||||
{size !== 0 && <span> ({prettySize(size)})</span>}
|
|
||||||
</a>
|
|
||||||
{!embedded && (
|
|
||||||
<div
|
|
||||||
onClick={handleRefresh}
|
|
||||||
className={`clickable fa ${
|
|
||||||
subtitle.searching ? "fa-spin" : ""
|
|
||||||
} fa-refresh`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Dropdown.Item>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
SubtitleEntry.propTypes = {
|
|
||||||
search: PropTypes.func.isRequired,
|
|
||||||
subtitle: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
|
|
||||||
export const DownloadProgress = ({ imdbId, season, episode }) => {
|
|
||||||
let history = useHistory();
|
|
||||||
const torrentGroup = useSelector((state) =>
|
|
||||||
state.torrents.torrents.get(imdbId)
|
|
||||||
);
|
|
||||||
if (!torrentGroup || torrentGroup.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let torrent;
|
|
||||||
const type = torrentGroup[0].type;
|
|
||||||
switch (type) {
|
|
||||||
case "movie":
|
|
||||||
torrent = torrentGroup[0];
|
|
||||||
break;
|
|
||||||
case "episode": {
|
|
||||||
if (!season || !episode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const torrents = torrentGroup.filter(
|
|
||||||
(torrent) => torrent.episode === episode && torrent.season === season
|
|
||||||
);
|
|
||||||
if (torrents.length !== 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
torrent = torrents[0];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!torrent || !torrent.status) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const progress = Number(torrent.status.percent_done).toFixed(1);
|
|
||||||
if (progress === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
history.push("/torrents/list");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-100 mt-n2 clickable" onClick={handleClick}>
|
|
||||||
<small className="text text-muted">Downloading...</small>
|
|
||||||
<div
|
|
||||||
className="progress"
|
|
||||||
style={{
|
|
||||||
height: "0.2rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="progress-bar bg-warning"
|
|
||||||
style={{
|
|
||||||
width: `${progress}%`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DownloadProgress.propTypes = {
|
|
||||||
imdbId: PropTypes.string.isRequired,
|
|
||||||
season: PropTypes.number,
|
|
||||||
episode: PropTypes.number,
|
|
||||||
};
|
|
@ -1,27 +1,23 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import { prettySize } from "../../utils";
|
|
||||||
|
|
||||||
export const PolochonMetadata = ({
|
export const PolochonMetadata = ({
|
||||||
quality,
|
quality,
|
||||||
container,
|
container,
|
||||||
videoCodec,
|
videoCodec,
|
||||||
audioCodec,
|
audioCodec,
|
||||||
size,
|
releaseGroup,
|
||||||
}) => {
|
}) => {
|
||||||
if (!quality || quality === "") {
|
if (!quality || quality === "") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const s = size === 0 ? "" : prettySize(size);
|
const metadata = [quality, container, videoCodec, audioCodec, releaseGroup]
|
||||||
|
|
||||||
const metadata = [quality, s, container, videoCodec, audioCodec]
|
|
||||||
.filter((m) => m && m !== "")
|
.filter((m) => m && m !== "")
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="text text-muted">
|
<span>
|
||||||
<i className="fa fa-file-video-o mr-1" />
|
<i className="fa fa-file-video-o mr-1" />
|
||||||
{metadata}
|
{metadata}
|
||||||
</span>
|
</span>
|
||||||
@ -33,5 +29,4 @@ PolochonMetadata.propTypes = {
|
|||||||
videoCodec: PropTypes.string,
|
videoCodec: PropTypes.string,
|
||||||
audioCodec: PropTypes.string,
|
audioCodec: PropTypes.string,
|
||||||
releaseGroup: PropTypes.string,
|
releaseGroup: PropTypes.string,
|
||||||
size: PropTypes.number,
|
|
||||||
};
|
};
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { format } from "timeago.js";
|
import moment from "moment";
|
||||||
|
|
||||||
const prettyDate = (input) => {
|
const prettyDate = (input) => {
|
||||||
if (typeof input !== "string" || input === "") {
|
if (typeof input !== "string" || input === "") {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
const date = new Date(input);
|
const date = moment(input, "YYYY-MM-DD HH:mm:ss Z");
|
||||||
if (isNaN(date)) {
|
if (!date.isValid()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const dd = date.getDay().toString().padStart(2, "0");
|
let output = date.format("DD/MM/YYYY");
|
||||||
const mm = date.getMonth().toString().padStart(2, "0");
|
|
||||||
const yyyy = date.getFullYear();
|
|
||||||
let output = `${dd}/${mm}/${yyyy}`;
|
|
||||||
|
|
||||||
const now = new Date();
|
if (date > moment().subtract(1, "month") && date < moment().add(1, "month")) {
|
||||||
const days = Math.abs((now - date) / (24 * 60 * 60 * 1000));
|
output += " (" + date.fromNow() + ")";
|
||||||
if (days < 31) {
|
|
||||||
output += ` (${format(date)})`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -12,7 +12,6 @@ import { Rating } from "../details/rating";
|
|||||||
import { ReleaseDate } from "../details/releaseDate";
|
import { ReleaseDate } from "../details/releaseDate";
|
||||||
import { Runtime } from "../details/runtime";
|
import { Runtime } from "../details/runtime";
|
||||||
import { Title } from "../details/title";
|
import { Title } from "../details/title";
|
||||||
import { DownloadProgress } from "../details/downloadProgress";
|
|
||||||
|
|
||||||
const ListDetails = (props) => {
|
const ListDetails = (props) => {
|
||||||
if (!props.data || Object.keys(props.data).length === 0) {
|
if (!props.data || Object.keys(props.data).length === 0) {
|
||||||
@ -42,7 +41,6 @@ const ListDetails = (props) => {
|
|||||||
subtitles={props.data.subtitles}
|
subtitles={props.data.subtitles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DownloadProgress imdbId={props.data.imdb_id} />
|
|
||||||
<TrackingLabel
|
<TrackingLabel
|
||||||
wishlisted={props.data.wishlisted}
|
wishlisted={props.data.wishlisted}
|
||||||
inLibrary={props.data.polochon_url !== ""}
|
inLibrary={props.data.polochon_url !== ""}
|
||||||
@ -55,7 +53,6 @@ const ListDetails = (props) => {
|
|||||||
container={props.data.container}
|
container={props.data.container}
|
||||||
audioCodec={props.data.audio_codec}
|
audioCodec={props.data.audio_codec}
|
||||||
videoCodec={props.data.video_codec}
|
videoCodec={props.data.video_codec}
|
||||||
size={props.data.size}
|
|
||||||
/>
|
/>
|
||||||
<Plot plot={props.data.plot} />
|
<Plot plot={props.data.plot} />
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -29,8 +29,9 @@ export const KeyboardNavigation = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const containerWidth = containerRef.current.getBoundingClientRect().width;
|
const containerWidth = containerRef.current.getBoundingClientRect().width;
|
||||||
const posterContainer =
|
const posterContainer = containerRef.current.getElementsByClassName(
|
||||||
containerRef.current.getElementsByClassName("img-thumbnail");
|
"img-thumbnail"
|
||||||
|
);
|
||||||
|
|
||||||
let posterWidth = 0;
|
let posterWidth = 0;
|
||||||
if (posterContainer !== null && posterContainer.item(0) !== null) {
|
if (posterContainer !== null && posterContainer.item(0) !== null) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import Loader from "../loader/loader";
|
import Loader from "../loader/loader";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
@ -7,29 +7,11 @@ import { upperCaseFirst } from "../../utils";
|
|||||||
// TODO: udpate this
|
// TODO: udpate this
|
||||||
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||||
|
|
||||||
const Modules = ({ isLoading, modules, fetchModules }) => {
|
const Modules = ({ isLoading, modules }) => {
|
||||||
const [show, setShow] = useState(false);
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
fetchModules();
|
|
||||||
setShow(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!show) {
|
|
||||||
return (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-12 col-md-8 offset-md-2 mb-3">
|
|
||||||
<div className="btn btn-secondary w-100" onClick={handleClick}>
|
|
||||||
Show modules status
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
{Object.keys(modules).map((type) => (
|
{Object.keys(modules).map((type) => (
|
||||||
@ -41,7 +23,6 @@ const Modules = ({ isLoading, modules, fetchModules }) => {
|
|||||||
Modules.propTypes = {
|
Modules.propTypes = {
|
||||||
isLoading: PropTypes.bool.isRequired,
|
isLoading: PropTypes.bool.isRequired,
|
||||||
modules: PropTypes.object.isRequired,
|
modules: PropTypes.object.isRequired,
|
||||||
fetchModules: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
export default Modules;
|
export default Modules;
|
||||||
|
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
import React, { useEffect } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
|
|
||||||
import {
|
|
||||||
fetchMovieDetails,
|
|
||||||
getMovieDetails,
|
|
||||||
movieWishlistToggle,
|
|
||||||
} from "../../actions/movies";
|
|
||||||
import { searchMovieSubtitle } from "../../actions/subtitles";
|
|
||||||
|
|
||||||
import Loader from "../loader/loader";
|
|
||||||
|
|
||||||
import { Fanart } from "../details/fanart";
|
|
||||||
import { Plot } from "../details/plot";
|
|
||||||
import { Rating } from "../details/rating";
|
|
||||||
import { ReleaseDate } from "../details/releaseDate";
|
|
||||||
import { Title } from "../details/title";
|
|
||||||
import { PolochonMetadata } from "../details/polochon";
|
|
||||||
import { TrackingLabel } from "../details/tracking";
|
|
||||||
import { Genres } from "../details/genres";
|
|
||||||
import { Runtime } from "../details/runtime";
|
|
||||||
import { DownloadProgress } from "../details/downloadProgress";
|
|
||||||
|
|
||||||
import { DownloadAndStream } from "../buttons/download";
|
|
||||||
import { ImdbBadge } from "../buttons/imdb";
|
|
||||||
import { TorrentsButton } from "../buttons/torrents";
|
|
||||||
import { SubtitlesButton } from "../buttons/subtitles";
|
|
||||||
import { ShowMore } from "../buttons/showMore";
|
|
||||||
|
|
||||||
export const MovieDetails = ({ match }) => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const loading = useSelector((state) => state.movie.loading);
|
|
||||||
const fanartUrl = useSelector((state) => state.movie.movie.fanart);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchMovieDetails(match.params.imdbId));
|
|
||||||
}, [dispatch, match]);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <Loader />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Fanart url={fanartUrl} />
|
|
||||||
<Header />
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
MovieDetails.propTypes = {
|
|
||||||
match: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Header = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const {
|
|
||||||
audioCodec,
|
|
||||||
container,
|
|
||||||
fetchingDetails,
|
|
||||||
fetchingSubtitles,
|
|
||||||
genres,
|
|
||||||
imdb_id: imdbId,
|
|
||||||
plot,
|
|
||||||
polochon_url: polochonUrl,
|
|
||||||
poster_url: posterUrl,
|
|
||||||
quality,
|
|
||||||
rating,
|
|
||||||
runtime,
|
|
||||||
size,
|
|
||||||
subtitles,
|
|
||||||
title,
|
|
||||||
torrents,
|
|
||||||
videoCodec,
|
|
||||||
votes,
|
|
||||||
wishlisted,
|
|
||||||
year,
|
|
||||||
release_group: releaseGroup,
|
|
||||||
} = useSelector((state) => state.movie.movie);
|
|
||||||
|
|
||||||
const inLibrary = polochonUrl !== "";
|
|
||||||
|
|
||||||
if (!imdbId || imdbId === "") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="col-12 col-lg-10 offset-lg-1 mb-3">
|
|
||||||
<div className="d-flex flex-column flex-md-row">
|
|
||||||
<div className="d-flex justify-content-center">
|
|
||||||
<img className="overflow-hidden object-fit-cover" src={posterUrl} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="ml-sm-1">
|
|
||||||
<div className="card-body">
|
|
||||||
<p className="card-title">
|
|
||||||
<Title
|
|
||||||
title={title}
|
|
||||||
wishlisted={wishlisted}
|
|
||||||
wishlist={() =>
|
|
||||||
dispatch(movieWishlistToggle(imdbId, wishlisted))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<ReleaseDate date={year} />
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<Runtime runtime={runtime} />
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<Genres genres={genres} />
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<Rating rating={rating} votes={votes} />
|
|
||||||
</p>
|
|
||||||
<div className="card-text">
|
|
||||||
<ImdbBadge imdbId={imdbId} />
|
|
||||||
<DownloadAndStream
|
|
||||||
url={polochonUrl}
|
|
||||||
name={title}
|
|
||||||
subtitles={subtitles}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="card-text mt-2">
|
|
||||||
<DownloadProgress imdbId={imdbId} />
|
|
||||||
</div>
|
|
||||||
<p className="card-text">
|
|
||||||
<TrackingLabel inLibrary={inLibrary} wishlisted={wishlisted} />
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<PolochonMetadata
|
|
||||||
quality={quality}
|
|
||||||
releaseGroup={releaseGroup}
|
|
||||||
container={container}
|
|
||||||
audioCodec={audioCodec}
|
|
||||||
videoCodec={videoCodec}
|
|
||||||
size={size}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
<p className="card-text">
|
|
||||||
<Plot plot={plot} />
|
|
||||||
</p>
|
|
||||||
<div className="card-text">
|
|
||||||
<ShowMore id={imdbId} inLibrary={inLibrary}>
|
|
||||||
<TorrentsButton
|
|
||||||
torrents={torrents}
|
|
||||||
searching={fetchingDetails}
|
|
||||||
search={() => dispatch(getMovieDetails(imdbId))}
|
|
||||||
url={`#/torrents/search/movies/${encodeURI(title)}`}
|
|
||||||
/>
|
|
||||||
<SubtitlesButton
|
|
||||||
inLibrary={inLibrary}
|
|
||||||
fetchingSubtitles={fetchingSubtitles}
|
|
||||||
subtitles={subtitles}
|
|
||||||
search={(lang) => dispatch(searchMovieSubtitle(imdbId, lang))}
|
|
||||||
/>
|
|
||||||
</ShowMore>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -37,7 +37,7 @@ const fetchUrl = (match) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const MovieList = ({ match, history }) => {
|
const MovieList = ({ match }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -63,10 +63,6 @@ const MovieList = ({ match, history }) => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const movieDetails = (imdbId) => {
|
|
||||||
history.push("/movies/details/" + imdbId);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<ListPosters
|
<ListPosters
|
||||||
@ -77,8 +73,8 @@ const MovieList = ({ match, history }) => {
|
|||||||
exploreOptions={exploreOptions}
|
exploreOptions={exploreOptions}
|
||||||
selectedImdbId={selectedImdbId}
|
selectedImdbId={selectedImdbId}
|
||||||
onClick={selectFunc}
|
onClick={selectFunc}
|
||||||
onDoubleClick={movieDetails}
|
onDoubleClick={() => {}}
|
||||||
onKeyEnter={movieDetails}
|
onKeyEnter={() => {}}
|
||||||
params={match.params}
|
params={match.params}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
@ -109,7 +105,6 @@ MovieList.propTypes = {
|
|||||||
updateFilter: PropTypes.func,
|
updateFilter: PropTypes.func,
|
||||||
movieWishlistToggle: PropTypes.func,
|
movieWishlistToggle: PropTypes.func,
|
||||||
selectMovie: PropTypes.func,
|
selectMovie: PropTypes.func,
|
||||||
history: PropTypes.object,
|
|
||||||
match: PropTypes.object,
|
match: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import { searchMovieSubtitle } from "../../actions/subtitles";
|
import { searchMovieSubtitles } from "../../actions/subtitles";
|
||||||
|
|
||||||
import { SubtitlesButton } from "../buttons/subtitles";
|
import { SubtitlesButton } from "../buttons/subtitles";
|
||||||
|
|
||||||
@ -15,16 +15,16 @@ export const MovieSubtitlesButton = () => {
|
|||||||
const subtitles = useSelector(
|
const subtitles = useSelector(
|
||||||
(state) => state.movies.movies.get(imdbId).subtitles
|
(state) => state.movies.movies.get(imdbId).subtitles
|
||||||
);
|
);
|
||||||
const fetchingSubtitles = useSelector(
|
const searching = useSelector(
|
||||||
(state) => state.movies.movies.get(imdbId).fetchingSubtitles
|
(state) => state.movies.movies.get(imdbId).fetchingSubtitles
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubtitlesButton
|
<SubtitlesButton
|
||||||
inLibrary={inLibrary}
|
inLibrary={inLibrary}
|
||||||
fetchingSubtitles={fetchingSubtitles}
|
searching={searching}
|
||||||
subtitles={subtitles}
|
subtitles={subtitles}
|
||||||
search={(lang) => dispatch(searchMovieSubtitle(imdbId, lang))}
|
search={() => dispatch(searchMovieSubtitles(imdbId))}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -99,7 +99,7 @@ Search.propTypes = {
|
|||||||
|
|
||||||
const MoviesDropdown = () => (
|
const MoviesDropdown = () => (
|
||||||
<NavDropdown title="Movies" id="navbar-movies-dropdown">
|
<NavDropdown title="Movies" id="navbar-movies-dropdown">
|
||||||
<LinkContainer to="/movies/explore/trakttv/trending">
|
<LinkContainer to="/movies/explore/yts/seeds">
|
||||||
<NavDropdown.Item>Discover</NavDropdown.Item>
|
<NavDropdown.Item>Discover</NavDropdown.Item>
|
||||||
</LinkContainer>
|
</LinkContainer>
|
||||||
<LinkContainer to="/movies/polochon">
|
<LinkContainer to="/movies/polochon">
|
||||||
|
@ -4,7 +4,7 @@ import { useSelector, useDispatch } from "react-redux";
|
|||||||
|
|
||||||
import Loader from "../loader/loader";
|
import Loader from "../loader/loader";
|
||||||
|
|
||||||
import { Fanart } from "../details/fanart";
|
import { Fanart } from "./details/fanart";
|
||||||
import { Header } from "./details/header";
|
import { Header } from "./details/header";
|
||||||
import { SeasonsList } from "./details/seasons";
|
import { SeasonsList } from "./details/seasons";
|
||||||
|
|
||||||
@ -13,7 +13,6 @@ import { fetchShowDetails } from "../../actions/shows";
|
|||||||
export const ShowDetails = ({ match }) => {
|
export const ShowDetails = ({ match }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const loading = useSelector((state) => state.show.loading);
|
const loading = useSelector((state) => state.show.loading);
|
||||||
const fanartUrl = useSelector((state) => state.show.show.fanart_url);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchShowDetails(match.params.imdbId));
|
dispatch(fetchShowDetails(match.params.imdbId));
|
||||||
@ -25,7 +24,7 @@ export const ShowDetails = ({ match }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Fanart url={fanartUrl} />
|
<Fanart />
|
||||||
<div className="row no-gutters">
|
<div className="row no-gutters">
|
||||||
<Header />
|
<Header />
|
||||||
<SeasonsList />
|
<SeasonsList />
|
||||||
|
@ -11,7 +11,6 @@ import { PolochonMetadata } from "../../details/polochon";
|
|||||||
import { ReleaseDate } from "../../details/releaseDate";
|
import { ReleaseDate } from "../../details/releaseDate";
|
||||||
import { Runtime } from "../../details/runtime";
|
import { Runtime } from "../../details/runtime";
|
||||||
import { Title } from "../../details/title";
|
import { Title } from "../../details/title";
|
||||||
import { DownloadProgress } from "../../details/downloadProgress";
|
|
||||||
|
|
||||||
import { DownloadAndStream } from "../../buttons/download";
|
import { DownloadAndStream } from "../../buttons/download";
|
||||||
import { ShowMore } from "../../buttons/showMore";
|
import { ShowMore } from "../../buttons/showMore";
|
||||||
@ -63,7 +62,6 @@ export const Episode = ({ season, episode }) => {
|
|||||||
/>
|
/>
|
||||||
<ReleaseDate date={data.aired} />
|
<ReleaseDate date={data.aired} />
|
||||||
<Runtime runtime={data.runtime} />
|
<Runtime runtime={data.runtime} />
|
||||||
<DownloadProgress imdbId={imdbId} season={season} episode={episode} />
|
|
||||||
<Plot plot={data.plot} />
|
<Plot plot={data.plot} />
|
||||||
<DownloadAndStream
|
<DownloadAndStream
|
||||||
name={prettyEpisodeName(showTitle, season, episode)}
|
name={prettyEpisodeName(showTitle, season, episode)}
|
||||||
@ -76,7 +74,6 @@ export const Episode = ({ season, episode }) => {
|
|||||||
container={data.container}
|
container={data.container}
|
||||||
audioCodec={data.audio_codec}
|
audioCodec={data.audio_codec}
|
||||||
videoCodec={data.video_codec}
|
videoCodec={data.video_codec}
|
||||||
size={data.size}
|
|
||||||
light
|
light
|
||||||
/>
|
/>
|
||||||
<ShowMore
|
<ShowMore
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
export const Fanart = ({ url }) => {
|
export const Fanart = () => {
|
||||||
if (url == "") {
|
const url = useSelector((state) => state.show.show.fanart_url);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="show-fanart mx-n3 mt-n1">
|
<div className="show-fanart mx-n3 mt-n1">
|
||||||
<img
|
<img
|
||||||
@ -14,9 +12,3 @@ export const Fanart = ({ url }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Fanart.propTypes = {
|
|
||||||
url: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
Fanart.defaultProps = {
|
|
||||||
url: "",
|
|
||||||
};
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import { searchEpisodeSubtitle } from "../../../actions/subtitles";
|
import { searchEpisodeSubtitles } from "../../../actions/subtitles";
|
||||||
|
|
||||||
import { SubtitlesButton } from "../../buttons/subtitles";
|
import { SubtitlesButton } from "../../buttons/subtitles";
|
||||||
|
|
||||||
@ -10,27 +10,30 @@ export const EpisodeSubtitlesButton = ({ season, episode }) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const imdbId = useSelector((state) => state.show.show.imdb_id);
|
const imdbId = useSelector((state) => state.show.show.imdb_id);
|
||||||
const fetchingSubtitles = useSelector(
|
const searching = useSelector((state) =>
|
||||||
(state) =>
|
state.show.show.seasons.get(season).get(episode).fetchingSubtitles
|
||||||
state.show.show.seasons.get(season).get(episode).fetchingSubtitles
|
? state.show.show.seasons.get(season).get(episode).fetchingSubtitles
|
||||||
|
: false
|
||||||
);
|
);
|
||||||
const inLibrary = useSelector(
|
const inLibrary = useSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
state.show.show.seasons.get(season).get(episode).polochon_url !== ""
|
state.show.show.seasons.get(season).get(episode).polochon_url !== ""
|
||||||
);
|
);
|
||||||
const subtitles = useSelector(
|
const subtitles = useSelector((state) =>
|
||||||
(state) => state.show.show.seasons.get(season).get(episode).subtitles
|
state.show.show.seasons.get(season).get(episode).subtitles
|
||||||
|
? state.show.show.seasons.get(season).get(episode).subtitles
|
||||||
|
: []
|
||||||
);
|
);
|
||||||
|
|
||||||
const search = (lang) => {
|
const search = () => {
|
||||||
dispatch(searchEpisodeSubtitle(imdbId, season, episode, lang));
|
dispatch(searchEpisodeSubtitles(imdbId, season, episode));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubtitlesButton
|
<SubtitlesButton
|
||||||
subtitles={subtitles}
|
subtitles={subtitles}
|
||||||
inLibrary={inLibrary}
|
inLibrary={inLibrary}
|
||||||
fetchingSubtitles={fetchingSubtitles}
|
searching={searching}
|
||||||
search={search}
|
search={search}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,27 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
export const Poster = ({ thumb, fanart }) => {
|
export const Poster = ({ url }) => {
|
||||||
if (thumb === "" && fanart === "") {
|
if (!url || url === "") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-md-2">
|
<div className="col-md-2 d-none d-md-block">
|
||||||
{thumb !== "" && (
|
<img className="card-img" src={url} />
|
||||||
<img className="card-img d-none d-sm-block" src={thumb} />
|
|
||||||
)}
|
|
||||||
{fanart !== "" && (
|
|
||||||
<img className="card-img d-block d-sm-none" src={fanart} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Poster.propTypes = {
|
Poster.propTypes = {
|
||||||
thumb: PropTypes.string,
|
url: PropTypes.string,
|
||||||
fanart: PropTypes.string,
|
|
||||||
};
|
|
||||||
Poster.defaultProps = {
|
|
||||||
thumb: "",
|
|
||||||
fanart: "",
|
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Torrent } from "./torrent";
|
import { Torrent } from "./torrent";
|
||||||
import { Poster } from "./poster";
|
import { Poster } from "./poster";
|
||||||
@ -18,32 +17,18 @@ export const TorrentGroup = ({ torrentKey }) => {
|
|||||||
const title = (torrent) => {
|
const title = (torrent) => {
|
||||||
switch (torrent.type) {
|
switch (torrent.type) {
|
||||||
case "movie":
|
case "movie":
|
||||||
return (
|
return torrent.video.title;
|
||||||
<Link
|
|
||||||
className="link-unstyled"
|
|
||||||
to={`/movies/details/${torrent.video.imdb_id}`}
|
|
||||||
>
|
|
||||||
{torrent.video.title}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
case "episode":
|
case "episode":
|
||||||
return (
|
return torrent.video.show_title;
|
||||||
<Link
|
|
||||||
className="link-unstyled"
|
|
||||||
to={`/shows/details/${torrent.video.show_imdb_id}`}
|
|
||||||
>
|
|
||||||
{torrent.video.show_title}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return <span>Files</span>;
|
return "Files";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-100 mb-3 card">
|
<div className="w-100 mb-3 card">
|
||||||
<div className="row no-gutters">
|
<div className="row no-gutters">
|
||||||
<Poster thumb={torrents[0].thumb} fanart={torrents[0].fanart} />
|
<Poster url={torrents[0].img} />
|
||||||
<div className="col-sm">
|
<div className="col-sm">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h4 className="card-title">{title(torrents[0])}</h4>
|
<h4 className="card-title">{title(torrents[0])}</h4>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import { PolochonList } from "../polochons/list";
|
import { PolochonList } from "../polochons/list";
|
||||||
@ -12,19 +12,15 @@ export const UserProfile = () => {
|
|||||||
const modules = useSelector((state) => state.user.modules);
|
const modules = useSelector((state) => state.user.modules);
|
||||||
const modulesLoading = useSelector((state) => state.user.modulesLoading);
|
const modulesLoading = useSelector((state) => state.user.modulesLoading);
|
||||||
|
|
||||||
const fetchModules = () => {
|
useEffect(() => {
|
||||||
dispatch(getUserModules());
|
dispatch(getUserModules());
|
||||||
};
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<UserEdit />
|
<UserEdit />
|
||||||
<PolochonList />
|
<PolochonList />
|
||||||
<Modules
|
<Modules modules={modules} isLoading={modulesLoading} />
|
||||||
modules={modules}
|
|
||||||
isLoading={modulesLoading}
|
|
||||||
fetchModules={fetchModules}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ import React, { useEffect } from "react";
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { UAParser } from "ua-parser-js";
|
import { UAParser } from "ua-parser-js";
|
||||||
import { format } from "timeago.js";
|
import moment from "moment";
|
||||||
|
|
||||||
import { getUserTokens, deleteUserToken } from "../../actions/users";
|
import { getUserTokens, deleteUserToken } from "../../actions/users";
|
||||||
|
|
||||||
@ -27,9 +27,6 @@ export const UserTokens = () => {
|
|||||||
|
|
||||||
const Token = ({ token }) => {
|
const Token = ({ token }) => {
|
||||||
const ua = UAParser(token.user_agent);
|
const ua = UAParser(token.user_agent);
|
||||||
const lastUsed = new Date(token.last_used);
|
|
||||||
const createdAt = new Date(token.created_at);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card mt-3">
|
<div className="card mt-3">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
@ -42,8 +39,8 @@ const Token = ({ token }) => {
|
|||||||
<div className="card-body row">
|
<div className="card-body row">
|
||||||
<div className="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<p>Last IP: {token.ip}</p>
|
<p>Last IP: {token.ip}</p>
|
||||||
<p>Last used: {isNaN(lastUsed) ? "-" : format(lastUsed)}</p>
|
<p>Last used: {moment(token.last_used).fromNow()}</p>
|
||||||
<p>Created: {isNaN(createdAt) ? "-" : format(createdAt)}</p>
|
<p>Created: {moment(token.created_at).fromNow()}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { setFetchedTorrents } from "../actions/torrents";
|
import { setFetchedTorrents } from "../actions/torrents";
|
||||||
import { newMovieEvent, getMovieDetails } from "../actions/movies";
|
import { newMovieEvent } from "../actions/movies";
|
||||||
import { newEpisodeEvent, getEpisodeDetails } from "../actions/shows";
|
import { newEpisodeEvent } from "../actions/shows";
|
||||||
|
|
||||||
export const WsHandler = () => {
|
export const WsHandler = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -67,16 +67,8 @@ export const WsHandler = () => {
|
|||||||
case "newVideo":
|
case "newVideo":
|
||||||
if (data.message.type === "movie") {
|
if (data.message.type === "movie") {
|
||||||
dispatch(newMovieEvent(data.message.data));
|
dispatch(newMovieEvent(data.message.data));
|
||||||
dispatch(getMovieDetails(data.message.data.imdb_id));
|
|
||||||
} else if (data.message.type === "episode") {
|
} else if (data.message.type === "episode") {
|
||||||
dispatch(newEpisodeEvent(data.message.data));
|
dispatch(newEpisodeEvent(data.message.data));
|
||||||
dispatch(
|
|
||||||
getEpisodeDetails(
|
|
||||||
data.message.data.show_imdb_id,
|
|
||||||
data.message.data.season,
|
|
||||||
data.message.data.episode
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,6 @@ export default (state = defaultState, action) =>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "ADMIN_DELETE_USER_FULFILLED": {
|
|
||||||
draft.users.delete(action.payload.main.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "ADMIN_GET_STATS_FULFILLED": {
|
case "ADMIN_GET_STATS_FULFILLED": {
|
||||||
draft.stats = action.payload.response.data;
|
draft.stats = action.payload.response.data;
|
||||||
break;
|
break;
|
||||||
|
@ -5,7 +5,6 @@ import { enableMapSet } from "immer";
|
|||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
import movies from "./movies";
|
import movies from "./movies";
|
||||||
import movie from "./movie";
|
|
||||||
import shows from "./shows";
|
import shows from "./shows";
|
||||||
import show from "./show";
|
import show from "./show";
|
||||||
import user from "./users";
|
import user from "./users";
|
||||||
@ -17,7 +16,6 @@ import notifications from "./notifications";
|
|||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
movies,
|
movies,
|
||||||
movie,
|
|
||||||
shows,
|
shows,
|
||||||
show,
|
show,
|
||||||
user,
|
user,
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
import { produce } from "immer";
|
|
||||||
|
|
||||||
import { formatSubtitle, formatMovie } from "./utils";
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
loading: false,
|
|
||||||
movie: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default (state = defaultState, action) =>
|
|
||||||
produce(state, (draft) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case "MOVIE_FETCH_DETAILS_PENDING":
|
|
||||||
draft.loading = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MOVIE_FETCH_DETAILS_FULFILLED": {
|
|
||||||
draft.movie = formatMovie(action.payload.response.data);
|
|
||||||
draft.loading = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "MOVIE_GET_DETAILS_PENDING": {
|
|
||||||
let imdbId = action.payload.main.imdbId;
|
|
||||||
if (draft.movie.imdb_id !== imdbId) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.movie.fetchingDetails = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "MOVIE_GET_DETAILS_FULFILLED": {
|
|
||||||
let imdbId = action.payload.main.imdbId;
|
|
||||||
if (draft.movie.imdb_id !== imdbId) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.movie = formatMovie(action.payload.response.data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "MOVIE_SUBTITLES_UPDATE_PENDING": {
|
|
||||||
let imdbId = action.payload.main.imdbId;
|
|
||||||
if (draft.movie.imdb_id !== imdbId) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lang = action.payload.main.lang;
|
|
||||||
draft.movie.fetchingSubtitles.push(lang);
|
|
||||||
if (draft.movie.subtitles.get(lang)) {
|
|
||||||
draft.movie.subtitles.get(lang).searching = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "MOVIE_SUBTITLES_UPDATE_FULFILLED": {
|
|
||||||
let imdbId = action.payload.main.imdbId;
|
|
||||||
if (draft.movie.imdb_id !== imdbId) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lang = action.payload.main.lang;
|
|
||||||
let data = action.payload.response.data;
|
|
||||||
draft.movie.fetchingSubtitles = draft.movie.fetchingSubtitles.filter(
|
|
||||||
(l) => l != lang
|
|
||||||
);
|
|
||||||
if (data) {
|
|
||||||
draft.movie.subtitles.set(lang, formatSubtitle(data));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return draft;
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,6 +1,6 @@
|
|||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
|
||||||
import { formatSubtitle, formatMovie } from "./utils";
|
import { formatTorrents } from "../utils";
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -10,6 +10,13 @@ const defaultState = {
|
|||||||
exploreOptions: {},
|
exploreOptions: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatMovie = (movie) => {
|
||||||
|
movie.fetchingDetails = false;
|
||||||
|
movie.fetchingSubtitles = false;
|
||||||
|
movie.torrents = formatTorrents(movie);
|
||||||
|
return movie;
|
||||||
|
};
|
||||||
|
|
||||||
const formatMovies = (movies = []) => {
|
const formatMovies = (movies = []) => {
|
||||||
let allMoviesInPolochon = true;
|
let allMoviesInPolochon = true;
|
||||||
movies.map((movie) => {
|
movies.map((movie) => {
|
||||||
@ -57,17 +64,10 @@ export default (state = defaultState, action) =>
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "MOVIE_GET_DETAILS_PENDING":
|
case "MOVIE_GET_DETAILS_PENDING":
|
||||||
if (!draft.movies.get(action.payload.main.imdbId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
draft.movies.get(action.payload.main.imdbId).fetchingDetails = true;
|
draft.movies.get(action.payload.main.imdbId).fetchingDetails = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "MOVIE_GET_DETAILS_FULFILLED":
|
case "MOVIE_GET_DETAILS_FULFILLED":
|
||||||
if (!draft.movies.get(action.payload.main.imdbId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.movies.set(
|
draft.movies.set(
|
||||||
action.payload.response.data.imdb_id,
|
action.payload.response.data.imdb_id,
|
||||||
formatMovie(action.payload.response.data)
|
formatMovie(action.payload.response.data)
|
||||||
@ -87,36 +87,15 @@ export default (state = defaultState, action) =>
|
|||||||
draft.lastFetchUrl = action.payload.url;
|
draft.lastFetchUrl = action.payload.url;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "MOVIE_SUBTITLES_UPDATE_PENDING": {
|
case "MOVIE_SUBTITLES_UPDATE_PENDING":
|
||||||
let imdbId = action.payload.main.imdbId;
|
draft.movies.get(action.payload.main.imdbId).fetchingSubtitles = true;
|
||||||
let lang = action.payload.main.lang;
|
|
||||||
if (!draft.movies.get(imdbId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.movies.get(imdbId).fetchingSubtitles.push(lang);
|
|
||||||
if (draft.movies.get(imdbId).subtitles.get(lang)) {
|
|
||||||
draft.movies.get(imdbId).subtitles.get(lang).searching = true;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case "MOVIE_SUBTITLES_UPDATE_FULFILLED": {
|
case "MOVIE_SUBTITLES_UPDATE_FULFILLED":
|
||||||
let imdbId = action.payload.main.imdbId;
|
draft.movies.get(action.payload.main.imdbId).fetchingSubtitles = false;
|
||||||
let lang = action.payload.main.lang;
|
draft.movies.get(action.payload.main.imdbId).subtitles =
|
||||||
let data = action.payload.response.data;
|
action.payload.response.data;
|
||||||
if (!draft.movies.get(imdbId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.movies.get(imdbId).fetchingSubtitles = draft.movies
|
|
||||||
.get(imdbId)
|
|
||||||
.fetchingSubtitles.filter((l) => l != lang);
|
|
||||||
if (data) {
|
|
||||||
draft.movies.get(imdbId).subtitles.set(lang, formatSubtitle(data));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case "SELECT_MOVIE":
|
case "SELECT_MOVIE":
|
||||||
draft.selectedImdbId = action.payload.imdbId;
|
draft.selectedImdbId = action.payload.imdbId;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
|
||||||
import { formatTorrents, formatSubtitle, formatSubtitles } from "./utils";
|
import { formatTorrents } from "../utils";
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -10,12 +10,10 @@ const defaultState = {
|
|||||||
const formatEpisode = (episode) => {
|
const formatEpisode = (episode) => {
|
||||||
// Format the episode's torrents
|
// Format the episode's torrents
|
||||||
episode.torrents = formatTorrents(episode);
|
episode.torrents = formatTorrents(episode);
|
||||||
episode.subtitles = formatSubtitles(episode.subtitles);
|
|
||||||
|
|
||||||
// Set the default fetching data
|
// Set the default fetching data
|
||||||
episode.fetching = false;
|
episode.fetching = false;
|
||||||
// Holds the languages of the subtitles currently fetching
|
episode.fetchingSubtitles = false;
|
||||||
episode.fetchingSubtitles = [];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (state = defaultState, action) =>
|
export default (state = defaultState, action) =>
|
||||||
@ -84,69 +82,38 @@ export default (state = defaultState, action) =>
|
|||||||
draft.show.tracked_episode = action.payload.episode; // eslint-disable-line camelcase
|
draft.show.tracked_episode = action.payload.episode; // eslint-disable-line camelcase
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "EPISODE_GET_DETAILS_PENDING": {
|
case "EPISODE_GET_DETAILS_PENDING":
|
||||||
const imdbId = action.payload.main.imdbId;
|
draft.show.seasons
|
||||||
if (!draft.show || draft.show.imdb_id !== imdbId) {
|
.get(action.payload.main.season)
|
||||||
break;
|
.get(action.payload.main.episode).fetching = true;
|
||||||
}
|
|
||||||
|
|
||||||
const season = action.payload.main.season;
|
|
||||||
const episode = action.payload.main.episode;
|
|
||||||
draft.show.seasons.get(season).get(episode).fetching = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case "EPISODE_GET_DETAILS_FULFILLED": {
|
case "EPISODE_GET_DETAILS_FULFILLED": {
|
||||||
const imdbId = action.payload.main.imdbId;
|
let episode = action.payload.response.data;
|
||||||
if (!draft.show || draft.show.imdb_id !== imdbId) {
|
if (!episode) {
|
||||||
break;
|
return draft;
|
||||||
}
|
}
|
||||||
|
formatEpisode(episode);
|
||||||
const season = action.payload.main.season;
|
draft.show.seasons.get(episode.season).set(episode.episode, episode);
|
||||||
const episode = action.payload.main.episode;
|
|
||||||
|
|
||||||
let data = action.payload.response.data;
|
|
||||||
formatEpisode(data);
|
|
||||||
draft.show.seasons.get(season).set(episode, data);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "EPISODE_SUBTITLES_UPDATE_PENDING": {
|
case "EPISODE_SUBTITLES_UPDATE_PENDING":
|
||||||
let season = action.payload.main.season;
|
|
||||||
let episode = action.payload.main.episode;
|
|
||||||
let lang = action.payload.main.lang;
|
|
||||||
draft.show.seasons
|
draft.show.seasons
|
||||||
.get(season)
|
.get(action.payload.main.season)
|
||||||
.get(episode)
|
.get(action.payload.main.episode).fetchingSubtitles = true;
|
||||||
.fetchingSubtitles.push(lang);
|
|
||||||
if (draft.show.seasons.get(season).get(episode).subtitles.get(lang)) {
|
|
||||||
draft.show.seasons
|
|
||||||
.get(season)
|
|
||||||
.get(episode)
|
|
||||||
.subtitles.get(lang).searching = true;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case "EPISODE_SUBTITLES_UPDATE_FULFILLED": {
|
case "EPISODE_SUBTITLES_UPDATE_FULFILLED": {
|
||||||
let season = action.payload.main.season;
|
draft.show.seasons
|
||||||
let episode = action.payload.main.episode;
|
.get(action.payload.main.season)
|
||||||
let lang = action.payload.main.lang;
|
.get(action.payload.main.episode).subtitles =
|
||||||
let data = action.payload.response.data;
|
action.payload.response.data;
|
||||||
draft.show.seasons.get(season).get(episode).fetchingSubtitles =
|
draft.show.seasons
|
||||||
draft.show.seasons
|
.get(action.payload.main.season)
|
||||||
.get(season)
|
.get(action.payload.main.episode).fetchingSubtitles = false;
|
||||||
.get(episode)
|
|
||||||
.fetchingSubtitles.filter((l) => l != lang);
|
|
||||||
if (data) {
|
|
||||||
draft.show.seasons
|
|
||||||
.get(season)
|
|
||||||
.get(episode)
|
|
||||||
.subtitles.set(lang, formatSubtitle(data));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return draft;
|
return draft;
|
||||||
}
|
}
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
export const formatSubtitles = (subtitles) => {
|
|
||||||
if (!subtitles || subtitles.length == 0) {
|
|
||||||
return new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
let map = new Map();
|
|
||||||
subtitles.forEach((subtitle) => {
|
|
||||||
subtitle = formatSubtitle(subtitle);
|
|
||||||
map.set(subtitle.lang, subtitle);
|
|
||||||
});
|
|
||||||
|
|
||||||
return map;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatSubtitle = (subtitle) => {
|
|
||||||
if (!subtitle) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
subtitle.searching = false;
|
|
||||||
return subtitle;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatTorrents = (input) => {
|
|
||||||
if (!input.torrents || input.torrents.length == 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
let torrentMap = new Map();
|
|
||||||
input.torrents.forEach((torrent) => {
|
|
||||||
if (!torrent.result || !torrent.result.source) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!torrentMap.has(torrent.result.source)) {
|
|
||||||
torrentMap.set(torrent.result.source, new Map());
|
|
||||||
}
|
|
||||||
|
|
||||||
torrentMap.get(torrent.result.source).set(torrent.quality, torrent);
|
|
||||||
});
|
|
||||||
|
|
||||||
return torrentMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatMovie = (movie) => {
|
|
||||||
movie.fetchingDetails = false;
|
|
||||||
movie.fetchingSubtitles = [];
|
|
||||||
movie.torrents = formatTorrents(movie);
|
|
||||||
movie.subtitles = formatSubtitles(movie.subtitles);
|
|
||||||
return movie;
|
|
||||||
};
|
|
@ -35,5 +35,26 @@ export const prettySize = (fileSizeInBytes) => {
|
|||||||
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
|
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatTorrents = (input) => {
|
||||||
|
if (!input.torrents || input.torrents.length == 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let torrentMap = new Map();
|
||||||
|
input.torrents.forEach((torrent) => {
|
||||||
|
if (!torrent.result || !torrent.result.source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!torrentMap.has(torrent.result.source)) {
|
||||||
|
torrentMap.set(torrent.result.source, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
torrentMap.get(torrent.result.source).set(torrent.quality, torrent);
|
||||||
|
});
|
||||||
|
|
||||||
|
return torrentMap;
|
||||||
|
};
|
||||||
|
|
||||||
export const upperCaseFirst = (string) =>
|
export const upperCaseFirst = (string) =>
|
||||||
string.charAt(0).toUpperCase() + string.slice(1);
|
string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
|
20818
frontend/package-lock.json
generated
20818
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,65 +1,64 @@
|
|||||||
{
|
{
|
||||||
"name": "canape",
|
"name": "canape",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "NODE_ENV=development npx webpack --progress --color --watch",
|
"start": "NODE_ENV=development ./node_modules/webpack/bin/webpack.js -d --progress --colors --watch",
|
||||||
"build": "NODE_ENV=production npx webpack --progress --color",
|
"build": "NODE_ENV=production ./node_modules/webpack/bin/webpack.js -p --progress --colors",
|
||||||
"lint": "npx eslint js"
|
"lint": "./node_modules/eslint/bin/eslint.js js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^4.4.1",
|
"bootstrap": "^4.4.1",
|
||||||
"bootswatch": "^4.4.1",
|
"bootswatch": "^4.4.1",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"fuzzy": "^0.1.3",
|
"fuzzy": "^0.1.3",
|
||||||
"history": "^5.0.0",
|
"history": "^4.9.0",
|
||||||
"immer": "^9.0.5",
|
"immer": "^6.0.3",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.4.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^2.1.0",
|
||||||
|
"moment": "^2.20.1",
|
||||||
"popper.js": "^1.15.0",
|
"popper.js": "^1.15.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.6.0",
|
||||||
"react": "^17.0.2",
|
"react": "^16.13.1",
|
||||||
"react-bootstrap": "^1.6.1",
|
"react-bootstrap": "^1.0.0",
|
||||||
"react-bootstrap-sweetalert": "^5.2.0",
|
"react-bootstrap-sweetalert": "^5.1.9",
|
||||||
"react-bootstrap-toggle": "^2.3.2",
|
"react-bootstrap-toggle": "^2.2.6",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^16.13.1",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^5.0.4",
|
||||||
"react-loading": "2.0.3",
|
"react-loading": "2.0.3",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.0",
|
||||||
"react-router-bootstrap": "^0.25.0",
|
"react-router-bootstrap": "^0.25.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.1.2",
|
||||||
"redux": "^4.1.1",
|
"redux": "^4.0.5",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"ua-parser-js": "^0.7.28"
|
"ua-parser-js": "^0.7.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.0",
|
"@babel/core": "^7.9.0",
|
||||||
"@babel/preset-env": "^7.15.0",
|
"@babel/preset-env": "^7.9.0",
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.9.4",
|
||||||
"autoprefixer": "^10.3.1",
|
"autoprefixer": "^9.7.5",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.19.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.1.0",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"css-loader": "^6.2.0",
|
"css-loader": "^3.4.2",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^6.10.1",
|
||||||
"eslint-plugin-import": "^2.24.0",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"eslint-plugin-prettier": "^3.1.2",
|
||||||
"eslint-plugin-react": "^7.24.0",
|
"eslint-plugin-react": "^7.19.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^2.5.1",
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"file-loader": "^5.1.0",
|
||||||
"mini-css-extract-plugin": "^2.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"postcss-loader": "^6.1.1",
|
"node-sass": "^4.12.0",
|
||||||
"prettier": "^2.3.2",
|
"postcss-loader": "^3.0.0",
|
||||||
"purgecss-webpack-plugin": "^4.0.3",
|
"prettier": "^2.0.2",
|
||||||
"sass": "^1.37.5",
|
"sass-loader": "^8.0.2",
|
||||||
"sass-loader": "^12.1.0",
|
"style-loader": "^1.1.3",
|
||||||
"style-loader": "^3.2.1",
|
"universal-cookie": "^4.0.3",
|
||||||
"timeago.js": "^4.0.2",
|
"url-loader": "^3.0.0",
|
||||||
"universal-cookie": "^4.0.4",
|
"webpack": "^4.42.1",
|
||||||
"webpack": "^5.50.0",
|
"webpack-cli": "^3.3.11",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-pwa-manifest": "^4.0.0"
|
||||||
"webpack-pwa-manifest": "^4.3.0",
|
|
||||||
"workbox-webpack-plugin": "^6.2.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,7 @@ div.show.dropdown.nav-item > div {
|
|||||||
.video-details {
|
.video-details {
|
||||||
> div, > p, > span {
|
> div, > p, > span {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@media (max-height: 820px) {
|
@media (max-width: 330px) {
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
@media (max-height: 580px) {
|
|
||||||
margin-bottom: 0rem;
|
margin-bottom: 0rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,8 +146,3 @@ div.sweet-alert > h2 {
|
|||||||
.toast {
|
.toast {
|
||||||
background-color: $card-bg;
|
background-color: $card-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-unstyled, .link-unstyled:link, .link-unstyled:hover {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
var webpack = require("webpack");
|
var webpack = require("webpack");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
const glob = require("glob");
|
|
||||||
var WebpackPwaManifest = require("webpack-pwa-manifest");
|
var WebpackPwaManifest = require("webpack-pwa-manifest");
|
||||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
var { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
var { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
|
||||||
const PurgeCSSPlugin = require("purgecss-webpack-plugin");
|
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
|
||||||
|
|
||||||
var mode = "development";
|
var mode = "development";
|
||||||
var BUILD_DIR = path.resolve(__dirname, "../build/public/");
|
var BUILD_DIR = path.resolve(__dirname, "../build/public/");
|
||||||
@ -24,9 +20,19 @@ const config = {
|
|||||||
output: {
|
output: {
|
||||||
path: BUILD_DIR,
|
path: BUILD_DIR,
|
||||||
filename: "[contenthash]-app.js",
|
filename: "[contenthash]-app.js",
|
||||||
assetModuleFilename: "[contenthash]-[name][ext][query]",
|
|
||||||
},
|
},
|
||||||
optimization: {},
|
optimization: {
|
||||||
|
runtimeChunk: "single",
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
vendor: {
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
name: "vendors",
|
||||||
|
chunks: "all",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
@ -39,30 +45,21 @@ const config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
test: /\.css$/i,
|
|
||||||
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: [
|
use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"],
|
||||||
MiniCssExtractPlugin.loader,
|
|
||||||
"css-loader",
|
|
||||||
"sass-loader",
|
|
||||||
"postcss-loader",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpg|svg|ico)$/,
|
test: /\.(png|jpg|svg|ico)$/,
|
||||||
type: "asset/resource",
|
use: ["file-loader?name=[hash]-[name].[ext]"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
type: "asset",
|
use: ["url-loader?limit=10000&mimetype=application/font-woff"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
type: "asset",
|
use: ["file-loader"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -70,18 +67,18 @@ const config = {
|
|||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
||||||
}),
|
}),
|
||||||
|
new webpack.HashedModuleIdsPlugin(),
|
||||||
new CleanWebpackPlugin({
|
new CleanWebpackPlugin({
|
||||||
cleanOnceBeforeBuildPatterns: ["**/*", "!img/**/*", "!img"],
|
cleanOnceBeforeBuildPatterns: ["**/*", "!img/**/*", "!img"],
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: path.join(SRC_DIR, "index.html"),
|
template: path.join(SRC_DIR, "index.html"),
|
||||||
favicon: path.join(SRC_DIR, "img/favicon.ico"),
|
|
||||||
}),
|
}),
|
||||||
new WebpackPwaManifest({
|
new WebpackPwaManifest({
|
||||||
fingerprints: true,
|
fingerprints: true,
|
||||||
inject: true,
|
inject: true,
|
||||||
ios: {
|
ios: {
|
||||||
"apple-mobile-web-app-status-bar-style": "#4e5d6c",
|
"apple-mobile-web-app-status-bar-style": "default",
|
||||||
"apple-mobile-web-app-title": "Canapé",
|
"apple-mobile-web-app-title": "Canapé",
|
||||||
},
|
},
|
||||||
name: "Canapé",
|
name: "Canapé",
|
||||||
@ -90,7 +87,6 @@ const config = {
|
|||||||
theme_color: "#4e5d6c", // eslint-disable-line camelcase
|
theme_color: "#4e5d6c", // eslint-disable-line camelcase
|
||||||
display: "standalone",
|
display: "standalone",
|
||||||
orientation: "omit",
|
orientation: "omit",
|
||||||
publicPath: "/",
|
|
||||||
scope: "/",
|
scope: "/",
|
||||||
start_url: "/", // eslint-disable-line camelcase
|
start_url: "/", // eslint-disable-line camelcase
|
||||||
icons: [
|
icons: [
|
||||||
@ -98,11 +94,6 @@ const config = {
|
|||||||
src: path.resolve(__dirname, "img/android-chrome-512x512.png"),
|
src: path.resolve(__dirname, "img/android-chrome-512x512.png"),
|
||||||
sizes: [96, 128, 192, 256, 384, 512],
|
sizes: [96, 128, 192, 256, 384, 512],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
src: path.resolve(__dirname, "img/maskable_icon_x512.png"),
|
|
||||||
size: "512x512",
|
|
||||||
purpose: "maskable",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
src: path.resolve(__dirname, "img/apple-touch-icon.png"),
|
src: path.resolve(__dirname, "img/apple-touch-icon.png"),
|
||||||
sizes: [80, 120, 152, 167, 180],
|
sizes: [80, 120, 152, 167, 180],
|
||||||
@ -110,23 +101,11 @@ const config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new MiniCssExtractPlugin({
|
|
||||||
filename: "[contenthash].css",
|
|
||||||
}),
|
|
||||||
new WorkboxPlugin.GenerateSW(),
|
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".js"],
|
extensions: [".js"],
|
||||||
},
|
},
|
||||||
devtool: mode === "production" ? false : "source-map",
|
devtool: mode === "production" ? "source-map" : "inline-source-map",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mode === "production") {
|
|
||||||
config.plugins.push(
|
|
||||||
new PurgeCSSPlugin({
|
|
||||||
paths: () => glob.sync(`${SRC_DIR}/**/*`, { nodir: true }),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
37
go.mod
37
go.mod
@ -1,45 +1,28 @@
|
|||||||
module git.quimbo.fr/odwrtw/canape
|
module git.quimbo.fr/odwrtw/canape
|
||||||
|
|
||||||
go 1.21.3
|
go 1.15
|
||||||
|
|
||||||
toolchain go1.21.4
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/go-sql-driver/mysql v1.4.1 // indirect
|
github.com/go-sql-driver/mysql v1.4.1 // indirect
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible
|
github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.4.0
|
||||||
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d
|
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d
|
||||||
github.com/jmoiron/sqlx v1.2.0
|
github.com/jmoiron/sqlx v1.2.0
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181 // indirect
|
||||||
github.com/lib/pq v1.1.1
|
github.com/lib/pq v1.1.1
|
||||||
github.com/mattn/go-sqlite3 v1.10.0 // indirect
|
github.com/mattn/go-sqlite3 v1.10.0 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833
|
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833
|
||||||
github.com/odwrtw/polochon v0.0.0-20240209100804-2b5c1d7f4df1
|
github.com/odwrtw/polochon v0.0.0-20201122193839-1d18b9eb03c0
|
||||||
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029
|
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029
|
||||||
github.com/pioz/tvdb v0.0.0-20221212235421-03519fb7a0e2 // indirect
|
github.com/pioz/tvdb v0.0.0-20190503215423-f45c687faba9 // indirect
|
||||||
github.com/robfig/cron v1.1.0
|
github.com/robfig/cron v1.1.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/unrolled/render v1.0.0
|
github.com/unrolled/render v1.0.0
|
||||||
github.com/urfave/negroni v1.0.0
|
github.com/urfave/negroni v1.0.0
|
||||||
golang.org/x/crypto v0.19.0
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
)
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
|
||||||
github.com/odwrtw/eztv v0.0.0-20231026192001-039613c81a8e // indirect
|
|
||||||
github.com/odwrtw/fanarttv v0.0.0-20170412122542-9f67d3cf0188 // indirect
|
|
||||||
github.com/odwrtw/guessit v0.0.0-20221028215709-d4336685bdaa // indirect
|
|
||||||
github.com/odwrtw/tpb v0.0.0-20200507114501-df19547bbff1 // indirect
|
|
||||||
github.com/odwrtw/trakttv v0.0.0-20240209094722-243ee0386b1a // indirect
|
|
||||||
github.com/odwrtw/yts v0.0.0-20231024130053-dfa826fee7b6 // indirect
|
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
|
||||||
github.com/ryanbradynd05/go-tmdb v0.0.0-20230108222638-2a68dc6ff40c // indirect
|
|
||||||
golang.org/x/net v0.21.0 // indirect
|
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
|
||||||
)
|
)
|
||||||
|
219
go.sum
219
go.sum
@ -1,97 +1,222 @@
|
|||||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
|
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
|
||||||
|
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||||
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
|
||||||
|
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
|
||||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
|
||||||
|
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/rpc v1.1.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
|
||||||
|
github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
|
||||||
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gregdel/argo v0.0.0-20190104143955-4ac365771987/go.mod h1:dS8cBtIK+0jAZy09xmjPn0W1EMmNKiYQMuZ0BXseLio=
|
||||||
|
github.com/gregdel/pushover v0.0.0-20190217183207-15d3fef40636 h1:6agUllU8gUNAallyB+afeLXMRLL6Q1z+S6YC7Pi1EIY=
|
||||||
|
github.com/gregdel/pushover v0.0.0-20190217183207-15d3fef40636/go.mod h1:NbuXd8Iwy5dU99qFToB8mSE29qJOQNW/bphiV8CWj/k=
|
||||||
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d h1:1CqMCoF82nYY0XWwrSpLNZ86hpdhB0bSmTUXZP3AgJc=
|
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d h1:1CqMCoF82nYY0XWwrSpLNZ86hpdhB0bSmTUXZP3AgJc=
|
||||||
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d/go.mod h1:BnFQhn6sVyPpOQw/R/YohZ6Wgd3REouiFYFtDKET7S0=
|
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d/go.mod h1:BnFQhn6sVyPpOQw/R/YohZ6Wgd3REouiFYFtDKET7S0=
|
||||||
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
|
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
|
||||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||||
|
github.com/jpillora/opts v0.0.0-20160806153215-0b3373f6c34d/go.mod h1:8/sC6XyMKHq/ybiU9Oqc1i0tDjFA/6otW7+Ha/qtnfo=
|
||||||
|
github.com/jpillora/scraper v0.0.0-20190930150335-f06f890eb841 h1:DSiWcaEafVv5lLmUoYgr+Q/0ZhlOFzZHHqvmkEC8vKY=
|
||||||
|
github.com/jpillora/scraper v0.0.0-20190930150335-f06f890eb841/go.mod h1:rwZ1CpR9lO34N/3IDicjhl2N/jfe124j1zdwYJNjsEI=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20190417161013-de6d879202d7 h1:kL2yi3DjwkRWFgKwD5COyl4XMLKhfOvqck4xyis7EIw=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20190417161013-de6d879202d7/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181 h1:TrxPzApUukas24OMMVDUMlCs1XCExJtnGaDEiIAR4oQ=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28 h1:mkl3tvPHIuPaWsLtmHTybJeoVEW7cbePK73Ir8VtruA=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28/go.mod h1:T/T7jsxVqf9k/zYOqbgNAsANsjxTd1Yq3htjDhQ1H0c=
|
||||||
github.com/kylelemons/go-gypsy v1.0.0 h1:7/wQ7A3UL1bnqRMnZ6T8cwCOArfZCxFmb1iTxaOOo1s=
|
|
||||||
github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2KKkQWxfJUcU=
|
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/meatballhat/negroni-logrus v0.0.0-20170801195057-31067281800f h1:V6GHkMOIsnpGDasS1iYiNxEYTY8TmyjQXEF8PqYkKQ8=
|
||||||
|
github.com/meatballhat/negroni-logrus v0.0.0-20170801195057-31067281800f/go.mod h1:Ylx55XGW4gjY7McWT0pgqU0aQquIOChDnYkOVbSuF/c=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/odwrtw/addicted v0.0.0-20200509145452-a5d36bf9d0f8 h1:Urlj8ZMuBIOQyweZ4c0vkM9DBo5Sjxr+yHlOPvjaP9Y=
|
||||||
|
github.com/odwrtw/addicted v0.0.0-20200509145452-a5d36bf9d0f8/go.mod h1:330AeCGPQuyCBQzOCetLtMotSao0LH0+3FJcEAkq5dg=
|
||||||
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833 h1:JbimDtyFaekFnqCkv3goowabwGNeDfWZk8aCnmRJvT4=
|
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833 h1:JbimDtyFaekFnqCkv3goowabwGNeDfWZk8aCnmRJvT4=
|
||||||
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833/go.mod h1:KwBfTZvOQqi/hpk4jq8SKmKlf4yoxW8yap93PEvhs0c=
|
github.com/odwrtw/errors v0.0.0-20170604160533-c747b9d17833/go.mod h1:KwBfTZvOQqi/hpk4jq8SKmKlf4yoxW8yap93PEvhs0c=
|
||||||
github.com/odwrtw/eztv v0.0.0-20231026192001-039613c81a8e h1:qfTB4d29ziuVym+v5XAIdp6z41uDGZll43raruxUtok=
|
github.com/odwrtw/eztv v0.0.0-20201119092300-1cf1c95b0e34 h1:SXRmy0NRvXr5CvUtRAyuJuyNuwULC+DJQGI733xtnzw=
|
||||||
github.com/odwrtw/eztv v0.0.0-20231026192001-039613c81a8e/go.mod h1:d4WspgGb6fVz1XCUrgjoW3SzLC0Mq5GNFPA09ED9IP0=
|
github.com/odwrtw/eztv v0.0.0-20201119092300-1cf1c95b0e34/go.mod h1:fAKpKPEs8in5Mela1ImVeaaLETZ8zejDV05GCEtJ5Rk=
|
||||||
github.com/odwrtw/fanarttv v0.0.0-20170412122542-9f67d3cf0188 h1:mjAgidPA3aNSZU6EgB2iSu1yxxNU2tw9U14n57Gk7ZQ=
|
github.com/odwrtw/fanarttv v0.0.0-20170412122542-9f67d3cf0188 h1:mjAgidPA3aNSZU6EgB2iSu1yxxNU2tw9U14n57Gk7ZQ=
|
||||||
github.com/odwrtw/fanarttv v0.0.0-20170412122542-9f67d3cf0188/go.mod h1:AMkzq9fSbIpWKdWbU4FAsIherygisNEeYglKfmTfq1U=
|
github.com/odwrtw/fanarttv v0.0.0-20170412122542-9f67d3cf0188/go.mod h1:AMkzq9fSbIpWKdWbU4FAsIherygisNEeYglKfmTfq1U=
|
||||||
github.com/odwrtw/guessit v0.0.0-20221028215709-d4336685bdaa h1:NM16GxadULEwclj2K95cA+ZMqFl5AV4B41Sjc0kyqvA=
|
github.com/odwrtw/guessit v0.0.0-20200131084001-f88613483547 h1:O0wEl/ORBHpPpZtibQyiZR1kQVFWo97gzcQQH4qI5C8=
|
||||||
github.com/odwrtw/guessit v0.0.0-20221028215709-d4336685bdaa/go.mod h1:DzI77tN750RiQrJjZDmNUs3DjQi41Qy0cblJ9iJq33k=
|
github.com/odwrtw/guessit v0.0.0-20200131084001-f88613483547/go.mod h1:W22g7wtc0AGczEARBAs+77gvBk8monDaM6U6i9Wa0vQ=
|
||||||
github.com/odwrtw/polochon v0.0.0-20240209100804-2b5c1d7f4df1 h1:QEm9LQ5zdaJmVdDulAef9T9Z+cuCWUPh8c3E2SahDiM=
|
github.com/odwrtw/imdb-watchlist v0.0.0-20190417175016-b7a9f7503d69 h1:ow6b/4Jj7J5iYwU678/rbijvaNUJrYkg13j9Nivkung=
|
||||||
github.com/odwrtw/polochon v0.0.0-20240209100804-2b5c1d7f4df1/go.mod h1:X3UetRDDnSp8qEhHawceQv4vLqO1X3n8iC9zFtUwFc8=
|
github.com/odwrtw/imdb-watchlist v0.0.0-20190417175016-b7a9f7503d69/go.mod h1:o2tLH95CtNdqhDb0aS2NbU+1I4PmaNsODpr33Ry0JC0=
|
||||||
|
github.com/odwrtw/polochon v0.0.0-20201122193839-1d18b9eb03c0 h1:5nc5CdQvgj2r1euv5w/yxv5KBmPZmT06+8gl4GE+k3E=
|
||||||
|
github.com/odwrtw/polochon v0.0.0-20201122193839-1d18b9eb03c0/go.mod h1:651VdToWlRowtMfMZ1/A6rKp2TmvjP9Yk6jSo1CS1pM=
|
||||||
github.com/odwrtw/tpb v0.0.0-20200507114501-df19547bbff1 h1:ZxN8n11Muc25mS/wmqblHMUDl4TSr9ekePAx1xeTcRE=
|
github.com/odwrtw/tpb v0.0.0-20200507114501-df19547bbff1 h1:ZxN8n11Muc25mS/wmqblHMUDl4TSr9ekePAx1xeTcRE=
|
||||||
github.com/odwrtw/tpb v0.0.0-20200507114501-df19547bbff1/go.mod h1:sIHKrrfBBSG6KO92wfgWmYq8yYf3hBnnNyFx2nTCOmU=
|
github.com/odwrtw/tpb v0.0.0-20200507114501-df19547bbff1/go.mod h1:sIHKrrfBBSG6KO92wfgWmYq8yYf3hBnnNyFx2nTCOmU=
|
||||||
github.com/odwrtw/trakttv v0.0.0-20240209094722-243ee0386b1a h1:/NG+5swnU+aKYpNmVmojk5IxERbgXIZUt1pemrILt4M=
|
github.com/odwrtw/trakttv v0.0.0-20200404161731-0d594827e4f9 h1:PuQLHO75MXUsJpf9BcTVxvR/FCkdn1MZnZt6h3o6cJI=
|
||||||
github.com/odwrtw/trakttv v0.0.0-20240209094722-243ee0386b1a/go.mod h1:qniDMuPCtH3a9gr8MwYLdIHkw+xyK9XVslQG6pFSVNU=
|
github.com/odwrtw/trakttv v0.0.0-20200404161731-0d594827e4f9/go.mod h1:I2ogRfOYYqNpMhljPYdFUVUrLbZQ89Ba7QdfiW6EcJ0=
|
||||||
github.com/odwrtw/yts v0.0.0-20231024130053-dfa826fee7b6 h1:gxnFA91mK8/KdOOfgnQqTYWQtRAAhJ6sK+5cIDCRyjw=
|
github.com/odwrtw/transmission v0.0.0-20200408122227-b562b9fbcbee h1:BttuvaEGtpe3rL7zm+e2RwVkj1DBo/Mb6XdbWd0a7ds=
|
||||||
github.com/odwrtw/yts v0.0.0-20231024130053-dfa826fee7b6/go.mod h1:AnDs7BMR4LH3PFIqBWprZMb23uec9B9Dvc1tlxD8vZs=
|
github.com/odwrtw/transmission v0.0.0-20200408122227-b562b9fbcbee/go.mod h1:o+HdjqpVQPFqVTG4jncidwE0/cQoAfCJ4JMFaB481xc=
|
||||||
|
github.com/odwrtw/yifysubs v0.0.0-20190417174645-d3bba6e4cfe0 h1:LasNCTYd9Pc3x34xc1p054ZF8rVPLhD2Vfk3Db2KSpo=
|
||||||
|
github.com/odwrtw/yifysubs v0.0.0-20190417174645-d3bba6e4cfe0/go.mod h1:9TPMeWCUplybvf4aCW6Hu3KwJian+oSeK6jZ597z9i4=
|
||||||
|
github.com/odwrtw/yts v0.0.0-20190417175129-d51f8755d93d h1:f100x20G/oZHNqC4iebFULY0wj4Inv5HRN+j53flOaA=
|
||||||
|
github.com/odwrtw/yts v0.0.0-20190417175129-d51f8755d93d/go.mod h1:6cNtd2zi+BIa0JuBSYz2mKv90jCJ5NiubkYnyskIjD4=
|
||||||
|
github.com/oz/osdb v0.0.0-20190204162748-da06ada9cdc1 h1:yxQkNBp/nQAJE3p/0A7vdIEFdM/8w4LnDSX7bh3gYOo=
|
||||||
|
github.com/oz/osdb v0.0.0-20190204162748-da06ada9cdc1/go.mod h1:xIvcOs03IPml6sU+k9o/mEAm8aJhvGTSpDNlUs8RoOQ=
|
||||||
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029 h1:d6HcSW4ZoNlUWrPyZtBwIu8yv4WAWIU3R/jorwVkFtQ=
|
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029 h1:d6HcSW4ZoNlUWrPyZtBwIu8yv4WAWIU3R/jorwVkFtQ=
|
||||||
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029/go.mod h1:94RTq2fypdZCze25ZEZSjtbAQRT3cL/8EuRUqAZC/+w=
|
github.com/phyber/negroni-gzip v0.0.0-20180113114010-ef6356a5d029/go.mod h1:94RTq2fypdZCze25ZEZSjtbAQRT3cL/8EuRUqAZC/+w=
|
||||||
github.com/pioz/tvdb v0.0.0-20221212235421-03519fb7a0e2 h1:yIKFkxT0Que0D6APKDEltO7Vuq/4lb2za/VpnZlG/IA=
|
github.com/pioz/tvdb v0.0.0-20190107104821-ffdf1011c717 h1:llCrc82+T+DAodGyyo+NNhC7Zx2/64YmcFnyOJlcV2o=
|
||||||
github.com/pioz/tvdb v0.0.0-20221212235421-03519fb7a0e2/go.mod h1:nhHRTrbEzdp4lXtiozX4Yuvo4AHi29nOvM1J7H/XJMM=
|
github.com/pioz/tvdb v0.0.0-20190107104821-ffdf1011c717/go.mod h1:nhHRTrbEzdp4lXtiozX4Yuvo4AHi29nOvM1J7H/XJMM=
|
||||||
|
github.com/pioz/tvdb v0.0.0-20190503215423-f45c687faba9 h1:bduYa9c0qpYfrxuAnursm8RfmeGsdgQeB3NeW4N0zQ8=
|
||||||
|
github.com/pioz/tvdb v0.0.0-20190503215423-f45c687faba9/go.mod h1:nhHRTrbEzdp4lXtiozX4Yuvo4AHi29nOvM1J7H/XJMM=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
|
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
|
||||||
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/ryanbradynd05/go-tmdb v0.0.0-20230108222638-2a68dc6ff40c h1:TJP+nrMt7riGqrsnD3pGnF6/YW4r5WZ9cHFIJwCWJxQ=
|
github.com/ryanbradynd05/go-tmdb v0.0.0-20181220020137-291a20d25ffd h1:u2QODuSimmxBHwbYs2+AEJ+fgaLicaf3zEoOMNmntzs=
|
||||||
github.com/ryanbradynd05/go-tmdb v0.0.0-20230108222638-2a68dc6ff40c/go.mod h1:k/112WTJ3EoR7wjhtx8kOXO22CKNvJy+rNzGPXnuEsI=
|
github.com/ryanbradynd05/go-tmdb v0.0.0-20181220020137-291a20d25ffd/go.mod h1:k/112WTJ3EoR7wjhtx8kOXO22CKNvJy+rNzGPXnuEsI=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/unrolled/render v1.0.0 h1:XYtvhA3UkpB7PqkvhUFYmpKD55OudoIeygcfus4vcd4=
|
github.com/unrolled/render v1.0.0 h1:XYtvhA3UkpB7PqkvhUFYmpKD55OudoIeygcfus4vcd4=
|
||||||
github.com/unrolled/render v1.0.0/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg=
|
github.com/unrolled/render v1.0.0/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg=
|
||||||
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
|
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o=
|
||||||
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/unrolled/render.v1 v1.0.0/go.mod h1:D8ZfMFuggVdNUNlNz/R8zVjPPHGyMxLuJPA+MSx8na0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc h1:LMEBgNcZUqXaP7evD1PZcL6EcDVa2QOFuI+cqM3+AJM=
|
||||||
|
gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc/go.mod h1:N8UOSI6/c2yOpa/XDz3KVUiegocTziPiqNkeNTMiG1k=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
CREATE OR REPLACE VIEW movies_with_rating AS
|
|
||||||
SELECT
|
|
||||||
m.id, m.imdb_id, m.title, m.plot, m.tmdb_id, m.year, m.original_title, m.runtime, m.sort_title, m.tagline, m.genres,
|
|
||||||
r.rating,
|
|
||||||
r.votes,
|
|
||||||
m.updated_at, m.created_at
|
|
||||||
FROM movies m
|
|
||||||
JOIN imdb_ratings r
|
|
||||||
ON m.imdb_id = r.imdb_id;
|
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW shows_with_rating AS
|
|
||||||
SELECT
|
|
||||||
s.id, s.imdb_id, s.title, s.plot, s.tvdb_id, s.year, s.first_aired,
|
|
||||||
r.rating,
|
|
||||||
r.votes,
|
|
||||||
s.updated_at, s.created_at
|
|
||||||
FROM shows s
|
|
||||||
JOIN imdb_ratings r
|
|
||||||
ON s.imdb_id = r.imdb_id;
|
|
@ -1,19 +0,0 @@
|
|||||||
CREATE OR REPLACE VIEW movies_with_rating AS
|
|
||||||
SELECT
|
|
||||||
m.id, m.imdb_id, m.title, m.plot, m.tmdb_id, m.year, m.original_title, m.runtime, m.sort_title, m.tagline, m.genres,
|
|
||||||
CASE WHEN r.rating IS NULL THEN m.rating ELSE r.rating END,
|
|
||||||
CASE WHEN r.votes IS NULL THEN m.votes ELSE r.votes END,
|
|
||||||
m.updated_at, m.created_at
|
|
||||||
FROM movies m
|
|
||||||
LEFT JOIN imdb_ratings r
|
|
||||||
ON m.imdb_id = r.imdb_id;
|
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW shows_with_rating AS
|
|
||||||
SELECT
|
|
||||||
s.id, s.imdb_id, s.title, s.plot, s.tvdb_id, s.year, s.first_aired,
|
|
||||||
CASE WHEN r.rating IS NULL THEN s.rating ELSE r.rating END,
|
|
||||||
CASE WHEN r.votes IS NULL THEN 0 ELSE r.votes END,
|
|
||||||
s.updated_at, s.created_at
|
|
||||||
FROM shows s
|
|
||||||
LEFT JOIN imdb_ratings r
|
|
||||||
ON s.imdb_id = r.imdb_id;
|
|
Loading…
x
Reference in New Issue
Block a user