-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from raitonoberu/dev
Improved codebase. Customization & piping.
- Loading branch information
Showing
13 changed files
with
728 additions
and
412 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"sptlrx/cookie" | ||
|
||
"github.com/muesli/coral" | ||
) | ||
|
||
var clearCmd = &coral.Command{ | ||
Use: "clear", | ||
Short: "Clear saved cookie", | ||
|
||
RunE: func(cmd *coral.Command, args []string) error { | ||
err := cookie.Clear() | ||
if err == nil { | ||
fmt.Println("Cookie have been cleared.") | ||
} else if errors.Is(err, os.ErrNotExist) { | ||
fmt.Println("You haven't saved any cookies 🍪") | ||
return nil | ||
} | ||
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,97 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"sptlrx/cookie" | ||
"sptlrx/pool" | ||
"sptlrx/spotify" | ||
"strings" | ||
|
||
"github.com/muesli/coral" | ||
"github.com/muesli/reflow/wordwrap" | ||
"github.com/muesli/reflow/wrap" | ||
) | ||
|
||
var ( | ||
FlagLength int | ||
FlagOverflow string | ||
FlagIgnoreErrors bool | ||
) | ||
|
||
var pipeCmd = &coral.Command{ | ||
Use: "pipe", | ||
Short: "Pipes the current line to stdout", | ||
|
||
RunE: func(cmd *coral.Command, args []string) error { | ||
var clientCookie string | ||
|
||
if FlagCookie != "" { | ||
clientCookie = FlagCookie | ||
} else if envCookie := os.Getenv("SPOTIFY_COOKIE"); envCookie != "" { | ||
clientCookie = envCookie | ||
} else { | ||
clientCookie, _ = cookie.Load() | ||
} | ||
|
||
if clientCookie == "" { | ||
return errors.New("couldn't find cookie") | ||
} | ||
|
||
client, err := spotify.NewClient(clientCookie) | ||
if err != nil { | ||
return fmt.Errorf("couldn't create client: %w", err) | ||
} | ||
if err := cookie.Save(clientCookie); err != nil { | ||
return fmt.Errorf("couldn't save cookie: %w", err) | ||
} | ||
|
||
ch := make(chan pool.Update) | ||
go pool.Listen(client, ch) | ||
|
||
for update := range ch { | ||
if update.Err != nil { | ||
if !FlagIgnoreErrors { | ||
fmt.Println(err.Error()) | ||
} | ||
continue | ||
} | ||
if update.Lines == nil || !update.Lines.Timesynced() { | ||
fmt.Println("") | ||
continue | ||
} | ||
line := update.Lines[update.Index].Words | ||
if FlagLength == 0 { | ||
fmt.Println(line) | ||
} else { | ||
// TODO: find out if there is a better way to cut the line | ||
switch FlagOverflow { | ||
case "word": | ||
s := wordwrap.String(line, FlagLength) | ||
fmt.Println(strings.Split(s, "\n")[0]) | ||
case "none": | ||
s := wrap.String(line, FlagLength) | ||
fmt.Println(strings.Split(s, "\n")[0]) | ||
case "ellipsis": | ||
s := wrap.String(line, FlagLength) | ||
lines := strings.Split(s, "\n") | ||
if len(lines) == 1 { | ||
fmt.Println(lines[0]) | ||
} else { | ||
s := wrap.String(lines[0], FlagLength-3) | ||
fmt.Println(strings.Split(s, "\n")[0] + "...") | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
pipeCmd.Flags().StringVar(&FlagCookie, "cookie", "", "your cookie") | ||
pipeCmd.Flags().IntVar(&FlagLength, "length", 0, "max length of line") | ||
pipeCmd.Flags().StringVar(&FlagOverflow, "overflow", "word", "how to wrap an overflowed line (none/word/ellipsis)") | ||
pipeCmd.Flags().BoolVar(&FlagIgnoreErrors, "ignore-errors", false, "don't print errors") | ||
} |
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,193 @@ | ||
package cmd | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"log" | ||
"os" | ||
"sptlrx/cookie" | ||
"sptlrx/spotify" | ||
"sptlrx/ui" | ||
"strconv" | ||
"strings" | ||
|
||
tea "github.com/charmbracelet/bubbletea" | ||
gloss "github.com/charmbracelet/lipgloss" | ||
"github.com/muesli/coral" | ||
) | ||
|
||
const banner = ` | ||
_ _ | ||
___ _ __ | |_ | | _ __ __ __ | ||
/ __|| '_ \ | __|| || '__|\ \/ / | ||
\__ \| |_) || |_ | || | > < | ||
|___/| .__/ \__||_||_| /_/\_\ | ||
|_| | ||
` | ||
|
||
const help = ` | ||
How to get setup: | ||
1. Open your browser. | ||
2. Press F12, open the 'Network' tab and go to open.spotify.com. | ||
3. Click on the first request to open.spotify.com. | ||
4. Scroll down to the 'Request Headers', right click the 'cookie' field and select 'Copy value'. | ||
` | ||
|
||
var ( | ||
FlagCookie string | ||
|
||
FlagStyleBefore string | ||
FlagStyleCurrent string | ||
FlagStyleAfter string | ||
|
||
FlagHAlignment string | ||
) | ||
|
||
var rootCmd = &coral.Command{ | ||
Use: "sptlrx", | ||
Short: "Spotify lyrics in your terminal", | ||
Long: "A CLI app that shows time-synced Spotify lyrics in your terminal.", | ||
Version: "v0.1.0", | ||
SilenceUsage: true, | ||
|
||
RunE: func(cmd *coral.Command, args []string) error { | ||
var clientCookie string | ||
|
||
if FlagCookie != "" { | ||
clientCookie = FlagCookie | ||
} else if envCookie := os.Getenv("SPOTIFY_COOKIE"); envCookie != "" { | ||
clientCookie = envCookie | ||
} else { | ||
fileCookie, err := cookie.Load() | ||
if err != nil { | ||
return fmt.Errorf("couldn't load cookie: %w", err) | ||
} | ||
clientCookie = fileCookie | ||
} | ||
|
||
if clientCookie == "" { | ||
fmt.Print(banner) | ||
fmt.Printf("Cookie will be stored in %s\n", cookie.Directory) | ||
fmt.Print(help) | ||
ask("Enter your cookie:", &clientCookie) | ||
fmt.Println("You can always clear cookie by running 'sptlrx clear'.") | ||
} | ||
|
||
client, err := spotify.NewClient(clientCookie) | ||
if err != nil { | ||
return fmt.Errorf("couldn't create client: %w", err) | ||
} | ||
if err := cookie.Save(clientCookie); err != nil { | ||
return fmt.Errorf("couldn't save cookie: %w", err) | ||
} | ||
|
||
hAlignment := 0.5 | ||
switch FlagHAlignment { | ||
case "left": | ||
hAlignment = 0 | ||
case "right": | ||
hAlignment = 1 | ||
} | ||
|
||
p := tea.NewProgram( | ||
&ui.Model{ | ||
Client: client, | ||
HAlignment: gloss.Position(hAlignment), | ||
StyleBefore: parseStyle(FlagStyleBefore), | ||
StyleCurrent: parseStyle(FlagStyleCurrent), | ||
StyleAfter: parseStyle(FlagStyleAfter), | ||
}, | ||
tea.WithAltScreen(), | ||
) | ||
return p.Start() | ||
}, | ||
} | ||
|
||
func ask(what string, answer *string) { | ||
var ok bool | ||
scanner := bufio.NewScanner(os.Stdin) | ||
for !ok { | ||
fmt.Println("\n" + what) | ||
scanner.Scan() | ||
|
||
if err := scanner.Err(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
line := strings.TrimSpace(scanner.Text()) | ||
|
||
if line != "" { | ||
ok = true | ||
*answer = line | ||
} else { | ||
fmt.Println("The value can't be empty.") | ||
} | ||
} | ||
} | ||
|
||
func parseStyle(value string) gloss.Style { | ||
var style gloss.Style | ||
|
||
if value == "" { | ||
return style | ||
} | ||
|
||
for _, part := range strings.Split(value, ",") { | ||
switch part { | ||
case "bold": | ||
style = style.Bold(true) | ||
case "italic": | ||
style = style.Italic(true) | ||
case "underline": | ||
style = style.Underline(true) | ||
case "strikethrough": | ||
style = style.Strikethrough(true) | ||
case "blink": | ||
style = style.Blink(true) | ||
case "faint": | ||
style = style.Faint(true) | ||
default: | ||
if validateColor(part) { | ||
if style.GetForeground() == (gloss.NoColor{}) { | ||
style = style.Foreground(gloss.Color(part)) | ||
} else { | ||
style = style.Background(gloss.Color(part)) | ||
style.ColorWhitespace(false) | ||
} | ||
} else { | ||
fmt.Println("Invalid style:", part) | ||
} | ||
} | ||
} | ||
return style | ||
} | ||
|
||
func validateColor(color string) bool { | ||
if _, err := strconv.Atoi(color); err == nil { | ||
return true | ||
} | ||
if strings.HasPrefix(color, "#") { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
func init() { | ||
rootCmd.Flags().StringVar(&FlagCookie, "cookie", "", "your cookie") | ||
|
||
rootCmd.Flags().StringVar(&FlagStyleBefore, "before", "bold", "style of the lines before the current ones") | ||
rootCmd.Flags().StringVar(&FlagStyleCurrent, "current", "bold", "style of the current lines") | ||
rootCmd.Flags().StringVar(&FlagStyleAfter, "after", "faint", "style of the lines after the current ones") | ||
|
||
rootCmd.Flags().StringVar(&FlagHAlignment, "halign", "center", "initial horizontal alignment (left/center/right)") | ||
|
||
rootCmd.AddCommand(clearCmd) | ||
rootCmd.AddCommand(pipeCmd) | ||
} | ||
|
||
func Execute() { | ||
if err := rootCmd.Execute(); err != nil { | ||
os.Exit(1) | ||
} | ||
} |
Oops, something went wrong.