Добавил "усыновление" существующих торрентов при добавлении тега или
категории
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"git.vakhrushev.me/av/jellybit/internal/qbt"
|
||||
"git.vakhrushev.me/av/jellybit/internal/store"
|
||||
)
|
||||
|
||||
// discover усыновляет новые раздачи: для каждого торрента с нашей категорией
|
||||
// ИЛИ тегом, чьего infohash ещё нет в БД, заводит задачу downloading. Дальше
|
||||
// её ведёт обычный reconcile. Вызывается под w.mu.
|
||||
//
|
||||
// Корректность при гонке с Ingest (другая горутина): Ingest пишет строку в
|
||||
// БД до добавления в qBit и ставит idempotency_key=infohash, на который есть
|
||||
// UNIQUE-индекс. Поэтому даже если тик и Ingest столкнутся в окне «проверил →
|
||||
// вставляю», второй INSERT упадёт на индексе, и adopt просто пропустит.
|
||||
func (w *Worker) discover(ctx context.Context, torrents []qbt.Torrent) {
|
||||
for _, t := range torrents {
|
||||
if w.tracked(t) {
|
||||
w.adopt(ctx, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tracked — относится ли торрент к jellybit (категория или тег из конфига).
|
||||
func (w *Worker) tracked(t qbt.Torrent) bool {
|
||||
if w.cfg.Category != "" && t.Category == w.cfg.Category {
|
||||
return true
|
||||
}
|
||||
return hasTag(t.Tags, w.cfg.Tag)
|
||||
}
|
||||
|
||||
// adopt заводит задачу под торрент, если его ещё не видели.
|
||||
func (w *Worker) adopt(ctx context.Context, t qbt.Torrent) {
|
||||
infohash := firstInfohash(t)
|
||||
if infohash == "" {
|
||||
return // нечем идентифицировать (напр. ещё metaDL без хэша)
|
||||
}
|
||||
exists, err := w.store.ExistsByInfohash(ctx, infohash)
|
||||
if err != nil {
|
||||
w.log.Warn("discover: exists check failed", "infohash", infohash, "err", err)
|
||||
return
|
||||
}
|
||||
if exists {
|
||||
return // уже усыновлён ранее (или обработан) — не трогаем
|
||||
}
|
||||
|
||||
d := &store.Download{
|
||||
SourceType: store.SourceMagnet,
|
||||
SourceRef: "magnet:?xt=urn:btih:" + infohash,
|
||||
Infohash: store.NullString(infohash),
|
||||
IdempotencyKey: store.NullString(infohash),
|
||||
State: store.StateDownloading,
|
||||
}
|
||||
id, err := w.store.CreateDownload(ctx, d)
|
||||
if err != nil {
|
||||
// Гонка: Ingest/другой тик мог вставить запись между проверкой и
|
||||
// вставкой — UNIQUE-индекс это отсёк. Если запись появилась, всё ок.
|
||||
if ex, _ := w.store.ExistsByInfohash(ctx, infohash); ex {
|
||||
return
|
||||
}
|
||||
w.log.Error("discover: adopt failed", "infohash", infohash, "err", err)
|
||||
return
|
||||
}
|
||||
w.log.Info("discover: adopted torrent",
|
||||
"download_id", id, "infohash", infohash, "name", t.Name,
|
||||
"category", t.Category, "tags", t.Tags)
|
||||
}
|
||||
|
||||
// hasTag сообщает, есть ли tag среди списка тегов qBit (через запятую).
|
||||
func hasTag(tags, tag string) bool {
|
||||
if tag == "" {
|
||||
return false
|
||||
}
|
||||
for _, x := range strings.Split(tags, ",") {
|
||||
if strings.TrimSpace(x) == tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// firstInfohash возвращает первый непустой infohash торрента (нижний регистр).
|
||||
func firstInfohash(t qbt.Torrent) string {
|
||||
for _, h := range []string{t.Hash, t.InfohashV1, t.InfohashV2} {
|
||||
if h != "" {
|
||||
return strings.ToLower(h)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user