Skip to content

Commit e1eafea

Browse files
guitaripodclaude
andcommitted
Revamp --help command with comprehensive documentation
- Add detailed help system with main help and per-command help screens - Support both -h and --help flags globally and per-command - Document all features, flags, and requirements clearly - Include practical examples for each command - Add supported/unsupported content types for playlist command - Provide installation instructions and troubleshooting tips - Add step-by-step API setup guide in config help The help system now provides complete documentation matching all features described in the README, making the CLI more user-friendly and self-documenting. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 2ea75c3 commit e1eafea

File tree

1 file changed

+267
-21
lines changed

1 file changed

+267
-21
lines changed

main.go

Lines changed: 267 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ var (
1616
xFlag = flag.Bool("x", false, "Return the song.link URL without surrounding <>")
1717
dFlag = flag.Bool("d", false, "Return the song.link URL surrounded by <> and the Spotify URL")
1818
sFlag = flag.Bool("s", false, "Return only the Spotify URL")
19+
hFlag = flag.Bool("h", false, "Show help information")
20+
helpFlag = flag.Bool("help", false, "Show help information")
1921
)
2022

2123
// Command represents a CLI command
@@ -53,6 +55,12 @@ func main() {
5355
// Define base flags
5456
flag.Parse()
5557

58+
// Check for help flags
59+
if *hFlag || *helpFlag {
60+
printHelp("")
61+
os.Exit(0)
62+
}
63+
5664
// Check if a subcommand is provided
5765
args := flag.Args()
5866
if len(args) > 0 {
@@ -70,9 +78,15 @@ func main() {
7078
}
7179
}
7280

81+
// Check if asking for help on a subcommand
82+
if subcommand == "help" && len(args) > 1 {
83+
printHelp(args[1])
84+
os.Exit(0)
85+
}
86+
7387
// If we get here, the subcommand wasn't recognized
7488
fmt.Printf("Unknown command: %s\n\n", subcommand)
75-
printUsage()
89+
printHelp("")
7690
os.Exit(1)
7791
}
7892

@@ -91,12 +105,20 @@ func executeSearch(args []string) error {
91105
typeFlag := searchCmd.String("type", "song", "Type of search: song, album, or both (default: song)")
92106
outFlag := searchCmd.String("out", "downloads", "Output directory for downloaded files")
93107
debugFlag := searchCmd.Bool("debug", false, "Enable debug logging during download")
108+
helpFlag := searchCmd.Bool("help", false, "Show help for search command")
109+
hFlag := searchCmd.Bool("h", false, "Show help for search command")
94110

95111
// Parse search flags
96112
if err := searchCmd.Parse(args); err != nil {
97113
return err
98114
}
99115

116+
// Check for help
117+
if *helpFlag || *hFlag {
118+
printSearchHelp()
119+
os.Exit(0)
120+
}
121+
100122
// Get search query
101123
searchArgs := searchCmd.Args()
102124
if len(searchArgs) == 0 {
@@ -123,6 +145,22 @@ func executeSearch(args []string) error {
123145

124146
// executeConfig handles the config subcommand
125147
func executeConfig(args []string) error {
148+
// Define config flags
149+
configCmd := flag.NewFlagSet("config", flag.ExitOnError)
150+
helpFlag := configCmd.Bool("help", false, "Show help for config command")
151+
hFlag := configCmd.Bool("h", false, "Show help for config command")
152+
153+
// Parse flags
154+
if err := configCmd.Parse(args); err != nil {
155+
return err
156+
}
157+
158+
// Check for help
159+
if *helpFlag || *hFlag {
160+
printConfigHelp()
161+
os.Exit(0)
162+
}
163+
126164
fmt.Println("Configuring Apple Music API credentials...")
127165
return RunOnboarding()
128166
}
@@ -135,11 +173,19 @@ func executeDownload(args []string) error {
135173
formatFlag := downloadCmd.String("format", "mp3", "Download format: mp3 or mp4 (default: mp3)")
136174
outFlag := downloadCmd.String("out", "downloads", "Output directory for downloaded files")
137175
debugFlag := downloadCmd.Bool("debug", false, "Enable debug logging (show yt-dlp/ffmpeg output)")
176+
helpFlag := downloadCmd.Bool("help", false, "Show help for download command")
177+
hFlag := downloadCmd.Bool("h", false, "Show help for download command")
138178

139179
// Parse flags
140180
if err := downloadCmd.Parse(args); err != nil {
141181
return err
142182
}
183+
184+
// Check for help
185+
if *helpFlag || *hFlag {
186+
printDownloadHelp()
187+
os.Exit(0)
188+
}
143189

144190
// Get search query
145191
queryArgs := downloadCmd.Args()
@@ -215,11 +261,19 @@ func executePlaylist(args []string) error {
215261
concurrentFlag := playlistCmd.Int("concurrent", 3, "Number of parallel downloads (default: 3)")
216262
metadataFlag := playlistCmd.Bool("metadata", false, "Save playlist metadata JSON")
217263
debugFlag := playlistCmd.Bool("debug", false, "Enable debug logging")
264+
helpFlag := playlistCmd.Bool("help", false, "Show help for playlist command")
265+
hFlag := playlistCmd.Bool("h", false, "Show help for playlist command")
218266

219267
// Parse flags
220268
if err := playlistCmd.Parse(args); err != nil {
221269
return err
222270
}
271+
272+
// Check for help
273+
if *helpFlag || *hFlag {
274+
printPlaylistHelp()
275+
os.Exit(0)
276+
}
223277

224278
// Get URL argument
225279
urlArgs := playlistCmd.Args()
@@ -388,26 +442,218 @@ func runDefault() error {
388442
return nil
389443
}
390444

391-
// printUsage prints usage information
392-
func printUsage() {
393-
fmt.Println("Usage:")
394-
fmt.Println(" songlink-cli [flags] Process URL from clipboard")
395-
fmt.Println(" songlink-cli search [flags] <query> Search for a song or album")
396-
fmt.Println(" songlink-cli config Configure Apple Music API credentials")
397-
fmt.Println(" songlink-cli download [flags] <query> Download a song or album")
398-
fmt.Println(" songlink-cli playlist [flags] <url> Download entire playlist/album")
399-
fmt.Println("\nFlags:")
400-
fmt.Println(" -x Return the song.link URL without surrounding <>")
401-
fmt.Println(" -d Return the song.link URL surrounded by <> and the Spotify URL")
402-
fmt.Println(" -s Return only the Spotify URL")
403-
fmt.Println("\nSearch Flags:")
404-
fmt.Println(" -type=<type> Type of search: song, album, or both (default: song)")
405-
fmt.Println("\nPlaylist Flags:")
406-
fmt.Println(" --format=<fmt> Download format: mp3 or mp4 (default: mp3)")
407-
fmt.Println(" --out=<dir> Output directory (default: downloads)")
408-
fmt.Println(" --concurrent=<n> Number of parallel downloads (default: 3)")
409-
fmt.Println(" --metadata Save playlist metadata JSON")
410-
fmt.Println(" --debug Show debug output")
445+
// printHelp prints comprehensive help information
446+
func printHelp(command string) {
447+
switch command {
448+
case "search":
449+
printSearchHelp()
450+
case "download":
451+
printDownloadHelp()
452+
case "playlist":
453+
printPlaylistHelp()
454+
case "config":
455+
printConfigHelp()
456+
default:
457+
printGeneralHelp()
458+
}
459+
}
460+
461+
// printGeneralHelp prints the main help screen
462+
func printGeneralHelp() {
463+
fmt.Println("Songlink CLI - A powerful tool for music sharing and downloading")
464+
fmt.Println("")
465+
fmt.Println("USAGE:")
466+
fmt.Println(" songlink-cli [flags] Process URL from clipboard")
467+
fmt.Println(" songlink-cli <command> [flags] <args> Run a specific command")
468+
fmt.Println(" songlink-cli help <command> Show help for a command")
469+
fmt.Println("")
470+
fmt.Println("COMMANDS:")
471+
fmt.Println(" search Search for songs/albums and get shareable links")
472+
fmt.Println(" download Search and download tracks as MP3 or MP4 files")
473+
fmt.Println(" playlist Download entire playlists or albums from Apple Music")
474+
fmt.Println(" config Configure Apple Music API credentials")
475+
fmt.Println("")
476+
fmt.Println("GLOBAL FLAGS:")
477+
fmt.Println(" -h, --help Show this help message")
478+
fmt.Println("")
479+
fmt.Println("URL PROCESSING FLAGS (when run without command):")
480+
fmt.Println(" -x Return song.link URL without <> brackets (for Twitter)")
481+
fmt.Println(" -d Return song.link URL with <> + Spotify URL (for Discord)")
482+
fmt.Println(" -s Return only the Spotify URL")
483+
fmt.Println("")
484+
fmt.Println("EXAMPLES:")
485+
fmt.Println(" # Process URL from clipboard (default)")
486+
fmt.Println(" songlink-cli")
487+
fmt.Println("")
488+
fmt.Println(" # Get link for Twitter sharing")
489+
fmt.Println(" songlink-cli -x")
490+
fmt.Println("")
491+
fmt.Println(" # Search for a song")
492+
fmt.Println(" songlink-cli search \"Bohemian Rhapsody\"")
493+
fmt.Println("")
494+
fmt.Println(" # Download a track as MP4 with artwork")
495+
fmt.Println(" songlink-cli download -format=mp4 \"Purple Rain\"")
496+
fmt.Println("")
497+
fmt.Println(" # Download an entire album")
498+
fmt.Println(" songlink-cli playlist \"https://music.apple.com/us/album/...\"")
499+
fmt.Println("")
500+
fmt.Println("For more information on a command, run:")
501+
fmt.Println(" songlink-cli help <command>")
502+
}
503+
504+
// printSearchHelp prints help for the search command
505+
func printSearchHelp() {
506+
fmt.Println("songlink-cli search - Search for songs or albums and get shareable links")
507+
fmt.Println("")
508+
fmt.Println("USAGE:")
509+
fmt.Println(" songlink-cli search [flags] <query>")
510+
fmt.Println("")
511+
fmt.Println("DESCRIPTION:")
512+
fmt.Println(" Search Apple Music for songs or albums and interactively select a result.")
513+
fmt.Println(" After selection, you can choose to:")
514+
fmt.Println(" 1) Copy shareable links to clipboard")
515+
fmt.Println(" 2) Download the track as MP3")
516+
fmt.Println(" 3) Download as MP4 video with album artwork")
517+
fmt.Println("")
518+
fmt.Println("FLAGS:")
519+
fmt.Println(" -type=<type> Search type: song, album, or both (default: song)")
520+
fmt.Println(" -out=<dir> Output directory for downloads (default: downloads)")
521+
fmt.Println(" -debug Enable debug logging during download")
522+
fmt.Println("")
523+
fmt.Println("GLOBAL FLAGS (when copying links):")
524+
fmt.Println(" -x Format link for Twitter (no brackets)")
525+
fmt.Println(" -d Format for Discord (with Spotify URL)")
526+
fmt.Println(" -s Copy only Spotify URL")
527+
fmt.Println("")
528+
fmt.Println("EXAMPLES:")
529+
fmt.Println(" # Search for a song")
530+
fmt.Println(" songlink-cli search \"Imagine\"")
531+
fmt.Println("")
532+
fmt.Println(" # Search for albums only")
533+
fmt.Println(" songlink-cli search -type=album \"Dark Side of the Moon\"")
534+
fmt.Println("")
535+
fmt.Println(" # Search and format for Discord")
536+
fmt.Println(" songlink-cli search -d \"Hotel California\"")
537+
fmt.Println("")
538+
fmt.Println("REQUIREMENTS:")
539+
fmt.Println(" - Apple Music API credentials (run 'songlink-cli config' to set up)")
540+
fmt.Println(" - For downloads: yt-dlp and ffmpeg must be installed")
541+
}
542+
543+
// printDownloadHelp prints help for the download command
544+
func printDownloadHelp() {
545+
fmt.Println("songlink-cli download - Search and download tracks directly")
546+
fmt.Println("")
547+
fmt.Println("USAGE:")
548+
fmt.Println(" songlink-cli download [flags] <query>")
549+
fmt.Println("")
550+
fmt.Println("DESCRIPTION:")
551+
fmt.Println(" Search for a song or album and download it immediately as an audio")
552+
fmt.Println(" file (MP3) or video file with album artwork (MP4). This command")
553+
fmt.Println(" combines search and download into a single step.")
554+
fmt.Println("")
555+
fmt.Println("FLAGS:")
556+
fmt.Println(" -type=<type> Search type: song or album (default: song)")
557+
fmt.Println(" -format=<fmt> Download format: mp3 or mp4 (default: mp3)")
558+
fmt.Println(" -out=<dir> Output directory (default: downloads)")
559+
fmt.Println(" -debug Show yt-dlp and ffmpeg output")
560+
fmt.Println("")
561+
fmt.Println("EXAMPLES:")
562+
fmt.Println(" # Download a song as MP3")
563+
fmt.Println(" songlink-cli download \"Stairway to Heaven\"")
564+
fmt.Println("")
565+
fmt.Println(" # Download as MP4 with album artwork")
566+
fmt.Println(" songlink-cli download -format=mp4 \"Wonderwall\"")
567+
fmt.Println("")
568+
fmt.Println(" # Download to custom directory")
569+
fmt.Println(" songlink-cli download -out=~/Music \"Yesterday\"")
570+
fmt.Println("")
571+
fmt.Println("REQUIREMENTS:")
572+
fmt.Println(" - Apple Music API credentials (run 'songlink-cli config' to set up)")
573+
fmt.Println(" - yt-dlp: For downloading audio from YouTube")
574+
fmt.Println(" - ffmpeg: For audio/video processing")
575+
fmt.Println("")
576+
fmt.Println("INSTALLATION (macOS):")
577+
fmt.Println(" brew install yt-dlp ffmpeg")
578+
}
579+
580+
// printPlaylistHelp prints help for the playlist command
581+
func printPlaylistHelp() {
582+
fmt.Println("songlink-cli playlist - Download entire playlists or albums")
583+
fmt.Println("")
584+
fmt.Println("USAGE:")
585+
fmt.Println(" songlink-cli playlist [flags] <apple-music-url>")
586+
fmt.Println("")
587+
fmt.Println("DESCRIPTION:")
588+
fmt.Println(" Download all tracks from an Apple Music playlist or album URL.")
589+
fmt.Println(" Supports parallel downloads and automatic retry on failures.")
590+
fmt.Println("")
591+
fmt.Println("SUPPORTED CONTENT:")
592+
fmt.Println(" ✓ Public catalog albums")
593+
fmt.Println(" ✓ Public catalog playlists")
594+
fmt.Println(" ✗ Personal library playlists")
595+
fmt.Println(" ✗ User-created playlists")
596+
fmt.Println(" ✗ Radio stations")
597+
fmt.Println("")
598+
fmt.Println("FLAGS:")
599+
fmt.Println(" --format=<fmt> Download format: mp3 or mp4 (default: mp3)")
600+
fmt.Println(" --out=<dir> Output directory (default: downloads)")
601+
fmt.Println(" --concurrent=<n> Parallel downloads, 1-10 (default: 3)")
602+
fmt.Println(" --metadata Save playlist/album info as JSON")
603+
fmt.Println(" --debug Show detailed progress and errors")
604+
fmt.Println("")
605+
fmt.Println("EXAMPLES:")
606+
fmt.Println(" # Download an album")
607+
fmt.Println(" songlink-cli playlist \"https://music.apple.com/us/album/abbey-road/401469823\"")
608+
fmt.Println("")
609+
fmt.Println(" # Download playlist with metadata")
610+
fmt.Println(" songlink-cli playlist --metadata \"https://music.apple.com/playlist/...\"")
611+
fmt.Println("")
612+
fmt.Println(" # Fast download with 5 workers")
613+
fmt.Println(" songlink-cli playlist --concurrent=5 --format=mp4 \"https://...\"")
614+
fmt.Println("")
615+
fmt.Println("FEATURES:")
616+
fmt.Println(" - Progress tracking for each download")
617+
fmt.Println(" - Automatic retry with exponential backoff")
618+
fmt.Println(" - Saves metadata including track status")
619+
fmt.Println(" - Creates organized directory structure")
620+
fmt.Println("")
621+
fmt.Println("TROUBLESHOOTING:")
622+
fmt.Println(" 404 errors: Content may be region-locked, try different")
623+
fmt.Println(" storefront in URL (e.g., /us/, /gb/, /jp/)")
624+
}
625+
626+
// printConfigHelp prints help for the config command
627+
func printConfigHelp() {
628+
fmt.Println("songlink-cli config - Configure Apple Music API credentials")
629+
fmt.Println("")
630+
fmt.Println("USAGE:")
631+
fmt.Println(" songlink-cli config")
632+
fmt.Println("")
633+
fmt.Println("DESCRIPTION:")
634+
fmt.Println(" Interactive setup wizard for Apple Music API credentials.")
635+
fmt.Println(" Required for search, download, and playlist features.")
636+
fmt.Println("")
637+
fmt.Println("WHAT YOU'LL NEED:")
638+
fmt.Println(" 1. Apple Developer account")
639+
fmt.Println(" 2. MusicKit-enabled API key")
640+
fmt.Println(" 3. Team ID and Key ID")
641+
fmt.Println(" 4. Private key (.p8 file)")
642+
fmt.Println("")
643+
fmt.Println("SETUP STEPS:")
644+
fmt.Println(" 1. Sign in to https://developer.apple.com")
645+
fmt.Println(" 2. Go to Certificates, Identifiers & Profiles")
646+
fmt.Println(" 3. Under Keys, create a new key")
647+
fmt.Println(" 4. Enable MusicKit service")
648+
fmt.Println(" 5. Download the .p8 private key file")
649+
fmt.Println(" 6. Note your Team ID and Key ID")
650+
fmt.Println("")
651+
fmt.Println("STORED LOCATION:")
652+
fmt.Println(" ~/.songlink-cli/config.json")
653+
fmt.Println("")
654+
fmt.Println("SECURITY:")
655+
fmt.Println(" Credentials are stored locally and never transmitted")
656+
fmt.Println(" except to Apple's API servers.")
411657
}
412658

413659
func loadingIndicator(stop chan bool) {

0 commit comments

Comments
 (0)