Add Torrents
Change the DB to store torrents in database Add Torrenters to the party Add raw links to movie Torrents in the web interface Change the way explore works with multiple source and categories with external_medias Delete StringSlice and use pq StringArray type to avoid problems
This commit is contained in:
parent
a9615b0aba
commit
ab7503997f
@ -85,6 +85,7 @@ CREATE TABLE movies (
|
|||||||
runtime integer NOT NULL,
|
runtime integer NOT NULL,
|
||||||
sort_title text NOT NULL,
|
sort_title text NOT NULL,
|
||||||
tagline text NOT NULL,
|
tagline text NOT NULL,
|
||||||
|
genres text[] NOT NULL,
|
||||||
LIKE base INCLUDING DEFAULTS
|
LIKE base INCLUDING DEFAULTS
|
||||||
);
|
);
|
||||||
CREATE INDEX ON movies (imdb_id);
|
CREATE INDEX ON movies (imdb_id);
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
CREATE TYPE media_type AS ENUM ('movie', 'show');
|
CREATE TYPE media_type AS ENUM ('movie', 'show');
|
||||||
CREATE TYPE media_category AS ENUM ('trending', 'popular', 'anticipated', 'box_office');
|
CREATE TYPE media_category AS ENUM ('trending', 'popular', 'anticipated', 'box_office');
|
||||||
CREATE TYPE media_source AS ENUM ('trakttv');
|
CREATE TYPE media_source AS ENUM ('trakttv', 'yts');
|
||||||
|
|
||||||
CREATE TABLE external_medias (
|
CREATE TABLE external_medias (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
type media_type NOT NULL,
|
type media_type NOT NULL,
|
||||||
source media_source NOT NULL,
|
source media_source NOT NULL,
|
||||||
category media_category NOT NULL,
|
category media_category NOT NULL,
|
||||||
ids text[][] NOT NULL,
|
ids text[] NOT NULL,
|
||||||
LIKE base INCLUDING DEFAULTS
|
LIKE base INCLUDING DEFAULTS
|
||||||
);
|
);
|
||||||
CREATE TRIGGER update_external_medias_updated_at BEFORE UPDATE ON external_medias FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
CREATE TRIGGER update_external_medias_updated_at BEFORE UPDATE ON external_medias FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||||
|
CREATE UNIQUE INDEX ON external_medias (type, source, category);
|
||||||
CREATE INDEX ON external_medias (type, source, category);
|
CREATE INDEX ON external_medias (type, source, category);
|
||||||
|
|
||||||
|
3
sql/migration/0003_torrents.down.sql
Normal file
3
sql/migration/0003_torrents.down.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DROP TABLE movie_torrents;
|
||||||
|
DROP TABLE episode_torrents;
|
||||||
|
DROP TABLE torrents_abstract;
|
30
sql/migration/0003_torrents.up.sql
Normal file
30
sql/migration/0003_torrents.up.sql
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
CREATE TABLE torrents_abstract (
|
||||||
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
imdb_id text NOT NULL,
|
||||||
|
url text NOT NULL,
|
||||||
|
source text NOT NULL,
|
||||||
|
quality text NOT NULL,
|
||||||
|
upload_user text NOT NULL,
|
||||||
|
seeders integer NOT NULL,
|
||||||
|
leechers integer NOT NULL,
|
||||||
|
LIKE base INCLUDING DEFAULTS
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE movie_torrents (
|
||||||
|
)
|
||||||
|
INHERITS (torrents_abstract);
|
||||||
|
|
||||||
|
CREATE TABLE episode_torrents (
|
||||||
|
season integer NOT NULL,
|
||||||
|
episode integer NOT NULL
|
||||||
|
)
|
||||||
|
INHERITS (torrents_abstract);
|
||||||
|
|
||||||
|
CREATE INDEX ON movie_torrents (imdb_id);
|
||||||
|
CREATE UNIQUE INDEX ON movie_torrents (imdb_id, source, quality);
|
||||||
|
CREATE INDEX ON episode_torrents (imdb_id);
|
||||||
|
CREATE INDEX ON episode_torrents (imdb_id, season, episode);
|
||||||
|
CREATE INDEX ON torrents_abstract (imdb_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER update_movie_torrents_updated_at BEFORE UPDATE ON movie_torrents FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||||
|
CREATE TRIGGER update_episode_torrents_updated_at BEFORE UPDATE ON episode_torrents FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
"github.com/odwrtw/polochon/modules/tmdb"
|
"github.com/odwrtw/polochon/modules/tmdb"
|
||||||
|
"github.com/odwrtw/polochon/modules/yts"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@ -17,9 +18,10 @@ type Config struct {
|
|||||||
TraktTVClientID string `yaml:"trakttv_client_id"`
|
TraktTVClientID string `yaml:"trakttv_client_id"`
|
||||||
PublicDir string `yaml:"public_dir"`
|
PublicDir string `yaml:"public_dir"`
|
||||||
|
|
||||||
// TODO improve the detailers configurations
|
// TODO improve the detailers and torrenters configurations
|
||||||
TmdbAPIKey string `yaml:"tmdb_api_key"`
|
TmdbAPIKey string `yaml:"tmdb_api_key"`
|
||||||
MovieDetailers []polochon.Detailer
|
MovieDetailers []polochon.Detailer
|
||||||
|
MovieTorrenters []polochon.Torrenter
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizerConfig struct {
|
type AuthorizerConfig struct {
|
||||||
@ -47,6 +49,7 @@ func Load(path string) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default detailers
|
||||||
cf.MovieDetailers = []polochon.Detailer{}
|
cf.MovieDetailers = []polochon.Detailer{}
|
||||||
if cf.TmdbAPIKey != "" {
|
if cf.TmdbAPIKey != "" {
|
||||||
d, err := tmdb.New(&tmdb.Params{cf.TmdbAPIKey})
|
d, err := tmdb.New(&tmdb.Params{cf.TmdbAPIKey})
|
||||||
@ -56,5 +59,13 @@ func Load(path string) (*Config, error) {
|
|||||||
cf.MovieDetailers = append(cf.MovieDetailers, d)
|
cf.MovieDetailers = append(cf.MovieDetailers, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default torrenters
|
||||||
|
cf.MovieTorrenters = []polochon.Torrenter{}
|
||||||
|
d, err := yts.New()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cf.MovieTorrenters = append(cf.MovieTorrenters, d)
|
||||||
|
|
||||||
return cf, nil
|
return cf, nil
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,18 @@ package extmedias
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/lib/pq"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
addExternalMediaQuery = `INSERT INTO external_medias (type, source, category, ids) VALUES ($1, $2, $3, $4) RETURNING id;`
|
// addExternalMediaQuery = `INSERT INTO external_medias (type, source, category, ids) VALUES ($1, $2, $3, $4) RETURNING id;`
|
||||||
updateExternalMediaQuery = `UPDATE external_medias SET type=:type, source=:source, category=:category, ids=:ids WHERE id=:id RETURNING *;`
|
upsertExternalMediaQuery = `
|
||||||
|
INSERT INTO external_medias (type, source, category, ids)
|
||||||
|
VALUES (:type, :source, :category, :ids)
|
||||||
|
ON CONFLICT (type, source, category)
|
||||||
|
DO UPDATE SET type=:type, source=:source, category=:category, ids=:ids
|
||||||
|
RETURNING id;`
|
||||||
|
|
||||||
deleteExternalMediaQuery = `DELETE FROM external_medias WHERE id=:id;`
|
deleteExternalMediaQuery = `DELETE FROM external_medias WHERE id=:id;`
|
||||||
getExternalMediaQuery = `SELECT * FROM external_medias WHERE type=$1 AND source=$2 AND category=$3 LIMIT 1;`
|
getExternalMediaQuery = `SELECT * FROM external_medias WHERE type=$1 AND source=$2 AND category=$3 LIMIT 1;`
|
||||||
@ -16,45 +22,36 @@ const (
|
|||||||
// Media represents an external media
|
// Media represents an external media
|
||||||
type Media struct {
|
type Media struct {
|
||||||
sqly.BaseModel
|
sqly.BaseModel
|
||||||
Type string `db:"type"`
|
Type string `db:"type"`
|
||||||
Source string `db:"source"`
|
Source string `db:"source"`
|
||||||
Category string `db:"category"`
|
Category string `db:"category"`
|
||||||
IDs sqly.StringSlice `db:"ids"`
|
IDs pq.StringArray `db:"ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds the Media in the database
|
// Upsert adds or updates or adds the Media in the database
|
||||||
func (m *Media) Add(q sqlx.Queryer) error {
|
func (m *Media) Upsert(db *sqlx.DB) error {
|
||||||
var id string
|
var id string
|
||||||
err := q.QueryRowx(addExternalMediaQuery, m.Type, m.Source, m.Category, m.IDs).Scan(&id)
|
r, err := db.NamedQuery(upsertExternalMediaQuery, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for r.Next() {
|
||||||
|
r.Scan(&id)
|
||||||
|
}
|
||||||
m.ID = id
|
m.ID = id
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ids only updates the IDs of the media
|
|
||||||
func (m *Media) Update(ex *sqlx.DB) error {
|
|
||||||
rows, err := ex.NamedQuery(updateExternalMediaQuery, m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
rows.StructScan(m)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the media from database or raise an error
|
// Delete the media from database or raise an error
|
||||||
func (m *Media) Delete(ex *sqlx.DB) error {
|
func (m *Media) Delete(db *sqlx.DB) error {
|
||||||
_, err := ex.NamedExec(deleteExternalMediaQuery, m)
|
_, err := db.NamedExec(deleteExternalMediaQuery, m)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets a media
|
// Get gets a media
|
||||||
func Get(q sqlx.Queryer, mtype, msrc, mcat string) (*Media, error) {
|
func Get(db *sqlx.DB, mtype, msrc, mcat string) (*Media, error) {
|
||||||
m := &Media{}
|
m := &Media{}
|
||||||
if err := q.QueryRowx(getExternalMediaQuery, mtype, msrc, mcat).StructScan(m); err != nil {
|
if err := db.QueryRowx(getExternalMediaQuery, mtype, msrc, mcat).StructScan(m); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
171
src/internal/external_medias/handlers.go
Normal file
171
src/internal/external_medias/handlers.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package extmedias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
traktExplorer "github.com/odwrtw/polochon/modules/trakttv"
|
||||||
|
ytsExplorer "github.com/odwrtw/polochon/modules/yts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMediaIDs will get some media IDs
|
||||||
|
func GetMediaIDs(env *web.Env, mediaType string, source string, category string, force bool) ([]string, error) {
|
||||||
|
log := env.Log.WithFields(logrus.Fields{
|
||||||
|
"mediaType": mediaType,
|
||||||
|
"source": source,
|
||||||
|
"category": category,
|
||||||
|
"function": "extmedias.GetMediaIds",
|
||||||
|
})
|
||||||
|
|
||||||
|
var err error
|
||||||
|
media, err := Get(env.Database, mediaType, source, category)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
log.Debug("medias found in database")
|
||||||
|
if !force {
|
||||||
|
log.Debug("returning medias from db")
|
||||||
|
return media.IDs, nil
|
||||||
|
}
|
||||||
|
case sql.ErrNoRows:
|
||||||
|
log.Debug("medias not found in database")
|
||||||
|
default:
|
||||||
|
// Unexpected error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
explorer, err := NewExplorer(env, source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids []string
|
||||||
|
movies, err := explorer.GetMovieList(polochon.ExploreByRate, log)
|
||||||
|
for _, movie := range movies {
|
||||||
|
ids = append(ids, movie.ImdbID)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("got %d medias from %s", len(ids), source)
|
||||||
|
|
||||||
|
media = &Media{
|
||||||
|
Type: mediaType,
|
||||||
|
Source: source,
|
||||||
|
Category: category,
|
||||||
|
IDs: ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Inserting int to DB %+v", media)
|
||||||
|
err = media.Upsert(env.Database)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("medias updated in database")
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMedias get some movies
|
||||||
|
func GetMedias(env *web.Env, source string, category string, force bool) ([]*movies.Movie, error) {
|
||||||
|
movieIds, err := GetMediaIDs(env, "movie", source, category, force)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
movieList := []*movies.Movie{}
|
||||||
|
for _, id := range movieIds {
|
||||||
|
movie := movies.New(id)
|
||||||
|
err := movie.GetDetails(env, force)
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = movie.GetTorrents(env, force)
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
movieList = append(movieList, movie)
|
||||||
|
}
|
||||||
|
return movieList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explore will explore some movies
|
||||||
|
func Explore(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source := r.FormValue("source")
|
||||||
|
// Default source
|
||||||
|
if source == "" {
|
||||||
|
source = "yts"
|
||||||
|
}
|
||||||
|
|
||||||
|
category := r.FormValue("category")
|
||||||
|
// Default category
|
||||||
|
if category == "" {
|
||||||
|
category = "popular"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the medias without trying to refresh them
|
||||||
|
movies, err := GetMedias(env, source, category, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, movies)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaSources represents the implemented media sources
|
||||||
|
var MediaSources = []string{
|
||||||
|
"trakttv",
|
||||||
|
"yts",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh will refresh the movie list
|
||||||
|
func Refresh(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
env.Log.Debugf("refreshing infos ...")
|
||||||
|
source := r.FormValue("source")
|
||||||
|
if source == "" {
|
||||||
|
source = "yts"
|
||||||
|
}
|
||||||
|
|
||||||
|
category := r.FormValue("category")
|
||||||
|
if category == "" {
|
||||||
|
category = "popular"
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll refresh the medias for each sources
|
||||||
|
for _, source := range MediaSources {
|
||||||
|
env.Log.Debugf("refreshing %s", source)
|
||||||
|
// GetMedias and refresh them
|
||||||
|
_, err := GetMedias(env, source, category, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, map[string]string{"message": "Refresh is done"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExplorer returns a polochon.Explorer
|
||||||
|
func NewExplorer(env *web.Env, source string) (polochon.Explorer, error) {
|
||||||
|
switch source {
|
||||||
|
case "trakttv":
|
||||||
|
return traktExplorer.NewExplorer(&traktExplorer.Params{
|
||||||
|
ClientID: env.Config.TraktTVClientID,
|
||||||
|
})
|
||||||
|
case "yts":
|
||||||
|
return ytsExplorer.NewExplorer()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown explorer")
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package movies
|
|
||||||
|
|
||||||
import "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
|
||||||
|
|
||||||
func updatePopular(env *web.Env, clientID string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/odwrtw/papi"
|
"github.com/odwrtw/papi"
|
||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
"github.com/odwrtw/polochon/modules/pam"
|
"github.com/odwrtw/polochon/modules/pam"
|
||||||
"github.com/odwrtw/trakttv"
|
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
||||||
@ -63,6 +62,7 @@ func getPolochonMovies(user *users.User) ([]*Movie, error) {
|
|||||||
return movies, nil
|
return movies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromPolochon will returns movies from Polochon
|
||||||
func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
v := auth.GetCurrentUser(r, env.Log)
|
v := auth.GetCurrentUser(r, env.Log)
|
||||||
user, ok := v.(*users.User)
|
user, ok := v.(*users.User)
|
||||||
@ -97,45 +97,6 @@ func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
return env.RenderJSON(w, movies)
|
return env.RenderJSON(w, movies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExplorePopular returns the popular movies
|
|
||||||
func ExplorePopular(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|
||||||
log := env.Log.WithField("function", "movies.ExplorePopular")
|
|
||||||
|
|
||||||
queryOption := trakttv.QueryOption{
|
|
||||||
ExtendedInfos: []trakttv.ExtendedInfo{
|
|
||||||
trakttv.ExtendedInfoMin,
|
|
||||||
},
|
|
||||||
Pagination: trakttv.Pagination{
|
|
||||||
Page: 1,
|
|
||||||
Limit: 20,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
trakt := trakttv.New(env.Config.TraktTVClientID)
|
|
||||||
trakt.Endpoint = trakttv.ProductionEndpoint
|
|
||||||
|
|
||||||
log.Debug("getting movies from trakttv")
|
|
||||||
tmovies, err := trakt.PopularMovies(queryOption)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debugf("got %d movies from trakttv", len(tmovies))
|
|
||||||
|
|
||||||
movies := []*Movie{}
|
|
||||||
ids := []string{}
|
|
||||||
for _, m := range tmovies {
|
|
||||||
movie := New(m.IDs.ImDB)
|
|
||||||
err := movie.GetDetails(env, false)
|
|
||||||
if err != nil {
|
|
||||||
env.Log.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
movies = append(movies, movie)
|
|
||||||
ids = append(ids, m.IDs.ImDB)
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.RenderJSON(w, movies)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDetailsHandler retrieves details for a movie
|
// GetDetailsHandler retrieves details for a movie
|
||||||
func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
@ -1,51 +1,106 @@
|
|||||||
package movies
|
package movies
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/lib/pq"
|
||||||
"github.com/odwrtw/polochon/lib"
|
"github.com/odwrtw/polochon/lib"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/torrents"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
addMovieQuery = `
|
upsertMovieQuery = `
|
||||||
INSERT INTO movies (imdb_id, title, rating, votes, plot, tmdb_id, year, original_title, runtime, sort_title, tagline)
|
INSERT INTO movies (imdb_id, title, rating, votes, plot, tmdb_id, year,
|
||||||
VALUES (:imdbid, :title, :rating, :votes, :plot, :tmdbid, :year, :originaltitle, :runtime, :sorttitle, :tagline)
|
genres, original_title, runtime, sort_title, tagline)
|
||||||
|
VALUES (:imdb_id, :title, :rating, :votes, :plot, :tmdb_id, :year, :genres,
|
||||||
|
:original_title, :runtime, :sort_title, :tagline)
|
||||||
|
ON CONFLICT (imdb_id)
|
||||||
|
DO UPDATE
|
||||||
|
SET imdb_id=:imdb_id, title=:title, rating=:rating, votes=:votes,
|
||||||
|
plot=:plot, tmdb_id=:tmdb_id, year=:year, genres=:genres,
|
||||||
|
original_title=:original_title, runtime=:runtime, sort_title=:sort_title,
|
||||||
|
tagline=:tagline
|
||||||
RETURNING id;`
|
RETURNING id;`
|
||||||
|
|
||||||
updateMovieQuery = `
|
|
||||||
UPDATE movies
|
|
||||||
SET imdb_id=:imdbid, title=:title, rating=:rating, votes=:votes, plot=:plot, tmdb_id=:tmdbid, year=:year, original_title=:originaltitle, runtime=:runtime, sort_title=:sorttitle, tagline=:tagline
|
|
||||||
WHERE ID = :id;`
|
|
||||||
|
|
||||||
getMovieQueryByImdbID = `
|
getMovieQueryByImdbID = `
|
||||||
SELECT
|
SELECT *
|
||||||
id, imdb_id AS imdbid, title, rating, votes, plot,
|
|
||||||
tmdb_id AS tmdbid, year,
|
|
||||||
original_title AS originaltitle, runtime,
|
|
||||||
sort_title AS sorttitle, tagline,
|
|
||||||
created_at, updated_at
|
|
||||||
FROM movies WHERE imdb_id=$1;`
|
FROM movies WHERE imdb_id=$1;`
|
||||||
|
|
||||||
getMovieQueryByID = `
|
getMovieQueryByID = `
|
||||||
SELECT
|
SELECT *
|
||||||
id, imdb_id AS imdbid, title, rating, votes, plot,
|
|
||||||
tmdb_id AS tmdbid, year,
|
|
||||||
original_title AS originaltitle, runtime,
|
|
||||||
sort_title AS sorttitle, tagline,
|
|
||||||
created_at, updated_at
|
|
||||||
FROM movies WHERE id=$1;`
|
FROM movies WHERE id=$1;`
|
||||||
|
|
||||||
deleteMovieQuery = `DELETE FROM movies WHERE id=$1;`
|
deleteMovieQuery = `DELETE FROM movies WHERE id=$1;`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// MovieDB represents the Movie in the DB
|
||||||
// ErrNotFound error returned when show not found in database
|
type MovieDB struct {
|
||||||
ErrNotFound = fmt.Errorf("Not found")
|
ID string `db:"id"`
|
||||||
)
|
ImdbID string `db:"imdb_id"`
|
||||||
|
TmdbID int `db:"tmdb_id"`
|
||||||
|
Title string `db:"title"`
|
||||||
|
OriginalTitle string `db:"original_title"`
|
||||||
|
SortTitle string `db:"sort_title"`
|
||||||
|
Rating float32 `db:"rating"`
|
||||||
|
Votes int `db:"votes"`
|
||||||
|
Plot string `db:"plot"`
|
||||||
|
Year int `db:"year"`
|
||||||
|
Runtime int `db:"runtime"`
|
||||||
|
Tagline string `db:"tagline"`
|
||||||
|
Genres pq.StringArray `db:"genres"`
|
||||||
|
Created time.Time `db:"created_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMovieDB returns a Movie ready to be put in DB from a
|
||||||
|
// Movie
|
||||||
|
func NewMovieDB(m *Movie) MovieDB {
|
||||||
|
return MovieDB{
|
||||||
|
ID: m.ID,
|
||||||
|
ImdbID: m.ImdbID,
|
||||||
|
Title: m.Title,
|
||||||
|
Rating: m.Rating,
|
||||||
|
Votes: m.Votes,
|
||||||
|
Plot: m.Plot,
|
||||||
|
TmdbID: m.TmdbID,
|
||||||
|
Year: m.Year,
|
||||||
|
OriginalTitle: m.OriginalTitle,
|
||||||
|
Runtime: m.Runtime,
|
||||||
|
SortTitle: m.SortTitle,
|
||||||
|
Tagline: m.Tagline,
|
||||||
|
Genres: m.Genres,
|
||||||
|
Created: m.Created,
|
||||||
|
Updated: m.Updated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillFromDB returns a Movie from a MovieDB extracted from the DB
|
||||||
|
func (m *Movie) FillFromDB(mDB *MovieDB) {
|
||||||
|
m.Created = mDB.Created
|
||||||
|
m.Updated = mDB.Updated
|
||||||
|
m.ID = mDB.ID
|
||||||
|
m.ImdbID = mDB.ImdbID
|
||||||
|
m.Title = mDB.Title
|
||||||
|
m.Rating = mDB.Rating
|
||||||
|
m.Votes = mDB.Votes
|
||||||
|
m.Plot = mDB.Plot
|
||||||
|
m.TmdbID = mDB.TmdbID
|
||||||
|
m.Year = mDB.Year
|
||||||
|
m.OriginalTitle = mDB.OriginalTitle
|
||||||
|
m.Runtime = mDB.Runtime
|
||||||
|
m.Genres = mDB.Genres
|
||||||
|
m.SortTitle = mDB.SortTitle
|
||||||
|
m.Tagline = mDB.Tagline
|
||||||
|
}
|
||||||
|
|
||||||
// Movie represents a movie
|
// Movie represents a movie
|
||||||
type Movie struct {
|
type Movie struct {
|
||||||
@ -55,6 +110,7 @@ type Movie struct {
|
|||||||
PosterURL string `json:"poster_url"`
|
PosterURL string `json:"poster_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns a new Movie with an ImDB id
|
||||||
func New(imdbID string) *Movie {
|
func New(imdbID string) *Movie {
|
||||||
return &Movie{
|
return &Movie{
|
||||||
Movie: polochon.Movie{
|
Movie: polochon.Movie{
|
||||||
@ -65,24 +121,23 @@ func New(imdbID string) *Movie {
|
|||||||
|
|
||||||
// Get returns show details in database from id or imdbid or an error
|
// Get returns show details in database from id or imdbid or an error
|
||||||
func (m *Movie) Get(env *web.Env) error {
|
func (m *Movie) Get(env *web.Env) error {
|
||||||
|
var mDB MovieDB
|
||||||
var err error
|
var err error
|
||||||
if m.ID != "" {
|
if m.ID != "" {
|
||||||
err = env.Database.QueryRowx(getMovieQueryByID, m.ID).StructScan(m)
|
err = env.Database.QueryRowx(getMovieQueryByID, m.ID).StructScan(&mDB)
|
||||||
} else if m.ImdbID != "" {
|
} else if m.ImdbID != "" {
|
||||||
err = env.Database.QueryRowx(getMovieQueryByImdbID, m.ImdbID).StructScan(m)
|
err = env.Database.QueryRowx(getMovieQueryByImdbID, m.ImdbID).StructScan(&mDB)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Can't get movie details, you have to specify an ID or ImdbID")
|
err = fmt.Errorf("Can't get movie details, you have to specify an ID or ImdbID")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "sql: no rows in result set" {
|
|
||||||
return ErrNotFound
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the poster url
|
// Set the poster url
|
||||||
m.PosterURL = m.GetPosterURL(env)
|
m.PosterURL = m.GetPosterURL(env)
|
||||||
|
|
||||||
|
m.FillFromDB(&mDB)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,37 +148,33 @@ func (m *Movie) Get(env *web.Env) error {
|
|||||||
// If force is used, the detailer will be used even if the movie is found in
|
// If force is used, the detailer will be used even if the movie is found in
|
||||||
// database
|
// database
|
||||||
func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
||||||
if len(m.Detailers) == 0 {
|
|
||||||
m.Detailers = env.Config.MovieDetailers
|
|
||||||
}
|
|
||||||
|
|
||||||
log := env.Log.WithFields(logrus.Fields{
|
log := env.Log.WithFields(logrus.Fields{
|
||||||
"imdb_id": m.ImdbID,
|
"imdb_id": m.ImdbID,
|
||||||
"function": "movies.GetDetails",
|
"function": "movies.GetDetails",
|
||||||
})
|
})
|
||||||
|
log.Debugf("getting details")
|
||||||
|
|
||||||
// If the movie is not in db, we should add it, otherwise we should update
|
if len(m.Detailers) == 0 {
|
||||||
// it
|
m.Detailers = env.Config.MovieDetailers
|
||||||
var dbFunc func(db *sqlx.DB) error
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
err = m.Get(env)
|
err = m.Get(env)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
log.Debug("movie found in database")
|
log.Debug("movie found in database")
|
||||||
dbFunc = m.Update
|
|
||||||
if !force {
|
if !force {
|
||||||
|
log.Debug("returning movie from db")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case ErrNotFound:
|
case sql.ErrNoRows:
|
||||||
dbFunc = m.Add
|
|
||||||
log.Debug("movie not found in database")
|
log.Debug("movie not found in database")
|
||||||
default:
|
default:
|
||||||
// Unexpected error
|
// Unexpected error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// so we got ErrNotFound so GetDetails from a detailer
|
// GetDetail
|
||||||
err = m.Movie.GetDetails(env.Log)
|
err = m.Movie.GetDetails(env.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -131,8 +182,9 @@ func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
|||||||
|
|
||||||
log.Debug("got details from detailers")
|
log.Debug("got details from detailers")
|
||||||
|
|
||||||
err = dbFunc(env.Database)
|
err = m.Upsert(env.Database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debug("error while doing db func")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,10 +204,67 @@ func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a movie in the database
|
// GetTorrents retrieves torrents for the movie, first try to get info from db,
|
||||||
func (m *Movie) Add(db *sqlx.DB) error {
|
// if not exists, use polochon.Torrenter and save informations in the database
|
||||||
|
// for future use
|
||||||
|
//
|
||||||
|
// If force is used, the torrenter will be used even if the torrent is found in
|
||||||
|
// database
|
||||||
|
func (m *Movie) GetTorrents(env *web.Env, force bool) error {
|
||||||
|
log := env.Log.WithFields(logrus.Fields{
|
||||||
|
"imdb_id": m.ImdbID,
|
||||||
|
"function": "movies.GetTorrents",
|
||||||
|
})
|
||||||
|
log.Debugf("getting torrents")
|
||||||
|
|
||||||
|
if len(m.Torrenters) == 0 {
|
||||||
|
m.Torrenters = env.Config.MovieTorrenters
|
||||||
|
}
|
||||||
|
|
||||||
|
movieTorrents, err := torrents.GetMovieTorrents(env.Database, m.ImdbID)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
log.Debug("torrents found in database")
|
||||||
|
if !force {
|
||||||
|
log.Debugf("returning %d torrents from db", len(movieTorrents))
|
||||||
|
// Add the torrents to the movie
|
||||||
|
for _, t := range movieTorrents {
|
||||||
|
m.Torrents = append(m.Torrents, t.Torrent)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case sql.ErrNoRows:
|
||||||
|
log.Debug("torrent not found in database")
|
||||||
|
// We'll need to GetTorrents from torrenters
|
||||||
|
default:
|
||||||
|
// Unexpected error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Movie.GetTorrents(env.Log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("got %d torrents from torrenters", len(m.Movie.Torrents))
|
||||||
|
|
||||||
|
for _, t := range m.Movie.Torrents {
|
||||||
|
torrent := torrents.NewMovie(m.ImdbID, t)
|
||||||
|
err = torrent.Upsert(env.Database)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("error while adding torrent", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert a movie in the database
|
||||||
|
func (m *Movie) Upsert(db *sqlx.DB) error {
|
||||||
|
mDB := NewMovieDB(m)
|
||||||
var id string
|
var id string
|
||||||
r, err := db.NamedQuery(addMovieQuery, m)
|
r, err := db.NamedQuery(upsertMovieQuery, mDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -167,12 +276,6 @@ func (m *Movie) Add(db *sqlx.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update a movie in the database
|
|
||||||
func (m *Movie) Update(db *sqlx.DB) error {
|
|
||||||
_, err := db.NamedQuery(updateMovieQuery, m)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete movie from database
|
// Delete movie from database
|
||||||
func (m *Movie) Delete(db *sqlx.DB) error {
|
func (m *Movie) Delete(db *sqlx.DB) error {
|
||||||
r, err := db.Exec(deleteMovieQuery, m.ID)
|
r, err := db.Exec(deleteMovieQuery, m.ID)
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
package sqly
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/csv"
|
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This mainly comes from https://gist.github.com/adharris/4163702
|
|
||||||
|
|
||||||
// StringSlice represents an array of string. The custom type is needed because
|
|
||||||
// pq does not support slices yet..
|
|
||||||
type StringSlice []string
|
|
||||||
|
|
||||||
// for replacing escaped quotes except if it is preceded by a literal backslash
|
|
||||||
// eg "\\" should translate to a quoted element whose value is \
|
|
||||||
|
|
||||||
var quoteEscapeRegex = regexp.MustCompile(`([^\\]([\\]{2})*)\\"`)
|
|
||||||
|
|
||||||
// Scan convert to a slice of strings
|
|
||||||
// http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
|
|
||||||
func (s *StringSlice) Scan(src interface{}) error {
|
|
||||||
asBytes, ok := src.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return error(errors.New("Scan source was not []bytes"))
|
|
||||||
}
|
|
||||||
str := string(asBytes)
|
|
||||||
|
|
||||||
// change quote escapes for csv parser
|
|
||||||
str = quoteEscapeRegex.ReplaceAllString(str, `$1""`)
|
|
||||||
str = strings.Replace(str, `\\`, `\`, -1)
|
|
||||||
// remove braces
|
|
||||||
str = str[1 : len(str)-1]
|
|
||||||
csvReader := csv.NewReader(strings.NewReader(str))
|
|
||||||
|
|
||||||
slice, err := csvReader.Read()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
(*s) = StringSlice(slice)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the Valuer interface
|
|
||||||
func (s StringSlice) Value() (driver.Value, error) {
|
|
||||||
// string escapes.
|
|
||||||
// \ => \\\
|
|
||||||
// " => \"
|
|
||||||
for i, elem := range s {
|
|
||||||
s[i] = `"` + strings.Replace(strings.Replace(elem, `\`, `\\\`, -1), `"`, `\"`, -1) + `"`
|
|
||||||
}
|
|
||||||
return "{" + strings.Join(s, ",") + "}", nil
|
|
||||||
}
|
|
180
src/internal/torrents/torrents.go
Normal file
180
src/internal/torrents/torrents.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package torrents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
upsertMovieTorrentQuery = `
|
||||||
|
INSERT INTO movie_torrents (imdb_id, url, source, quality, upload_user,
|
||||||
|
seeders, leechers)
|
||||||
|
VALUES (:imdb_id, :url, :source, :quality, :upload_user, :seeders,
|
||||||
|
:leechers)
|
||||||
|
ON CONFLICT (imdb_id, quality, source)
|
||||||
|
DO UPDATE SET imdb_id=:imdb_id, url=:url, source=:source, quality=:quality,
|
||||||
|
upload_user=:upload_user, seeders=:seeders, leechers=:leechers
|
||||||
|
RETURNING id;`
|
||||||
|
|
||||||
|
getMovieTorrentQueryByImdbID = `
|
||||||
|
SELECT *
|
||||||
|
FROM movie_torrents WHERE imdb_id=$1;`
|
||||||
|
|
||||||
|
getMovieTorrentQueryByID = `
|
||||||
|
SELECT *
|
||||||
|
FROM movie_torrents WHERE id=$1;`
|
||||||
|
|
||||||
|
deleteMovieTorrentQuery = `DELETE FROM movie_torrents WHERE id=$1;`
|
||||||
|
)
|
||||||
|
|
||||||
|
// MovieTorrent represents a movie torrent
|
||||||
|
type MovieTorrent struct {
|
||||||
|
sqly.BaseModel
|
||||||
|
polochon.Torrent
|
||||||
|
ImdbID string `json:"imdb_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MovieTorrentDB represents the MovieTorrent in the DB
|
||||||
|
type MovieTorrentDB struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
ImdbID string `db:"imdb_id"`
|
||||||
|
URL string `db:"url"`
|
||||||
|
Source string `db:"source"`
|
||||||
|
Quality string `db:"quality"`
|
||||||
|
UploadUser string `db:"upload_user"`
|
||||||
|
Seeders int `db:"seeders"`
|
||||||
|
Leechers int `db:"leechers"`
|
||||||
|
Created time.Time `db:"created_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpisodeTorrent represents an episode torrent
|
||||||
|
type EpisodeTorrent struct {
|
||||||
|
sqly.BaseModel
|
||||||
|
polochon.Torrent
|
||||||
|
ShowImdbID string `json:"show_imdb_id"`
|
||||||
|
Season int `json:"season"`
|
||||||
|
Episode int `json:"episode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMovieFromImDB returns a new MovieTorrent with an ImDB id
|
||||||
|
func NewMovieFromImDB(imdbID string) *MovieTorrent {
|
||||||
|
return &MovieTorrent{
|
||||||
|
ImdbID: imdbID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMovie returns a new MovieTorrent with an ImDB id
|
||||||
|
func NewMovie(imdbID string, poTo polochon.Torrent) *MovieTorrent {
|
||||||
|
return &MovieTorrent{
|
||||||
|
ImdbID: imdbID,
|
||||||
|
Torrent: poTo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns show details in database from id or imdbid or an error
|
||||||
|
func (m *MovieTorrent) Get(db *sqlx.DB) error {
|
||||||
|
var mDB MovieTorrentDB
|
||||||
|
var err error
|
||||||
|
if m.ID != "" {
|
||||||
|
err = db.QueryRowx(getMovieTorrentQueryByID, m.ID).StructScan(&mDB)
|
||||||
|
} else if m.ImdbID != "" {
|
||||||
|
err = db.QueryRowx(getMovieTorrentQueryByImdbID, m.ImdbID).StructScan(&mDB)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("can't get movie torrent details, you have to specify an ID or ImdbID")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.FillFromDB(&mDB)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMovieTorrents returns show details in database from id or imdbid or an error
|
||||||
|
func GetMovieTorrents(db *sqlx.DB, imdbID string) ([]*MovieTorrent, error) {
|
||||||
|
var torrentsDB = []*MovieTorrentDB{}
|
||||||
|
err := db.Select(&torrentsDB, getMovieTorrentQueryByImdbID, imdbID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(torrentsDB) == 0 {
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
var torrents []*MovieTorrent
|
||||||
|
for _, torrentDB := range torrentsDB {
|
||||||
|
movie := NewMovieFromImDB(imdbID)
|
||||||
|
movie.FillFromDB(torrentDB)
|
||||||
|
torrents = append(torrents, movie)
|
||||||
|
}
|
||||||
|
|
||||||
|
return torrents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert a movie torrent in the database
|
||||||
|
func (m *MovieTorrent) Upsert(db *sqlx.DB) error {
|
||||||
|
mDB := NewMovieTorrentDB(m)
|
||||||
|
var id string
|
||||||
|
r, err := db.NamedQuery(upsertMovieTorrentQuery, mDB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for r.Next() {
|
||||||
|
r.Scan(&id)
|
||||||
|
}
|
||||||
|
m.ID = id
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete movie from database
|
||||||
|
func (m *MovieTorrent) Delete(db *sqlx.DB) error {
|
||||||
|
r, err := db.Exec(deleteMovieTorrentQuery, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count, _ := r.RowsAffected()
|
||||||
|
if count != 1 {
|
||||||
|
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMovieTorrentDB returns a MovieTorrent ready to be put in DB from a
|
||||||
|
// MovieTorrent
|
||||||
|
func NewMovieTorrentDB(m *MovieTorrent) MovieTorrentDB {
|
||||||
|
return MovieTorrentDB{
|
||||||
|
ID: m.ID,
|
||||||
|
ImdbID: m.ImdbID,
|
||||||
|
URL: m.URL,
|
||||||
|
Source: m.Source,
|
||||||
|
Quality: string(m.Quality),
|
||||||
|
UploadUser: m.UploadUser,
|
||||||
|
Seeders: m.Seeders,
|
||||||
|
Leechers: m.Leechers,
|
||||||
|
Created: m.Created,
|
||||||
|
Updated: m.Updated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillFromDB will fill a MovieTorrent from a MovieTorrentDB extracted from the DB
|
||||||
|
func (m *MovieTorrent) FillFromDB(mDB *MovieTorrentDB) {
|
||||||
|
q, _ := polochon.StringToQuality(mDB.Quality)
|
||||||
|
m.ImdbID = mDB.ImdbID
|
||||||
|
m.Created = mDB.Created
|
||||||
|
m.Updated = mDB.Updated
|
||||||
|
m.ID = mDB.ID
|
||||||
|
m.URL = mDB.URL
|
||||||
|
m.Source = mDB.Source
|
||||||
|
m.Quality = *q
|
||||||
|
m.UploadUser = mDB.UploadUser
|
||||||
|
m.Seeders = mDB.Seeders
|
||||||
|
m.Leechers = mDB.Leechers
|
||||||
|
}
|
@ -4,9 +4,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/external_medias"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
|
||||||
@ -74,8 +76,9 @@ func main() {
|
|||||||
env.Handle("/users/edit", users.EditHandler).WithRole(users.UserRole)
|
env.Handle("/users/edit", users.EditHandler).WithRole(users.UserRole)
|
||||||
|
|
||||||
env.Handle("/movies/polochon", movies.FromPolochon).WithRole(users.UserRole)
|
env.Handle("/movies/polochon", movies.FromPolochon).WithRole(users.UserRole)
|
||||||
env.Handle("/movies/explore/popular", movies.ExplorePopular).WithRole(users.UserRole)
|
|
||||||
env.Handle("/movies/{id:tt[0-9]+}/get_details", movies.GetDetailsHandler).WithRole(users.UserRole)
|
env.Handle("/movies/{id:tt[0-9]+}/get_details", movies.GetDetailsHandler).WithRole(users.UserRole)
|
||||||
|
env.Handle("/movies/explore", extmedias.Explore)
|
||||||
|
env.Handle("/movies/refresh", extmedias.Refresh)
|
||||||
|
|
||||||
n := negroni.Classic()
|
n := negroni.Classic()
|
||||||
n.Use(authMiddleware)
|
n.Use(authMiddleware)
|
||||||
|
@ -75,7 +75,7 @@ const UserIsAuthenticated = UserAuthWrapper({
|
|||||||
|
|
||||||
// TODO find a better way
|
// TODO find a better way
|
||||||
const MovieListPopular = (props) => (
|
const MovieListPopular = (props) => (
|
||||||
<MovieList {...props} moviesUrl='/movies/explore/popular'/>
|
<MovieList {...props} moviesUrl='/movies/explore'/>
|
||||||
)
|
)
|
||||||
const MovieListPolochon = (props) => (
|
const MovieListPolochon = (props) => (
|
||||||
<MovieList {...props} moviesUrl='/movies/polochon'/>
|
<MovieList {...props} moviesUrl='/movies/polochon'/>
|
||||||
|
@ -130,6 +130,13 @@ class MovieButtons extends React.Component {
|
|||||||
<i className="fa fa-download"></i> Download
|
<i className="fa fa-download"></i> Download
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
{this.props.movie.torrents && this.props.movie.torrents.map(function(torrent, index) {
|
||||||
|
return (
|
||||||
|
<a key={torrent.url} type="button" className="btn btn-primary btn-sm" href={torrent.url}>
|
||||||
|
<i className="fa fa-download"></i> {torrent.quality} Torrent
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
)}
|
||||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||||
<i className="fa fa-external-link"></i> IMDB
|
<i className="fa fa-external-link"></i> IMDB
|
||||||
</a>
|
</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user