diff --git a/ent/history/where.go b/ent/history/where.go index fce2c37..cbb9793 100644 --- a/ent/history/where.go +++ b/ent/history/where.go @@ -169,6 +169,16 @@ func EpisodeIDLTE(v int) predicate.History { return predicate.History(sql.FieldLTE(FieldEpisodeID, v)) } +// EpisodeIDIsNil applies the IsNil predicate on the "episode_id" field. +func EpisodeIDIsNil() predicate.History { + return predicate.History(sql.FieldIsNull(FieldEpisodeID)) +} + +// EpisodeIDNotNil applies the NotNil predicate on the "episode_id" field. +func EpisodeIDNotNil() predicate.History { + return predicate.History(sql.FieldNotNull(FieldEpisodeID)) +} + // SourceTitleEQ applies the EQ predicate on the "source_title" field. func SourceTitleEQ(v string) predicate.History { return predicate.History(sql.FieldEQ(FieldSourceTitle, v)) diff --git a/ent/history_create.go b/ent/history_create.go index 128b6d7..5902c41 100644 --- a/ent/history_create.go +++ b/ent/history_create.go @@ -32,6 +32,14 @@ func (hc *HistoryCreate) SetEpisodeID(i int) *HistoryCreate { return hc } +// SetNillableEpisodeID sets the "episode_id" field if the given value is not nil. +func (hc *HistoryCreate) SetNillableEpisodeID(i *int) *HistoryCreate { + if i != nil { + hc.SetEpisodeID(*i) + } + return hc +} + // SetSourceTitle sets the "source_title" field. func (hc *HistoryCreate) SetSourceTitle(s string) *HistoryCreate { hc.mutation.SetSourceTitle(s) @@ -130,9 +138,6 @@ func (hc *HistoryCreate) check() error { if _, ok := hc.mutation.MediaID(); !ok { return &ValidationError{Name: "media_id", err: errors.New(`ent: missing required field "History.media_id"`)} } - if _, ok := hc.mutation.EpisodeID(); !ok { - return &ValidationError{Name: "episode_id", err: errors.New(`ent: missing required field "History.episode_id"`)} - } if _, ok := hc.mutation.SourceTitle(); !ok { return &ValidationError{Name: "source_title", err: errors.New(`ent: missing required field "History.source_title"`)} } diff --git a/ent/history_update.go b/ent/history_update.go index 223034a..f407684 100644 --- a/ent/history_update.go +++ b/ent/history_update.go @@ -70,6 +70,12 @@ func (hu *HistoryUpdate) AddEpisodeID(i int) *HistoryUpdate { return hu } +// ClearEpisodeID clears the value of the "episode_id" field. +func (hu *HistoryUpdate) ClearEpisodeID() *HistoryUpdate { + hu.mutation.ClearEpisodeID() + return hu +} + // SetSourceTitle sets the "source_title" field. func (hu *HistoryUpdate) SetSourceTitle(s string) *HistoryUpdate { hu.mutation.SetSourceTitle(s) @@ -233,6 +239,9 @@ func (hu *HistoryUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := hu.mutation.AddedEpisodeID(); ok { _spec.AddField(history.FieldEpisodeID, field.TypeInt, value) } + if hu.mutation.EpisodeIDCleared() { + _spec.ClearField(history.FieldEpisodeID, field.TypeInt) + } if value, ok := hu.mutation.SourceTitle(); ok { _spec.SetField(history.FieldSourceTitle, field.TypeString, value) } @@ -319,6 +328,12 @@ func (huo *HistoryUpdateOne) AddEpisodeID(i int) *HistoryUpdateOne { return huo } +// ClearEpisodeID clears the value of the "episode_id" field. +func (huo *HistoryUpdateOne) ClearEpisodeID() *HistoryUpdateOne { + huo.mutation.ClearEpisodeID() + return huo +} + // SetSourceTitle sets the "source_title" field. func (huo *HistoryUpdateOne) SetSourceTitle(s string) *HistoryUpdateOne { huo.mutation.SetSourceTitle(s) @@ -512,6 +527,9 @@ func (huo *HistoryUpdateOne) sqlSave(ctx context.Context) (_node *History, err e if value, ok := huo.mutation.AddedEpisodeID(); ok { _spec.AddField(history.FieldEpisodeID, field.TypeInt, value) } + if huo.mutation.EpisodeIDCleared() { + _spec.ClearField(history.FieldEpisodeID, field.TypeInt) + } if value, ok := huo.mutation.SourceTitle(); ok { _spec.SetField(history.FieldSourceTitle, field.TypeString, value) } diff --git a/ent/media.go b/ent/media.go index 5b93b57..abee565 100644 --- a/ent/media.go +++ b/ent/media.go @@ -36,7 +36,7 @@ type Media struct { // AirDate holds the value of the "air_date" field. AirDate string `json:"air_date,omitempty"` // Resolution holds the value of the "resolution" field. - Resolution string `json:"resolution,omitempty"` + Resolution media.Resolution `json:"resolution,omitempty"` // StorageID holds the value of the "storage_id" field. StorageID int `json:"storage_id,omitempty"` // TargetDir holds the value of the "target_dir" field. @@ -155,7 +155,7 @@ func (m *Media) assignValues(columns []string, values []any) error { if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field resolution", values[i]) } else if value.Valid { - m.Resolution = value.String + m.Resolution = media.Resolution(value.String) } case media.FieldStorageID: if value, ok := values[i].(*sql.NullInt64); !ok { @@ -238,7 +238,7 @@ func (m *Media) String() string { builder.WriteString(m.AirDate) builder.WriteString(", ") builder.WriteString("resolution=") - builder.WriteString(m.Resolution) + builder.WriteString(fmt.Sprintf("%v", m.Resolution)) builder.WriteString(", ") builder.WriteString("storage_id=") builder.WriteString(fmt.Sprintf("%v", m.StorageID)) diff --git a/ent/media/media.go b/ent/media/media.go index c2f65d3..7058446 100644 --- a/ent/media/media.go +++ b/ent/media/media.go @@ -84,8 +84,6 @@ var ( DefaultCreatedAt time.Time // DefaultAirDate holds the default value on creation for the "air_date" field. DefaultAirDate string - // DefaultResolution holds the default value on creation for the "resolution" field. - DefaultResolution string ) // MediaType defines the type for the "media_type" enum field. @@ -111,6 +109,33 @@ func MediaTypeValidator(mt MediaType) error { } } +// Resolution defines the type for the "resolution" enum field. +type Resolution string + +// Resolution1080p is the default value of the Resolution enum. +const DefaultResolution = Resolution1080p + +// Resolution values. +const ( + Resolution720p Resolution = "720p" + Resolution1080p Resolution = "1080p" + Resolution4k Resolution = "4k" +) + +func (r Resolution) String() string { + return string(r) +} + +// ResolutionValidator is a validator for the "resolution" field enum values. It is called by the builders before save. +func ResolutionValidator(r Resolution) error { + switch r { + case Resolution720p, Resolution1080p, Resolution4k: + return nil + default: + return fmt.Errorf("media: invalid enum value for resolution field: %q", r) + } +} + // OrderOption defines the ordering options for the Media queries. type OrderOption func(*sql.Selector) diff --git a/ent/media/where.go b/ent/media/where.go index 13540cb..8359a7b 100644 --- a/ent/media/where.go +++ b/ent/media/where.go @@ -95,11 +95,6 @@ func AirDate(v string) predicate.Media { return predicate.Media(sql.FieldEQ(FieldAirDate, v)) } -// Resolution applies equality check predicate on the "resolution" field. It's identical to ResolutionEQ. -func Resolution(v string) predicate.Media { - return predicate.Media(sql.FieldEQ(FieldResolution, v)) -} - // StorageID applies equality check predicate on the "storage_id" field. It's identical to StorageIDEQ. func StorageID(v int) predicate.Media { return predicate.Media(sql.FieldEQ(FieldStorageID, v)) @@ -611,70 +606,25 @@ func AirDateContainsFold(v string) predicate.Media { } // ResolutionEQ applies the EQ predicate on the "resolution" field. -func ResolutionEQ(v string) predicate.Media { +func ResolutionEQ(v Resolution) predicate.Media { return predicate.Media(sql.FieldEQ(FieldResolution, v)) } // ResolutionNEQ applies the NEQ predicate on the "resolution" field. -func ResolutionNEQ(v string) predicate.Media { +func ResolutionNEQ(v Resolution) predicate.Media { return predicate.Media(sql.FieldNEQ(FieldResolution, v)) } // ResolutionIn applies the In predicate on the "resolution" field. -func ResolutionIn(vs ...string) predicate.Media { +func ResolutionIn(vs ...Resolution) predicate.Media { return predicate.Media(sql.FieldIn(FieldResolution, vs...)) } // ResolutionNotIn applies the NotIn predicate on the "resolution" field. -func ResolutionNotIn(vs ...string) predicate.Media { +func ResolutionNotIn(vs ...Resolution) predicate.Media { return predicate.Media(sql.FieldNotIn(FieldResolution, vs...)) } -// ResolutionGT applies the GT predicate on the "resolution" field. -func ResolutionGT(v string) predicate.Media { - return predicate.Media(sql.FieldGT(FieldResolution, v)) -} - -// ResolutionGTE applies the GTE predicate on the "resolution" field. -func ResolutionGTE(v string) predicate.Media { - return predicate.Media(sql.FieldGTE(FieldResolution, v)) -} - -// ResolutionLT applies the LT predicate on the "resolution" field. -func ResolutionLT(v string) predicate.Media { - return predicate.Media(sql.FieldLT(FieldResolution, v)) -} - -// ResolutionLTE applies the LTE predicate on the "resolution" field. -func ResolutionLTE(v string) predicate.Media { - return predicate.Media(sql.FieldLTE(FieldResolution, v)) -} - -// ResolutionContains applies the Contains predicate on the "resolution" field. -func ResolutionContains(v string) predicate.Media { - return predicate.Media(sql.FieldContains(FieldResolution, v)) -} - -// ResolutionHasPrefix applies the HasPrefix predicate on the "resolution" field. -func ResolutionHasPrefix(v string) predicate.Media { - return predicate.Media(sql.FieldHasPrefix(FieldResolution, v)) -} - -// ResolutionHasSuffix applies the HasSuffix predicate on the "resolution" field. -func ResolutionHasSuffix(v string) predicate.Media { - return predicate.Media(sql.FieldHasSuffix(FieldResolution, v)) -} - -// ResolutionEqualFold applies the EqualFold predicate on the "resolution" field. -func ResolutionEqualFold(v string) predicate.Media { - return predicate.Media(sql.FieldEqualFold(FieldResolution, v)) -} - -// ResolutionContainsFold applies the ContainsFold predicate on the "resolution" field. -func ResolutionContainsFold(v string) predicate.Media { - return predicate.Media(sql.FieldContainsFold(FieldResolution, v)) -} - // StorageIDEQ applies the EQ predicate on the "storage_id" field. func StorageIDEQ(v int) predicate.Media { return predicate.Media(sql.FieldEQ(FieldStorageID, v)) diff --git a/ent/media_create.go b/ent/media_create.go index e60eee3..2e3544f 100644 --- a/ent/media_create.go +++ b/ent/media_create.go @@ -100,15 +100,15 @@ func (mc *MediaCreate) SetNillableAirDate(s *string) *MediaCreate { } // SetResolution sets the "resolution" field. -func (mc *MediaCreate) SetResolution(s string) *MediaCreate { - mc.mutation.SetResolution(s) +func (mc *MediaCreate) SetResolution(m media.Resolution) *MediaCreate { + mc.mutation.SetResolution(m) return mc } // SetNillableResolution sets the "resolution" field if the given value is not nil. -func (mc *MediaCreate) SetNillableResolution(s *string) *MediaCreate { - if s != nil { - mc.SetResolution(*s) +func (mc *MediaCreate) SetNillableResolution(m *media.Resolution) *MediaCreate { + if m != nil { + mc.SetResolution(*m) } return mc } @@ -239,6 +239,11 @@ func (mc *MediaCreate) check() error { if _, ok := mc.mutation.Resolution(); !ok { return &ValidationError{Name: "resolution", err: errors.New(`ent: missing required field "Media.resolution"`)} } + if v, ok := mc.mutation.Resolution(); ok { + if err := media.ResolutionValidator(v); err != nil { + return &ValidationError{Name: "resolution", err: fmt.Errorf(`ent: validator failed for field "Media.resolution": %w`, err)} + } + } return nil } @@ -302,7 +307,7 @@ func (mc *MediaCreate) createSpec() (*Media, *sqlgraph.CreateSpec) { _node.AirDate = value } if value, ok := mc.mutation.Resolution(); ok { - _spec.SetField(media.FieldResolution, field.TypeString, value) + _spec.SetField(media.FieldResolution, field.TypeEnum, value) _node.Resolution = value } if value, ok := mc.mutation.StorageID(); ok { diff --git a/ent/media_update.go b/ent/media_update.go index 7d3903b..34bd88b 100644 --- a/ent/media_update.go +++ b/ent/media_update.go @@ -169,15 +169,15 @@ func (mu *MediaUpdate) SetNillableAirDate(s *string) *MediaUpdate { } // SetResolution sets the "resolution" field. -func (mu *MediaUpdate) SetResolution(s string) *MediaUpdate { - mu.mutation.SetResolution(s) +func (mu *MediaUpdate) SetResolution(m media.Resolution) *MediaUpdate { + mu.mutation.SetResolution(m) return mu } // SetNillableResolution sets the "resolution" field if the given value is not nil. -func (mu *MediaUpdate) SetNillableResolution(s *string) *MediaUpdate { - if s != nil { - mu.SetResolution(*s) +func (mu *MediaUpdate) SetNillableResolution(m *media.Resolution) *MediaUpdate { + if m != nil { + mu.SetResolution(*m) } return mu } @@ -304,6 +304,11 @@ func (mu *MediaUpdate) check() error { return &ValidationError{Name: "media_type", err: fmt.Errorf(`ent: validator failed for field "Media.media_type": %w`, err)} } } + if v, ok := mu.mutation.Resolution(); ok { + if err := media.ResolutionValidator(v); err != nil { + return &ValidationError{Name: "resolution", err: fmt.Errorf(`ent: validator failed for field "Media.resolution": %w`, err)} + } + } return nil } @@ -353,7 +358,7 @@ func (mu *MediaUpdate) sqlSave(ctx context.Context) (n int, err error) { _spec.SetField(media.FieldAirDate, field.TypeString, value) } if value, ok := mu.mutation.Resolution(); ok { - _spec.SetField(media.FieldResolution, field.TypeString, value) + _spec.SetField(media.FieldResolution, field.TypeEnum, value) } if value, ok := mu.mutation.StorageID(); ok { _spec.SetField(media.FieldStorageID, field.TypeInt, value) @@ -575,15 +580,15 @@ func (muo *MediaUpdateOne) SetNillableAirDate(s *string) *MediaUpdateOne { } // SetResolution sets the "resolution" field. -func (muo *MediaUpdateOne) SetResolution(s string) *MediaUpdateOne { - muo.mutation.SetResolution(s) +func (muo *MediaUpdateOne) SetResolution(m media.Resolution) *MediaUpdateOne { + muo.mutation.SetResolution(m) return muo } // SetNillableResolution sets the "resolution" field if the given value is not nil. -func (muo *MediaUpdateOne) SetNillableResolution(s *string) *MediaUpdateOne { - if s != nil { - muo.SetResolution(*s) +func (muo *MediaUpdateOne) SetNillableResolution(m *media.Resolution) *MediaUpdateOne { + if m != nil { + muo.SetResolution(*m) } return muo } @@ -723,6 +728,11 @@ func (muo *MediaUpdateOne) check() error { return &ValidationError{Name: "media_type", err: fmt.Errorf(`ent: validator failed for field "Media.media_type": %w`, err)} } } + if v, ok := muo.mutation.Resolution(); ok { + if err := media.ResolutionValidator(v); err != nil { + return &ValidationError{Name: "resolution", err: fmt.Errorf(`ent: validator failed for field "Media.resolution": %w`, err)} + } + } return nil } @@ -789,7 +799,7 @@ func (muo *MediaUpdateOne) sqlSave(ctx context.Context) (_node *Media, err error _spec.SetField(media.FieldAirDate, field.TypeString, value) } if value, ok := muo.mutation.Resolution(); ok { - _spec.SetField(media.FieldResolution, field.TypeString, value) + _spec.SetField(media.FieldResolution, field.TypeEnum, value) } if value, ok := muo.mutation.StorageID(); ok { _spec.SetField(media.FieldStorageID, field.TypeInt, value) diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index f9d2f06..5e88cc3 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -59,7 +59,7 @@ var ( HistoriesColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "media_id", Type: field.TypeInt}, - {Name: "episode_id", Type: field.TypeInt}, + {Name: "episode_id", Type: field.TypeInt, Nullable: true}, {Name: "source_title", Type: field.TypeString}, {Name: "date", Type: field.TypeTime}, {Name: "target_dir", Type: field.TypeString}, @@ -100,7 +100,7 @@ var ( {Name: "overview", Type: field.TypeString}, {Name: "created_at", Type: field.TypeTime}, {Name: "air_date", Type: field.TypeString, Default: ""}, - {Name: "resolution", Type: field.TypeString, Default: ""}, + {Name: "resolution", Type: field.TypeEnum, Enums: []string{"720p", "1080p", "4k"}, Default: "1080p"}, {Name: "storage_id", Type: field.TypeInt, Nullable: true}, {Name: "target_dir", Type: field.TypeString, Nullable: true}, } diff --git a/ent/mutation.go b/ent/mutation.go index 6339ef3..fe85d72 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -2000,10 +2000,24 @@ func (m *HistoryMutation) AddedEpisodeID() (r int, exists bool) { return *v, true } +// ClearEpisodeID clears the value of the "episode_id" field. +func (m *HistoryMutation) ClearEpisodeID() { + m.episode_id = nil + m.addepisode_id = nil + m.clearedFields[history.FieldEpisodeID] = struct{}{} +} + +// EpisodeIDCleared returns if the "episode_id" field was cleared in this mutation. +func (m *HistoryMutation) EpisodeIDCleared() bool { + _, ok := m.clearedFields[history.FieldEpisodeID] + return ok +} + // ResetEpisodeID resets all changes to the "episode_id" field. func (m *HistoryMutation) ResetEpisodeID() { m.episode_id = nil m.addepisode_id = nil + delete(m.clearedFields, history.FieldEpisodeID) } // SetSourceTitle sets the "source_title" field. @@ -2497,6 +2511,9 @@ func (m *HistoryMutation) AddField(name string, value ent.Value) error { // mutation. func (m *HistoryMutation) ClearedFields() []string { var fields []string + if m.FieldCleared(history.FieldEpisodeID) { + fields = append(fields, history.FieldEpisodeID) + } if m.FieldCleared(history.FieldSaved) { fields = append(fields, history.FieldSaved) } @@ -2514,6 +2531,9 @@ func (m *HistoryMutation) FieldCleared(name string) bool { // error if the field is not defined in the schema. func (m *HistoryMutation) ClearField(name string) error { switch name { + case history.FieldEpisodeID: + m.ClearEpisodeID() + return nil case history.FieldSaved: m.ClearSaved() return nil @@ -3195,7 +3215,7 @@ type MediaMutation struct { overview *string created_at *time.Time air_date *string - resolution *string + resolution *media.Resolution storage_id *int addstorage_id *int target_dir *string @@ -3664,12 +3684,12 @@ func (m *MediaMutation) ResetAirDate() { } // SetResolution sets the "resolution" field. -func (m *MediaMutation) SetResolution(s string) { - m.resolution = &s +func (m *MediaMutation) SetResolution(value media.Resolution) { + m.resolution = &value } // Resolution returns the value of the "resolution" field in the mutation. -func (m *MediaMutation) Resolution() (r string, exists bool) { +func (m *MediaMutation) Resolution() (r media.Resolution, exists bool) { v := m.resolution if v == nil { return @@ -3680,7 +3700,7 @@ func (m *MediaMutation) Resolution() (r string, exists bool) { // OldResolution returns the old "resolution" field's value of the Media entity. // If the Media 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 *MediaMutation) OldResolution(ctx context.Context) (v string, err error) { +func (m *MediaMutation) OldResolution(ctx context.Context) (v media.Resolution, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldResolution is only allowed on UpdateOne operations") } @@ -4081,7 +4101,7 @@ func (m *MediaMutation) SetField(name string, value ent.Value) error { m.SetAirDate(v) return nil case media.FieldResolution: - v, ok := value.(string) + v, ok := value.(media.Resolution) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } diff --git a/ent/runtime.go b/ent/runtime.go index 19589ba..db801ea 100644 --- a/ent/runtime.go +++ b/ent/runtime.go @@ -70,10 +70,6 @@ func init() { mediaDescAirDate := mediaFields[8].Descriptor() // media.DefaultAirDate holds the default value on creation for the air_date field. media.DefaultAirDate = mediaDescAirDate.Default.(string) - // mediaDescResolution is the schema descriptor for resolution field. - mediaDescResolution := mediaFields[9].Descriptor() - // media.DefaultResolution holds the default value on creation for the resolution field. - media.DefaultResolution = mediaDescResolution.Default.(string) storageFields := schema.Storage{}.Fields() _ = storageFields // storageDescDeleted is the schema descriptor for deleted field. diff --git a/server/core/torrent.go b/server/core/torrent.go index 340cbb2..dbae738 100644 --- a/server/core/torrent.go +++ b/server/core/torrent.go @@ -15,7 +15,7 @@ import ( "github.com/pkg/errors" ) -func SearchSeasonPackage(db1 *db.Client, seriesId, seasonNum int) ([]torznab.Result, error) { +func SearchSeasonPackage(db1 *db.Client, seriesId, seasonNum int, checkResolution bool) ([]torznab.Result, error) { series := db1.GetMediaDetails(seriesId) if series == nil { return nil, fmt.Errorf("no tv series of id %v", seriesId) @@ -31,8 +31,12 @@ func SearchSeasonPackage(db1 *db.Client, seriesId, seasonNum int) ([]torznab.Res if !isNameAcceptable(r.Name, series.Media, seasonNum, -1) { continue } + if checkResolution && IsWantedResolution(r.Name, series.Resolution) { + continue + } + filtered = append(filtered, r) - + } if len(filtered) == 0 { @@ -41,7 +45,7 @@ func SearchSeasonPackage(db1 *db.Client, seriesId, seasonNum int) ([]torznab.Res return filtered, nil } -func SearchEpisode(db1 *db.Client, seriesId, seasonNum, episodeNum int) ([]torznab.Result, error) { +func SearchEpisode(db1 *db.Client, seriesId, seasonNum, episodeNum int, checkResolution bool) ([]torznab.Result, error) { series := db1.GetMediaDetails(seriesId) if series == nil { return nil, fmt.Errorf("no tv series of id %v", seriesId) @@ -58,6 +62,10 @@ func SearchEpisode(db1 *db.Client, seriesId, seasonNum, episodeNum int) ([]torzn if !isNameAcceptable(r.Name, series.Media, seasonNum, episodeNum) { continue } + if checkResolution && IsWantedResolution(r.Name, series.Resolution) { + continue + } + filtered = append(filtered, r) } @@ -65,7 +73,7 @@ func SearchEpisode(db1 *db.Client, seriesId, seasonNum, episodeNum int) ([]torzn } -func SearchMovie(db1 *db.Client, movieId int) ([]torznab.Result, error) { +func SearchMovie(db1 *db.Client, movieId int, checkResolution bool) ([]torznab.Result, error) { movieDetail := db1.GetMediaDetails(movieId) if movieDetail == nil { return nil, errors.New("no media found of id") @@ -84,6 +92,10 @@ func SearchMovie(db1 *db.Client, movieId int) ([]torznab.Result, error) { if !isNameAcceptable(r.Name, movieDetail.Media, -1, -1) { continue } + if checkResolution && IsWantedResolution(r.Name, movieDetail.Resolution) { + continue + } + filtered = append(filtered, r) } @@ -117,10 +129,6 @@ func searchWithTorznab(db *db.Client, q string) []torznab.Result { } func isNameAcceptable(torrentName string, m *ent.Media, seasonNum, episodeNum int) bool { - if !utils.ContainsIgnoreCase(torrentName,m.NameCn) && !utils.ContainsIgnoreCase(torrentName,m.NameEn) && - !utils.ContainsIgnoreCase(torrentName,m.OriginalName) { - return false - } if !utils.IsNameAcceptable(torrentName, m.NameCn) && !utils.IsNameAcceptable(torrentName, m.NameEn) && !utils.IsNameAcceptable(torrentName, m.OriginalName){ return false //name not match } @@ -156,4 +164,16 @@ func isNameAcceptable(torrentName string, m *ent.Media, seasonNum, episodeNum in } } return true +} + +func IsWantedResolution(name string, res media.Resolution) bool { + switch res { + case media.Resolution720p: + return utils.ContainsIgnoreCase(name, "720p") + case media.Resolution1080p: + return utils.ContainsIgnoreCase(name, "1080p") + case media.Resolution4k: + return utils.ContainsIgnoreCase(name, "4k") || utils.ContainsIgnoreCase(name, "2160p") + } + return false } \ No newline at end of file diff --git a/server/resources.go b/server/resources.go index fce2684..570a28b 100644 --- a/server/resources.go +++ b/server/resources.go @@ -74,7 +74,7 @@ func (s *Server) searchAndDownloadSeasonPackage(seriesId, seasonNum int) (*strin return nil, errors.Wrap(err, "connect transmission") } - res, err := core.SearchSeasonPackage(s.db, seriesId, seasonNum) + res, err := core.SearchSeasonPackage(s.db, seriesId, seasonNum, true) if err != nil { return nil, err } @@ -89,7 +89,6 @@ func (s *Server) searchAndDownloadSeasonPackage(seriesId, seasonNum int) (*strin return nil, errors.New("no enough space") } - torrent, err := trc.Download(r1.Magnet, s.db.GetDownloadDir()) if err != nil { return nil, errors.Wrap(err, "downloading") @@ -139,7 +138,7 @@ func (s *Server) searchAndDownload(seriesId, seasonNum, episodeNum int) (*string return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, episodeNum) } - res, err := core.SearchEpisode(s.db, seriesId, seasonNum, episodeNum) + res, err := core.SearchEpisode(s.db, seriesId, seasonNum, episodeNum, true) if err != nil { return nil, err } @@ -179,13 +178,13 @@ type searchAndDownloadIn struct { Episode int `json:"episode"` } -func (s *Server) SearchAvailableEpisodeResource(c *gin.Context) (interface{}, error) { +func (s *Server) SearchAvailableEpisodeResource(c *gin.Context) (interface{}, error) { var in searchAndDownloadIn if err := c.ShouldBindJSON(&in); err != nil { return nil, errors.Wrap(err, "bind json") } log.Infof("search episode resources link: %v", in) - res, err := core.SearchEpisode(s.db, in.ID, in.Season, in.Episode) + res, err := core.SearchEpisode(s.db, in.ID, in.Season, in.Episode, true) if err != nil { return nil, errors.Wrap(err, "search episode") } @@ -254,7 +253,7 @@ func (s *Server) SearchAvailableMovies(c *gin.Context) (interface{}, error) { return nil, errors.New("no media found of id " + ids) } - res, err := core.SearchMovie(s.db, id) + res, err := core.SearchMovie(s.db, id, false) if err != nil { return nil, err } @@ -277,7 +276,7 @@ func (s *Server) SearchAvailableMovies(c *gin.Context) (interface{}, error) { type downloadTorrentIn struct { MediaID int `json:"media_id" binding:"required"` - Link string `json:"link" binding:"required"` + TorznabSearchResult } func (s *Server) DownloadMovieTorrent(c *gin.Context) (interface{}, error) { @@ -303,26 +302,23 @@ func (s *Server) DownloadMovieTorrent(c *gin.Context) (interface{}, error) { torrent.Start() go func() { - for { - if !torrent.Exists() { - continue - } - history, err := s.db.SaveHistoryRecord(ent.History{ - MediaID: media.ID, - SourceTitle: torrent.Name(), - TargetDir: "./", - Status: history.StatusRunning, - Size: torrent.Size(), - Saved: torrent.Save(), - }) - if err != nil { - log.Errorf("save history error: %v", err) - } - - s.tasks[history.ID] = &Task{Torrent: torrent} - - break + ep := media.Episodes[0] + history, err := s.db.SaveHistoryRecord(ent.History{ + MediaID: media.ID, + EpisodeID: ep.ID, + SourceTitle: media.NameCn, + TargetDir: "./", + Status: history.StatusRunning, + Size: in.Size, + Saved: torrent.Save(), + }) + if err != nil { + log.Errorf("save history error: %v", err) } + + s.tasks[history.ID] = &Task{Torrent: torrent} + + s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) }() log.Infof("success add %s to download task", media.NameEn) diff --git a/server/scheduler.go b/server/scheduler.go index 033f702..7457146 100644 --- a/server/scheduler.go +++ b/server/scheduler.go @@ -11,6 +11,7 @@ import ( "polaris/pkg" "polaris/pkg/storage" "polaris/pkg/utils" + "polaris/server/core" "github.com/pkg/errors" ) @@ -19,6 +20,7 @@ func (s *Server) scheduler() { s.mustAddCron("@every 1m", s.checkTasks) //s.mustAddCron("@every 1h", s.checkAllFiles) s.mustAddCron("@every 1h", s.downloadTvSeries) + s.mustAddCron("@every 1h", s.downloadMovie) s.cron.Start() } @@ -241,3 +243,58 @@ func (s *Server) downloadTvSeries() { } } + +func (s *Server) downloadMovie() { + log.Infof("begin check all movie resources") + allSeries := s.db.GetMediaWatchlist(media.MediaTypeMovie) + + for _, series := range allSeries { + detail := s.db.GetMediaDetails(series.ID) + if len(detail.Episodes) == 0 { + log.Errorf("no related dummy episode: %v", detail.NameEn) + continue + } + + if err := s.downloadMovieSingleEpisode(detail.Episodes[0]); err != nil { + log.Errorf("download movie error: %v", err) + } + } +} + +func (s *Server) downloadMovieSingleEpisode(ep *ent.Episode) error { + trc, err := s.getDownloadClient() + if err != nil { + return errors.Wrap(err, "connect transmission") + } + + res, err := core.SearchMovie(s.db, ep.MediaID, true) + if err != nil { + + return errors.Wrap(err, "search movie") + } + r1 := res[0] + log.Infof("begin download torrent resource: %v", r1.Name) + torrent, err := trc.Download(r1.Magnet, s.db.GetDownloadDir()) + if err != nil { + return errors.Wrap(err, "downloading") + } + torrent.Start() + + history, err := s.db.SaveHistoryRecord(ent.History{ + MediaID: ep.MediaID, + EpisodeID: ep.ID, + SourceTitle: r1.Name, + TargetDir: "./", + Status: history.StatusRunning, + Size: r1.Size, + Saved: torrent.Save(), + }) + if err != nil { + log.Errorf("save history error: %v", err) + } + + s.tasks[history.ID] = &Task{Torrent: torrent} + + s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) + return nil +} diff --git a/server/watchlist.go b/server/watchlist.go index aa2fe35..d8ca753 100644 --- a/server/watchlist.go +++ b/server/watchlist.go @@ -116,7 +116,7 @@ func (s *Server) AddTv2Watchlist(c *gin.Context) (interface{}, error) { OriginalName: detail.OriginalName, Overview: detail.Overview, AirDate: detail.FirstAirDate, - Resolution: string(in.Resolution), + Resolution: media.Resolution(in.Resolution), StorageID: in.StorageID, TargetDir: in.Folder, }, epIds) @@ -181,7 +181,7 @@ func (s *Server) AddMovie2Watchlist(c *gin.Context) (interface{}, error) { OriginalName: detail.OriginalTitle, Overview: detail.Overview, AirDate: detail.ReleaseDate, - Resolution: string(in.Resolution), + Resolution: media.Resolution(in.Resolution), StorageID: in.StorageID, TargetDir: "./", }, []int{epid})