Feature: Get size by api for ptp btn and ggn (#66)

* chore: add package

* feat: get size by api for ptp and btn

* feat: download and parse torrent if not api

* feat: bypass tls check and load meta from file

* fix: no invite command needed for btn

* feat: add ggn api

* feat: imrpove logging

* feat: build request url

* feat: improve err logging
This commit is contained in:
Ludvig Lundgren 2022-01-05 23:52:29 +01:00 committed by GitHub
parent d2aa7c1e7e
commit 2ea2293745
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 2181 additions and 99 deletions

121
internal/indexer/api.go Normal file
View file

@ -0,0 +1,121 @@
package indexer
import (
"fmt"
"github.com/rs/zerolog/log"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/btn"
"github.com/autobrr/autobrr/pkg/ggn"
"github.com/autobrr/autobrr/pkg/ptp"
)
type APIService interface {
TestConnection(indexer string) (bool, error)
GetTorrentByID(indexer string, torrentID string) (*domain.TorrentBasic, error)
AddClient(indexer string, settings map[string]string) error
RemoveClient(indexer string) error
}
type apiClient interface {
GetTorrentByID(torrentID string) (*domain.TorrentBasic, error)
TestAPI() (bool, error)
}
type apiService struct {
apiClients map[string]apiClient
}
func NewAPIService() APIService {
return &apiService{
apiClients: make(map[string]apiClient),
}
}
func (s *apiService) GetTorrentByID(indexer string, torrentID string) (*domain.TorrentBasic, error) {
v, ok := s.apiClients[indexer]
if !ok {
return nil, nil
}
log.Trace().Str("service", "api").Str("method", "GetTorrentByID").Msgf("'%v' trying to fetch torrent from api", indexer)
t, err := v.GetTorrentByID(torrentID)
if err != nil {
log.Error().Stack().Err(err).Msgf("could not get torrent: '%v' from: %v", torrentID, indexer)
return nil, err
}
log.Trace().Str("service", "api").Str("method", "GetTorrentByID").Msgf("'%v' successfully fetched torrent from api: %+v", indexer, t)
return t, nil
}
func (s *apiService) TestConnection(indexer string) (bool, error) {
v, ok := s.apiClients[indexer]
if !ok {
return false, nil
}
t, err := v.TestAPI()
if err != nil {
return false, err
}
return t, nil
}
func (s *apiService) AddClient(indexer string, settings map[string]string) error {
// basic validation
if indexer == "" {
return fmt.Errorf("api_service.add_client: validation falied: indexer can't be empty")
} else if len(settings) == 0 {
return fmt.Errorf("api_service.add_client: validation falied: settings can't be empty")
}
log.Trace().Msgf("api-service.add_client: init api client for '%v'", indexer)
// init client
switch indexer {
case "btn":
key, ok := settings["api_key"]
if !ok || key == "" {
return fmt.Errorf("api_service: 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 fmt.Errorf("api_service: could not initialize ptp client: missing var 'api_user'")
}
key, ok := settings["api_key"]
if !ok || key == "" {
return fmt.Errorf("api_service: 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 fmt.Errorf("api_service: could not initialize ggn client: missing var 'api_key'")
}
s.apiClients[indexer] = ggn.NewClient("", key)
default:
return fmt.Errorf("api_service: could not initialize client: unsupported indexer '%v'", indexer)
}
return nil
}
func (s *apiService) RemoveClient(indexer string) error {
_, ok := s.apiClients[indexer]
if ok {
delete(s.apiClients, indexer)
}
return nil
}

View file

@ -11,6 +11,7 @@ protocol: torrent
supports:
- irc
- rss
- api
source: gazelle
settings:
- name: authkey
@ -21,6 +22,22 @@ settings:
type: secret
label: Torrent pass
help: Right click DL on a torrent and get the torrent_pass.
- name: api_key
type: secret
label: API Key
help: Username -> Edit Profile -> API
api:
url: https://api.broadcasthe.net
type: jsonrpc
limits:
max: 150
per: hour
settings:
- name: api_key
type: secret
label: API Key
help: Username -> Edit Profile -> API
irc:
network: BroadcasTheNet
@ -42,12 +59,6 @@ irc:
required: true
label: NickServ Password
help: NickServ password
- name: invite_command
type: secret
default: "CableGuy IDENTIFY USERNAME IRCKey"
required: true
label: Invite command
help: Invite auth with CableGuy.
parse:
type: multi

View file

@ -11,6 +11,7 @@ protocol: torrent
supports:
- irc
- rss
- api
source: gazelle
settings:
- name: authkey
@ -21,6 +22,22 @@ settings:
type: secret
label: Torrent pass
help: Right click DL on a torrent and get the torrent_pass.
- name: api_key
type: secret
label: API Key
help: Username -> Edit / Settings -> API Keys
api:
url: https://gazellegames.net/api.php
type: json
limits:
max: 5
per: 10 seconds
settings:
- name: api_key
type: secret
label: API Key
help: Username -> Edit / Settings -> API Keys
irc:
network: GGn

View file

@ -11,6 +11,7 @@ protocol: torrent
supports:
- irc
- rss
- api
source: gazelle
settings:
- name: authkey
@ -21,6 +22,30 @@ settings:
type: secret
label: Torrent pass
help: Right click DL on a torrent and get the torrent_pass.
- name: api_user
type: secret
label: API User
help: Edit profile -> Security -> Generate new api keys
- name: api_key
type: secret
label: API Key
help: Edit profile -> Security -> Generate new api keys
api:
url: https://passthepopcorn.me/
type: json
limits:
max: 60
per: minute
settings:
- name: api_user
type: secret
label: API User
help: Edit profile -> Security -> Generate new api keys
- name: api_key
type: secret
label: API Key
help: Edit profile -> Security -> Generate new api keys
irc:
network: PassThePopcorn

View file

@ -26,7 +26,8 @@ type Service interface {
}
type service struct {
repo domain.IndexerRepo
repo domain.IndexerRepo
apiService APIService
// contains all raw indexer definitions
indexerDefinitions map[string]domain.IndexerDefinition
@ -37,9 +38,10 @@ type service struct {
lookupIRCServerDefinition map[string]map[string]domain.IndexerDefinition
}
func NewService(repo domain.IndexerRepo) Service {
func NewService(repo domain.IndexerRepo, apiService APIService) Service {
return &service{
repo: repo,
apiService: apiService,
indexerDefinitions: make(map[string]domain.IndexerDefinition),
mapIndexerIRCToName: make(map[string]string),
lookupIRCServerDefinition: make(map[string]map[string]domain.IndexerDefinition),
@ -150,6 +152,7 @@ func (s *service) mapIndexer(indexer domain.Indexer) (*domain.IndexerDefinition,
Privacy: in.Privacy,
Protocol: in.Protocol,
URLS: in.URLS,
Supports: in.Supports,
Settings: nil,
SettingsMap: make(map[string]string),
IRC: in.IRC,
@ -184,23 +187,34 @@ func (s *service) GetTemplates() ([]domain.IndexerDefinition, error) {
}
func (s *service) Start() error {
// load all indexer definitions
err := s.LoadIndexerDefinitions()
if err != nil {
return err
}
// load the indexers' setup by the user
indexerDefinitions, err := s.GetAll()
if err != nil {
return err
}
for _, indexerDefinition := range indexerDefinitions {
s.mapIRCIndexerLookup(indexerDefinition.Identifier, *indexerDefinition)
for _, indexer := range indexerDefinitions {
s.mapIRCIndexerLookup(indexer.Identifier, *indexer)
// add to irc server lookup table
s.mapIRCServerDefinitionLookup(indexerDefinition.IRC.Server, *indexerDefinition)
s.mapIRCServerDefinitionLookup(indexer.IRC.Server, *indexer)
// check if it has api and add to api service
if indexer.Enabled && indexer.HasApi() {
if err := s.apiService.AddClient(indexer.Identifier, indexer.SettingsMap); err != nil {
log.Error().Stack().Err(err).Msgf("indexer.start: could not init api client for: '%v'", indexer.Identifier)
}
}
}
log.Info().Msgf("Loaded %d indexers", len(indexerDefinitions))
return nil
}
@ -305,7 +319,7 @@ func (s *service) LoadIndexerDefinitions() error {
}
}
log.Info().Msgf("Loaded %d indexer definitions", len(s.indexerDefinitions))
log.Debug().Msgf("Loaded %d indexer definitions", len(s.indexerDefinitions))
return nil
}