208 lines
5.3 KiB
Go
208 lines
5.3 KiB
Go
package updater
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/goccy/go-json"
|
|
)
|
|
|
|
// We fetch the latest release from the website first, if it fails we fallback to GitHub API
|
|
// This allows updates even if Seanime is removed from GitHub
|
|
var (
|
|
websiteUrl = "https://seanime.app/api/release"
|
|
fallbackGithubUrl = "https://api.github.com/repos/5rahim/seanime/releases/latest"
|
|
)
|
|
|
|
type (
|
|
GitHubResponse struct {
|
|
Url string `json:"url"`
|
|
AssetsUrl string `json:"assets_url"`
|
|
UploadUrl string `json:"upload_url"`
|
|
HtmlUrl string `json:"html_url"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
TagName string `json:"tag_name"`
|
|
TargetCommitish string `json:"target_commitish"`
|
|
Name string `json:"name"`
|
|
Draft bool `json:"draft"`
|
|
Prerelease bool `json:"prerelease"`
|
|
CreatedAt string `json:"created_at"`
|
|
PublishedAt string `json:"published_at"`
|
|
Assets []struct {
|
|
Url string `json:"url"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
Name string `json:"name"`
|
|
Label string `json:"label"`
|
|
ContentType string `json:"content_type"`
|
|
State string `json:"state"`
|
|
Size int64 `json:"size"`
|
|
DownloadCount int64 `json:"download_count"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
BrowserDownloadURL string `json:"browser_download_url"`
|
|
} `json:"assets"`
|
|
TarballURL string `json:"tarball_url"`
|
|
ZipballURL string `json:"zipball_url"`
|
|
Body string `json:"body"`
|
|
}
|
|
|
|
DocsResponse struct {
|
|
Release Release `json:"release"`
|
|
}
|
|
|
|
Release struct {
|
|
Url string `json:"url"`
|
|
HtmlUrl string `json:"html_url"`
|
|
NodeId string `json:"node_id"`
|
|
TagName string `json:"tag_name"`
|
|
Name string `json:"name"`
|
|
Body string `json:"body"`
|
|
PublishedAt string `json:"published_at"`
|
|
Released bool `json:"released"`
|
|
Version string `json:"version"`
|
|
Assets []ReleaseAsset `json:"assets"`
|
|
}
|
|
ReleaseAsset struct {
|
|
Url string `json:"url"`
|
|
Id int64 `json:"id"`
|
|
NodeId string `json:"node_id"`
|
|
Name string `json:"name"`
|
|
ContentType string `json:"content_type"`
|
|
Uploaded bool `json:"uploaded"`
|
|
Size int64 `json:"size"`
|
|
BrowserDownloadUrl string `json:"browser_download_url"`
|
|
}
|
|
)
|
|
|
|
func (u *Updater) GetReleaseName(version string) string {
|
|
|
|
arch := runtime.GOARCH
|
|
switch runtime.GOARCH {
|
|
case "amd64":
|
|
arch = "x86_64"
|
|
case "arm64":
|
|
arch = "arm64"
|
|
case "386":
|
|
return "i386"
|
|
}
|
|
oos := runtime.GOOS
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
oos = "Linux"
|
|
case "windows":
|
|
oos = "Windows"
|
|
case "darwin":
|
|
oos = "MacOS"
|
|
}
|
|
|
|
ext := "tar.gz"
|
|
if oos == "Windows" {
|
|
ext = "zip"
|
|
}
|
|
|
|
return fmt.Sprintf("seanime-%s_%s_%s.%s", version, oos, arch, ext)
|
|
}
|
|
|
|
func (u *Updater) fetchLatestRelease() (*Release, error) {
|
|
var release *Release
|
|
docsRelease, err := u.fetchLatestReleaseFromDocs()
|
|
if err != nil {
|
|
ghRelease, err := u.fetchLatestReleaseFromGitHub()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
release = ghRelease
|
|
} else {
|
|
release = docsRelease
|
|
}
|
|
|
|
return release, nil
|
|
}
|
|
|
|
func (u *Updater) fetchLatestReleaseFromGitHub() (*Release, error) {
|
|
|
|
response, err := u.client.Get(fallbackGithubUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
byteArr, readErr := io.ReadAll(response.Body)
|
|
if readErr != nil {
|
|
return nil, fmt.Errorf("error reading response: %w\n", readErr)
|
|
}
|
|
|
|
var res GitHubResponse
|
|
err = json.Unmarshal(byteArr, &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
release := &Release{
|
|
Url: res.Url,
|
|
HtmlUrl: res.HtmlUrl,
|
|
NodeId: res.NodeID,
|
|
TagName: res.TagName,
|
|
Name: res.Name,
|
|
Body: res.Body,
|
|
PublishedAt: res.PublishedAt,
|
|
Released: !res.Prerelease && !res.Draft,
|
|
Version: strings.TrimPrefix(res.TagName, "v"),
|
|
Assets: make([]ReleaseAsset, len(res.Assets)),
|
|
}
|
|
|
|
for i, asset := range res.Assets {
|
|
release.Assets[i] = ReleaseAsset{
|
|
Url: asset.Url,
|
|
Id: asset.ID,
|
|
NodeId: asset.NodeID,
|
|
Name: asset.Name,
|
|
ContentType: asset.ContentType,
|
|
Uploaded: asset.State == "uploaded",
|
|
Size: asset.Size,
|
|
BrowserDownloadUrl: asset.BrowserDownloadURL,
|
|
}
|
|
}
|
|
|
|
return release, nil
|
|
}
|
|
|
|
func (u *Updater) fetchLatestReleaseFromDocs() (*Release, error) {
|
|
|
|
response, err := u.client.Get(websiteUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
statusCode := response.StatusCode
|
|
|
|
if statusCode == 429 {
|
|
return nil, errors.New("rate limited, try again later")
|
|
}
|
|
|
|
if !((statusCode >= 200) && (statusCode <= 299)) {
|
|
return nil, fmt.Errorf("http error code: %d\n", statusCode)
|
|
}
|
|
|
|
byteArr, readErr := io.ReadAll(response.Body)
|
|
if readErr != nil {
|
|
return nil, fmt.Errorf("error reading response: %w", readErr)
|
|
}
|
|
|
|
var res DocsResponse
|
|
err = json.Unmarshal(byteArr, &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res.Release.Version = strings.TrimPrefix(res.Release.TagName, "v")
|
|
|
|
return &res.Release, nil
|
|
}
|