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
+
+
+
+
+[![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
+}