Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ sha256sums=('')
build() {
cd "$srcdir/GopherTube-$pkgver"
export CGO_ENABLED=0
go build -ldflags "-X main.version=$pkgver" -o gophertube main.go
go build -ldflags "-X gophertube/internal/app.version=$pkgver" -o gophertube main.go
}

package() {
Expand All @@ -23,4 +23,4 @@ package() {
install -Dm644 man/gophertube.1 "$pkgdir/usr/share/man/man1/gophertube.1"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
install -Dm644 config/gophertube.toml "$pkgdir/usr/share/doc/$pkgname/gophertube.toml.example"
}
}
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")
echo "Building GopherTube with version: $LATEST_TAG"
go build -ldflags "-X main.version=$LATEST_TAG" -o gophertube main.go
go build -ldflags "-X gophertube/internal/app.version=$LATEST_TAG" -o gophertube main.go
9 changes: 4 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
module gophertube

go 1.23.0
go 1.23.2

toolchain go1.24.5

require (
github.com/BurntSushi/toml v1.5.0
github.com/chzyer/readline v1.5.1
github.com/urfave/cli-altsrc/v3 v3.0.1
github.com/urfave/cli/v3 v3.3.8
)

require (
github.com/creack/pty v1.1.24 // indirect
golang.org/x/sys v0.34.0 // indirect
)
require golang.org/x/sys v0.34.0 // indirect
14 changes: 12 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli-altsrc/v3 v3.0.1 h1:v+gHk59syLk8ao9rYybZs43+D5ut/gzj0omqQ1XYl8k=
github.com/urfave/cli-altsrc/v3 v3.0.1/go.mod h1:8UtsKKcxFVzvaoySFPfvQOk413T+IXJhaCWyyoPW3yM=
github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E=
github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62 changes: 62 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package app

import (
"context"
_ "embed"
"fmt"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"

"github.com/urfave/cli/v3"
)

var version = "dev"

//go:embed description.txt
var Desc string

func New() cli.Command {
return cli.Command{
Name: "GopherTube",
Authors: []any{
"KrishnaSSH <krishna.pytech@gmail.com>",
},
Usage: "Terminal YouTube Search & Play",
Description: Desc,
Flags: Flags(),
Version: version,
Action: Action,
}
}

// Action is the equivalent of the main except that all flags/configs
// have already been parsed and sanitized.
func Action(ctx context.Context, cmd *cli.Command) error {
// Handle Ctrl+C gracefully
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Println("\n\033[1;33mExiting...\033[0m")
os.Exit(0)
}()

for {
mainMenu := []string{"Search YouTube", "Search Downloads"}
action := exec.Command("fzf", "--prompt=Select mode: ")
action.Stdin = strings.NewReader(strings.Join(mainMenu, "\n"))
out, _ := action.Output()
choice := strings.TrimSpace(string(out))
switch choice {
case "Search YouTube":
gophertubeYouTubeMode(cmd)
case "Search Downloads":
gophertubeDownloadsMode(cmd)
default:
return nil
}
}
}
9 changes: 9 additions & 0 deletions internal/app/description.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Gophertube is a terminal-based YouTube search and video player application.
Navigate through YouTube content efficiently using keyboard shortcuts and enjoy
seamless video playback directly from your terminal.

Controls:
• Type your search query and press Enter
• Use ↑/↓ to navigate results
• Press Tab to load more results
• Press Esc to go back or exit
89 changes: 89 additions & 0 deletions internal/app/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package app

import (
"errors"
"os"

"github.com/urfave/cli-altsrc/v3"
toml "github.com/urfave/cli-altsrc/v3/toml"
"github.com/urfave/cli/v3"
)

// Flag names are constant since they are also used as keys to query the data.
// This ensures a single source of truth.
const (
FlagQuality = "quality"
FlagSearchLimit = "search-limit"
FlagConfig = "config"
FlagDownloadsPath = "downloads-path"

defaultConfigPath = "$HOME/.config/gophertube/gophertube.toml"
defaultDownloadsPath = "$HOME/Videos/GopherTube"
)

var (
errQualityFormat = errors.New("invalid format for quality provided")
)

func Flags() []cli.Flag {
var confDir string

// --help and -version flags are free, no need to set them up :)
return []cli.Flag{
&cli.StringFlag{
Name: FlagConfig,
Aliases: []string{"c"},
Sources: cli.EnvVars("GOPHERTUBE_CONFIG"),
Value: os.ExpandEnv(defaultConfigPath),
DefaultText: defaultConfigPath, // otherwise `--help` prints it expanded
Destination: &confDir,
TakesFile: true,
},
&cli.StringFlag{
Name: FlagDownloadsPath,
Aliases: []string{"d"},
TakesFile: true,
Sources: cli.NewValueSourceChain(
toml.TOML("downloads_path", altsrc.NewStringPtrSourcer(&confDir)),
),
Value: os.ExpandEnv(defaultDownloadsPath),
DefaultText: defaultDownloadsPath, // otherwise `--help` prints it expanded
},
&cli.IntFlag{
Name: FlagSearchLimit,
Aliases: []string{"l"},
Sources: cli.NewValueSourceChain(
toml.TOML("search_limit", altsrc.NewStringPtrSourcer(&confDir)),
),
Value: 30,
},
&cli.StringFlag{
Name: FlagQuality,
Aliases: []string{"q"},
Sources: cli.NewValueSourceChain(
toml.TOML("quality", altsrc.NewStringPtrSourcer(&confDir)),
),
Value: "720p",
Validator: IsValidQualityFmt,
},
}
}

// Ensure argument format follows the "<int>p" pattern.
// Ex: 720p, 1080p, etc...
func IsValidQualityFmt(s string) error {
// Check if string ends with 'p'
if len(s) < 2 || s[len(s)-1] != 'p' {
return errQualityFormat
}

// Check if everything before 'p' is a valid integer
numPart := s[:len(s)-1]
for _, char := range numPart {
if char < '0' || char > '9' {
return errQualityFormat
}
}

return nil
}
Loading