From 26783bfcb59ed0521deb86ae7178b3fcc67407db Mon Sep 17 00:00:00 2001 From: Ivan Bravo Date: Sat, 27 Dec 2025 19:58:54 +0100 Subject: [PATCH] Verbose mode on classifier --- internal/classifier/action.go | 7 +++- internal/classifier/action_add_tag.go | 1 + .../action_attach_local_content_by_id.go | 13 +++++++ .../action_attach_local_content_by_search.go | 14 +++++++ .../action_attach_tmdb_content_by_id.go | 16 ++++++++ .../action_attach_tmdb_content_by_search.go | 24 ++++++++++++ internal/classifier/action_delete.go | 1 + internal/classifier/action_find_match.go | 1 + internal/classifier/action_if_else.go | 10 ++++- internal/classifier/action_parse_date.go | 2 + .../classifier/action_parse_video_content.go | 17 +++++++++ internal/classifier/action_run_workflow.go | 1 + .../classifier/action_set_content_type.go | 1 + internal/classifier/action_unmatched.go | 1 + internal/classifier/classifier_test.go | 3 ++ internal/classifier/condition_and.go | 11 +++++- internal/classifier/condition_expression.go | 5 +++ internal/classifier/condition_not.go | 1 + internal/classifier/condition_or.go | 11 +++++- internal/classifier/config.go | 1 + internal/classifier/dependencies.go | 38 +++++++++++++++++++ internal/classifier/factory.go | 10 +++++ internal/classifier/runner.go | 17 ++++++++- 23 files changed, 199 insertions(+), 7 deletions(-) diff --git a/internal/classifier/action.go b/internal/classifier/action.go index 653ae5364..1e8d318e0 100644 --- a/internal/classifier/action.go +++ b/internal/classifier/action.go @@ -3,6 +3,7 @@ package classifier import ( "errors" "fmt" + "strings" "github.com/bitmagnet-io/bitmagnet/internal/classifier/classification" ) @@ -61,14 +62,18 @@ outer: return action{}, errors.Join(errs...) } + path := ctx.path + return action{func(ctx executionContext) (classification.Result, error) { - for _, a := range actions { + for i, a := range actions { + ctx.logger = ctx._logger.Named(strings.Join(path, ".")+".["+fmt.Sprint(i)+"]") result, err := a.run(ctx) if err != nil { return classification.Result{}, err } ctx = ctx.withResult(result) } + ctx.logger = ctx._logger return ctx.result, nil }}, errors.Join(errs...) } diff --git a/internal/classifier/action_add_tag.go b/internal/classifier/action_add_tag.go index e068dbec3..30f540bca 100644 --- a/internal/classifier/action_add_tag.go +++ b/internal/classifier/action_add_tag.go @@ -45,6 +45,7 @@ func (addTagAction) compileAction(ctx compilerContext) (action, error) { return action{ func(ctx executionContext) (classification.Result, error) { + ctx.logger.Info(tags) cl := ctx.result if cl.Tags == nil { cl.Tags = make(map[string]struct{}) diff --git a/internal/classifier/action_attach_local_content_by_id.go b/internal/classifier/action_attach_local_content_by_id.go index 10035512c..9f879ee71 100644 --- a/internal/classifier/action_attach_local_content_by_id.go +++ b/internal/classifier/action_attach_local_content_by_id.go @@ -27,6 +27,7 @@ func (attachLocalContentByIDAction) compileAction(ctx compilerContext) (action, run: func(ctx executionContext) (classification.Result, error) { cl := ctx.result if ctx.torrent.Hint.IsNil() || !ctx.torrent.Hint.ContentSource.Valid { + ctx.logger.Info("hint missing or invalid") return cl, classification.ErrUnmatched } content, err := ctx.search.ContentByID(ctx, model.ContentRef{ @@ -35,8 +36,20 @@ func (attachLocalContentByIDAction) compileAction(ctx compilerContext) (action, ID: ctx.torrent.Hint.ContentID.String, }) if err != nil { + ctx.logger.Infow( + "local search failed", + "source",ctx.torrent.Hint.ContentSource.String, + "type",ctx.torrent.Hint.ContentType.String(), + "id",ctx.torrent.Hint.ContentID.String) return cl, err } + ctx.logger.Infow( + "local search succeeded", + "source",ctx.torrent.Hint.ContentSource.String, + "type", content.Type.String(), + "id",content.ID, + "title",content.Title, + "year",content.ReleaseYear.String()) cl.AttachContent(&content) return cl, nil }, diff --git a/internal/classifier/action_attach_local_content_by_search.go b/internal/classifier/action_attach_local_content_by_search.go index 15b1f9de0..9d1cf53a8 100644 --- a/internal/classifier/action_attach_local_content_by_search.go +++ b/internal/classifier/action_attach_local_content_by_search.go @@ -26,6 +26,7 @@ func (attachLocalContentBySearchAction) compileAction(ctx compilerContext) (acti run: func(ctx executionContext) (classification.Result, error) { cl := ctx.result if !cl.ContentType.Valid || !cl.BaseTitle.Valid { + ctx.logger.Info("invalid content type or base title") return cl, classification.ErrUnmatched } content, err := ctx.search.ContentBySearch( @@ -35,8 +36,21 @@ func (attachLocalContentBySearchAction) compileAction(ctx compilerContext) (acti cl.Date.Year, ) if err != nil { + ctx.logger.Infow( + "local search failed", + "type",cl.ContentType.ContentType.String(), + "base_title",cl.BaseTitle.String, + "date",cl.Date.IsoDateString()) return cl, err } + ctx.logger.Infow( + "local search succeeded", + "base_title",cl.BaseTitle.String, + "date",cl.Date.IsoDateString(), + "id",content.ID, + "type",content.Type.String(), + "title",content.Title, + "year",content.ReleaseYear.String()) cl.AttachContent(&content) return cl, nil }, diff --git a/internal/classifier/action_attach_tmdb_content_by_id.go b/internal/classifier/action_attach_tmdb_content_by_id.go index ed6e7660d..4708bd59f 100644 --- a/internal/classifier/action_attach_tmdb_content_by_id.go +++ b/internal/classifier/action_attach_tmdb_content_by_id.go @@ -31,6 +31,7 @@ func (attachTMDBContentByIDAction) compileAction(ctx compilerContext) (action, e var ref model.ContentRef maybeRef := ctx.torrent.Hint.ContentRef() if !maybeRef.Valid { + ctx.logger.Info("hint missing or invalid") return cl, classification.ErrUnmatched } ref = maybeRef.Val @@ -42,12 +43,14 @@ func (attachTMDBContentByIDAction) compileAction(ctx compilerContext) (action, e case model.SourceTmdb: id, err := strconv.Atoi(ref.ID) if err != nil { + ctx.logger.Info("invalid tmdb id") return cl, classification.ErrUnmatched } tmdbID = int64(id) default: id, err := ctx.tmdbGetTMDBIDByExternalID(ref) if err != nil { + ctx.logger.Info("failed to get tmdb id by external id") return cl, err } tmdbID = id @@ -57,16 +60,29 @@ func (attachTMDBContentByIDAction) compileAction(ctx compilerContext) (action, e case model.ContentTypeMovie, model.ContentTypeXxx: c, err := ctx.tmdbGetMovieByTMDBID(tmdbID) if err != nil { + ctx.logger.Infow("movie not found", "id", tmdbID) return cl, err } + ctx.logger.Infow( + "movie", + "id", tmdbID, + "title", c.Title, + "year", c.ReleaseYear.String()) content = &c case model.ContentTypeTvShow: c, err := ctx.tmdbGetTVShowByTMDBID(tmdbID) if err != nil { + ctx.logger.Infow("tv show not found", "id", tmdbID) return cl, err } + ctx.logger.Infow( + "tv show", + "id", tmdbID, + "title", c.Title, + "year", c.ReleaseYear.String()) content = &c default: + ctx.logger.Info("invalid content type") return cl, classification.ErrUnmatched } cl.AttachContent(content) diff --git a/internal/classifier/action_attach_tmdb_content_by_search.go b/internal/classifier/action_attach_tmdb_content_by_search.go index 6449fcd75..d9ff8f279 100644 --- a/internal/classifier/action_attach_tmdb_content_by_search.go +++ b/internal/classifier/action_attach_tmdb_content_by_search.go @@ -27,6 +27,7 @@ func (attachTmdbContentBySearchAction) compileAction(ctx compilerContext) (actio run: func(ctx executionContext) (classification.Result, error) { cl := ctx.result if !cl.BaseTitle.Valid { + ctx.logger.Info("invalid base title") return cl, classification.ErrUnmatched } var content *model.Content @@ -34,17 +35,40 @@ func (attachTmdbContentBySearchAction) compileAction(ctx compilerContext) (actio case model.ContentTypeTvShow: result, searchErr := ctx.tmdbSearchTVShow(cl.BaseTitle.String, cl.Date.Year) if searchErr != nil { + ctx.logger.Infow( + "tv show not found", + "base_title", cl.BaseTitle.String, + "date", cl.Date.IsoDateString()) return cl, searchErr } + ctx.logger.Infow( + "tv show", + "base_title", cl.BaseTitle.String, + "date", cl.Date.IsoDateString(), + "id", result.ID, + "title", result.Title, + "year", result.ReleaseYear.String()) content = &result default: if len(cl.Episodes) > 0 { + ctx.logger.Info("content type is not tv show but episodes are present") return cl, classification.ErrUnmatched } result, searchErr := ctx.tmdbSearchMovie(cl.BaseTitle.String, cl.Date.Year) if searchErr != nil { + ctx.logger.Infow( + "movie not found", + "base_title", cl.BaseTitle.String, + "date", cl.Date.IsoDateString()) return cl, searchErr } + ctx.logger.Infow( + "movie", + "base_title", cl.BaseTitle.String, + "date", cl.Date.IsoDateString(), + "id", result.ID, + "title", result.Title, + "year", result.ReleaseYear.String()) content = &result } cl.AttachContent(content) diff --git a/internal/classifier/action_delete.go b/internal/classifier/action_delete.go index 8aaf36200..5ed0b789e 100644 --- a/internal/classifier/action_delete.go +++ b/internal/classifier/action_delete.go @@ -24,6 +24,7 @@ func (deleteAction) compileAction(ctx compilerContext) (action, error) { return action{ run: func(ctx executionContext) (classification.Result, error) { + ctx.logger.Info() return ctx.result, classification.RuntimeError{ Cause: classification.ErrDeleteTorrent, Path: path, diff --git a/internal/classifier/action_find_match.go b/internal/classifier/action_find_match.go index f0e896ea7..3c7272513 100644 --- a/internal/classifier/action_find_match.go +++ b/internal/classifier/action_find_match.go @@ -45,6 +45,7 @@ func (findMatchAction) compileAction(ctx compilerContext) (action, error) { return action{ func(ctx executionContext) (classification.Result, error) { + ctx.logger.Info() for _, action := range actions { result, err := action.run(ctx) if err != nil { diff --git a/internal/classifier/action_if_else.go b/internal/classifier/action_if_else.go index 2eff3b846..601b01d95 100644 --- a/internal/classifier/action_if_else.go +++ b/internal/classifier/action_if_else.go @@ -72,13 +72,21 @@ func (ifElseAction) compileAction(ctx compilerContext) (action, error) { return action{ run: func(ctx executionContext) (classification.Result, error) { - if result, err := cond.check(ctx); err != nil { + ctx.logger.Info() + logger:=ctx.logger + ctx.logger=logger.Named("if_else.condition") + result, err := cond.check(ctx) + ctx.logger=logger + if err != nil { + ctx.logger.Info("error evaluating condition") return classification.Result{}, err } else if result { + ctx.logger.Named("if_else.if_action").Info() if ifAction.run != nil { return ifAction.run(ctx) } } else { + ctx.logger.Named("if_else.else_action").Info() if elseAction.run != nil { return elseAction.run(ctx) } diff --git a/internal/classifier/action_parse_date.go b/internal/classifier/action_parse_date.go index fe17f9c9c..b52ffa2b9 100644 --- a/internal/classifier/action_parse_date.go +++ b/internal/classifier/action_parse_date.go @@ -27,10 +27,12 @@ func (parseDateAction) compileAction(ctx compilerContext) (action, error) { run: func(ctx executionContext) (classification.Result, error) { parsed := parsers.ParseDate(ctx.torrent.Name) if parsed.IsNil() { + ctx.logger.Info(nil) return ctx.result, classification.ErrUnmatched } cl := ctx.result cl.Date = parsed + ctx.logger.Info(parsed.IsoDateString()) return cl, nil }, }, nil diff --git a/internal/classifier/action_parse_video_content.go b/internal/classifier/action_parse_video_content.go index 30415f223..03da53f3c 100644 --- a/internal/classifier/action_parse_video_content.go +++ b/internal/classifier/action_parse_video_content.go @@ -1,8 +1,11 @@ package classifier import ( + "encoding/json" + "github.com/bitmagnet-io/bitmagnet/internal/classifier/classification" "github.com/bitmagnet-io/bitmagnet/internal/classifier/parsers" + "github.com/bitmagnet-io/bitmagnet/internal/model" ) const parseVideoContentName = "parse_video_content" @@ -28,8 +31,22 @@ func (parseVideoContentAction) compileAction(ctx compilerContext) (action, error parsed, err := parsers.ParseVideoContent(ctx.torrent, ctx.result) cl := ctx.result if err != nil { + ctx.logger.Info("error") return cl, err } + + nulldate := model.Date{ Year: 0, Month: 0, Day: 0 } + var mparsed map[string]any + jparsed, _ := json.Marshal(parsed) + json.Unmarshal(jparsed, &mparsed) + for k, v := range mparsed { + arr, ok := v.([]any) + if (ok && len(arr) == 0) || v == nil || v == 0 || v == "" || v == "0001-01-01T00:00:00Z" || v == "0000000000000000000000000000000000000000" || v == nulldate { + delete(mparsed, k) + } + } + + ctx.logger.Infow("result", "parsed", mparsed) cl.Merge(parsed) return cl, nil }, diff --git a/internal/classifier/action_run_workflow.go b/internal/classifier/action_run_workflow.go index 21518750a..0722368a0 100644 --- a/internal/classifier/action_run_workflow.go +++ b/internal/classifier/action_run_workflow.go @@ -46,6 +46,7 @@ func (runWorkflowAction) compileAction(ctx compilerContext) (action, error) { var err error cl := ctx.result for _, name := range names { + ctx.logger.Info(name) cl, err = ctx.workflows[name].run(ctx.withResult(cl)) if err != nil { return cl, err diff --git a/internal/classifier/action_set_content_type.go b/internal/classifier/action_set_content_type.go index 7503a8d0e..983099b52 100644 --- a/internal/classifier/action_set_content_type.go +++ b/internal/classifier/action_set_content_type.go @@ -31,6 +31,7 @@ func (setContentTypeAction) compileAction(ctx compilerContext) (action, error) { func(ctx executionContext) (classification.Result, error) { cl := ctx.result cl.ContentType = contentType + ctx.logger.Info(contentType.ContentType.String()) return cl, nil }, }, nil diff --git a/internal/classifier/action_unmatched.go b/internal/classifier/action_unmatched.go index e27087fd2..4bccffcff 100644 --- a/internal/classifier/action_unmatched.go +++ b/internal/classifier/action_unmatched.go @@ -24,6 +24,7 @@ func (unmatchedAction) compileAction(ctx compilerContext) (action, error) { return action{ run: func(ctx executionContext) (classification.Result, error) { + ctx.logger.Info() return ctx.result, classification.RuntimeError{Cause: classification.ErrUnmatched, Path: path} }, }, nil diff --git a/internal/classifier/classifier_test.go b/internal/classifier/classifier_test.go index e9677bc5e..54d26cc9b 100644 --- a/internal/classifier/classifier_test.go +++ b/internal/classifier/classifier_test.go @@ -12,6 +12,7 @@ import ( tmdb_mocks "github.com/bitmagnet-io/bitmagnet/internal/tmdb/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) func TestClassifier(t *testing.T) { @@ -250,6 +251,8 @@ func newTestClassifierMocks(t *testing.T) testClassifierMocks { dependencies: dependencies{ search: search, tmdbClient: tmdbClient, + _logger: zap.NewNop().Sugar(), + logger: zap.NewNop().Sugar(), }, }, search: search, diff --git a/internal/classifier/condition_and.go b/internal/classifier/condition_and.go index 5aebb130a..4e359e32c 100644 --- a/internal/classifier/condition_and.go +++ b/internal/classifier/condition_and.go @@ -1,5 +1,7 @@ package classifier +import "fmt" + const andName = "and" type andCondition struct{} @@ -39,8 +41,13 @@ func (andCondition) compileCondition(ctx compilerContext) (condition, error) { return condition{ check: func(ctx executionContext) (bool, error) { - for _, c := range conds { - if result, err := c.check(ctx); err != nil { + ctx.logger.Info() + for i, c := range conds { + logger := ctx.logger + ctx.logger = logger.Named(fmt.Sprintf("and.[%d]", i)) + result, err := c.check(ctx) + ctx.logger = logger + if err != nil { return false, err } else if !result { return false, nil diff --git a/internal/classifier/condition_expression.go b/internal/classifier/condition_expression.go index ce88a73aa..a3605b440 100644 --- a/internal/classifier/condition_expression.go +++ b/internal/classifier/condition_expression.go @@ -60,6 +60,8 @@ func (expressionCondition) compileCondition(ctx compilerContext) (condition, err return condition{}, ctx.error(err) } + source := ctx.source + return condition{ check: func(ctx executionContext) (bool, error) { vars := map[string]any{ @@ -71,12 +73,15 @@ func (expressionCondition) compileCondition(ctx compilerContext) (condition, err } result, _, err := prg.ContextEval(ctx.Context, vars) if err != nil { + ctx.logger.Infow("error","source",source) return false, err } bl, ok := result.Value().(bool) if !ok { + ctx.logger.Infow("not bool","source",source) return false, errors.New("not bool") } + ctx.logger.Infow(fmt.Sprint(bl),"source",source) return bl, nil }, }, nil diff --git a/internal/classifier/condition_not.go b/internal/classifier/condition_not.go index fa486eef4..0312bbc48 100644 --- a/internal/classifier/condition_not.go +++ b/internal/classifier/condition_not.go @@ -31,6 +31,7 @@ func (notCondition) compileCondition(ctx compilerContext) (condition, error) { return condition{ check: func(ctx executionContext) (bool, error) { + ctx.logger.Info() result, err := cond.check(ctx) return !result, err }, diff --git a/internal/classifier/condition_or.go b/internal/classifier/condition_or.go index 075cdc1dd..6a6c4c206 100644 --- a/internal/classifier/condition_or.go +++ b/internal/classifier/condition_or.go @@ -1,5 +1,7 @@ package classifier +import "fmt" + const orName = "or" type orCondition struct{} @@ -38,8 +40,13 @@ func (orCondition) compileCondition(ctx compilerContext) (condition, error) { } return condition{func(ctx executionContext) (bool, error) { - for _, c := range conds { - if result, err := c.check(ctx); err != nil { + ctx.logger.Info() + for i, c := range conds { + logger := ctx.logger + ctx.logger = logger.Named(fmt.Sprintf("or.[%d]", i)) + result, err := c.check(ctx) + ctx.logger = logger + if err != nil { return false, err } else if result { return true, nil diff --git a/internal/classifier/config.go b/internal/classifier/config.go index 42732c0b2..912472e5d 100644 --- a/internal/classifier/config.go +++ b/internal/classifier/config.go @@ -7,6 +7,7 @@ type Config struct { Flags map[string]any DeleteXxx bool Concurrency int + Verbose bool } func NewDefaultConfig() Config { diff --git a/internal/classifier/dependencies.go b/internal/classifier/dependencies.go index 077a010ca..ada0ebc4f 100644 --- a/internal/classifier/dependencies.go +++ b/internal/classifier/dependencies.go @@ -1,10 +1,48 @@ package classifier import ( + "encoding/json" + "regexp" + + "github.com/bitmagnet-io/bitmagnet/internal/model" "github.com/bitmagnet-io/bitmagnet/internal/tmdb" + "go.uber.org/zap" ) type dependencies struct { search LocalSearch tmdbClient tmdb.Client + _logger *zap.SugaredLogger + logger *zap.SugaredLogger } + +func (d *dependencies) CleanObj(o interface{}) map[string]any { + var isEmptyString = regexp.MustCompile("^(?:[0\\s]*|0001-01-01T00:00:00Z)$") + var m map[string]any + jhint,_ := json.Marshal(o) + json.Unmarshal(jhint, &m) + for k,v := range m { + if s, sOk := v.(string); sOk && isEmptyString.MatchString(s) { + delete(m, k) + continue + } + if a, aOk := v.([]any); aOk && len(a) == 0 { + delete(m, k) + continue + } + if f, fOk := v.(float64); fOk && f == 0.0 { + delete(m, k) + continue + } + d, dOk := v.(model.Date) + if dOk && (d.Year == 0 || d.Month == 0 || d.Day == 0) { + delete(m, k) + continue + } + if v == nil { + delete(m, k) + continue + } + } + return m +} \ No newline at end of file diff --git a/internal/classifier/factory.go b/internal/classifier/factory.go index 7e0d39745..04d57fe7d 100644 --- a/internal/classifier/factory.go +++ b/internal/classifier/factory.go @@ -7,6 +7,7 @@ import ( "github.com/bitmagnet-io/bitmagnet/internal/lazy" "github.com/bitmagnet-io/bitmagnet/internal/tmdb" "go.uber.org/fx" + "go.uber.org/zap" ) type Params struct { @@ -15,6 +16,7 @@ type Params struct { TmdbConfig tmdb.Config Search lazy.Lazy[search.Search] TmdbClient lazy.Lazy[tmdb.Client] + Logger *zap.SugaredLogger } type Result struct { @@ -36,6 +38,12 @@ func New(params Params) Result { return nil, err } + logger := zap.NewNop().Sugar(); + verbose := params.Config.Verbose + if verbose == true { + logger = params.Logger + } + return compiler{ options: []compilerOption{ compilerFeatures(defaultFeatures), @@ -47,6 +55,8 @@ func New(params Params) Result { semaphore: make(chan struct{}, 1), }, tmdbClient: tmdbClient, + _logger: logger, + logger: logger, }, }, nil }) diff --git a/internal/classifier/runner.go b/internal/classifier/runner.go index d2a261d3b..2c7478939 100644 --- a/internal/classifier/runner.go +++ b/internal/classifier/runner.go @@ -73,5 +73,20 @@ func (r runner) Run(ctx context.Context, workflow string, flags Flags, t model.T result: cl, } - return w.run(exCtx) + logger:=exCtx._logger.Named("runner") + logger.Infow( + "start", + "workflow", workflow, + "hash",t.InfoHash, + "name",t.Name, + "files",t.Files, + "createdAt",t.CreatedAt, + "updatedAt",t.UpdatedAt, + "hint",r.CleanObj(t.Hint)) + + result,error:=w.run(exCtx) + + logger.Infow("done", "workflow", workflow, "result", r.CleanObj(result)) + + return result,error }