Добавил реализацию
This commit is contained in:
@@ -26,6 +26,12 @@ type QBittorrent interface {
|
||||
Add(ctx context.Context, ar qbt.AddRequest) error
|
||||
}
|
||||
|
||||
// Namer выводит человекочитаемое отображаемое имя торрента из контекста.
|
||||
// Пустой результат → имя в qBittorrent не задаём. nil → шаг пропускается.
|
||||
type Namer interface {
|
||||
DeriveName(ctx context.Context, contextText, hint string) string
|
||||
}
|
||||
|
||||
// Config — параметры добавления в qBittorrent.
|
||||
type Config struct {
|
||||
Category string
|
||||
@@ -36,13 +42,15 @@ type Config struct {
|
||||
type Service struct {
|
||||
store Store
|
||||
qbt QBittorrent
|
||||
namer Namer
|
||||
cfg Config
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// New собирает сервис приёма.
|
||||
func New(st Store, qb QBittorrent, cfg Config, log *slog.Logger) *Service {
|
||||
return &Service{store: st, qbt: qb, cfg: cfg, log: log}
|
||||
// New собирает сервис приёма. namer опционален (nil → отображаемое имя не
|
||||
// выводится; qBittorrent оставит своё).
|
||||
func New(st Store, qb QBittorrent, namer Namer, cfg Config, log *slog.Logger) *Service {
|
||||
return &Service{store: st, qbt: qb, namer: namer, cfg: cfg, log: log}
|
||||
}
|
||||
|
||||
// Request — входной запрос приёма.
|
||||
@@ -82,6 +90,15 @@ func (s *Service) Ingest(ctx context.Context, req Request) (Result, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Отображаемое имя для списка qBit — best-effort: не валит приём.
|
||||
// Выводится синхронно (param rename действует только при добавлении) и
|
||||
// ДО CreateDownload, чтобы возможный медленный вызов LLM не расширял окно
|
||||
// «строка в БД есть, в qBittorrent ещё нет». Имя от строки БД не зависит.
|
||||
var rename string
|
||||
if s.namer != nil {
|
||||
rename = s.namer.DeriveName(ctx, req.Context, info.DisplayName)
|
||||
}
|
||||
|
||||
d := &store.Download{
|
||||
SourceType: store.SourceMagnet,
|
||||
SourceRef: source,
|
||||
@@ -99,6 +116,7 @@ func (s *Service) Ingest(ctx context.Context, req Request) (Result, error) {
|
||||
URLs: []string{source},
|
||||
Category: s.cfg.Category,
|
||||
SavePath: s.cfg.SavePath,
|
||||
Rename: rename,
|
||||
})
|
||||
if addErr != nil {
|
||||
s.log.Warn("ingest: qbittorrent add failed, marking download failed",
|
||||
|
||||
@@ -58,8 +58,27 @@ func (f *fakeQbt) Add(_ context.Context, ar qbt.AddRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fakeNamer возвращает заранее заданное имя; фиксирует переданные аргументы.
|
||||
type fakeNamer struct {
|
||||
name string
|
||||
gotContext string
|
||||
gotHint string
|
||||
called bool
|
||||
}
|
||||
|
||||
func (f *fakeNamer) DeriveName(_ context.Context, contextText, hint string) string {
|
||||
f.called = true
|
||||
f.gotContext = contextText
|
||||
f.gotHint = hint
|
||||
return f.name
|
||||
}
|
||||
|
||||
func newService(st Store, qb QBittorrent) *Service {
|
||||
return New(st, qb, Config{Category: "jellybit", SavePath: "/srv/media/downloads"},
|
||||
return newServiceWithNamer(st, qb, nil)
|
||||
}
|
||||
|
||||
func newServiceWithNamer(st Store, qb QBittorrent, nm Namer) *Service {
|
||||
return New(st, qb, nm, Config{Category: "jellybit", SavePath: "/srv/media/downloads"},
|
||||
slog.New(slog.NewTextHandler(io.Discard, nil)))
|
||||
}
|
||||
|
||||
@@ -94,6 +113,36 @@ func TestIngestHappyPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestSetsDisplayName(t *testing.T) {
|
||||
fs := &fakeStore{}
|
||||
fq := &fakeQbt{}
|
||||
nm := &fakeNamer{name: "Дюна: Часть вторая (2024)"}
|
||||
_, err := newServiceWithNamer(fs, fq, nm).Ingest(context.Background(),
|
||||
Request{Source: sampleMagnet, Context: "Дюна 2"})
|
||||
if err != nil {
|
||||
t.Fatalf("Ingest: %v", err)
|
||||
}
|
||||
if !nm.called || nm.gotContext != "Дюна 2" || nm.gotHint != "Dune" {
|
||||
t.Errorf("namer получил context=%q hint=%q (called=%v)", nm.gotContext, nm.gotHint, nm.called)
|
||||
}
|
||||
if len(fq.added) != 1 || fq.added[0].Rename != "Дюна: Часть вторая (2024)" {
|
||||
t.Errorf("rename = %q, want %q", fq.added[0].Rename, "Дюна: Часть вторая (2024)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestEmptyNameOmitsRename(t *testing.T) {
|
||||
fs := &fakeStore{}
|
||||
fq := &fakeQbt{}
|
||||
nm := &fakeNamer{name: ""} // имя не получено
|
||||
if _, err := newServiceWithNamer(fs, fq, nm).Ingest(context.Background(),
|
||||
Request{Source: sampleMagnet}); err != nil {
|
||||
t.Fatalf("Ingest: %v", err)
|
||||
}
|
||||
if len(fq.added) != 1 || fq.added[0].Rename != "" {
|
||||
t.Errorf("rename = %q, want пусто", fq.added[0].Rename)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestIdempotent(t *testing.T) {
|
||||
existing := &store.Download{ID: 7, State: store.StateDownloading}
|
||||
fs := &fakeStore{active: existing}
|
||||
|
||||
Reference in New Issue
Block a user