welcome page update

This commit is contained in:
Simon Ding
2024-07-05 17:47:07 +08:00
parent 2cdb2b3332
commit c703552267
16 changed files with 516 additions and 86 deletions

View File

@@ -3,9 +3,11 @@ package db
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"polaris/ent" "polaris/ent"
"polaris/ent/downloadclients" "polaris/ent/downloadclients"
"polaris/ent/indexers" "polaris/ent/indexers"
"polaris/ent/series"
"polaris/ent/settings" "polaris/ent/settings"
"polaris/log" "polaris/log"
@@ -58,18 +60,23 @@ func (c *Client) GetLanguage() string {
lang := c.GetSetting(SettingLanguage) lang := c.GetSetting(SettingLanguage)
log.Infof("get application language: %s", lang) log.Infof("get application language: %s", lang)
if lang == "" { if lang == "" {
return "zh_CN" return "zh-CN"
} }
return lang return lang
} }
func (c *Client) AddWatchlist(path string, detail *tmdb.TVDetails) error { func (c *Client) AddWatchlist(path string, detail *tmdb.TVDetails) error {
count := c.ent.Series.Query().Where(series.TmdbID(int(detail.ID))).CountX(context.Background())
if (count > 0) {
return fmt.Errorf("tv series %s already in watchlist", detail.Name)
}
_, err := c.ent.Series.Create(). _, err := c.ent.Series.Create().
SetTmdbID(int(detail.ID)). SetTmdbID(int(detail.ID)).
SetPath(path). SetPath(path).
SetOverview(detail.Overview). SetOverview(detail.Overview).
SetTitle(detail.Name). SetTitle(detail.Name).
SetOriginalName(detail.OriginalName). SetOriginalName(detail.OriginalName).
SetPosterPath(detail.PosterPath).
Save(context.TODO()) Save(context.TODO())
return err return err
} }

View File

@@ -78,11 +78,12 @@ var (
SeriesColumns = []*schema.Column{ SeriesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true}, {Name: "id", Type: field.TypeInt, Increment: true},
{Name: "tmdb_id", Type: field.TypeInt}, {Name: "tmdb_id", Type: field.TypeInt},
{Name: "imdb_id", Type: field.TypeString}, {Name: "imdb_id", Type: field.TypeString, Nullable: true},
{Name: "title", Type: field.TypeString}, {Name: "title", Type: field.TypeString},
{Name: "original_name", Type: field.TypeString}, {Name: "original_name", Type: field.TypeString},
{Name: "overview", Type: field.TypeString}, {Name: "overview", Type: field.TypeString},
{Name: "path", Type: field.TypeString}, {Name: "path", Type: field.TypeString},
{Name: "poster_path", Type: field.TypeString, Nullable: true},
} }
// SeriesTable holds the schema information for the "series" table. // SeriesTable holds the schema information for the "series" table.
SeriesTable = &schema.Table{ SeriesTable = &schema.Table{

View File

@@ -2749,6 +2749,7 @@ type SeriesMutation struct {
original_name *string original_name *string
overview *string overview *string
_path *string _path *string
poster_path *string
clearedFields map[string]struct{} clearedFields map[string]struct{}
done bool done bool
oldValue func(context.Context) (*Series, error) oldValue func(context.Context) (*Series, error)
@@ -2940,9 +2941,22 @@ func (m *SeriesMutation) OldImdbID(ctx context.Context) (v string, err error) {
return oldValue.ImdbID, nil return oldValue.ImdbID, nil
} }
// ClearImdbID clears the value of the "imdb_id" field.
func (m *SeriesMutation) ClearImdbID() {
m.imdb_id = nil
m.clearedFields[series.FieldImdbID] = struct{}{}
}
// ImdbIDCleared returns if the "imdb_id" field was cleared in this mutation.
func (m *SeriesMutation) ImdbIDCleared() bool {
_, ok := m.clearedFields[series.FieldImdbID]
return ok
}
// ResetImdbID resets all changes to the "imdb_id" field. // ResetImdbID resets all changes to the "imdb_id" field.
func (m *SeriesMutation) ResetImdbID() { func (m *SeriesMutation) ResetImdbID() {
m.imdb_id = nil m.imdb_id = nil
delete(m.clearedFields, series.FieldImdbID)
} }
// SetTitle sets the "title" field. // SetTitle sets the "title" field.
@@ -3089,6 +3103,55 @@ func (m *SeriesMutation) ResetPath() {
m._path = nil m._path = nil
} }
// SetPosterPath sets the "poster_path" field.
func (m *SeriesMutation) SetPosterPath(s string) {
m.poster_path = &s
}
// PosterPath returns the value of the "poster_path" field in the mutation.
func (m *SeriesMutation) PosterPath() (r string, exists bool) {
v := m.poster_path
if v == nil {
return
}
return *v, true
}
// OldPosterPath returns the old "poster_path" field's value of the Series entity.
// If the Series object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *SeriesMutation) OldPosterPath(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldPosterPath is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldPosterPath requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldPosterPath: %w", err)
}
return oldValue.PosterPath, nil
}
// ClearPosterPath clears the value of the "poster_path" field.
func (m *SeriesMutation) ClearPosterPath() {
m.poster_path = nil
m.clearedFields[series.FieldPosterPath] = struct{}{}
}
// PosterPathCleared returns if the "poster_path" field was cleared in this mutation.
func (m *SeriesMutation) PosterPathCleared() bool {
_, ok := m.clearedFields[series.FieldPosterPath]
return ok
}
// ResetPosterPath resets all changes to the "poster_path" field.
func (m *SeriesMutation) ResetPosterPath() {
m.poster_path = nil
delete(m.clearedFields, series.FieldPosterPath)
}
// Where appends a list predicates to the SeriesMutation builder. // Where appends a list predicates to the SeriesMutation builder.
func (m *SeriesMutation) Where(ps ...predicate.Series) { func (m *SeriesMutation) Where(ps ...predicate.Series) {
m.predicates = append(m.predicates, ps...) m.predicates = append(m.predicates, ps...)
@@ -3123,7 +3186,7 @@ func (m *SeriesMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call // order to get all numeric fields that were incremented/decremented, call
// AddedFields(). // AddedFields().
func (m *SeriesMutation) Fields() []string { func (m *SeriesMutation) Fields() []string {
fields := make([]string, 0, 6) fields := make([]string, 0, 7)
if m.tmdb_id != nil { if m.tmdb_id != nil {
fields = append(fields, series.FieldTmdbID) fields = append(fields, series.FieldTmdbID)
} }
@@ -3142,6 +3205,9 @@ func (m *SeriesMutation) Fields() []string {
if m._path != nil { if m._path != nil {
fields = append(fields, series.FieldPath) fields = append(fields, series.FieldPath)
} }
if m.poster_path != nil {
fields = append(fields, series.FieldPosterPath)
}
return fields return fields
} }
@@ -3162,6 +3228,8 @@ func (m *SeriesMutation) Field(name string) (ent.Value, bool) {
return m.Overview() return m.Overview()
case series.FieldPath: case series.FieldPath:
return m.Path() return m.Path()
case series.FieldPosterPath:
return m.PosterPath()
} }
return nil, false return nil, false
} }
@@ -3183,6 +3251,8 @@ func (m *SeriesMutation) OldField(ctx context.Context, name string) (ent.Value,
return m.OldOverview(ctx) return m.OldOverview(ctx)
case series.FieldPath: case series.FieldPath:
return m.OldPath(ctx) return m.OldPath(ctx)
case series.FieldPosterPath:
return m.OldPosterPath(ctx)
} }
return nil, fmt.Errorf("unknown Series field %s", name) return nil, fmt.Errorf("unknown Series field %s", name)
} }
@@ -3234,6 +3304,13 @@ func (m *SeriesMutation) SetField(name string, value ent.Value) error {
} }
m.SetPath(v) m.SetPath(v)
return nil return nil
case series.FieldPosterPath:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetPosterPath(v)
return nil
} }
return fmt.Errorf("unknown Series field %s", name) return fmt.Errorf("unknown Series field %s", name)
} }
@@ -3278,7 +3355,14 @@ func (m *SeriesMutation) AddField(name string, value ent.Value) error {
// ClearedFields returns all nullable fields that were cleared during this // ClearedFields returns all nullable fields that were cleared during this
// mutation. // mutation.
func (m *SeriesMutation) ClearedFields() []string { func (m *SeriesMutation) ClearedFields() []string {
return nil var fields []string
if m.FieldCleared(series.FieldImdbID) {
fields = append(fields, series.FieldImdbID)
}
if m.FieldCleared(series.FieldPosterPath) {
fields = append(fields, series.FieldPosterPath)
}
return fields
} }
// FieldCleared returns a boolean indicating if a field with the given name was // FieldCleared returns a boolean indicating if a field with the given name was
@@ -3291,6 +3375,14 @@ func (m *SeriesMutation) FieldCleared(name string) bool {
// ClearField clears the value of the field with the given name. It returns an // ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema. // error if the field is not defined in the schema.
func (m *SeriesMutation) ClearField(name string) error { func (m *SeriesMutation) ClearField(name string) error {
switch name {
case series.FieldImdbID:
m.ClearImdbID()
return nil
case series.FieldPosterPath:
m.ClearPosterPath()
return nil
}
return fmt.Errorf("unknown Series nullable field %s", name) return fmt.Errorf("unknown Series nullable field %s", name)
} }
@@ -3316,6 +3408,9 @@ func (m *SeriesMutation) ResetField(name string) error {
case series.FieldPath: case series.FieldPath:
m.ResetPath() m.ResetPath()
return nil return nil
case series.FieldPosterPath:
m.ResetPosterPath()
return nil
} }
return fmt.Errorf("unknown Series field %s", name) return fmt.Errorf("unknown Series field %s", name)
} }

View File

@@ -14,11 +14,12 @@ type Series struct {
func (Series) Fields() []ent.Field { func (Series) Fields() []ent.Field {
return []ent.Field{ return []ent.Field{
field.Int("tmdb_id"), field.Int("tmdb_id"),
field.String("imdb_id"), field.String("imdb_id").Optional(),
field.String("title"), field.String("title"),
field.String("original_name"), field.String("original_name"),
field.String("overview"), field.String("overview"),
field.String("path"), field.String("path"),
field.String("poster_path").Optional(),
} }
} }

View File

@@ -27,7 +27,9 @@ type Series struct {
// Overview holds the value of the "overview" field. // Overview holds the value of the "overview" field.
Overview string `json:"overview,omitempty"` Overview string `json:"overview,omitempty"`
// Path holds the value of the "path" field. // Path holds the value of the "path" field.
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
// PosterPath holds the value of the "poster_path" field.
PosterPath string `json:"poster_path,omitempty"`
selectValues sql.SelectValues selectValues sql.SelectValues
} }
@@ -38,7 +40,7 @@ func (*Series) scanValues(columns []string) ([]any, error) {
switch columns[i] { switch columns[i] {
case series.FieldID, series.FieldTmdbID: case series.FieldID, series.FieldTmdbID:
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
case series.FieldImdbID, series.FieldTitle, series.FieldOriginalName, series.FieldOverview, series.FieldPath: case series.FieldImdbID, series.FieldTitle, series.FieldOriginalName, series.FieldOverview, series.FieldPath, series.FieldPosterPath:
values[i] = new(sql.NullString) values[i] = new(sql.NullString)
default: default:
values[i] = new(sql.UnknownType) values[i] = new(sql.UnknownType)
@@ -97,6 +99,12 @@ func (s *Series) assignValues(columns []string, values []any) error {
} else if value.Valid { } else if value.Valid {
s.Path = value.String s.Path = value.String
} }
case series.FieldPosterPath:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field poster_path", values[i])
} else if value.Valid {
s.PosterPath = value.String
}
default: default:
s.selectValues.Set(columns[i], values[i]) s.selectValues.Set(columns[i], values[i])
} }
@@ -150,6 +158,9 @@ func (s *Series) String() string {
builder.WriteString(", ") builder.WriteString(", ")
builder.WriteString("path=") builder.WriteString("path=")
builder.WriteString(s.Path) builder.WriteString(s.Path)
builder.WriteString(", ")
builder.WriteString("poster_path=")
builder.WriteString(s.PosterPath)
builder.WriteByte(')') builder.WriteByte(')')
return builder.String() return builder.String()
} }

View File

@@ -23,6 +23,8 @@ const (
FieldOverview = "overview" FieldOverview = "overview"
// FieldPath holds the string denoting the path field in the database. // FieldPath holds the string denoting the path field in the database.
FieldPath = "path" FieldPath = "path"
// FieldPosterPath holds the string denoting the poster_path field in the database.
FieldPosterPath = "poster_path"
// Table holds the table name of the series in the database. // Table holds the table name of the series in the database.
Table = "series" Table = "series"
) )
@@ -36,6 +38,7 @@ var Columns = []string{
FieldOriginalName, FieldOriginalName,
FieldOverview, FieldOverview,
FieldPath, FieldPath,
FieldPosterPath,
} }
// ValidColumn reports if the column name is valid (part of the table columns). // ValidColumn reports if the column name is valid (part of the table columns).
@@ -85,3 +88,8 @@ func ByOverview(opts ...sql.OrderTermOption) OrderOption {
func ByPath(opts ...sql.OrderTermOption) OrderOption { func ByPath(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPath, opts...).ToFunc() return sql.OrderByField(FieldPath, opts...).ToFunc()
} }
// ByPosterPath orders the results by the poster_path field.
func ByPosterPath(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPosterPath, opts...).ToFunc()
}

View File

@@ -83,6 +83,11 @@ func Path(v string) predicate.Series {
return predicate.Series(sql.FieldEQ(FieldPath, v)) return predicate.Series(sql.FieldEQ(FieldPath, v))
} }
// PosterPath applies equality check predicate on the "poster_path" field. It's identical to PosterPathEQ.
func PosterPath(v string) predicate.Series {
return predicate.Series(sql.FieldEQ(FieldPosterPath, v))
}
// TmdbIDEQ applies the EQ predicate on the "tmdb_id" field. // TmdbIDEQ applies the EQ predicate on the "tmdb_id" field.
func TmdbIDEQ(v int) predicate.Series { func TmdbIDEQ(v int) predicate.Series {
return predicate.Series(sql.FieldEQ(FieldTmdbID, v)) return predicate.Series(sql.FieldEQ(FieldTmdbID, v))
@@ -178,6 +183,16 @@ func ImdbIDHasSuffix(v string) predicate.Series {
return predicate.Series(sql.FieldHasSuffix(FieldImdbID, v)) return predicate.Series(sql.FieldHasSuffix(FieldImdbID, v))
} }
// ImdbIDIsNil applies the IsNil predicate on the "imdb_id" field.
func ImdbIDIsNil() predicate.Series {
return predicate.Series(sql.FieldIsNull(FieldImdbID))
}
// ImdbIDNotNil applies the NotNil predicate on the "imdb_id" field.
func ImdbIDNotNil() predicate.Series {
return predicate.Series(sql.FieldNotNull(FieldImdbID))
}
// ImdbIDEqualFold applies the EqualFold predicate on the "imdb_id" field. // ImdbIDEqualFold applies the EqualFold predicate on the "imdb_id" field.
func ImdbIDEqualFold(v string) predicate.Series { func ImdbIDEqualFold(v string) predicate.Series {
return predicate.Series(sql.FieldEqualFold(FieldImdbID, v)) return predicate.Series(sql.FieldEqualFold(FieldImdbID, v))
@@ -448,6 +463,81 @@ func PathContainsFold(v string) predicate.Series {
return predicate.Series(sql.FieldContainsFold(FieldPath, v)) return predicate.Series(sql.FieldContainsFold(FieldPath, v))
} }
// PosterPathEQ applies the EQ predicate on the "poster_path" field.
func PosterPathEQ(v string) predicate.Series {
return predicate.Series(sql.FieldEQ(FieldPosterPath, v))
}
// PosterPathNEQ applies the NEQ predicate on the "poster_path" field.
func PosterPathNEQ(v string) predicate.Series {
return predicate.Series(sql.FieldNEQ(FieldPosterPath, v))
}
// PosterPathIn applies the In predicate on the "poster_path" field.
func PosterPathIn(vs ...string) predicate.Series {
return predicate.Series(sql.FieldIn(FieldPosterPath, vs...))
}
// PosterPathNotIn applies the NotIn predicate on the "poster_path" field.
func PosterPathNotIn(vs ...string) predicate.Series {
return predicate.Series(sql.FieldNotIn(FieldPosterPath, vs...))
}
// PosterPathGT applies the GT predicate on the "poster_path" field.
func PosterPathGT(v string) predicate.Series {
return predicate.Series(sql.FieldGT(FieldPosterPath, v))
}
// PosterPathGTE applies the GTE predicate on the "poster_path" field.
func PosterPathGTE(v string) predicate.Series {
return predicate.Series(sql.FieldGTE(FieldPosterPath, v))
}
// PosterPathLT applies the LT predicate on the "poster_path" field.
func PosterPathLT(v string) predicate.Series {
return predicate.Series(sql.FieldLT(FieldPosterPath, v))
}
// PosterPathLTE applies the LTE predicate on the "poster_path" field.
func PosterPathLTE(v string) predicate.Series {
return predicate.Series(sql.FieldLTE(FieldPosterPath, v))
}
// PosterPathContains applies the Contains predicate on the "poster_path" field.
func PosterPathContains(v string) predicate.Series {
return predicate.Series(sql.FieldContains(FieldPosterPath, v))
}
// PosterPathHasPrefix applies the HasPrefix predicate on the "poster_path" field.
func PosterPathHasPrefix(v string) predicate.Series {
return predicate.Series(sql.FieldHasPrefix(FieldPosterPath, v))
}
// PosterPathHasSuffix applies the HasSuffix predicate on the "poster_path" field.
func PosterPathHasSuffix(v string) predicate.Series {
return predicate.Series(sql.FieldHasSuffix(FieldPosterPath, v))
}
// PosterPathIsNil applies the IsNil predicate on the "poster_path" field.
func PosterPathIsNil() predicate.Series {
return predicate.Series(sql.FieldIsNull(FieldPosterPath))
}
// PosterPathNotNil applies the NotNil predicate on the "poster_path" field.
func PosterPathNotNil() predicate.Series {
return predicate.Series(sql.FieldNotNull(FieldPosterPath))
}
// PosterPathEqualFold applies the EqualFold predicate on the "poster_path" field.
func PosterPathEqualFold(v string) predicate.Series {
return predicate.Series(sql.FieldEqualFold(FieldPosterPath, v))
}
// PosterPathContainsFold applies the ContainsFold predicate on the "poster_path" field.
func PosterPathContainsFold(v string) predicate.Series {
return predicate.Series(sql.FieldContainsFold(FieldPosterPath, v))
}
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.Series) predicate.Series { func And(predicates ...predicate.Series) predicate.Series {
return predicate.Series(sql.AndPredicates(predicates...)) return predicate.Series(sql.AndPredicates(predicates...))

View File

@@ -31,6 +31,14 @@ func (sc *SeriesCreate) SetImdbID(s string) *SeriesCreate {
return sc return sc
} }
// SetNillableImdbID sets the "imdb_id" field if the given value is not nil.
func (sc *SeriesCreate) SetNillableImdbID(s *string) *SeriesCreate {
if s != nil {
sc.SetImdbID(*s)
}
return sc
}
// SetTitle sets the "title" field. // SetTitle sets the "title" field.
func (sc *SeriesCreate) SetTitle(s string) *SeriesCreate { func (sc *SeriesCreate) SetTitle(s string) *SeriesCreate {
sc.mutation.SetTitle(s) sc.mutation.SetTitle(s)
@@ -55,6 +63,20 @@ func (sc *SeriesCreate) SetPath(s string) *SeriesCreate {
return sc return sc
} }
// SetPosterPath sets the "poster_path" field.
func (sc *SeriesCreate) SetPosterPath(s string) *SeriesCreate {
sc.mutation.SetPosterPath(s)
return sc
}
// SetNillablePosterPath sets the "poster_path" field if the given value is not nil.
func (sc *SeriesCreate) SetNillablePosterPath(s *string) *SeriesCreate {
if s != nil {
sc.SetPosterPath(*s)
}
return sc
}
// Mutation returns the SeriesMutation object of the builder. // Mutation returns the SeriesMutation object of the builder.
func (sc *SeriesCreate) Mutation() *SeriesMutation { func (sc *SeriesCreate) Mutation() *SeriesMutation {
return sc.mutation return sc.mutation
@@ -92,9 +114,6 @@ func (sc *SeriesCreate) check() error {
if _, ok := sc.mutation.TmdbID(); !ok { if _, ok := sc.mutation.TmdbID(); !ok {
return &ValidationError{Name: "tmdb_id", err: errors.New(`ent: missing required field "Series.tmdb_id"`)} return &ValidationError{Name: "tmdb_id", err: errors.New(`ent: missing required field "Series.tmdb_id"`)}
} }
if _, ok := sc.mutation.ImdbID(); !ok {
return &ValidationError{Name: "imdb_id", err: errors.New(`ent: missing required field "Series.imdb_id"`)}
}
if _, ok := sc.mutation.Title(); !ok { if _, ok := sc.mutation.Title(); !ok {
return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Series.title"`)} return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Series.title"`)}
} }
@@ -157,6 +176,10 @@ func (sc *SeriesCreate) createSpec() (*Series, *sqlgraph.CreateSpec) {
_spec.SetField(series.FieldPath, field.TypeString, value) _spec.SetField(series.FieldPath, field.TypeString, value)
_node.Path = value _node.Path = value
} }
if value, ok := sc.mutation.PosterPath(); ok {
_spec.SetField(series.FieldPosterPath, field.TypeString, value)
_node.PosterPath = value
}
return _node, _spec return _node, _spec
} }

View File

@@ -62,6 +62,12 @@ func (su *SeriesUpdate) SetNillableImdbID(s *string) *SeriesUpdate {
return su return su
} }
// ClearImdbID clears the value of the "imdb_id" field.
func (su *SeriesUpdate) ClearImdbID() *SeriesUpdate {
su.mutation.ClearImdbID()
return su
}
// SetTitle sets the "title" field. // SetTitle sets the "title" field.
func (su *SeriesUpdate) SetTitle(s string) *SeriesUpdate { func (su *SeriesUpdate) SetTitle(s string) *SeriesUpdate {
su.mutation.SetTitle(s) su.mutation.SetTitle(s)
@@ -118,6 +124,26 @@ func (su *SeriesUpdate) SetNillablePath(s *string) *SeriesUpdate {
return su return su
} }
// SetPosterPath sets the "poster_path" field.
func (su *SeriesUpdate) SetPosterPath(s string) *SeriesUpdate {
su.mutation.SetPosterPath(s)
return su
}
// SetNillablePosterPath sets the "poster_path" field if the given value is not nil.
func (su *SeriesUpdate) SetNillablePosterPath(s *string) *SeriesUpdate {
if s != nil {
su.SetPosterPath(*s)
}
return su
}
// ClearPosterPath clears the value of the "poster_path" field.
func (su *SeriesUpdate) ClearPosterPath() *SeriesUpdate {
su.mutation.ClearPosterPath()
return su
}
// Mutation returns the SeriesMutation object of the builder. // Mutation returns the SeriesMutation object of the builder.
func (su *SeriesUpdate) Mutation() *SeriesMutation { func (su *SeriesUpdate) Mutation() *SeriesMutation {
return su.mutation return su.mutation
@@ -168,6 +194,9 @@ func (su *SeriesUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := su.mutation.ImdbID(); ok { if value, ok := su.mutation.ImdbID(); ok {
_spec.SetField(series.FieldImdbID, field.TypeString, value) _spec.SetField(series.FieldImdbID, field.TypeString, value)
} }
if su.mutation.ImdbIDCleared() {
_spec.ClearField(series.FieldImdbID, field.TypeString)
}
if value, ok := su.mutation.Title(); ok { if value, ok := su.mutation.Title(); ok {
_spec.SetField(series.FieldTitle, field.TypeString, value) _spec.SetField(series.FieldTitle, field.TypeString, value)
} }
@@ -180,6 +209,12 @@ func (su *SeriesUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := su.mutation.Path(); ok { if value, ok := su.mutation.Path(); ok {
_spec.SetField(series.FieldPath, field.TypeString, value) _spec.SetField(series.FieldPath, field.TypeString, value)
} }
if value, ok := su.mutation.PosterPath(); ok {
_spec.SetField(series.FieldPosterPath, field.TypeString, value)
}
if su.mutation.PosterPathCleared() {
_spec.ClearField(series.FieldPosterPath, field.TypeString)
}
if n, err = sqlgraph.UpdateNodes(ctx, su.driver, _spec); err != nil { if n, err = sqlgraph.UpdateNodes(ctx, su.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{series.Label} err = &NotFoundError{series.Label}
@@ -235,6 +270,12 @@ func (suo *SeriesUpdateOne) SetNillableImdbID(s *string) *SeriesUpdateOne {
return suo return suo
} }
// ClearImdbID clears the value of the "imdb_id" field.
func (suo *SeriesUpdateOne) ClearImdbID() *SeriesUpdateOne {
suo.mutation.ClearImdbID()
return suo
}
// SetTitle sets the "title" field. // SetTitle sets the "title" field.
func (suo *SeriesUpdateOne) SetTitle(s string) *SeriesUpdateOne { func (suo *SeriesUpdateOne) SetTitle(s string) *SeriesUpdateOne {
suo.mutation.SetTitle(s) suo.mutation.SetTitle(s)
@@ -291,6 +332,26 @@ func (suo *SeriesUpdateOne) SetNillablePath(s *string) *SeriesUpdateOne {
return suo return suo
} }
// SetPosterPath sets the "poster_path" field.
func (suo *SeriesUpdateOne) SetPosterPath(s string) *SeriesUpdateOne {
suo.mutation.SetPosterPath(s)
return suo
}
// SetNillablePosterPath sets the "poster_path" field if the given value is not nil.
func (suo *SeriesUpdateOne) SetNillablePosterPath(s *string) *SeriesUpdateOne {
if s != nil {
suo.SetPosterPath(*s)
}
return suo
}
// ClearPosterPath clears the value of the "poster_path" field.
func (suo *SeriesUpdateOne) ClearPosterPath() *SeriesUpdateOne {
suo.mutation.ClearPosterPath()
return suo
}
// Mutation returns the SeriesMutation object of the builder. // Mutation returns the SeriesMutation object of the builder.
func (suo *SeriesUpdateOne) Mutation() *SeriesMutation { func (suo *SeriesUpdateOne) Mutation() *SeriesMutation {
return suo.mutation return suo.mutation
@@ -371,6 +432,9 @@ func (suo *SeriesUpdateOne) sqlSave(ctx context.Context) (_node *Series, err err
if value, ok := suo.mutation.ImdbID(); ok { if value, ok := suo.mutation.ImdbID(); ok {
_spec.SetField(series.FieldImdbID, field.TypeString, value) _spec.SetField(series.FieldImdbID, field.TypeString, value)
} }
if suo.mutation.ImdbIDCleared() {
_spec.ClearField(series.FieldImdbID, field.TypeString)
}
if value, ok := suo.mutation.Title(); ok { if value, ok := suo.mutation.Title(); ok {
_spec.SetField(series.FieldTitle, field.TypeString, value) _spec.SetField(series.FieldTitle, field.TypeString, value)
} }
@@ -383,6 +447,12 @@ func (suo *SeriesUpdateOne) sqlSave(ctx context.Context) (_node *Series, err err
if value, ok := suo.mutation.Path(); ok { if value, ok := suo.mutation.Path(); ok {
_spec.SetField(series.FieldPath, field.TypeString, value) _spec.SetField(series.FieldPath, field.TypeString, value)
} }
if value, ok := suo.mutation.PosterPath(); ok {
_spec.SetField(series.FieldPosterPath, field.TypeString, value)
}
if suo.mutation.PosterPathCleared() {
_spec.ClearField(series.FieldPosterPath, field.TypeString)
}
_node = &Series{config: suo.config} _node = &Series{config: suo.config}
_spec.Assign = _node.assignValues _spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues _spec.ScanValues = _node.scanValues

View File

@@ -1,6 +1,8 @@
package tmdb package tmdb
import ( import (
"polaris/log"
tmdb "github.com/cyruzin/golang-tmdb" tmdb "github.com/cyruzin/golang-tmdb"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -23,6 +25,7 @@ func NewClient(apiKey string) (*Client, error) {
} }
func (c *Client) GetTvDetails(id int, language string) (*tmdb.TVDetails, error) { func (c *Client) GetTvDetails(id int, language string) (*tmdb.TVDetails, error) {
log.Infof("tv id %d, language %s", id, language)
language = wrapLanguage(language) language = wrapLanguage(language)
d, err := c.tmdbClient.GetTVDetails(id, withLangOption(language)) d, err := c.tmdbClient.GetTVDetails(id, withLangOption(language))
return d, err return d, err

View File

@@ -12,6 +12,7 @@ func HttpHandler(f func(*gin.Context) (interface{}, error)) gin.HandlerFunc {
r, err := f(ctx) r, err := f(ctx)
if err != nil { if err != nil {
log.Errorf("url %v return error: %v", ctx.Request.URL, err)
ctx.JSON(200, Response{ ctx.JSON(200, Response{
Code: 1, Code: 1,
Message: fmt.Sprintf("%v", err), Message: fmt.Sprintf("%v", err),

View File

@@ -31,17 +31,19 @@ type addWatchlistIn struct {
func (s *Server) AddWatchlist(c *gin.Context) (interface{}, error) { func (s *Server) AddWatchlist(c *gin.Context) (interface{}, error) {
var in addWatchlistIn var in addWatchlistIn
if err := c.ShouldBindQuery(&in); err != nil { if err := c.ShouldBindJSON(&in); err != nil {
return nil, errors.Wrap(err, "bind query") return nil, errors.Wrap(err, "bind query")
} }
detail, err := s.MustTMDB().GetTvDetails(in.ID, s.language) detail, err := s.MustTMDB().GetTvDetails(in.ID, s.language)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "get tv detail") return nil, errors.Wrap(err, "get tv detail")
} }
log.Infof("find detail for tv id %d: %v", in.ID, detail)
if err := s.db.AddWatchlist(in.RootFolder, detail); err != nil { if err := s.db.AddWatchlist(in.RootFolder, detail); err != nil {
return nil, errors.Wrap(err, "add to list") return nil, errors.Wrap(err, "add to list")
} }
log.Infof("save watchlist success: %s", detail.Name)
for _, season := range detail.Seasons { for _, season := range detail.Seasons {
seasonId := season.SeasonNumber seasonId := season.SeasonNumber

View File

@@ -2,6 +2,7 @@ class APIs {
static const _baseUrl = "http://127.0.0.1:8080"; static const _baseUrl = "http://127.0.0.1:8080";
static const searchUrl = "$_baseUrl/api/v1/tv/search"; static const searchUrl = "$_baseUrl/api/v1/tv/search";
static const settingsUrl = "$_baseUrl/api/v1/setting/do"; static const settingsUrl = "$_baseUrl/api/v1/setting/do";
static const watchlistUrl = "$_baseUrl/api/v1/tv/watchlist";
static const tmdbImgBaseUrl = "https://image.tmdb.org/t/p/w500/"; static const tmdbImgBaseUrl = "https://image.tmdb.org/t/p/w500/";

View File

@@ -42,7 +42,7 @@ class _SearchPageState extends State<SearchPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var cards = List<Widget>.empty(growable: true); var cards = List<Widget>.empty(growable: true);
for (final item in list) { for (final item in list) {
var m = item as Map<String, dynamic>; var m = SearchResult.fromJson(item);
cards.add(Card( cards.add(Card(
margin: const EdgeInsets.all(4), margin: const EdgeInsets.all(4),
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
@@ -50,7 +50,7 @@ class _SearchPageState extends State<SearchPage> {
//splashColor: Colors.blue.withAlpha(30), //splashColor: Colors.blue.withAlpha(30),
onTap: () { onTap: () {
//showDialog(context: context, builder: builder) //showDialog(context: context, builder: builder)
debugPrint('Card tapped.'); _showSubmitDialog(context, m);
}, },
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
@@ -59,7 +59,7 @@ class _SearchPageState extends State<SearchPage> {
width: 150, width: 150,
height: 200, height: 200,
child: Image.network( child: Image.network(
APIs.tmdbImgBaseUrl + m["poster_path"], APIs.tmdbImgBaseUrl + m.posterPath!,
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
), ),
@@ -69,12 +69,12 @@ class _SearchPageState extends State<SearchPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
m["name"], m.name!,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.bold), fontSize: 14, fontWeight: FontWeight.bold),
), ),
const Text(""), const Text(""),
Text(m["overview"]) Text(m.overview!)
], ],
), ),
) )
@@ -83,25 +83,64 @@ class _SearchPageState extends State<SearchPage> {
))); )));
} }
return Expanded( return Column(
child: Column( children: [
children: [ TextField(
TextField( autofocus: true,
autofocus: true, onSubmitted: (value) => _queryResults(context, value),
onSubmitted: (value) => _queryResults(context,value), decoration: const InputDecoration(
decoration: const InputDecoration( labelText: "搜索",
labelText: "搜索", hintText: "搜索剧集名称",
hintText: "搜索剧集名称", prefixIcon: Icon(Icons.search)),
prefixIcon: Icon(Icons.search)), ),
), Expanded(
Expanded( child: ListView(
child: ListView( children: cards,
children: cards, ))
)) ],
],
),
); );
} }
Future<void> _showSubmitDialog(BuildContext context, SearchResult item) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('添加剧集'),
content: Text("是否添加剧集: ${item.name}"),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('取消'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('确定'),
onPressed: () {
_submit2Watchlist(context, item.id!);
Navigator.of(context).pop();
},
),
],
);
});
}
void _submit2Watchlist(BuildContext context, int id) async {
var resp = await Dio()
.post(APIs.watchlistUrl, data: {"id": id, "folder": "/downloads"});
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0 && context.mounted) {
Utils.showAlertDialog(context, sp.message);
}
}
} }
class SearchBarApp extends StatefulWidget { class SearchBarApp extends StatefulWidget {
@@ -142,3 +181,50 @@ class _SearchBarAppState extends State<SearchBarApp> {
}); });
} }
} }
class SearchResult {
String? originalName;
int? id;
String? name;
int? voteCount;
double? voteAverage;
String? posterPath;
String? firstAirDate;
double? popularity;
List<int>? genreIds;
String? originalLanguage;
String? backdropPath;
String? overview;
List<String>? originCountry;
SearchResult(
{this.originalName,
this.id,
this.name,
this.voteCount,
this.voteAverage,
this.posterPath,
this.firstAirDate,
this.popularity,
this.genreIds,
this.originalLanguage,
this.backdropPath,
this.overview,
this.originCountry});
SearchResult.fromJson(Map<String, dynamic> json) {
originalName = json['original_name'];
id = json['id'];
name = json['name'];
voteCount = json['vote_count'];
voteAverage = json['vote_average'];
posterPath = json['poster_path'];
firstAirDate = json['first_air_date'];
popularity = json['popularity'];
genreIds = json['genre_ids'].cast<int>();
originalLanguage = json['original_language'];
backdropPath = json['backdrop_path'];
overview = json['overview'];
originCountry = json['origin_country'].cast<String>();
}
}

View File

@@ -6,6 +6,8 @@ import 'package:ui/utils.dart';
class SystemSettingsPage extends StatefulWidget { class SystemSettingsPage extends StatefulWidget {
static const route = "/systemsettings"; static const route = "/systemsettings";
const SystemSettingsPage({super.key});
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _SystemSettingsPageState(); return _SystemSettingsPageState();
@@ -19,8 +21,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_handleRefresh(); _handleRefresh();
return Expanded( return Container(
child: Container(
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0), padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: _handleRefresh, onRefresh: _handleRefresh,
@@ -63,7 +64,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
], ],
), ),
)), )),
)); );
} }
Future<void> _handleRefresh() async { Future<void> _handleRefresh() async {

View File

@@ -1,5 +1,7 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:ui/APIs.dart'; import 'package:ui/APIs.dart';
import 'package:ui/server_response.dart';
class WelcomePage extends StatefulWidget { class WelcomePage extends StatefulWidget {
const WelcomePage({super.key}); const WelcomePage({super.key});
@@ -16,57 +18,85 @@ class _WeclomePageState extends State<WelcomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var cards = List<Widget>.empty(growable: true); _onRefresh();
for (final item in favList) { return GridView.builder(
var m = item as Map<String, dynamic>; itemCount: favList.length,
cards.add(Card( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
margin: const EdgeInsets.all(4), crossAxisCount: 4),
clipBehavior: Clip.hardEdge, itemBuilder: (context, i) {
child: InkWell( var item = TvSeries.fromJson(favList[i]);
//splashColor: Colors.blue.withAlpha(30), return Container(
onTap: () { child: Card(
//showDialog(context: context, builder: builder) margin: const EdgeInsets.all(4),
debugPrint('Card tapped.'); clipBehavior: Clip.hardEdge,
}, child: InkWell(
child: Row( //splashColor: Colors.blue.withAlpha(30),
children: <Widget>[ onTap: () {
Flexible( //showDialog(context: context, builder: builder)
child: SizedBox( },
width: 150, child: Column(
height: 200, children: <Widget>[
child: Image.network( Flexible(
APIs.tmdbImgBaseUrl + m["poster_path"], child: SizedBox(
fit: BoxFit.contain, width: 300,
), height: 600,
), child: Image.network(
), APIs.tmdbImgBaseUrl + item.posterPath!,
Flexible( fit: BoxFit.contain,
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, ),
children: [ ),
Text( Flexible(
m["name"], child: Text(
style: const TextStyle( item.title!,
fontSize: 14, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.bold),
),
)
],
), ),
const Text(""), )),
Text(m["overview"]) );
], });
),
)
],
),
)));
}
return Expanded(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: Expanded(
child: ListView(
children: cards,
))));
} }
Future<void> _onRefresh() async {} Future<void> _onRefresh() async {
if (favList.isNotEmpty) {
return;
}
var resp = await Dio().get(APIs.watchlistUrl);
var sp = ServerResponse.fromJson(resp.data);
setState(() {
favList = sp.data as List;
});
}
}
class TvSeries {
int? id;
int? tmdbId;
String? title;
String? originalName;
String? overview;
String? path;
String? posterPath;
TvSeries(
{this.id,
this.tmdbId,
this.title,
this.originalName,
this.overview,
this.path,
this.posterPath});
TvSeries.fromJson(Map<String, dynamic> json) {
id = json['id'];
tmdbId = json['tmdb_id'];
title = json['title'];
originalName = json['original_name'];
overview = json['overview'];
path = json['path'];
posterPath = json["poster_path"];
}
} }