diff --git a/db/db.go b/db/db.go index 6b9f1d9..35601fa 100644 --- a/db/db.go +++ b/db/db.go @@ -462,18 +462,10 @@ func (c *Client) SetDefaultStorageByName(name string) error { } func (c *Client) SaveHistoryRecord(h ent.History) (*ent.History, error) { - if h.Link != "" { - r, err := utils.Link2Magnet(h.Link) - if err != nil { - log.Warnf("convert link to magnet error, link %v, error: %v", h.Link, err) - } else { - h.Link = r - } - } return c.ent.History.Create().SetMediaID(h.MediaID).SetEpisodeID(h.EpisodeID).SetDate(time.Now()). SetStatus(h.Status).SetTargetDir(h.TargetDir).SetSourceTitle(h.SourceTitle).SetIndexerID(h.IndexerID). SetDownloadClientID(h.DownloadClientID).SetSize(h.Size).SetSaved(h.Saved).SetSeasonNum(h.SeasonNum). - SetEpisodeNums(h.EpisodeNums).SetLink(h.Link).Save(context.TODO()) + SetEpisodeNums(h.EpisodeNums).SetHash(h.Hash).SetLink(h.Link).Save(context.TODO()) } func (c *Client) SetHistoryStatus(id int, status history.Status) error { diff --git a/ent/history.go b/ent/history.go index d8f9462..ea4d570 100644 --- a/ent/history.go +++ b/ent/history.go @@ -38,8 +38,10 @@ type History struct { DownloadClientID int `json:"download_client_id,omitempty"` // IndexerID holds the value of the "indexer_id" field. IndexerID int `json:"indexer_id,omitempty"` - // Link holds the value of the "link" field. + // deprecated, use hash instead Link string `json:"link,omitempty"` + // torrent hash + Hash string `json:"hash,omitempty"` // Status holds the value of the "status" field. Status history.Status `json:"status,omitempty"` // deprecated @@ -56,7 +58,7 @@ func (*History) scanValues(columns []string) ([]any, error) { 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: + case history.FieldSourceTitle, history.FieldTargetDir, history.FieldLink, history.FieldHash, history.FieldStatus, history.FieldSaved: values[i] = new(sql.NullString) case history.FieldDate: values[i] = new(sql.NullTime) @@ -149,6 +151,12 @@ func (h *History) assignValues(columns []string, values []any) error { } else if value.Valid { h.Link = value.String } + case history.FieldHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field hash", values[i]) + } else if value.Valid { + h.Hash = value.String + } case history.FieldStatus: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field status", values[i]) @@ -230,6 +238,9 @@ func (h *History) String() string { builder.WriteString("link=") builder.WriteString(h.Link) builder.WriteString(", ") + builder.WriteString("hash=") + builder.WriteString(h.Hash) + builder.WriteString(", ") builder.WriteString("status=") builder.WriteString(fmt.Sprintf("%v", h.Status)) builder.WriteString(", ") diff --git a/ent/history/history.go b/ent/history/history.go index 1dcd79d..c3a0aa1 100644 --- a/ent/history/history.go +++ b/ent/history/history.go @@ -35,6 +35,8 @@ const ( FieldIndexerID = "indexer_id" // FieldLink holds the string denoting the link field in the database. FieldLink = "link" + // FieldHash holds the string denoting the hash field in the database. + FieldHash = "hash" // FieldStatus holds the string denoting the status field in the database. FieldStatus = "status" // FieldSaved holds the string denoting the saved field in the database. @@ -57,6 +59,7 @@ var Columns = []string{ FieldDownloadClientID, FieldIndexerID, FieldLink, + FieldHash, FieldStatus, FieldSaved, } @@ -160,6 +163,11 @@ func ByLink(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldLink, opts...).ToFunc() } +// ByHash orders the results by the hash field. +func ByHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldHash, opts...).ToFunc() +} + // ByStatus orders the results by the status field. func ByStatus(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatus, opts...).ToFunc() diff --git a/ent/history/where.go b/ent/history/where.go index 347f607..17f82b5 100644 --- a/ent/history/where.go +++ b/ent/history/where.go @@ -104,6 +104,11 @@ func Link(v string) predicate.History { return predicate.History(sql.FieldEQ(FieldLink, v)) } +// Hash applies equality check predicate on the "hash" field. It's identical to HashEQ. +func Hash(v string) predicate.History { + return predicate.History(sql.FieldEQ(FieldHash, v)) +} + // Saved applies equality check predicate on the "saved" field. It's identical to SavedEQ. func Saved(v string) predicate.History { return predicate.History(sql.FieldEQ(FieldSaved, v)) @@ -644,6 +649,81 @@ func LinkContainsFold(v string) predicate.History { return predicate.History(sql.FieldContainsFold(FieldLink, v)) } +// HashEQ applies the EQ predicate on the "hash" field. +func HashEQ(v string) predicate.History { + return predicate.History(sql.FieldEQ(FieldHash, v)) +} + +// HashNEQ applies the NEQ predicate on the "hash" field. +func HashNEQ(v string) predicate.History { + return predicate.History(sql.FieldNEQ(FieldHash, v)) +} + +// HashIn applies the In predicate on the "hash" field. +func HashIn(vs ...string) predicate.History { + return predicate.History(sql.FieldIn(FieldHash, vs...)) +} + +// HashNotIn applies the NotIn predicate on the "hash" field. +func HashNotIn(vs ...string) predicate.History { + return predicate.History(sql.FieldNotIn(FieldHash, vs...)) +} + +// HashGT applies the GT predicate on the "hash" field. +func HashGT(v string) predicate.History { + return predicate.History(sql.FieldGT(FieldHash, v)) +} + +// HashGTE applies the GTE predicate on the "hash" field. +func HashGTE(v string) predicate.History { + return predicate.History(sql.FieldGTE(FieldHash, v)) +} + +// HashLT applies the LT predicate on the "hash" field. +func HashLT(v string) predicate.History { + return predicate.History(sql.FieldLT(FieldHash, v)) +} + +// HashLTE applies the LTE predicate on the "hash" field. +func HashLTE(v string) predicate.History { + return predicate.History(sql.FieldLTE(FieldHash, v)) +} + +// HashContains applies the Contains predicate on the "hash" field. +func HashContains(v string) predicate.History { + return predicate.History(sql.FieldContains(FieldHash, v)) +} + +// HashHasPrefix applies the HasPrefix predicate on the "hash" field. +func HashHasPrefix(v string) predicate.History { + return predicate.History(sql.FieldHasPrefix(FieldHash, v)) +} + +// HashHasSuffix applies the HasSuffix predicate on the "hash" field. +func HashHasSuffix(v string) predicate.History { + return predicate.History(sql.FieldHasSuffix(FieldHash, v)) +} + +// HashIsNil applies the IsNil predicate on the "hash" field. +func HashIsNil() predicate.History { + return predicate.History(sql.FieldIsNull(FieldHash)) +} + +// HashNotNil applies the NotNil predicate on the "hash" field. +func HashNotNil() predicate.History { + return predicate.History(sql.FieldNotNull(FieldHash)) +} + +// HashEqualFold applies the EqualFold predicate on the "hash" field. +func HashEqualFold(v string) predicate.History { + return predicate.History(sql.FieldEqualFold(FieldHash, v)) +} + +// HashContainsFold applies the ContainsFold predicate on the "hash" field. +func HashContainsFold(v string) predicate.History { + return predicate.History(sql.FieldContainsFold(FieldHash, v)) +} + // StatusEQ applies the EQ predicate on the "status" field. func StatusEQ(v Status) predicate.History { return predicate.History(sql.FieldEQ(FieldStatus, v)) diff --git a/ent/history_create.go b/ent/history_create.go index ae7d4c7..cbd577b 100644 --- a/ent/history_create.go +++ b/ent/history_create.go @@ -134,6 +134,20 @@ func (hc *HistoryCreate) SetNillableLink(s *string) *HistoryCreate { return hc } +// SetHash sets the "hash" field. +func (hc *HistoryCreate) SetHash(s string) *HistoryCreate { + hc.mutation.SetHash(s) + return hc +} + +// SetNillableHash sets the "hash" field if the given value is not nil. +func (hc *HistoryCreate) SetNillableHash(s *string) *HistoryCreate { + if s != nil { + hc.SetHash(*s) + } + return hc +} + // SetStatus sets the "status" field. func (hc *HistoryCreate) SetStatus(h history.Status) *HistoryCreate { hc.mutation.SetStatus(h) @@ -290,6 +304,10 @@ func (hc *HistoryCreate) createSpec() (*History, *sqlgraph.CreateSpec) { _spec.SetField(history.FieldLink, field.TypeString, value) _node.Link = value } + if value, ok := hc.mutation.Hash(); ok { + _spec.SetField(history.FieldHash, field.TypeString, value) + _node.Hash = value + } if value, ok := hc.mutation.Status(); ok { _spec.SetField(history.FieldStatus, field.TypeEnum, value) _node.Status = value diff --git a/ent/history_update.go b/ent/history_update.go index ebe9cf9..2f13a81 100644 --- a/ent/history_update.go +++ b/ent/history_update.go @@ -259,6 +259,26 @@ func (hu *HistoryUpdate) ClearLink() *HistoryUpdate { return hu } +// SetHash sets the "hash" field. +func (hu *HistoryUpdate) SetHash(s string) *HistoryUpdate { + hu.mutation.SetHash(s) + return hu +} + +// SetNillableHash sets the "hash" field if the given value is not nil. +func (hu *HistoryUpdate) SetNillableHash(s *string) *HistoryUpdate { + if s != nil { + hu.SetHash(*s) + } + return hu +} + +// ClearHash clears the value of the "hash" field. +func (hu *HistoryUpdate) ClearHash() *HistoryUpdate { + hu.mutation.ClearHash() + return hu +} + // SetStatus sets the "status" field. func (hu *HistoryUpdate) SetStatus(h history.Status) *HistoryUpdate { hu.mutation.SetStatus(h) @@ -421,6 +441,12 @@ func (hu *HistoryUpdate) sqlSave(ctx context.Context) (n int, err error) { if hu.mutation.LinkCleared() { _spec.ClearField(history.FieldLink, field.TypeString) } + if value, ok := hu.mutation.Hash(); ok { + _spec.SetField(history.FieldHash, field.TypeString, value) + } + if hu.mutation.HashCleared() { + _spec.ClearField(history.FieldHash, field.TypeString) + } if value, ok := hu.mutation.Status(); ok { _spec.SetField(history.FieldStatus, field.TypeEnum, value) } @@ -680,6 +706,26 @@ func (huo *HistoryUpdateOne) ClearLink() *HistoryUpdateOne { return huo } +// SetHash sets the "hash" field. +func (huo *HistoryUpdateOne) SetHash(s string) *HistoryUpdateOne { + huo.mutation.SetHash(s) + return huo +} + +// SetNillableHash sets the "hash" field if the given value is not nil. +func (huo *HistoryUpdateOne) SetNillableHash(s *string) *HistoryUpdateOne { + if s != nil { + huo.SetHash(*s) + } + return huo +} + +// ClearHash clears the value of the "hash" field. +func (huo *HistoryUpdateOne) ClearHash() *HistoryUpdateOne { + huo.mutation.ClearHash() + return huo +} + // SetStatus sets the "status" field. func (huo *HistoryUpdateOne) SetStatus(h history.Status) *HistoryUpdateOne { huo.mutation.SetStatus(h) @@ -872,6 +918,12 @@ func (huo *HistoryUpdateOne) sqlSave(ctx context.Context) (_node *History, err e if huo.mutation.LinkCleared() { _spec.ClearField(history.FieldLink, field.TypeString) } + if value, ok := huo.mutation.Hash(); ok { + _spec.SetField(history.FieldHash, field.TypeString, value) + } + if huo.mutation.HashCleared() { + _spec.ClearField(history.FieldHash, field.TypeString) + } if value, ok := huo.mutation.Status(); ok { _spec.SetField(history.FieldStatus, field.TypeEnum, value) } diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 94e6711..88b513a 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -83,6 +83,7 @@ var ( {Name: "download_client_id", Type: field.TypeInt, Nullable: true}, {Name: "indexer_id", Type: field.TypeInt, Nullable: true}, {Name: "link", Type: field.TypeString, Nullable: true}, + {Name: "hash", Type: field.TypeString, Nullable: true}, {Name: "status", Type: field.TypeEnum, Enums: []string{"running", "success", "fail", "uploading", "seeding"}}, {Name: "saved", Type: field.TypeString, Nullable: true}, } diff --git a/ent/mutation.go b/ent/mutation.go index a03ab27..ef4529b 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -2350,6 +2350,7 @@ type HistoryMutation struct { indexer_id *int addindexer_id *int link *string + hash *string status *history.Status saved *string clearedFields map[string]struct{} @@ -3070,6 +3071,55 @@ func (m *HistoryMutation) ResetLink() { delete(m.clearedFields, history.FieldLink) } +// SetHash sets the "hash" field. +func (m *HistoryMutation) SetHash(s string) { + m.hash = &s +} + +// Hash returns the value of the "hash" field in the mutation. +func (m *HistoryMutation) Hash() (r string, exists bool) { + v := m.hash + if v == nil { + return + } + return *v, true +} + +// OldHash returns the old "hash" 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) OldHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldHash is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldHash requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldHash: %w", err) + } + return oldValue.Hash, nil +} + +// ClearHash clears the value of the "hash" field. +func (m *HistoryMutation) ClearHash() { + m.hash = nil + m.clearedFields[history.FieldHash] = struct{}{} +} + +// HashCleared returns if the "hash" field was cleared in this mutation. +func (m *HistoryMutation) HashCleared() bool { + _, ok := m.clearedFields[history.FieldHash] + return ok +} + +// ResetHash resets all changes to the "hash" field. +func (m *HistoryMutation) ResetHash() { + m.hash = nil + delete(m.clearedFields, history.FieldHash) +} + // SetStatus sets the "status" field. func (m *HistoryMutation) SetStatus(h history.Status) { m.status = &h @@ -3189,7 +3239,7 @@ 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, 13) + fields := make([]string, 0, 14) if m.media_id != nil { fields = append(fields, history.FieldMediaID) } @@ -3223,6 +3273,9 @@ func (m *HistoryMutation) Fields() []string { if m.link != nil { fields = append(fields, history.FieldLink) } + if m.hash != nil { + fields = append(fields, history.FieldHash) + } if m.status != nil { fields = append(fields, history.FieldStatus) } @@ -3259,6 +3312,8 @@ func (m *HistoryMutation) Field(name string) (ent.Value, bool) { return m.IndexerID() case history.FieldLink: return m.Link() + case history.FieldHash: + return m.Hash() case history.FieldStatus: return m.Status() case history.FieldSaved: @@ -3294,6 +3349,8 @@ func (m *HistoryMutation) OldField(ctx context.Context, name string) (ent.Value, return m.OldIndexerID(ctx) case history.FieldLink: return m.OldLink(ctx) + case history.FieldHash: + return m.OldHash(ctx) case history.FieldStatus: return m.OldStatus(ctx) case history.FieldSaved: @@ -3384,6 +3441,13 @@ func (m *HistoryMutation) SetField(name string, value ent.Value) error { } m.SetLink(v) return nil + case history.FieldHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetHash(v) + return nil case history.FieldStatus: v, ok := value.(history.Status) if !ok { @@ -3521,6 +3585,9 @@ func (m *HistoryMutation) ClearedFields() []string { if m.FieldCleared(history.FieldLink) { fields = append(fields, history.FieldLink) } + if m.FieldCleared(history.FieldHash) { + fields = append(fields, history.FieldHash) + } if m.FieldCleared(history.FieldSaved) { fields = append(fields, history.FieldSaved) } @@ -3556,6 +3623,9 @@ func (m *HistoryMutation) ClearField(name string) error { case history.FieldLink: m.ClearLink() return nil + case history.FieldHash: + m.ClearHash() + return nil case history.FieldSaved: m.ClearSaved() return nil @@ -3600,6 +3670,9 @@ func (m *HistoryMutation) ResetField(name string) error { case history.FieldLink: m.ResetLink() return nil + case history.FieldHash: + m.ResetHash() + return nil case history.FieldStatus: m.ResetStatus() return nil diff --git a/ent/schema/history.go b/ent/schema/history.go index 8c14c74..4c31e68 100644 --- a/ent/schema/history.go +++ b/ent/schema/history.go @@ -23,7 +23,8 @@ func (History) Fields() []ent.Field { field.Int("size").Default(0), field.Int("download_client_id").Optional(), field.Int("indexer_id").Optional(), - field.String("link").Optional(), //should be magnet link + field.String("link").Optional().Comment("deprecated, use hash instead"), //should be magnet link + field.String("hash").Optional().Comment("torrent hash"), field.Enum("status").Values("running", "success", "fail", "uploading", "seeding"), field.String("saved").Optional().Comment("deprecated"), //deprecated } diff --git a/go.sum b/go.sum index 3f91f36..a75955a 100644 --- a/go.sum +++ b/go.sum @@ -216,6 +216,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -249,6 +251,8 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/nikoksr/notify v1.0.0 h1:qe9/6FRsWdxBgQgWcpvQ0sv8LRGJZDpRB4TkL2uNdO8= github.com/nikoksr/notify v1.0.0/go.mod h1:hPaaDt30d6LAA7/5nb0e48Bp/MctDfycCSs8VEgN29I= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -306,6 +310,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= @@ -447,6 +453,8 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golift.io/starr v1.0.0 h1:IDSaSL+ZYxdLT/Lg//dg/iwZ39LHO3D5CmbLCOgSXbI= diff --git a/pkg/doc.go b/pkg/doc.go index ab20147..98e50c5 100644 --- a/pkg/doc.go +++ b/pkg/doc.go @@ -18,5 +18,5 @@ type Torrent interface { type Downloader interface { GetAll() ([]Torrent, error) - Download(link, dir string) (Torrent, error) + Download(link, hash, dir string) (Torrent, error) } diff --git a/pkg/qbittorrent/qbittorrent.go b/pkg/qbittorrent/qbittorrent.go index 48e40b8..1ace58f 100644 --- a/pkg/qbittorrent/qbittorrent.go +++ b/pkg/qbittorrent/qbittorrent.go @@ -61,49 +61,45 @@ func (c *Client) GetAll() ([]pkg.Torrent, error) { return res, nil } -func (c *Client) Download(link, dir string) (pkg.Torrent, error) { +func (c *Client) Download(link, hash, dir string) (pkg.Torrent, error) { err := c.c.DownloadLinks([]string{link}, qbt.DownloadOptions{Savepath: &dir, Category: &c.category}) if err != nil { return nil, errors.Wrap(err, "qbt download") } - magnet, err := utils.Link2Magnet(link) - if err != nil { - return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) - } - - hash, err := utils.MagnetHash(magnet) - if err != nil { - return nil, errors.Wrap(err, "get hash") - } - return &Torrent{hash: hash, c: c.c}, nil } -func NewTorrent(info Info, link string) (*Torrent, error) { +func NewTorrentHash(info Info, hash string) (*Torrent, error) { c, err := NewClient(info.URL, info.User, info.Password) if err != nil { return nil, err } - magnet, err := utils.Link2Magnet(link) - if err != nil { - return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) - } - hash, err := utils.MagnetHash(magnet) - if err != nil { - return nil, err - } t := &Torrent{ c: c.c, hash: hash, } if !t.Exists() { - return nil, errors.Errorf("torrent not exist: %v", magnet) + return nil, errors.Errorf("torrent not exist: %v", hash) } return t, nil } +func NewTorrent(info Info, link string) (*Torrent, error) { + magnet, err := utils.Link2Magnet(link) + if err != nil { + return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) + } + + hash, err := utils.MagnetHash(magnet) + if err != nil { + return nil, err + } + + return NewTorrentHash(info, hash) +} + type Torrent struct { c *qbt.Client hash string diff --git a/pkg/transmission/transmission.go b/pkg/transmission/transmission.go index 722c9f4..788549f 100644 --- a/pkg/transmission/transmission.go +++ b/pkg/transmission/transmission.go @@ -63,7 +63,7 @@ func (c *Client) GetAll() ([]pkg.Torrent, error) { return torrents, nil } -func (c *Client) Download(link, dir string) (pkg.Torrent, error) { +func (c *Client) Download(link, hash, dir string) (pkg.Torrent, error) { t, err := c.c.TorrentAdd(context.TODO(), transmissionrpc.TorrentAddPayload{ Filename: &link, @@ -74,16 +74,6 @@ func (c *Client) Download(link, dir string) (pkg.Torrent, error) { } log.Debugf("get torrent info: %+v", t) - magnet, err := utils.Link2Magnet(link) - if err != nil { - return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) - } - - hash, err := utils.MagnetHash(magnet) - if err != nil { - return nil, errors.Wrap(err, "get hash") - } - return &Torrent{ hash: hash, c: c.c, @@ -91,33 +81,38 @@ func (c *Client) Download(link, dir string) (pkg.Torrent, error) { }, err } -func NewTorrent(cfg Config, link string) (*Torrent, error) { +func NewTorrentHash(cfg Config, hash string) (*Torrent, error) { c, err := NewClient(cfg) if err != nil { return nil, err } - magnet, err := utils.Link2Magnet(link) - if err != nil { - return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) - } - - hash, err := utils.MagnetHash(magnet) - if err != nil { - return nil, err - } - t := &Torrent{ c: c.c, hash: hash, //cfg: cfg, } if !t.Exists() { - return nil, errors.Errorf("torrent not exist: %v", magnet) + return nil, errors.Errorf("torrent not exist: %v", hash) } return t, nil } +func NewTorrent(cfg Config, link string) (*Torrent, error) { + + magnet, err := utils.Link2Magnet(link) + if err != nil { + return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) + } + + hash, err := utils.MagnetHash(magnet) + if err != nil { + return nil, err + } + + return NewTorrentHash(cfg, hash) +} + type Torrent struct { //t *transmissionrpc.Torrent c *transmissionrpc.Client diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 242b792..1b215a0 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -222,6 +222,33 @@ func isWSL() bool { return strings.Contains(strings.ToLower(string(releaseData)), "microsoft") } +func Link2Hash(link string) (string, error) { + if strings.HasPrefix(strings.ToLower(link), "magnet:") { + return MagnetHash(link) + } + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse //do not follow redirects + }, + } + + resp, err := client.Get(link) + if err != nil { + return "", errors.Wrap(err, "get link") + } + defer resp.Body.Close() + if resp.StatusCode >= 300 && resp.StatusCode < 400 { + //redirects + tourl := resp.Header.Get("Location") + return Link2Hash(tourl) + } + info, err := metainfo.Load(resp.Body) + if err != nil { + return "", errors.Wrap(err, "parse response") + } + return info.HashInfoBytes().AsString(), nil +} + func Link2Magnet(link string) (string, error) { if strings.HasPrefix(strings.ToLower(link), "magnet:") { return link, nil diff --git a/server/core/client.go b/server/core/client.go index 440eb72..639cf1b 100644 --- a/server/core/client.go +++ b/server/core/client.go @@ -59,27 +59,55 @@ func (c *Client) reloadTasks() { } if dl.Implementation == downloadclients.ImplementationTransmission { - to, err := transmission.NewTorrent(transmission.Config{ - URL: dl.URL, - User: dl.User, - Password: dl.Password, - }, t.Link) - if err != nil { - log.Warnf("get task error: %v", err) - continue + if t.Hash != "" { //优先使用hash + to, err := transmission.NewTorrentHash(transmission.Config{ + URL: dl.URL, + User: dl.User, + Password: dl.Password, + }, t.Hash) + if err != nil { + log.Warnf("get task error: %v", err) + continue + } + c.tasks[t.ID] = &Task{Torrent: to} + } else if t.Link != "" { + to, err := transmission.NewTorrent(transmission.Config{ + URL: dl.URL, + User: dl.User, + Password: dl.Password, + }, t.Link) + if err != nil { + log.Warnf("get task error: %v", err) + continue + } + c.tasks[t.ID] = &Task{Torrent: to} } - c.tasks[t.ID] = &Task{Torrent: to} } else if dl.Implementation == downloadclients.ImplementationQbittorrent { - to, err := qbittorrent.NewTorrent(qbittorrent.Info{ - URL: dl.URL, - User: dl.User, - Password: dl.Password, - }, t.Link) - if err != nil { - log.Warnf("get task error: %v", err) - continue + if t.Hash != "" { + to, err := qbittorrent.NewTorrentHash(qbittorrent.Info{ + URL: dl.URL, + User: dl.User, + Password: dl.Password, + }, t.Hash) + if err != nil { + log.Warnf("get task error: %v", err) + continue + } + c.tasks[t.ID] = &Task{Torrent: to} + + } else if t.Link != "" { + to, err := qbittorrent.NewTorrent(qbittorrent.Info{ + URL: dl.URL, + User: dl.User, + Password: dl.Password, + }, t.Link) + if err != nil { + log.Warnf("get task error: %v", err) + continue + } + c.tasks[t.ID] = &Task{Torrent: to} + } - c.tasks[t.ID] = &Task{Torrent: to} } } diff --git a/server/core/resources.go b/server/core/resources.go index 02f3058..795f892 100644 --- a/server/core/resources.go +++ b/server/core/resources.go @@ -6,6 +6,7 @@ import ( "polaris/ent" "polaris/ent/episode" "polaris/ent/history" + "polaris/ent/media" "polaris/log" "polaris/pkg/metadata" "polaris/pkg/notifier/message" @@ -16,95 +17,13 @@ import ( ) func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum int, episodeNums ...int) (*string, error) { - trc, dlc, err := c.GetDownloadClient() + + series, err := c.db.GetMedia(seriesId) if err != nil { - return nil, errors.Wrap(err, "connect transmission") - } - series := c.db.GetMediaDetails(seriesId) - if series == nil { return nil, fmt.Errorf("no tv series of id %v", seriesId) } - //check space available - downloadDir := c.db.GetDownloadDir() - size := utils.AvailableSpace(downloadDir) - if size < uint64(r1.Size) { - log.Errorf("space available %v, space needed %v", size, r1.Size) - return nil, errors.New("no enough space") - } - - // 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) - // } - - 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, - EpisodeNums: episodeNums, - SeasonNum: seasonNum, - SourceTitle: r1.Name, - TargetDir: dir, - Status: history.StatusRunning, - Size: int(r1.Size), - //Saved: torrent.Save(), - Link: r1.Link, - DownloadClientID: dlc.ID, - IndexerID: r1.IndexerId, - }) - if err != nil { - return nil, errors.Wrap(err, "save record") - } - - torrent, err := trc.Download(r1.Link, downloadDir) - if err != nil { - return nil, errors.Wrap(err, "downloading") - } - torrent.Start() - - c.tasks[history.ID] = &Task{Torrent: torrent} - 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 + return c.downloadTorrent(series, r1, seasonNum, episodeNums...) } /* @@ -192,53 +111,95 @@ lo: return torrentNames, nil } -func (c *Client) DownloadMovie(m *ent.Media, link, name string, size int64, indexerID int) (*string, error) { +func (c *Client) DownloadMovie(m *ent.Media, r1 torznab.Result) (*string, error) { + return c.downloadTorrent(m, r1, 0) +} + +func (c *Client) downloadTorrent(m *ent.Media, r1 torznab.Result, seasonNum int, episodeNums ...int) (*string, error) { trc, dlc, err := c.GetDownloadClient() if err != nil { return nil, errors.Wrap(err, "connect transmission") } - // magnet, err := utils.Link2Magnet(link) - // if err != nil { - // return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err) - // } - torrent, err := trc.Download(link, c.db.GetDownloadDir()) + //check space available + downloadDir := c.db.GetDownloadDir() + size := utils.AvailableSpace(downloadDir) + if size < uint64(r1.Size) { + log.Errorf("space available %v, space needed %v", size, r1.Size) + return nil, errors.New("no enough space") + } + + var name = r1.Name + var targetDir = m.TargetDir + if m.MediaType == media.MediaTypeTv { //tv download + targetDir = fmt.Sprintf("%s/Season %02d/", m.TargetDir, seasonNum) + + if len(episodeNums) > 0 { + for _, epNum := range episodeNums { + ep, err := c.db.GetEpisode(m.ID, seasonNum, epNum) + if err != 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) + } + } + 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 { //season package download + name = fmt.Sprintf("全集 (%s)", name) + c.db.SetSeasonAllEpisodeStatus(m.ID, seasonNum, episode.StatusDownloading) + } + + } else { + ep, _ := c.db.GetMovieDummyEpisode(m.ID) + if ep.Status == episode.StatusMissing { + c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) + } + + } + hash, err := utils.Link2Hash(r1.Link) + if err != nil { + return nil, errors.Wrap(err, "get hash") + } + history, err := c.db.SaveHistoryRecord(ent.History{ + MediaID: m.ID, + EpisodeNums: episodeNums, + SeasonNum: seasonNum, + SourceTitle: r1.Name, + TargetDir: targetDir, + Status: history.StatusRunning, + Size: int(r1.Size), + //Saved: torrent.Save(), + Link: r1.Link, + Hash: hash, + DownloadClientID: dlc.ID, + IndexerID: r1.IndexerId, + }) + if err != nil { + return nil, errors.Wrap(err, "save record") + } + + torrent, err := trc.Download(r1.Link, hash, downloadDir) if err != nil { return nil, errors.Wrap(err, "downloading") } torrent.Start() - if name == "" { - name = m.OriginalName - } - go func() { - ep, _ := c.db.GetMovieDummyEpisode(m.ID) - history, err := c.db.SaveHistoryRecord(ent.History{ - MediaID: m.ID, - EpisodeID: ep.ID, - SourceTitle: name, - TargetDir: m.TargetDir, - Status: history.StatusRunning, - Size: int(size), - //Saved: torrent.Save(), - Link: link, - DownloadClientID: dlc.ID, - IndexerID: indexerID, - }) - if err != nil { - log.Errorf("save history error: %v", err) - } - - c.tasks[history.ID] = &Task{Torrent: torrent} - - if ep.Status == episode.StatusMissing { - c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) - } - - }() + c.tasks[history.ID] = &Task{Torrent: torrent} c.sendMsg(fmt.Sprintf(message.BeginDownload, name)) - log.Infof("success add %s to download task", name) - return &name, nil + log.Infof("success add %s to download task", r1.Name) + + return &r1.Name, nil } diff --git a/server/core/scheduler.go b/server/core/scheduler.go index 2ae923f..646a3d4 100644 --- a/server/core/scheduler.go +++ b/server/core/scheduler.go @@ -101,7 +101,7 @@ func (c *Client) checkTasks() error { } else if r.Status == history.StatusRunning { log.Infof("task is done: %v", name) c.sendMsg(fmt.Sprintf(message.DownloadComplete, name)) - go c.postTaskProcessing(id) + go c.postTaskProcessing(id) } } } @@ -273,7 +273,7 @@ func (c *Client) moveCompletedTask(id int) (err1 error) { if series == nil { return nil } - + st := c.db.GetStorage(series.StorageID) log.Infof("move task files to target dir: %v", r.TargetDir) stImpl, err := c.GetStorage(st.ID, series.MediaType) @@ -459,18 +459,15 @@ func (c *Client) DownloadMovieByID(id int) (string, error) { return "", nil } - if name, err := c.downloadMovieSingleEpisode(ep, detail.TargetDir); err != nil { + if name, err := c.downloadMovieSingleEpisode(detail.Media, ep); err != nil { return "", errors.Wrap(err, "download movie") } else { return name, nil } } -func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) (string, error) { - trc, dlc, err := c.GetDownloadClient() - if err != nil { - return "", errors.Wrap(err, "connect transmission") - } +func (c *Client) downloadMovieSingleEpisode(m *ent.Media, ep *ent.Episode) (string, error) { + qiangban := c.db.GetSetting(db.SettingAllowQiangban) allowQiangban := false if qiangban == "true" { @@ -484,43 +481,16 @@ func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) ( FilterQiangban: !allowQiangban, }) if err != nil { - return "", errors.Wrap(err, "search movie") } r1 := res[0] log.Infof("begin download torrent resource: %v", r1.Name) - magnet, err := utils.Link2Magnet(r1.Link) + s, err := c.downloadTorrent(m, r1, 0) if err != nil { - return "", errors.Errorf("converting link to magnet error, link: %v, error: %v", r1.Link, err) + return "", err } - - torrent, err := trc.Download(magnet, c.db.GetDownloadDir()) - if err != nil { - return "", errors.Wrap(err, "downloading") - } - torrent.Start() - - history, err := c.db.SaveHistoryRecord(ent.History{ - MediaID: ep.MediaID, - EpisodeID: ep.ID, - SourceTitle: r1.Name, - TargetDir: targetDir, - Status: history.StatusRunning, - Size: int(r1.Size), - //Saved: torrent.Save(), - Link: magnet, - DownloadClientID: dlc.ID, - IndexerID: r1.IndexerId, - }) - if err != nil { - log.Errorf("save history error: %v", err) - } - - c.tasks[history.ID] = &Task{Torrent: torrent} - - c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) - return r1.Name, nil + return *s, nil } func (c *Client) checkAllSeriesNewSeason() error { diff --git a/server/resources.go b/server/resources.go index fe6d17f..9020064 100644 --- a/server/resources.go +++ b/server/resources.go @@ -164,7 +164,13 @@ func (s *Server) DownloadTorrent(c *gin.Context) (interface{}, error) { return s.core.DownloadEpisodeTorrent(res, in.MediaID, in.Season, in.Episode) } else { //movie - return s.core.DownloadMovie(m, in.Link, in.Name, in.Size, in.IndexerId) + name := in.Name + if name == "" { + name = m.OriginalName + } + + res := torznab.Result{Name: name, Link: in.Link, Size: in.Size, IndexerId: in.IndexerId} + return s.core.DownloadMovie(m, res) } }