feat(releases): support magnet links (#730)

* feat(releases): support magnet links

* feat(feeds): support magnet links

* feat(actions): log messages

* fix: component warning

* fix: check hasprefix instead of hassuffix for magnet

* feat(release): resolve magnet uri from link

* fix(actions): deluge use magnet uri

* fix(macros): add `MagnetURI` var

* fix(actions): run magnet resolving before macros

* feat(feeds): set download type on creation
This commit is contained in:
ze0s 2023-02-28 22:16:10 +01:00 committed by GitHub
parent c6101cc765
commit ca196f0bf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 770 additions and 260 deletions

View file

@ -41,7 +41,7 @@ type Feed struct {
Capabilities []string `json:"capabilities"`
ApiKey string `json:"api_key"`
Cookie string `json:"cookie"`
Settings map[string]string `json:"settings"`
Settings *FeedSettingsJSON `json:"settings"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
IndexerID int `json:"indexer_id,omitempty"`
@ -50,6 +50,10 @@ type Feed struct {
LastRunData string `json:"last_run_data"`
}
type FeedSettingsJSON struct {
DownloadType FeedDownloadType `json:"download_type"`
}
type FeedIndexer struct {
ID int `json:"id"`
Name string `json:"name"`
@ -63,6 +67,13 @@ const (
FeedTypeRSS FeedType = "RSS"
)
type FeedDownloadType string
const (
FeedDownloadTypeMagnet FeedDownloadType = "MAGNET"
FeedDownloadTypeTorrent FeedDownloadType = "TORRENT"
)
type FeedCacheItem struct {
Bucket string `json:"bucket"`
Key string `json:"key"`

View file

@ -17,6 +17,7 @@ type Macro struct {
TorrentHash string
TorrentUrl string
TorrentDataRawBytes []byte
MagnetURI string
Indexer string
Title string
Resolution string
@ -44,6 +45,7 @@ func NewMacro(release Release) Macro {
TorrentPathName: release.TorrentTmpFile,
TorrentDataRawBytes: release.TorrentDataRawBytes,
TorrentHash: release.TorrentHash,
MagnetURI: release.MagnetURI,
Indexer: release.Indexer,
Title: release.Title,
Resolution: release.Resolution,

View file

@ -46,6 +46,7 @@ type Release struct {
Timestamp time.Time `json:"timestamp"`
InfoURL string `json:"info_url"`
TorrentURL string `json:"download_url"`
MagnetURI string `json:"-"`
GroupID string `json:"group_id"`
TorrentID string `json:"torrent_id"`
TorrentTmpFile string `json:"-"`
@ -290,6 +291,10 @@ func (r *Release) DownloadTorrentFile() error {
}
func (r *Release) downloadTorrentFile(ctx context.Context) error {
if r.HasMagnetUri() {
return fmt.Errorf("error trying to download magnet link: %s", r.MagnetURI)
}
if r.TorrentURL == "" {
return errors.New("download_file: url can't be empty")
} else if r.TorrentTmpFile != "" {
@ -389,6 +394,81 @@ func (r *Release) downloadTorrentFile(ctx context.Context) error {
return errFunc
}
// HasMagnetUri check uf MagnetURI is set or empty
func (r *Release) HasMagnetUri() bool {
return r.MagnetURI != ""
}
type magnetRoundTripper struct{}
func (rt *magnetRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
if r.URL.Scheme == "magnet" {
responseBody := r.URL.String()
respReader := io.NopCloser(strings.NewReader(responseBody))
resp := &http.Response{
Status: http.StatusText(http.StatusOK),
StatusCode: http.StatusOK,
Body: respReader,
ContentLength: int64(len(responseBody)),
Header: map[string][]string{
"Content-Type": {"text/plain"},
"Location": {responseBody},
},
Proto: "HTTP/2.0",
ProtoMajor: 2,
}
return resp, nil
}
return http.DefaultTransport.RoundTrip(r)
}
func (r *Release) ResolveMagnetUri(ctx context.Context) error {
if r.MagnetURI == "" {
return nil
} else if strings.HasPrefix(r.MagnetURI, "magnet:?") {
return nil
}
client := http.Client{
Transport: &magnetRoundTripper{},
Timeout: time.Second * 60,
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, r.MagnetURI, nil)
if err != nil {
return errors.Wrap(err, "could not build request to resolve magnet uri")
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "autobrr")
res, err := client.Do(req)
if err != nil {
return errors.Wrap(err, "could not make request to resolve magnet uri")
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return errors.New("unexpected status code: %d", res.StatusCode)
}
body, err := io.ReadAll(res.Body)
if err != nil {
return errors.Wrap(err, "could not read response body")
}
magnet := string(body)
if magnet != "" {
r.MagnetURI = magnet
}
return nil
}
func (r *Release) addRejection(reason string) {
r.Rejections = append(r.Rejections, reason)
}