diff --git a/README.md b/README.md index 5a8dcc0..5b2b45c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,66 @@ -# gobalt -Go library for cobalt.tools +
+Go API Reference +Latest Release + +[![Static Badge](https://img.shields.io/badge/cobalt_discord-join-blue?style=for-the-badge&logo=discord)](https://discord.gg/pQPt8HBUPu) +[![Static Badge](https://img.shields.io/badge/supported-services-0077b6?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0%2BPHBhdGggZD0iTTU3OS44IDI2Ny43YzU2LjUtNTYuNSA1Ni41LTE0OCAwLTIwNC41Yy01MC01MC0xMjguOC01Ni41LTE4Ni4zLTE1LjRsLTEuNiAxLjFjLTE0LjQgMTAuMy0xNy43IDMwLjMtNy40IDQ0LjZzMzAuMyAxNy43IDQ0LjYgNy40bDEuNi0xLjFjMzIuMS0yMi45IDc2LTE5LjMgMTAzLjggOC42YzMxLjUgMzEuNSAzMS41IDgyLjUgMCAxMTRMNDIyLjMgMzM0LjhjLTMxLjUgMzEuNS04Mi41IDMxLjUtMTE0IDBjLTI3LjktMjcuOS0zMS41LTcxLjgtOC42LTEwMy44bDEuMS0xLjZjMTAuMy0xNC40IDYuOS0zNC40LTcuNC00NC42cy0zNC40LTYuOS00NC42IDcuNGwtMS4xIDEuNkMyMDYuNSAyNTEuMiAyMTMgMzMwIDI2MyAzODBjNTYuNSA1Ni41IDE0OCA1Ni41IDIwNC41IDBMNTc5LjggMjY3Ljd6TTYwLjIgMjQ0LjNjLTU2LjUgNTYuNS01Ni41IDE0OCAwIDIwNC41YzUwIDUwIDEyOC44IDU2LjUgMTg2LjMgMTUuNGwxLjYtMS4xYzE0LjQtMTAuMyAxNy43LTMwLjMgNy40LTQ0LjZzLTMwLjMtMTcuNy00NC42LTcuNGwtMS42IDEuMWMtMzIuMSAyMi45LTc2IDE5LjMtMTAzLjgtOC42Qzc0IDM3MiA3NCAzMjEgMTA1LjUgMjg5LjVMMjE3LjcgMTc3LjJjMzEuNS0zMS41IDgyLjUtMzEuNSAxMTQgMGMyNy45IDI3LjkgMzEuNSA3MS44IDguNiAxMDMuOWwtMS4xIDEuNmMtMTAuMyAxNC40LTYuOSAzNC40IDcuNCA0NC42czM0LjQgNi45IDQ0LjYtNy40bDEuMS0xLjZDNDMzLjUgMjYwLjggNDI3IDE4MiAzNzcgMTMyYy01Ni41LTU2LjUtMTQ4LTU2LjUtMjA0LjUgMEw2MC4yIDI0NC4zeiIvPjwvc3ZnPg%3D%3D&logoColor=0077b6)](https://github.com/wukko/cobalt?tab=readme-ov-file#supported-services) +

gobalt

+
+ +Gobalt provides a way to communicate with [cobalt.tools](https://cobalt.tools) using Go. To use it in your projects, simply run this command: +```sh +go get https://github.com/lostdusty/gobalt +``` + +## Usage +First, make sure you call `gobalt.CreateDefaultSettings()` to create the `Settings` struct with default values, then set an url. + +```go +//Creates a Settings struct with default values, and save it to downloadMedia variable. +downloadMedia := gobalt.CreateDefaultSettings() + +//Sets the URL, you MUST set one before downloading the media. +downloadMedia.Url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" + +//After changing the url, Do() will make the necessary requests to cobalt to download your media +destination, err := gobalt.Do(downloadMedia) +if err != nil { + //Handle errors here +} + +//Prints out the url from cobalt to download the requested media. +fmt.Println(destination.URL) +//Output example: https://us4-co.wuk.sh/api/stream?t=wTn-71aaWAcV2RBejNFNV&e=1711777798864&h=fMtwwtW8AUmtTLB24DGjJJjrq1EJDBFaCDoDuZpX0pA&s=_YVZhz8fnzBBKKo7UZmGWOqfe4wWwH5P1azdgBqwf-I&i=6tGZqAXbW08_6KmAiLevZA +``` + +## Features +### Server info +You can query information about any cobalt server by using `CobaltServerInfo(apiurl)`. This will return a `ServerInfo` struct with [this info](https://github.com/wukko/cobalt/blob/current/docs/api.md#response-body-variables-1). + +Example code: +```go +server, err := gobalt.CobaltServerInfo(gobalt.CobaltApi) +if err != nil { + //Handle the error here +} +fmt.Printf("Downloading from %v!\n", server.URL) +//Output: Downloading from https://us4-co.wuk.sh/! +``` + +### Use/Query other cobalt instances +Using `GetCobaltInstances()` fetches a community maintaned list of third-party cobalt instances ran by the community, except for ``*-co.wuk.sh``, none of them are "official" cobalt instances. Use them if you can't download from the main instance for whatever reason. + +Example: +```go +cobalt, err := gobalt.GetCobaltInstances() +if err != nil { + //Handle errors here +} +fmt.Printf("Found %v online cobalt servers: ", len(cobalt)) +for _, value := range cobalt { + fmt.Printf("%v, ", value.URL) +} +/* Output: +* Found 8 online cobalt servers: co.wuk.sh, cobalt-api.hyper.lol, cobalt.api.timelessnesses.me, api-dl.cgm.rs, cobalt.synzr.space, capi.oak.li, co.tskau.team, api.co.rooot.gay +*/ +``` diff --git a/go.mod b/go.mod index 3d13665..2260af5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/princessmortix/gobalt +module github.com/lostdusty/gobalt go 1.21.5 diff --git a/gobalt.go b/gobalt.go index 9f386fa..64c14c9 100644 --- a/gobalt.go +++ b/gobalt.go @@ -1,3 +1,4 @@ +// Package Gobalt provides a go way to communicate with https://cobalt.tools servers. package gobalt import ( @@ -8,6 +9,7 @@ import ( "net/http" "net/url" "regexp" + "runtime" "strings" "time" ) @@ -15,10 +17,10 @@ import ( var ( CobaltApi = "https://co.wuk.sh" //Override this value to use your own cobalt instance. See https://instances.hyper.lol/ for alternatives from the main instance. UserLanguage = "en" //Replace this following the ISO 639-1 standard. This downloads dubbed YouTube audio according to the language set here. Only takes effect if DubbedYoutubeAudio is set to true. - useragent = "Gobalt/1.0" + useragent = fmt.Sprintf("Mozilla/5.0 (%v; %v); gobalt/v1.0.2 (%v; %v); +(https://github.com/lostdusty/gobalt)", runtime.GOOS, runtime.GOARCH, runtime.Compiler, runtime.Version()) ) -type serverInfo struct { +type ServerInfo struct { Version string `json:"version"` //cobalt version Commit string `json:"commit"` //git commit Branch string `json:"branch"` //git branch @@ -40,19 +42,31 @@ type cobaltResponse struct { URLs []string //If the status is picker all the urls will go here. } +type CobaltInstances []struct { + Cors int `json:"cors"` + Commit string `json:"commit,omitempty"` + Name string `json:"name,omitempty"` + StartTime int64 `json:"startTime"` + API string `json:"api"` + Version string `json:"version"` + Branch string `json:"branch,omitempty"` + FrontEnd string `json:"frontEnd"` + Status bool `json:"status"` +} + type Settings struct { - Url string `json:"url"` //Any URL from bilibili.com, instagram, pinterest, reddit, rutube, soundcloud, streamable, tiktok, tumblr, twitch clips, twitter/x, vimeo, vine archive, vk or youtube. Will be url encoded later. - VideoCodec codecs `json:"vCodec"` //H264, AV1 or VP9, defaults to H264. - VideoQuality int `json:"vQuality,string"` //144p to 2160p (4K), if not specified will default to 1080p. - AudioCodec audioCodec `json:"aFormat"` //MP3, Opus, Ogg or Wav. If not specified will default to best. - FilenamePattern pattern `json:"filenamePattern"` //Classic, Basic, Pretty or Nerdy. Defaults to Pretty - AudioOnly bool `json:"isAudioOnly"` //Removes the video, downloads audio only. Default: false - //RemoveTikTokWatermark bool `json:"isNoTTWatermark"` //Removes TikTok watermark from TikTok videos. Default: false - FullTikTokAudio bool `json:"isTTFullAudio"` //Enables download of original sound used in a tiktok video. Default: false - VideoOnly bool `json:"isAudioMuted"` //Downloads only the video, audio is muted/removed. Default: false - DubbedYoutubeAudio bool `json:"dubLang"` //Pass the User-Language HTTP header to use the dubbed audio of the respective language, must change according to user's preference, default is English (US). Uses ISO 639-1 standard. - DisableVideoMetadata bool `json:"disableMetadata"` //Removes file metadata. Default: false - ConvertTwitterGifs bool `json:"twitterGif"` //Changes whether twitter gifs are converted to .gif (Twitter gifs are usually stored in .mp4 format). Default: true + Url string `json:"url"` //Any URL from bilibili.com, instagram, pinterest, reddit, rutube, soundcloud, streamable, tiktok, tumblr, twitch clips, twitter/x, vimeo, vine archive, vk or youtube. Will be url encoded later. + VideoCodec codecs `json:"vCodec"` //H264, AV1 or VP9, defaults to H264. + VideoQuality int `json:"vQuality,string"` //144p to 2160p (4K), if not specified will default to 1080p. + AudioCodec audioCodec `json:"aFormat"` //MP3, Opus, Ogg or Wav. If not specified will default to best. + FilenamePattern pattern `json:"filenamePattern"` //Classic, Basic, Pretty or Nerdy. Defaults to Pretty + AudioOnly bool `json:"isAudioOnly"` //Removes the video, downloads audio only. Default: false + UseVimeoDash bool `json:"vimeoDash"` //Changes whether streamed file type is preferred for vimeo videos.. Default: false + FullTikTokAudio bool `json:"isTTFullAudio"` //Enables download of original sound used in a tiktok video. Default: false + VideoOnly bool `json:"isAudioMuted"` //Downloads only the video, audio is muted/removed. Default: false + DubbedYoutubeAudio bool `json:"dubLang"` //Pass the User-Language HTTP header to use the dubbed audio of the respective language, must change according to user's preference, default is English (US). Uses ISO 639-1 standard. + DisableVideoMetadata bool `json:"disableMetadata"` //Removes file metadata. Default: false + ConvertTwitterGifs bool `json:"twitterGif"` //Changes whether twitter gifs are converted to .gif (Twitter gifs are usually stored in .mp4 format). Default: true } type codecs string @@ -82,38 +96,35 @@ const ( Pretty pattern = "pretty" //Looks like: Video Title (1080p, h264, youtube).mp4 | audio: Audio Title - Audio Author (soundcloud).mp3 ) -/* - Function CreateDefaultSettings() creates the Settings struct with default values: - -Url: "" -VideoCodec: H264, -VideoQuality: 1080, -AudioCodec: Best, -FilenamePattern: Pretty, -AudioOnly: false, -FullTikTokAudio: false, -VideoOnly: false, -DubbedYoutubeAudio: false, -DisableVideoMetadata: false, -ConvertTwitterGifs: false, - -You MUST set an url before calling Run(). -*/ +//Function CreateDefaultSettings() creates the Settings struct with default values: + +// Url: "" +// VideoCodec: H264, +// VideoQuality: 1080, +// AudioCodec: Best, +// FilenamePattern: Pretty, +// AudioOnly: false, +// FullTikTokAudio: false, +// VideoOnly: false, +// DubbedYoutubeAudio: false, +// DisableVideoMetadata: false, +// ConvertTwitterGifs: false, +// You MUST set an url before calling Run(). func CreateDefaultSettings() Settings { options := Settings{ - Url: "", - VideoCodec: H264, - VideoQuality: 1080, - AudioCodec: Best, - FilenamePattern: Pretty, - AudioOnly: false, - //RemoveTikTokWatermark: false, - FullTikTokAudio: false, - VideoOnly: false, - DubbedYoutubeAudio: false, - DisableVideoMetadata: false, - ConvertTwitterGifs: true, + Url: "", + VideoCodec: H264, + VideoQuality: 1080, + AudioCodec: Best, + FilenamePattern: Pretty, + AudioOnly: false, + UseVimeoDash: false, + FullTikTokAudio: false, + VideoOnly: false, + DubbedYoutubeAudio: false, + DisableVideoMetadata: false, + ConvertTwitterGifs: true, } return options } @@ -135,18 +146,18 @@ func Run(opts Settings) (*cobaltResponse, error) { } optionsPayload := Settings{ - Url: url.QueryEscape(opts.Url), - VideoCodec: opts.VideoCodec, - VideoQuality: opts.VideoQuality, - AudioCodec: opts.AudioCodec, - FilenamePattern: opts.FilenamePattern, - AudioOnly: opts.AudioOnly, - //RemoveTikTokWatermark: opts.RemoveTikTokWatermark, - FullTikTokAudio: opts.FullTikTokAudio, - VideoOnly: opts.VideoOnly, - DubbedYoutubeAudio: opts.DubbedYoutubeAudio, - DisableVideoMetadata: opts.DisableVideoMetadata, - ConvertTwitterGifs: opts.ConvertTwitterGifs, + Url: url.QueryEscape(opts.Url), + VideoCodec: opts.VideoCodec, + VideoQuality: opts.VideoQuality, + AudioCodec: opts.AudioCodec, + FilenamePattern: opts.FilenamePattern, + AudioOnly: opts.AudioOnly, + UseVimeoDash: opts.UseVimeoDash, + FullTikTokAudio: opts.FullTikTokAudio, + VideoOnly: opts.VideoOnly, + DubbedYoutubeAudio: opts.DubbedYoutubeAudio, + DisableVideoMetadata: opts.DisableVideoMetadata, + ConvertTwitterGifs: opts.ConvertTwitterGifs, } payload, _ := json.Marshal(optionsPayload) @@ -197,8 +208,8 @@ func Run(opts Settings) (*cobaltResponse, error) { } // This function is called before Run() to check if the cobalt server used is reachable. -// If you can't contact the main server, you should using one of the instances listed in https://instances.hyper.lol/. -func CobaltServerInfo(api string) (*serverInfo, error) { +// If you can't contact the main server, try getting another instance using GetCobaltinstances(). +func CobaltServerInfo(api string) (*ServerInfo, error) { //Check if the server is reachable client := &http.Client{ Timeout: 15 * time.Second, @@ -220,13 +231,13 @@ func CobaltServerInfo(api string) (*serverInfo, error) { return nil, err } - var serverResponse serverInfo + var serverResponse ServerInfo err = json.Unmarshal(jsonbody, &serverResponse) if err != nil { return nil, err } res.Body.Close() - return &serverInfo{ + return &ServerInfo{ Branch: serverResponse.Branch, Commit: serverResponse.Commit, Name: serverResponse.Name, @@ -236,3 +247,50 @@ func CobaltServerInfo(api string) (*serverInfo, error) { StartTime: serverResponse.StartTime, }, nil } + +// GetCobaltInstances makes a request to instances.hyper.lol and returns a list of all online cobalt instances. +func GetCobaltInstances() ([]ServerInfo, error) { + client := &http.Client{ + Timeout: 5 * time.Second, + } + req, err := http.NewRequest("GET", "https://instances.hyper.lol/instances.json", nil) + req.Header.Add("User-Agent", useragent) + if err != nil { + return nil, err + } + + res, err := client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + jsonbody, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + var cobaltHyperInstances CobaltInstances + err = json.Unmarshal(jsonbody, &cobaltHyperInstances) + if err != nil { + return nil, err + } + res.Body.Close() + + instancesList := make([]ServerInfo, 0) + + for _, v := range cobaltHyperInstances { + if v.Status { + instancesList = append(instancesList, ServerInfo{ + Version: v.Version, + Commit: v.Commit, + Branch: v.Branch, + Name: v.Name, + URL: v.API, + Cors: v.Cors, + StartTime: fmt.Sprint(v.StartTime), + }) + } + } + return instancesList, nil +}