From ced533cd40dfa4bca14c0999e3cfa4e256ba2786 Mon Sep 17 00:00:00 2001 From: Felipe Marinho Date: Tue, 18 Jun 2024 12:38:08 -0300 Subject: [PATCH] Feat/manual indexer (#9) * new: feat: add manual torrents support * chg: docs: update documentation * chg: chore: fix lint ci --------- Co-authored-by: Felipe Marinho --- .golangci.yml | 7 +-- api/index.go | 39 +++++++++----- api/manual.go | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 1 + 4 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 api/manual.go diff --git a/.golangci.yml b/.golangci.yml index 8ba8e73..e5eaa9f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,4 @@ -run: - skip-files: - - scrape.go \ No newline at end of file +issues: + exclude-files: + - scrape.go + - infohash.go \ No newline at end of file diff --git a/api/index.go b/api/index.go index d410ba4..3b84c8a 100644 --- a/api/index.go +++ b/api/index.go @@ -56,20 +56,35 @@ func HandlerIndex(w http.ResponseWriter, r *http.Request) { err := json.NewEncoder(w).Encode(map[string]interface{}{ "time": currentTime, "endpoints": map[string]interface{}{ - "/indexers/comando_torrents": map[string]interface{}{ - "method": "GET", - "description": "Indexer for comando torrents", - "query_params": map[string]string{ - "q": "search query", - "filter_results": "if results with similarity equals to zero should be filtered (true/false)", + "/indexers/comando_torrents": []map[string]interface{}{ + { + "method": "GET", + "description": "Indexer for comando torrents", + "query_params": map[string]string{ + "q": "search query", + "filter_results": "if results with similarity equals to zero should be filtered (true/false)", + }, }, }, - "/indexers/bludv": map[string]interface{}{ - "method": "GET", - "description": "Indexer for bludv", - "query_params": map[string]string{ - "q": "search query", - "filter_results": "if results with similarity equals to zero should be filtered (true/false)", + "/indexers/bludv": []map[string]interface{}{ + { + "method": "GET", + "description": "Indexer for bludv", + "query_params": map[string]string{ + "q": "search query", + "filter_results": "if results with similarity equals to zero should be filtered (true/false)", + }}, + }, + "/indexers/manual": []map[string]interface{}{ + { + "method": "POST", + "description": "Add a manual torrent entry to the indexer for 12 hours", + "body": map[string]interface{}{ + "magnetLink": "magnet link", + }}, + { + "method": "GET", + "description": "Get all manual torrents", }, }, }, diff --git a/api/manual.go b/api/manual.go new file mode 100644 index 0000000..1054c95 --- /dev/null +++ b/api/manual.go @@ -0,0 +1,138 @@ +package handler + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "github.com/felipemarinho97/torrent-indexer/magnet" + "github.com/felipemarinho97/torrent-indexer/schema" + goscrape "github.com/felipemarinho97/torrent-indexer/scrape" + "github.com/redis/go-redis/v9" +) + +const manualTorrentsRedisKey = "manual:torrents" + +var manualTorrentExpiration = 8 * time.Hour + +type ManualIndexerRequest struct { + MagnetLink string `json:"magnetLink"` +} + +func (i *Indexer) HandlerManualIndexer(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var req ManualIndexerRequest + indexedTorrents := []IndexedTorrent{} + + // fetch from redis + out, err := i.redis.Get(ctx, manualTorrentsRedisKey) + if err != nil && !errors.Is(err, redis.Nil) { + w.WriteHeader(http.StatusInternalServerError) + fmt.Println(err) + err = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + if err != nil { + fmt.Println(err) + } + i.metrics.IndexerErrors.WithLabelValues("manual").Inc() + return + } else if errors.Is(err, redis.Nil) { + out = bytes.NewBufferString("[]").Bytes() + } + + err = json.Unmarshal([]byte(out), &indexedTorrents) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + err = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + if err != nil { + fmt.Println(err) + } + i.metrics.IndexerErrors.WithLabelValues("manual").Inc() + return + } + + // check if the request is a POST + if r.Method == http.MethodPost { + // decode the request body + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + if err != nil { + fmt.Println(err) + } + i.metrics.IndexerErrors.WithLabelValues("manual").Inc() + return + } + + magnet, err := magnet.ParseMagnetUri(req.MagnetLink) + if err != nil { + fmt.Println(err) + } + var audio []schema.Audio + releaseTitle := magnet.DisplayName + infoHash := magnet.InfoHash.String() + trackers := magnet.Trackers + magnetAudio := []schema.Audio{} + if strings.Contains(strings.ToLower(releaseTitle), "dual") || strings.Contains(strings.ToLower(releaseTitle), "dublado") { + magnetAudio = append(magnetAudio, audio...) + } else if len(audio) > 1 { + // remove portuguese audio, and append to magnetAudio + for _, a := range audio { + if a != schema.AudioPortuguese { + magnetAudio = append(magnetAudio, a) + } + } + } else { + magnetAudio = append(magnetAudio, audio...) + } + + peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, i.metrics, infoHash, trackers) + if err != nil { + fmt.Println(err) + } + + title := processTitle(releaseTitle, magnetAudio) + + ixt := IndexedTorrent{ + Title: appendAudioISO639_2Code(releaseTitle, magnetAudio), + OriginalTitle: title, + Audio: magnetAudio, + MagnetLink: req.MagnetLink, + InfoHash: infoHash, + Trackers: trackers, + LeechCount: peer, + SeedCount: seed, + } + + // write to redis + indexedTorrents = append(indexedTorrents, ixt) + out, err := json.Marshal(indexedTorrents) + if err != nil { + fmt.Println(err) + } + + err = i.redis.SetWithExpiration(ctx, manualTorrentsRedisKey, out, manualTorrentExpiration) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + err = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + if err != nil { + fmt.Println(err) + } + i.metrics.IndexerErrors.WithLabelValues("manual").Inc() + return + } + } + + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(Response{ + Results: indexedTorrents, + Count: len(indexedTorrents), + }) + if err != nil { + fmt.Println(err) + } +} diff --git a/main.go b/main.go index 94e32c8..2efda33 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ func main() { indexerMux.HandleFunc("/", handler.HandlerIndex) indexerMux.HandleFunc("/indexers/comando_torrents", indexers.HandlerComandoIndexer) indexerMux.HandleFunc("/indexers/bludv", indexers.HandlerBluDVIndexer) + indexerMux.HandleFunc("/indexers/manual", indexers.HandlerManualIndexer) metricsMux.Handle("/metrics", promhttp.Handler())