ingest: закрыты мелочи приёма — вырожденное имя, контракт Result, корреляция add
- имя раздачи нормализуется на границе разбора: вырожденное `-` (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки вывода имени - контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и удерживается структурно; три транспорта перестали обещать идентификатор, которого нет, и коррелируют отказ по request_id - scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах воркера — записи об отказе qBittorrent и метабаз получили download_id и infohash; граница разбора bencode записана в docs/research
This commit is contained in:
@@ -66,7 +66,9 @@ type Request struct {
|
||||
Context string // подсказка для распознавания (опц.)
|
||||
}
|
||||
|
||||
// Result — итог приёма.
|
||||
// Result — итог приёма. При ненулевой ошибке Ingest возвращает НУЛЕВОЙ Result:
|
||||
// идентификатор загрузки, хеши, состояние и признак дедупликации не
|
||||
// публикуются (см. Ingest).
|
||||
type Result struct {
|
||||
DownloadID string
|
||||
Infohashes []string // все хеши источника (гибридный magnet: v1 и v2, v1 первым)
|
||||
@@ -78,7 +80,24 @@ type Result struct {
|
||||
// полей ссылки, дедуплицирует по активной задаче, иначе сохраняет загрузку в
|
||||
// `catched` и сразу возвращает результат. Добавление в qBittorrent и вывод
|
||||
// имени выполняет worker (см. download-tracking).
|
||||
func (s *Service) Ingest(ctx context.Context, req Request) (Result, error) {
|
||||
//
|
||||
// Контракт: на ЛЮБОМ пути ошибки возвращается нулевой Result. Приём не создаёт
|
||||
// наблюдаемых последствий раньше, чем способен вернуть успех, а всё, что может
|
||||
// отказать после заведения загрузки, делает worker. Транспорты на это
|
||||
// опираются и не обещают идентификатора, которого нет: HTTP коррелирует отказ
|
||||
// по request_id, Telegram — ключа не даёт (см. ingest-спеку, требование
|
||||
// «Результат приёма при ошибке пуст»).
|
||||
//
|
||||
// Гарантия структурная — обнуление в одном defer, а не аккуратность каждой
|
||||
// ветки возврата: перечень веток растёт, и именно расхождение перечня с
|
||||
// комментариями транспортов породило исходный дефект.
|
||||
func (s *Service) Ingest(ctx context.Context, req Request) (res Result, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
res = Result{}
|
||||
}
|
||||
}()
|
||||
|
||||
src, err := s.parse(req)
|
||||
if err != nil {
|
||||
// Невалидный источник — норма (адресат не команда, а пользователь, и он
|
||||
@@ -179,10 +198,10 @@ func (s *Service) parse(req Request) (parsedSource, error) {
|
||||
}
|
||||
// SourceRef — человекочитаемый референс (имя раздачи), НЕ адрес
|
||||
// добавления: torrent добавляется байтами (см. worker), не по SourceRef.
|
||||
// Фолбек на имя файла, если у раздачи нет содержательного имени
|
||||
// (пустое или NoName-сентинел "-").
|
||||
ref := strings.TrimSpace(info.DisplayName)
|
||||
if ref == "" || ref == "-" {
|
||||
// Фолбек на имя файла, если у раздачи нет содержательного имени:
|
||||
// вырожденное значение отбросил разборщик, здесь остаётся пустота.
|
||||
ref := info.DisplayName
|
||||
if ref == "" {
|
||||
ref = strings.TrimSpace(req.TorrentName)
|
||||
}
|
||||
return parsedSource{
|
||||
|
||||
Reference in New Issue
Block a user