-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(store): initial debridlink integration
- Loading branch information
1 parent
1e0d576
commit 07fe97d
Showing
9 changed files
with
700 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package debridlink | ||
|
||
type GetAccountInfoDataSettings struct { | ||
Https bool `json:"https"` | ||
ThemeDark bool `json:"themeDark"` | ||
HideOldLinks bool `json:"hideOldLinks"` | ||
Cdn string `json:"cdn"` | ||
TwofaType string `json:"twofaType"` | ||
EmailsNews bool `json:"emailsNews"` | ||
EmailsAccount bool `json:"emailsAccount"` | ||
EmailsSupport bool `json:"emailsSupport"` | ||
} | ||
|
||
type GetAccountInfoData struct { | ||
Email string `json:"email"` | ||
EmailVerified bool `json:"emailVerified"` | ||
AccountType int `json:"accountType"` | ||
PremiumLeft int `json:"premiumLeft"` | ||
Pts int `json:"pts"` | ||
Trafficshare int `json:"trafficshare"` | ||
VouchersUrl string `json:"vouchersUrl"` | ||
EditPasswordUrl string `json:"editPasswordUrl"` | ||
EditEmailUrl string `json:"editEmailUrl"` | ||
ViewSessidUrl string `json:"viewSessidUrl"` | ||
UpgradeAccountUrl string `json:"upgradeAccountUrl"` | ||
RegisterDate string `json:"registerDate"` | ||
ServerDetected bool `json:"serverDetected"` | ||
Settings GetAccountInfoDataSettings `json:"settings"` | ||
AvatarUrl string `json:"avatarUrl"` | ||
Username string `json:"username"` | ||
} | ||
|
||
type GetAccountInfoParams struct { | ||
Ctx | ||
} | ||
|
||
func (c APIClient) GetAccountInfo(params *GetAccountInfoParams) (APIResponse[GetAccountInfoData], error) { | ||
response := &Response[GetAccountInfoData]{} | ||
res, err := c.Request("GET", "/v2/account/infos", params, response) | ||
return newAPIResponse(res, response.Value), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package debridlink | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/MunifTanjim/stremthru/core" | ||
"github.com/MunifTanjim/stremthru/store" | ||
) | ||
|
||
var DefaultHTTPTransport = core.DefaultHTTPTransport | ||
var DefaultHTTPClient = core.DefaultHTTPClient | ||
|
||
type APIClientConfig struct { | ||
BaseURL string | ||
APIKey string | ||
HTTPClient *http.Client | ||
agent string | ||
} | ||
|
||
type APIClient struct { | ||
BaseURL *url.URL | ||
HTTPClient *http.Client | ||
apiKey string | ||
agent string | ||
} | ||
|
||
func NewAPIClient(conf *APIClientConfig) *APIClient { | ||
if conf.agent == "" { | ||
conf.agent = "stremthru" | ||
} | ||
|
||
if conf.BaseURL == "" { | ||
conf.BaseURL = "https://debrid-link.com/api" | ||
} | ||
|
||
if conf.HTTPClient == nil { | ||
conf.HTTPClient = DefaultHTTPClient | ||
} | ||
|
||
c := &APIClient{} | ||
|
||
baseUrl, err := url.Parse(conf.BaseURL) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
c.BaseURL = baseUrl | ||
c.HTTPClient = conf.HTTPClient | ||
c.apiKey = conf.APIKey | ||
c.agent = conf.agent | ||
|
||
return c | ||
} | ||
|
||
type RequestContext interface { | ||
GetContext() context.Context | ||
PrepareBody(method string, query *url.Values) (body io.Reader, contentType string, err error) | ||
} | ||
|
||
type Ctx = store.Ctx | ||
|
||
func (c APIClient) newRequest(method, path string, params store.RequestContext) (req *http.Request, err error) { | ||
if params == nil { | ||
params = &Ctx{} | ||
} | ||
|
||
url := c.BaseURL.JoinPath(path) | ||
|
||
query := url.Query() | ||
|
||
body, contentType, err := params.PrepareBody(method, &query) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
url.RawQuery = query.Encode() | ||
|
||
req, err = http.NewRequestWithContext(params.GetContext(), method, url.String(), body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req.Header.Set("Authorization", "Bearer "+params.GetAPIKey(c.apiKey)) | ||
req.Header.Add("User-Agent", c.agent) | ||
if len(contentType) > 0 { | ||
req.Header.Add("Content-Type", contentType) | ||
} | ||
|
||
return req, nil | ||
} | ||
|
||
func (c APIClient) Request(method, path string, params store.RequestContext, v ResponseEnvelop) (*http.Response, error) { | ||
req, err := c.newRequest(method, path, params) | ||
if err != nil { | ||
error := core.NewStoreError("failed to create request") | ||
error.StoreName = string(store.StoreNameAlldebrid) | ||
error.Cause = err | ||
return nil, error | ||
} | ||
res, err := c.HTTPClient.Do(req) | ||
err = processResponseBody(res, err, v) | ||
if err != nil { | ||
return res, UpstreamErrorFromRequest(err, req, res) | ||
} | ||
return res, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package debridlink | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/MunifTanjim/stremthru/core" | ||
"github.com/MunifTanjim/stremthru/store" | ||
) | ||
|
||
type ErrorCode = core.ErrorCode | ||
|
||
const ( | ||
ErrorCodeBadToken ErrorCode = "badToken" | ||
ErrorCodeBadSign ErrorCode = "badSign" | ||
ErrorCodeHidedToken ErrorCode = "hidedToken" | ||
ErrorCodeServerError ErrorCode = "server_error" | ||
ErrorCodeAccessDenied ErrorCode = "access_denied" | ||
ErrorCodeAuthorizationPending ErrorCode = "authorization_pending" | ||
ErrorCodeUnsupportedGrantType ErrorCode = "unsupported_grant_type" | ||
ErrorCodeUnsupportedResponseType ErrorCode = "unsupported_response_type" | ||
ErrorCodeInvalidRequest ErrorCode = "invalid_request" | ||
ErrorCodeInvalidScope ErrorCode = "invalid_scope" | ||
ErrorCodeExpiredToken ErrorCode = "expired_token" | ||
ErrorCodeUnauthorizedClient ErrorCode = "unauthorized_client" | ||
ErrorCodeInvalidClient ErrorCode = "invalid_client" | ||
ErrorCodeUnknowR ErrorCode = "unknowR" | ||
ErrorCodeInternalError ErrorCode = "internalError" | ||
ErrorCodeBadArguments ErrorCode = "badArguments" | ||
ErrorCodeBadId ErrorCode = "badId" | ||
ErrorCodeFloodDetected ErrorCode = "floodDetected" | ||
ErrorCodeServerNotAllowed ErrorCode = "serverNotAllowed" | ||
ErrorCodeFreeServerOverload ErrorCode = "freeServerOverload" | ||
ErrorCodeMaxAttempts ErrorCode = "maxAttempts" | ||
ErrorCodeCaptchaRequired ErrorCode = "captchaRequired" | ||
ErrorCodeAccountLocked ErrorCode = "accountLocked" | ||
ErrorCodeNotDebrid ErrorCode = "notDebrid" | ||
ErrorCodeHostNotValid ErrorCode = "hostNotValid" | ||
ErrorCodeFileNotFound ErrorCode = "fileNotFound" | ||
ErrorCodeFileNotAvailable ErrorCode = "fileNotAvailable" | ||
ErrorCodeBadFileUrl ErrorCode = "badFileUrl" | ||
ErrorCodeBadFilePassword ErrorCode = "badFilePassword" | ||
ErrorCodeNotFreeHost ErrorCode = "notFreeHost" | ||
ErrorCodeMaintenanceHost ErrorCode = "maintenanceHost" | ||
ErrorCodeNoServerHost ErrorCode = "noServerHost" | ||
ErrorCodeMaxLink ErrorCode = "maxLink" | ||
ErrorCodeMaxLinkHost ErrorCode = "maxLinkHost" | ||
ErrorCodeMaxData ErrorCode = "maxData" | ||
ErrorCodeMaxDataHost ErrorCode = "maxDataHost" | ||
ErrorCodeDisabledServerHost ErrorCode = "disabledServerHost" | ||
ErrorCodeNotAddTorrent ErrorCode = "notAddTorrent" | ||
ErrorCodeTorrentTooBig ErrorCode = "torrentTooBig" | ||
ErrorCodeMaxTorrent ErrorCode = "maxTorrent" | ||
) | ||
|
||
func UpstreamErrorWithCause(cause error) *core.UpstreamError { | ||
err := core.NewUpstreamError("") | ||
err.StoreName = string(store.StoreNameDebridLink) | ||
|
||
if rerr, ok := cause.(*ResponseError); ok { | ||
if rerr.ErrDesc != "" { | ||
err.Msg = rerr.ErrDesc | ||
} else { | ||
err.Msg = "Debrid-Link Error Code: " + string(rerr.Err) | ||
} | ||
err.UpstreamCause = rerr | ||
} else { | ||
err.Cause = cause | ||
} | ||
|
||
return err | ||
} | ||
|
||
func UpstreamErrorFromRequest(cause error, req *http.Request, res *http.Response) error { | ||
err := UpstreamErrorWithCause(cause) | ||
err.InjectReq(req) | ||
if res != nil { | ||
err.StatusCode = res.StatusCode | ||
} | ||
if err.StatusCode <= http.StatusBadRequest { | ||
err.StatusCode = http.StatusBadRequest | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package debridlink | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/MunifTanjim/stremthru/core" | ||
) | ||
|
||
type ResponseError struct { | ||
Err ErrorCode `json:"error,omitempty"` | ||
ErrId string `json:"error_id,omitempty"` | ||
ErrDesc string `json:"error_description,omitempty"` | ||
} | ||
|
||
func (e *ResponseError) Error() string { | ||
ret, _ := json.Marshal(e) | ||
return string(ret) | ||
} | ||
|
||
type ResponsePagination struct { | ||
Page int `json:"page"` | ||
Pages int `json:"pages"` | ||
Next int `json:"next"` | ||
Previous int `json:"previous"` | ||
} | ||
|
||
type Response[T interface{}] struct { | ||
*ResponseError | ||
Success bool `json:"success"` | ||
Value T `json:"value,omitempty"` | ||
} | ||
|
||
type PaginatedResponse[T interface{}] struct { | ||
Response[[]T] | ||
Pagination ResponsePagination `json:"pagination"` | ||
} | ||
|
||
type ResponseEnvelop interface { | ||
IsSuccess() bool | ||
GetError() *ResponseError | ||
} | ||
|
||
func (r Response[any]) IsSuccess() bool { | ||
return r.Success | ||
} | ||
|
||
func (r Response[any]) GetError() *ResponseError { | ||
if r.IsSuccess() { | ||
return nil | ||
} | ||
return r.ResponseError | ||
} | ||
|
||
type APIResponse[T interface{}] struct { | ||
Header http.Header | ||
StatusCode int | ||
Data T | ||
} | ||
|
||
func newAPIResponse[T interface{}](res *http.Response, data T) APIResponse[T] { | ||
return APIResponse[T]{ | ||
Header: res.Header, | ||
StatusCode: res.StatusCode, | ||
Data: data, | ||
} | ||
} | ||
|
||
func extractResponseError(statusCode int, body []byte, v ResponseEnvelop) error { | ||
if !v.IsSuccess() { | ||
return v.GetError() | ||
} | ||
if statusCode >= http.StatusBadRequest { | ||
return errors.New(string(body)) | ||
} | ||
return nil | ||
} | ||
|
||
func processResponseBody(res *http.Response, err error, v ResponseEnvelop) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
body, err := io.ReadAll(res.Body) | ||
defer res.Body.Close() | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
err = core.UnmarshalJSON(res.StatusCode, body, v) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return extractResponseError(res.StatusCode, body, v) | ||
} |
Oops, something went wrong.