// Copyright (c) 2021 - 2023, Ludvig Lundgren and the autobrr contributors. // SPDX-License-Identifier: GPL-2.0-or-later package indexer import ( "context" "github.com/rs/zerolog" "github.com/autobrr/autobrr/internal/domain" "github.com/autobrr/autobrr/internal/logger" "github.com/autobrr/autobrr/internal/mock" "github.com/autobrr/autobrr/pkg/btn" "github.com/autobrr/autobrr/pkg/errors" "github.com/autobrr/autobrr/pkg/ggn" "github.com/autobrr/autobrr/pkg/ptp" "github.com/autobrr/autobrr/pkg/red" ) type APIService interface { TestConnection(ctx context.Context, req domain.IndexerTestApiRequest) (bool, error) GetTorrentByID(ctx context.Context, indexer string, torrentID string) (*domain.TorrentBasic, error) AddClient(indexer string, settings map[string]string) error RemoveClient(indexer string) error } type apiClient interface { GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } type apiService struct { log zerolog.Logger apiClients map[string]apiClient } func NewAPIService(log logger.Logger) APIService { return &apiService{ log: log.With().Str("module", "indexer-api").Logger(), apiClients: make(map[string]apiClient), } } func (s *apiService) GetTorrentByID(ctx context.Context, indexer string, torrentID string) (*domain.TorrentBasic, error) { client, err := s.getApiClient(indexer) if err != nil { s.log.Error().Stack().Err(err).Msgf("could not get api client for: %s", indexer) return nil, errors.Wrap(err, "could not get torrent via api for indexer: %s", indexer) } s.log.Trace().Str("method", "GetTorrentByID").Msgf("%s fetching torrent from api...", indexer) torrent, err := client.GetTorrentByID(ctx, torrentID) if err != nil { s.log.Error().Stack().Err(err).Msgf("could not get torrent: %s from: %s", torrentID, indexer) return nil, err } s.log.Trace().Str("method", "GetTorrentByID").Msgf("%s api successfully fetched torrent: %+v", indexer, torrent) return torrent, nil } func (s *apiService) TestConnection(ctx context.Context, req domain.IndexerTestApiRequest) (bool, error) { client, err := s.getClientForTest(req) if err != nil { return false, errors.New("could not init api client: %s", req.Identifier) } success, err := client.TestAPI(ctx) if err != nil { s.log.Error().Err(err).Msgf("error testing connection for api: %s", req.Identifier) return false, err } return success, nil } func (s *apiService) AddClient(indexer string, settings map[string]string) error { s.log.Trace().Msgf("api.Service.AddClient: init api client for: %s", indexer) // init client switch indexer { case "btn": key, ok := settings["api_key"] if !ok || key == "" { return errors.New("api.Service.AddClient: could not initialize btn client: missing var 'api_key'") } s.apiClients[indexer] = btn.NewClient("", key) case "ptp": user, ok := settings["api_user"] if !ok || user == "" { return errors.New("api.Service.AddClient: could not initialize ptp client: missing var 'api_user'") } key, ok := settings["api_key"] if !ok || key == "" { return errors.New("api.Service.AddClient: could not initialize ptp client: missing var 'api_key'") } s.apiClients[indexer] = ptp.NewClient(user, key) case "ggn": key, ok := settings["api_key"] if !ok || key == "" { return errors.New("api.Service.AddClient: could not initialize ggn client: missing var 'api_key'") } s.apiClients[indexer] = ggn.NewClient(key) case "redacted": key, ok := settings["api_key"] if !ok || key == "" { return errors.New("api.Service.AddClient: could not initialize red client: missing var 'api_key'") } s.apiClients[indexer] = red.NewClient(key) case "mock": s.apiClients[indexer] = mock.NewMockClient("", "mock") default: return errors.New("api.Service.AddClient: could not initialize client: unsupported indexer: %s", indexer) } return nil } func (s *apiService) getApiClient(indexer string) (apiClient, error) { client, ok := s.apiClients[indexer] if !ok { return nil, errors.New("could not find api client for: %s", indexer) } return client, nil } func (s *apiService) getClientForTest(req domain.IndexerTestApiRequest) (apiClient, error) { // init client switch req.Identifier { case "btn": if req.ApiKey == "" { return nil, errors.New("api.Service.AddClient: could not initialize btn client: missing var 'api_key'") } return btn.NewClient("", req.ApiKey), nil case "ptp": if req.ApiUser == "" { return nil, errors.New("api.Service.AddClient: could not initialize ptp client: missing var 'api_user'") } if req.ApiKey == "" { return nil, errors.New("api.Service.AddClient: could not initialize ptp client: missing var 'api_key'") } return ptp.NewClient(req.ApiUser, req.ApiKey), nil case "ggn": if req.ApiKey == "" { return nil, errors.New("api.Service.AddClient: could not initialize ggn client: missing var 'api_key'") } return ggn.NewClient(req.ApiKey), nil case "redacted": if req.ApiKey == "" { return nil, errors.New("api.Service.AddClient: could not initialize red client: missing var 'api_key'") } return red.NewClient(req.ApiKey), nil case "mock": return mock.NewMockClient("", "mock"), nil default: return nil, errors.New("api.Service.AddClient: could not initialize client: unsupported indexer: %s", req.Identifier) } } func (s *apiService) RemoveClient(indexer string) error { _, ok := s.apiClients[indexer] if ok { delete(s.apiClients, indexer) } return nil }