feat: support for torrent with multi episodes

This commit is contained in:
Simon Ding
2024-11-15 15:43:07 +08:00
parent c433ccaa0e
commit fbfee65a50
14 changed files with 649 additions and 91 deletions

View File

@@ -3,6 +3,7 @@
package ent
import (
"encoding/json"
"fmt"
"polaris/ent/history"
"strings"
@@ -19,8 +20,12 @@ type History struct {
ID int `json:"id,omitempty"`
// MediaID holds the value of the "media_id" field.
MediaID int `json:"media_id,omitempty"`
// EpisodeID holds the value of the "episode_id" field.
// deprecated
EpisodeID int `json:"episode_id,omitempty"`
// EpisodeNums holds the value of the "episode_nums" field.
EpisodeNums []int `json:"episode_nums,omitempty"`
// SeasonNum holds the value of the "season_num" field.
SeasonNum int `json:"season_num,omitempty"`
// SourceTitle holds the value of the "source_title" field.
SourceTitle string `json:"source_title,omitempty"`
// Date holds the value of the "date" field.
@@ -47,7 +52,9 @@ func (*History) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case history.FieldID, history.FieldMediaID, history.FieldEpisodeID, history.FieldSize, history.FieldDownloadClientID, history.FieldIndexerID:
case history.FieldEpisodeNums:
values[i] = new([]byte)
case history.FieldID, history.FieldMediaID, history.FieldEpisodeID, history.FieldSeasonNum, history.FieldSize, history.FieldDownloadClientID, history.FieldIndexerID:
values[i] = new(sql.NullInt64)
case history.FieldSourceTitle, history.FieldTargetDir, history.FieldLink, history.FieldStatus, history.FieldSaved:
values[i] = new(sql.NullString)
@@ -86,6 +93,20 @@ func (h *History) assignValues(columns []string, values []any) error {
} else if value.Valid {
h.EpisodeID = int(value.Int64)
}
case history.FieldEpisodeNums:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field episode_nums", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &h.EpisodeNums); err != nil {
return fmt.Errorf("unmarshal field episode_nums: %w", err)
}
}
case history.FieldSeasonNum:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field season_num", values[i])
} else if value.Valid {
h.SeasonNum = int(value.Int64)
}
case history.FieldSourceTitle:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field source_title", values[i])
@@ -182,6 +203,12 @@ func (h *History) String() string {
builder.WriteString("episode_id=")
builder.WriteString(fmt.Sprintf("%v", h.EpisodeID))
builder.WriteString(", ")
builder.WriteString("episode_nums=")
builder.WriteString(fmt.Sprintf("%v", h.EpisodeNums))
builder.WriteString(", ")
builder.WriteString("season_num=")
builder.WriteString(fmt.Sprintf("%v", h.SeasonNum))
builder.WriteString(", ")
builder.WriteString("source_title=")
builder.WriteString(h.SourceTitle)
builder.WriteString(", ")

View File

@@ -17,6 +17,10 @@ const (
FieldMediaID = "media_id"
// FieldEpisodeID holds the string denoting the episode_id field in the database.
FieldEpisodeID = "episode_id"
// FieldEpisodeNums holds the string denoting the episode_nums field in the database.
FieldEpisodeNums = "episode_nums"
// FieldSeasonNum holds the string denoting the season_num field in the database.
FieldSeasonNum = "season_num"
// FieldSourceTitle holds the string denoting the source_title field in the database.
FieldSourceTitle = "source_title"
// FieldDate holds the string denoting the date field in the database.
@@ -44,6 +48,8 @@ var Columns = []string{
FieldID,
FieldMediaID,
FieldEpisodeID,
FieldEpisodeNums,
FieldSeasonNum,
FieldSourceTitle,
FieldDate,
FieldTargetDir,
@@ -114,6 +120,11 @@ func ByEpisodeID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldEpisodeID, opts...).ToFunc()
}
// BySeasonNum orders the results by the season_num field.
func BySeasonNum(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSeasonNum, opts...).ToFunc()
}
// BySourceTitle orders the results by the source_title field.
func BySourceTitle(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSourceTitle, opts...).ToFunc()

View File

@@ -64,6 +64,11 @@ func EpisodeID(v int) predicate.History {
return predicate.History(sql.FieldEQ(FieldEpisodeID, v))
}
// SeasonNum applies equality check predicate on the "season_num" field. It's identical to SeasonNumEQ.
func SeasonNum(v int) predicate.History {
return predicate.History(sql.FieldEQ(FieldSeasonNum, v))
}
// SourceTitle applies equality check predicate on the "source_title" field. It's identical to SourceTitleEQ.
func SourceTitle(v string) predicate.History {
return predicate.History(sql.FieldEQ(FieldSourceTitle, v))
@@ -194,6 +199,66 @@ func EpisodeIDNotNil() predicate.History {
return predicate.History(sql.FieldNotNull(FieldEpisodeID))
}
// EpisodeNumsIsNil applies the IsNil predicate on the "episode_nums" field.
func EpisodeNumsIsNil() predicate.History {
return predicate.History(sql.FieldIsNull(FieldEpisodeNums))
}
// EpisodeNumsNotNil applies the NotNil predicate on the "episode_nums" field.
func EpisodeNumsNotNil() predicate.History {
return predicate.History(sql.FieldNotNull(FieldEpisodeNums))
}
// SeasonNumEQ applies the EQ predicate on the "season_num" field.
func SeasonNumEQ(v int) predicate.History {
return predicate.History(sql.FieldEQ(FieldSeasonNum, v))
}
// SeasonNumNEQ applies the NEQ predicate on the "season_num" field.
func SeasonNumNEQ(v int) predicate.History {
return predicate.History(sql.FieldNEQ(FieldSeasonNum, v))
}
// SeasonNumIn applies the In predicate on the "season_num" field.
func SeasonNumIn(vs ...int) predicate.History {
return predicate.History(sql.FieldIn(FieldSeasonNum, vs...))
}
// SeasonNumNotIn applies the NotIn predicate on the "season_num" field.
func SeasonNumNotIn(vs ...int) predicate.History {
return predicate.History(sql.FieldNotIn(FieldSeasonNum, vs...))
}
// SeasonNumGT applies the GT predicate on the "season_num" field.
func SeasonNumGT(v int) predicate.History {
return predicate.History(sql.FieldGT(FieldSeasonNum, v))
}
// SeasonNumGTE applies the GTE predicate on the "season_num" field.
func SeasonNumGTE(v int) predicate.History {
return predicate.History(sql.FieldGTE(FieldSeasonNum, v))
}
// SeasonNumLT applies the LT predicate on the "season_num" field.
func SeasonNumLT(v int) predicate.History {
return predicate.History(sql.FieldLT(FieldSeasonNum, v))
}
// SeasonNumLTE applies the LTE predicate on the "season_num" field.
func SeasonNumLTE(v int) predicate.History {
return predicate.History(sql.FieldLTE(FieldSeasonNum, v))
}
// SeasonNumIsNil applies the IsNil predicate on the "season_num" field.
func SeasonNumIsNil() predicate.History {
return predicate.History(sql.FieldIsNull(FieldSeasonNum))
}
// SeasonNumNotNil applies the NotNil predicate on the "season_num" field.
func SeasonNumNotNil() predicate.History {
return predicate.History(sql.FieldNotNull(FieldSeasonNum))
}
// SourceTitleEQ applies the EQ predicate on the "source_title" field.
func SourceTitleEQ(v string) predicate.History {
return predicate.History(sql.FieldEQ(FieldSourceTitle, v))

View File

@@ -40,6 +40,26 @@ func (hc *HistoryCreate) SetNillableEpisodeID(i *int) *HistoryCreate {
return hc
}
// SetEpisodeNums sets the "episode_nums" field.
func (hc *HistoryCreate) SetEpisodeNums(i []int) *HistoryCreate {
hc.mutation.SetEpisodeNums(i)
return hc
}
// SetSeasonNum sets the "season_num" field.
func (hc *HistoryCreate) SetSeasonNum(i int) *HistoryCreate {
hc.mutation.SetSeasonNum(i)
return hc
}
// SetNillableSeasonNum sets the "season_num" field if the given value is not nil.
func (hc *HistoryCreate) SetNillableSeasonNum(i *int) *HistoryCreate {
if i != nil {
hc.SetSeasonNum(*i)
}
return hc
}
// SetSourceTitle sets the "source_title" field.
func (hc *HistoryCreate) SetSourceTitle(s string) *HistoryCreate {
hc.mutation.SetSourceTitle(s)
@@ -234,6 +254,14 @@ func (hc *HistoryCreate) createSpec() (*History, *sqlgraph.CreateSpec) {
_spec.SetField(history.FieldEpisodeID, field.TypeInt, value)
_node.EpisodeID = value
}
if value, ok := hc.mutation.EpisodeNums(); ok {
_spec.SetField(history.FieldEpisodeNums, field.TypeJSON, value)
_node.EpisodeNums = value
}
if value, ok := hc.mutation.SeasonNum(); ok {
_spec.SetField(history.FieldSeasonNum, field.TypeInt, value)
_node.SeasonNum = value
}
if value, ok := hc.mutation.SourceTitle(); ok {
_spec.SetField(history.FieldSourceTitle, field.TypeString, value)
_node.SourceTitle = value

View File

@@ -12,6 +12,7 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/dialect/sql/sqljson"
"entgo.io/ent/schema/field"
)
@@ -76,6 +77,51 @@ func (hu *HistoryUpdate) ClearEpisodeID() *HistoryUpdate {
return hu
}
// SetEpisodeNums sets the "episode_nums" field.
func (hu *HistoryUpdate) SetEpisodeNums(i []int) *HistoryUpdate {
hu.mutation.SetEpisodeNums(i)
return hu
}
// AppendEpisodeNums appends i to the "episode_nums" field.
func (hu *HistoryUpdate) AppendEpisodeNums(i []int) *HistoryUpdate {
hu.mutation.AppendEpisodeNums(i)
return hu
}
// ClearEpisodeNums clears the value of the "episode_nums" field.
func (hu *HistoryUpdate) ClearEpisodeNums() *HistoryUpdate {
hu.mutation.ClearEpisodeNums()
return hu
}
// SetSeasonNum sets the "season_num" field.
func (hu *HistoryUpdate) SetSeasonNum(i int) *HistoryUpdate {
hu.mutation.ResetSeasonNum()
hu.mutation.SetSeasonNum(i)
return hu
}
// SetNillableSeasonNum sets the "season_num" field if the given value is not nil.
func (hu *HistoryUpdate) SetNillableSeasonNum(i *int) *HistoryUpdate {
if i != nil {
hu.SetSeasonNum(*i)
}
return hu
}
// AddSeasonNum adds i to the "season_num" field.
func (hu *HistoryUpdate) AddSeasonNum(i int) *HistoryUpdate {
hu.mutation.AddSeasonNum(i)
return hu
}
// ClearSeasonNum clears the value of the "season_num" field.
func (hu *HistoryUpdate) ClearSeasonNum() *HistoryUpdate {
hu.mutation.ClearSeasonNum()
return hu
}
// SetSourceTitle sets the "source_title" field.
func (hu *HistoryUpdate) SetSourceTitle(s string) *HistoryUpdate {
hu.mutation.SetSourceTitle(s)
@@ -316,6 +362,26 @@ func (hu *HistoryUpdate) sqlSave(ctx context.Context) (n int, err error) {
if hu.mutation.EpisodeIDCleared() {
_spec.ClearField(history.FieldEpisodeID, field.TypeInt)
}
if value, ok := hu.mutation.EpisodeNums(); ok {
_spec.SetField(history.FieldEpisodeNums, field.TypeJSON, value)
}
if value, ok := hu.mutation.AppendedEpisodeNums(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, history.FieldEpisodeNums, value)
})
}
if hu.mutation.EpisodeNumsCleared() {
_spec.ClearField(history.FieldEpisodeNums, field.TypeJSON)
}
if value, ok := hu.mutation.SeasonNum(); ok {
_spec.SetField(history.FieldSeasonNum, field.TypeInt, value)
}
if value, ok := hu.mutation.AddedSeasonNum(); ok {
_spec.AddField(history.FieldSeasonNum, field.TypeInt, value)
}
if hu.mutation.SeasonNumCleared() {
_spec.ClearField(history.FieldSeasonNum, field.TypeInt)
}
if value, ok := hu.mutation.SourceTitle(); ok {
_spec.SetField(history.FieldSourceTitle, field.TypeString, value)
}
@@ -432,6 +498,51 @@ func (huo *HistoryUpdateOne) ClearEpisodeID() *HistoryUpdateOne {
return huo
}
// SetEpisodeNums sets the "episode_nums" field.
func (huo *HistoryUpdateOne) SetEpisodeNums(i []int) *HistoryUpdateOne {
huo.mutation.SetEpisodeNums(i)
return huo
}
// AppendEpisodeNums appends i to the "episode_nums" field.
func (huo *HistoryUpdateOne) AppendEpisodeNums(i []int) *HistoryUpdateOne {
huo.mutation.AppendEpisodeNums(i)
return huo
}
// ClearEpisodeNums clears the value of the "episode_nums" field.
func (huo *HistoryUpdateOne) ClearEpisodeNums() *HistoryUpdateOne {
huo.mutation.ClearEpisodeNums()
return huo
}
// SetSeasonNum sets the "season_num" field.
func (huo *HistoryUpdateOne) SetSeasonNum(i int) *HistoryUpdateOne {
huo.mutation.ResetSeasonNum()
huo.mutation.SetSeasonNum(i)
return huo
}
// SetNillableSeasonNum sets the "season_num" field if the given value is not nil.
func (huo *HistoryUpdateOne) SetNillableSeasonNum(i *int) *HistoryUpdateOne {
if i != nil {
huo.SetSeasonNum(*i)
}
return huo
}
// AddSeasonNum adds i to the "season_num" field.
func (huo *HistoryUpdateOne) AddSeasonNum(i int) *HistoryUpdateOne {
huo.mutation.AddSeasonNum(i)
return huo
}
// ClearSeasonNum clears the value of the "season_num" field.
func (huo *HistoryUpdateOne) ClearSeasonNum() *HistoryUpdateOne {
huo.mutation.ClearSeasonNum()
return huo
}
// SetSourceTitle sets the "source_title" field.
func (huo *HistoryUpdateOne) SetSourceTitle(s string) *HistoryUpdateOne {
huo.mutation.SetSourceTitle(s)
@@ -702,6 +813,26 @@ func (huo *HistoryUpdateOne) sqlSave(ctx context.Context) (_node *History, err e
if huo.mutation.EpisodeIDCleared() {
_spec.ClearField(history.FieldEpisodeID, field.TypeInt)
}
if value, ok := huo.mutation.EpisodeNums(); ok {
_spec.SetField(history.FieldEpisodeNums, field.TypeJSON, value)
}
if value, ok := huo.mutation.AppendedEpisodeNums(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, history.FieldEpisodeNums, value)
})
}
if huo.mutation.EpisodeNumsCleared() {
_spec.ClearField(history.FieldEpisodeNums, field.TypeJSON)
}
if value, ok := huo.mutation.SeasonNum(); ok {
_spec.SetField(history.FieldSeasonNum, field.TypeInt, value)
}
if value, ok := huo.mutation.AddedSeasonNum(); ok {
_spec.AddField(history.FieldSeasonNum, field.TypeInt, value)
}
if huo.mutation.SeasonNumCleared() {
_spec.ClearField(history.FieldSeasonNum, field.TypeInt)
}
if value, ok := huo.mutation.SourceTitle(); ok {
_spec.SetField(history.FieldSourceTitle, field.TypeString, value)
}

View File

@@ -74,6 +74,8 @@ var (
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "media_id", Type: field.TypeInt},
{Name: "episode_id", Type: field.TypeInt, Nullable: true},
{Name: "episode_nums", Type: field.TypeJSON, Nullable: true},
{Name: "season_num", Type: field.TypeInt, Nullable: true},
{Name: "source_title", Type: field.TypeString},
{Name: "date", Type: field.TypeTime},
{Name: "target_dir", Type: field.TypeString},

View File

@@ -2336,6 +2336,10 @@ type HistoryMutation struct {
addmedia_id *int
episode_id *int
addepisode_id *int
episode_nums *[]int
appendepisode_nums []int
season_num *int
addseason_num *int
source_title *string
date *time.Time
target_dir *string
@@ -2578,6 +2582,141 @@ func (m *HistoryMutation) ResetEpisodeID() {
delete(m.clearedFields, history.FieldEpisodeID)
}
// SetEpisodeNums sets the "episode_nums" field.
func (m *HistoryMutation) SetEpisodeNums(i []int) {
m.episode_nums = &i
m.appendepisode_nums = nil
}
// EpisodeNums returns the value of the "episode_nums" field in the mutation.
func (m *HistoryMutation) EpisodeNums() (r []int, exists bool) {
v := m.episode_nums
if v == nil {
return
}
return *v, true
}
// OldEpisodeNums returns the old "episode_nums" field's value of the History entity.
// If the History 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 *HistoryMutation) OldEpisodeNums(ctx context.Context) (v []int, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldEpisodeNums is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldEpisodeNums requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldEpisodeNums: %w", err)
}
return oldValue.EpisodeNums, nil
}
// AppendEpisodeNums adds i to the "episode_nums" field.
func (m *HistoryMutation) AppendEpisodeNums(i []int) {
m.appendepisode_nums = append(m.appendepisode_nums, i...)
}
// AppendedEpisodeNums returns the list of values that were appended to the "episode_nums" field in this mutation.
func (m *HistoryMutation) AppendedEpisodeNums() ([]int, bool) {
if len(m.appendepisode_nums) == 0 {
return nil, false
}
return m.appendepisode_nums, true
}
// ClearEpisodeNums clears the value of the "episode_nums" field.
func (m *HistoryMutation) ClearEpisodeNums() {
m.episode_nums = nil
m.appendepisode_nums = nil
m.clearedFields[history.FieldEpisodeNums] = struct{}{}
}
// EpisodeNumsCleared returns if the "episode_nums" field was cleared in this mutation.
func (m *HistoryMutation) EpisodeNumsCleared() bool {
_, ok := m.clearedFields[history.FieldEpisodeNums]
return ok
}
// ResetEpisodeNums resets all changes to the "episode_nums" field.
func (m *HistoryMutation) ResetEpisodeNums() {
m.episode_nums = nil
m.appendepisode_nums = nil
delete(m.clearedFields, history.FieldEpisodeNums)
}
// SetSeasonNum sets the "season_num" field.
func (m *HistoryMutation) SetSeasonNum(i int) {
m.season_num = &i
m.addseason_num = nil
}
// SeasonNum returns the value of the "season_num" field in the mutation.
func (m *HistoryMutation) SeasonNum() (r int, exists bool) {
v := m.season_num
if v == nil {
return
}
return *v, true
}
// OldSeasonNum returns the old "season_num" field's value of the History entity.
// If the History 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 *HistoryMutation) OldSeasonNum(ctx context.Context) (v int, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldSeasonNum is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldSeasonNum requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldSeasonNum: %w", err)
}
return oldValue.SeasonNum, nil
}
// AddSeasonNum adds i to the "season_num" field.
func (m *HistoryMutation) AddSeasonNum(i int) {
if m.addseason_num != nil {
*m.addseason_num += i
} else {
m.addseason_num = &i
}
}
// AddedSeasonNum returns the value that was added to the "season_num" field in this mutation.
func (m *HistoryMutation) AddedSeasonNum() (r int, exists bool) {
v := m.addseason_num
if v == nil {
return
}
return *v, true
}
// ClearSeasonNum clears the value of the "season_num" field.
func (m *HistoryMutation) ClearSeasonNum() {
m.season_num = nil
m.addseason_num = nil
m.clearedFields[history.FieldSeasonNum] = struct{}{}
}
// SeasonNumCleared returns if the "season_num" field was cleared in this mutation.
func (m *HistoryMutation) SeasonNumCleared() bool {
_, ok := m.clearedFields[history.FieldSeasonNum]
return ok
}
// ResetSeasonNum resets all changes to the "season_num" field.
func (m *HistoryMutation) ResetSeasonNum() {
m.season_num = nil
m.addseason_num = nil
delete(m.clearedFields, history.FieldSeasonNum)
}
// SetSourceTitle sets the "source_title" field.
func (m *HistoryMutation) SetSourceTitle(s string) {
m.source_title = &s
@@ -3050,13 +3189,19 @@ func (m *HistoryMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *HistoryMutation) Fields() []string {
fields := make([]string, 0, 11)
fields := make([]string, 0, 13)
if m.media_id != nil {
fields = append(fields, history.FieldMediaID)
}
if m.episode_id != nil {
fields = append(fields, history.FieldEpisodeID)
}
if m.episode_nums != nil {
fields = append(fields, history.FieldEpisodeNums)
}
if m.season_num != nil {
fields = append(fields, history.FieldSeasonNum)
}
if m.source_title != nil {
fields = append(fields, history.FieldSourceTitle)
}
@@ -3096,6 +3241,10 @@ func (m *HistoryMutation) Field(name string) (ent.Value, bool) {
return m.MediaID()
case history.FieldEpisodeID:
return m.EpisodeID()
case history.FieldEpisodeNums:
return m.EpisodeNums()
case history.FieldSeasonNum:
return m.SeasonNum()
case history.FieldSourceTitle:
return m.SourceTitle()
case history.FieldDate:
@@ -3127,6 +3276,10 @@ func (m *HistoryMutation) OldField(ctx context.Context, name string) (ent.Value,
return m.OldMediaID(ctx)
case history.FieldEpisodeID:
return m.OldEpisodeID(ctx)
case history.FieldEpisodeNums:
return m.OldEpisodeNums(ctx)
case history.FieldSeasonNum:
return m.OldSeasonNum(ctx)
case history.FieldSourceTitle:
return m.OldSourceTitle(ctx)
case history.FieldDate:
@@ -3168,6 +3321,20 @@ func (m *HistoryMutation) SetField(name string, value ent.Value) error {
}
m.SetEpisodeID(v)
return nil
case history.FieldEpisodeNums:
v, ok := value.([]int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetEpisodeNums(v)
return nil
case history.FieldSeasonNum:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetSeasonNum(v)
return nil
case history.FieldSourceTitle:
v, ok := value.(string)
if !ok {
@@ -3245,6 +3412,9 @@ func (m *HistoryMutation) AddedFields() []string {
if m.addepisode_id != nil {
fields = append(fields, history.FieldEpisodeID)
}
if m.addseason_num != nil {
fields = append(fields, history.FieldSeasonNum)
}
if m.addsize != nil {
fields = append(fields, history.FieldSize)
}
@@ -3266,6 +3436,8 @@ func (m *HistoryMutation) AddedField(name string) (ent.Value, bool) {
return m.AddedMediaID()
case history.FieldEpisodeID:
return m.AddedEpisodeID()
case history.FieldSeasonNum:
return m.AddedSeasonNum()
case history.FieldSize:
return m.AddedSize()
case history.FieldDownloadClientID:
@@ -3295,6 +3467,13 @@ func (m *HistoryMutation) AddField(name string, value ent.Value) error {
}
m.AddEpisodeID(v)
return nil
case history.FieldSeasonNum:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddSeasonNum(v)
return nil
case history.FieldSize:
v, ok := value.(int)
if !ok {
@@ -3327,6 +3506,12 @@ func (m *HistoryMutation) ClearedFields() []string {
if m.FieldCleared(history.FieldEpisodeID) {
fields = append(fields, history.FieldEpisodeID)
}
if m.FieldCleared(history.FieldEpisodeNums) {
fields = append(fields, history.FieldEpisodeNums)
}
if m.FieldCleared(history.FieldSeasonNum) {
fields = append(fields, history.FieldSeasonNum)
}
if m.FieldCleared(history.FieldDownloadClientID) {
fields = append(fields, history.FieldDownloadClientID)
}
@@ -3356,6 +3541,12 @@ func (m *HistoryMutation) ClearField(name string) error {
case history.FieldEpisodeID:
m.ClearEpisodeID()
return nil
case history.FieldEpisodeNums:
m.ClearEpisodeNums()
return nil
case history.FieldSeasonNum:
m.ClearSeasonNum()
return nil
case history.FieldDownloadClientID:
m.ClearDownloadClientID()
return nil
@@ -3382,6 +3573,12 @@ func (m *HistoryMutation) ResetField(name string) error {
case history.FieldEpisodeID:
m.ResetEpisodeID()
return nil
case history.FieldEpisodeNums:
m.ResetEpisodeNums()
return nil
case history.FieldSeasonNum:
m.ResetSeasonNum()
return nil
case history.FieldSourceTitle:
m.ResetSourceTitle()
return nil

View File

@@ -66,7 +66,7 @@ func init() {
historyFields := schema.History{}.Fields()
_ = historyFields
// historyDescSize is the schema descriptor for size field.
historyDescSize := historyFields[5].Descriptor()
historyDescSize := historyFields[7].Descriptor()
// history.DefaultSize holds the default value on creation for the size field.
history.DefaultSize = historyDescSize.Default.(int)
indexersFields := schema.Indexers{}.Fields()

View File

@@ -14,7 +14,9 @@ type History struct {
func (History) Fields() []ent.Field {
return []ent.Field{
field.Int("media_id"),
field.Int("episode_id").Optional(),
field.Int("episode_id").Optional().Comment("deprecated"),
field.Ints("episode_nums").Optional(),
field.Int("season_num").Optional(),
field.String("source_title"),
field.Time("date"),
field.String("target_dir"),

View File

@@ -20,6 +20,17 @@ type Info struct {
IsSeasonPack bool
}
func (m *Info) ParseExtraDescription(desc string) {
if m.IsSeasonPack { //try to parse episode number with description
mm := ParseTv(desc)
if mm.StartEpisode > 0 { //sometimes they put episode info in desc text
m.IsSeasonPack = false
m.StartEpisode = mm.StartEpisode
m.EndEpisode = mm.EndEpisode
}
}
}
func (m *Info) IsAcceptable(names ...string) bool {
for _, name := range names {
re := regexp.MustCompile(`[^\p{L}\w\s]`)

View File

@@ -1,6 +1,7 @@
package core
import (
"bytes"
"fmt"
"polaris/ent"
"polaris/ent/episode"
@@ -14,7 +15,7 @@ import (
"github.com/pkg/errors"
)
func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum, episodeNum int) (*string, error) {
func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum int, episodeNums ...int) (*string, error) {
trc, dlc, err := c.GetDownloadClient()
if err != nil {
return nil, errors.Wrap(err, "connect transmission")
@@ -32,19 +33,6 @@ func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum,
return nil, errors.New("no enough space")
}
var ep *ent.Episode
if episodeNum > 0 {
for _, e := range series.Episodes {
if e.SeasonNumber == seasonNum && e.EpisodeNumber == episodeNum {
ep = e
}
}
if ep == nil {
return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, episodeNum)
}
} else { //season package download
ep = &ent.Episode{}
}
magnet, err := utils.Link2Magnet(r1.Link)
if err != nil {
return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", r1.Link, err)
@@ -58,9 +46,31 @@ func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum,
dir := fmt.Sprintf("%s/Season %02d/", series.TargetDir, seasonNum)
if len(episodeNums) > 0 {
for _, epNum := range episodeNums {
var ep *ent.Episode
for _, e := range series.Episodes {
if e.SeasonNumber == seasonNum && e.EpisodeNumber == epNum {
ep = e
}
}
if ep == nil {
return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, epNum)
}
if ep.Status == episode.StatusMissing {
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
}
}
} else { //season package download
c.db.SetSeasonAllEpisodeStatus(seriesId, seasonNum, episode.StatusDownloading)
}
history, err := c.db.SaveHistoryRecord(ent.History{
MediaID: seriesId,
EpisodeID: ep.ID,
EpisodeNums: episodeNums,
SeasonNum: seasonNum,
SourceTitle: r1.Name,
TargetDir: dir,
Status: history.StatusRunning,
@@ -73,72 +83,112 @@ func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum,
if err != nil {
return nil, errors.Wrap(err, "save record")
}
if episodeNum > 0 {
if ep.Status == episode.StatusMissing {
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
}
} else {
c.db.SetSeasonAllEpisodeStatus(seriesId, seasonNum, episode.StatusDownloading)
}
c.tasks[history.ID] = &Task{Torrent: torrent}
c.sendMsg(fmt.Sprintf(message.BeginDownload, r1.Name))
name := r1.Name
if len(episodeNums) > 0 {
buff := &bytes.Buffer{}
for i, ep := range episodeNums {
if i != 0 {
buff.WriteString(",")
}
buff.WriteString(fmt.Sprint(ep))
}
name = fmt.Sprintf("第%s集 (%s)", buff.String(), name)
} else {
name = fmt.Sprintf("全集 (%s)", name)
}
c.sendMsg(fmt.Sprintf(message.BeginDownload, name))
log.Infof("success add %s to download task", r1.Name)
return &r1.Name, nil
}
func (c *Client) SearchAndDownload(seriesId, seasonNum, episodeNum int) (*string, error) {
var episodes []int
if episodeNum > 0 {
episodes = append(episodes, episodeNum)
/*
tmdb 校验获取的资源名如果用资源名在tmdb搜索出来的结果能匹配上想要的资源则认为资源有效否则无效
解决名称过于简单的影视会匹配过多资源的问题, 例如:梦魇绝镇 FROM
*/
func (c *Client) checkBtReourceWithTmdb(r *torznab.Result, seriesId int) bool {
m := metadata.ParseTv(r.Name)
se, err := c.MustTMDB().SearchMedia(m.NameEn, "", 1)
if err != nil {
log.Warnf("tmdb search error, consider this torrent ok: ", err)
return true
} else {
if len(se.Results) == 0 {
log.Debugf("tmdb search no result, consider this torrent ok: %s", r.Name) //because tv name parse is not accurate
return true
}
series := c.db.GetMediaDetails(seriesId)
se0 := se.Results[0]
if se0.ID != int64(series.TmdbID) {
log.Warnf("bt reosurce name not match tmdb id: %s", r.Name)
return false
} else { //resource tmdb id match
return true
}
}
}
func (c *Client) SearchAndDownload(seriesId, seasonNum int, episodeNums ...int) ([]string, error) {
res, err := SearchTvSeries(c.db, &SearchParam{
MediaId: seriesId,
SeasonNum: seasonNum,
Episodes: episodes,
Episodes: episodeNums,
CheckFileSize: true,
CheckResolution: true,
})
if err != nil {
return nil, err
}
/*
tmdb 校验获取的资源名如果用资源名在tmdb搜索出来的结果能匹配上想要的资源则认为资源有效否则无效
解决名称过于简单的影视会匹配过多资源的问题, 例如:梦魇绝镇 FROM
*/
var r1 *torznab.Result
for _, r := range res { //
wanted := make(map[int]bool, len(episodeNums))
for _, ep := range episodeNums {
wanted[ep] = true
}
var torrentNames []string
lo:
for _, r := range res {
if !c.checkBtReourceWithTmdb(&r, seriesId) {
continue
}
m := metadata.ParseTv(r.Name)
se, err := c.MustTMDB().SearchMedia(m.NameEn, "", 1)
if err != nil {
log.Warnf("tmdb search error, consider this torrent ok: ", err)
r1 = &r
break
} else {
if len(se.Results) == 0 {
log.Debugf("tmdb search no result, consider this torrent ok: %s", r.Name) //because tv name parse is not accurate
r1 = &r
break
m.ParseExtraDescription(r.Description)
if len(episodeNums) == 0 { //want season pack
if m.IsSeasonPack {
name, err := c.DownloadEpisodeTorrent(r, seriesId, seasonNum)
if err != nil {
return nil, err
}
torrentNames = append(torrentNames, *name)
}
series := c.db.GetMediaDetails(seriesId)
} else {
torrentEpisodes := make([]int, 0)
for i := m.StartEpisode; i <= m.EndEpisode; i++ {
if !wanted[i] { //torrent has episode not wanted
continue lo
}
torrentEpisodes = append(torrentEpisodes, i)
}
name, err := c.DownloadEpisodeTorrent(r, seriesId, seasonNum, torrentEpisodes...)
if err != nil {
return nil, err
}
torrentNames = append(torrentNames, *name)
se0 := se.Results[0]
if se0.ID != int64(series.TmdbID) {
log.Warnf("bt reosurce name not match tmdb id: %s", r.Name)
continue
} else { //resource tmdb id match
r1 = &r
for _, num := range torrentEpisodes {
delete(wanted, num) //delete downloaded episode from wanted
}
}
}
if r1 != nil {
log.Infof("found resource to download: %+v", r1)
return c.DownloadEpisodeTorrent(*r1, seriesId, seasonNum, episodeNum)
} else {
return nil, errors.Errorf("no resource")
if len(wanted) > 0 {
log.Warnf("still wanted but not downloaded episodes: %v", wanted)
}
return torrentNames, nil
}
func (c *Client) DownloadMovie(m *ent.Media, link, name string, size int, indexerID int) (*string, error) {

View File

@@ -115,6 +115,38 @@ func (c *Client) postTaskProcessing(id int) {
}
}
func getSeasonNum(h *ent.History) int {
if h.SeasonNum != 0 {
return h.SeasonNum
}
seasonNum, err := utils.SeasonId(h.TargetDir)
if err != nil {
log.Errorf("no season id: %v", h.TargetDir)
seasonNum = -1
}
return seasonNum
}
func (c *Client) GetEpisodeIds(r *ent.History) []int {
var episodeIds []int
seasonNum := getSeasonNum(r)
if r.EpisodeID > 0 {
episodeIds = append(episodeIds, r.EpisodeID)
}
if len(r.EpisodeNums) > 0 {
series := c.db.GetMediaDetails(r.MediaID)
for _, epNum := range r.EpisodeNums {
for _, ep := range series.Episodes {
if ep.SeasonNumber == seasonNum && ep.EpisodeNumber == epNum {
episodeIds = append(episodeIds, ep.ID)
}
}
}
}
return episodeIds
}
func (c *Client) moveCompletedTask(id int) (err1 error) {
torrent := c.tasks[id]
r := c.db.GetHistory(id)
@@ -123,11 +155,7 @@ func (c *Client) moveCompletedTask(id int) (err1 error) {
return nil
}
c.db.SetHistoryStatus(r.ID, history.StatusUploading)
seasonNum, err := utils.SeasonId(r.TargetDir)
if err != nil {
log.Errorf("no season id: %v", r.TargetDir)
seasonNum = -1
}
downloadclient, err := c.db.GetDownloadClient(r.DownloadClientID)
if err != nil {
log.Errorf("get task download client error: %v, use default one", err)
@@ -138,13 +166,19 @@ func (c *Client) moveCompletedTask(id int) (err1 error) {
return err
}
seasonNum := getSeasonNum(r)
episodeIds := c.GetEpisodeIds(r)
defer func() {
if err1 != nil {
c.db.SetHistoryStatus(r.ID, history.StatusFail)
if r.EpisodeID != 0 {
if !c.db.IsEpisodeDownloadingOrDownloaded(r.EpisodeID) {
c.db.SetEpisodeStatus(r.EpisodeID, episode.StatusMissing)
if len(episodeIds) > 0 {
for _, id := range episodeIds {
if !c.db.IsEpisodeDownloadingOrDownloaded(id) {
c.db.SetEpisodeStatus(id, episode.StatusMissing)
}
}
} else {
c.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusMissing)
@@ -175,8 +209,10 @@ func (c *Client) moveCompletedTask(id int) (err1 error) {
}
c.db.SetHistoryStatus(r.ID, history.StatusSeeding)
if r.EpisodeID != 0 {
c.db.SetEpisodeStatus(r.EpisodeID, episode.StatusDownloaded)
if len(episodeIds) > 0 {
for _, id := range episodeIds {
c.db.SetEpisodeStatus(id, episode.StatusDownloaded)
}
} else {
c.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusDownloaded)
}
@@ -286,10 +322,10 @@ func (c *Client) DownloadSeriesAllEpisodes(id int) []string {
}
}
if wantedSeasonPack {
name, err := c.SearchAndDownload(id, seasonNum, -1)
names, err := c.SearchAndDownload(id, seasonNum)
if err == nil {
allNames = append(allNames, *name)
log.Infof("begin download torrent resource: %v", name)
allNames = append(allNames, names...)
log.Infof("begin download torrent resource: %v", names)
} else {
log.Warnf("finding season pack error: %v", err)
wantedSeasonPack = false
@@ -297,6 +333,7 @@ func (c *Client) DownloadSeriesAllEpisodes(id int) []string {
}
if !wantedSeasonPack {
seasonEpisodesWanted := make(map[int][]int, 0)
for _, ep := range epsides {
if !ep.Monitored {
continue
@@ -317,14 +354,16 @@ func (c *Client) DownloadSeriesAllEpisodes(id int) []string {
}
}
name, err := c.SearchAndDownload(id, ep.SeasonNumber, ep.EpisodeNumber)
seasonEpisodesWanted[ep.SeasonNumber] = append(seasonEpisodesWanted[ep.SeasonNumber], ep.EpisodeNumber)
}
for se, eps := range seasonEpisodesWanted {
names, err := c.SearchAndDownload(id, se, eps...)
if err != nil {
log.Warnf("finding resoruces of season %d episode %d error: %v", ep.SeasonNumber, ep.EpisodeNumber, err)
log.Warnf("finding resoruces of season %d episode %v error: %v", se, eps, err)
continue
} else {
allNames = append(allNames, *name)
log.Infof("begin download torrent resource: %v", name)
allNames = append(allNames, names...)
log.Infof("begin download torrent resource: %v", names)
}
}

View File

@@ -40,16 +40,8 @@ lo:
for _, r := range res {
//log.Infof("torrent resource: %+v", r)
meta := metadata.ParseTv(r.Name)
meta.ParseExtraDescription(r.Description)
if meta.IsSeasonPack { //try to parse episode number with description
mm := metadata.ParseTv(r.Description)
if mm.StartEpisode > 0 { //sometimes they put episode info in desc text
meta.IsSeasonPack = false
meta.StartEpisode = mm.StartEpisode
meta.EndEpisode = mm.EndEpisode
}
}
if isImdbidNotMatch(series.ImdbID, r.ImdbId) { //has imdb id and not match
continue
}
@@ -70,6 +62,9 @@ lo:
}
if len(param.Episodes) > 0 { //not season pack, but episode number not equal
if meta.StartEpisode <= 0 {
continue lo
}
for i := meta.StartEpisode; i <= meta.EndEpisode; i++ {
if !slices.Contains(param.Episodes, i) {
continue lo

View File

@@ -120,7 +120,7 @@ func (s *Server) SearchTvAndDownload(c *gin.Context) (interface{}, error) {
if err != nil {
return nil, errors.Wrap(err, "download")
}
name = *name1
name = name1[0]
}
return gin.H{