package main import ( "context" "fmt" "os" "strings" "streamrip-go/internal/app" "streamrip-go/internal/config" "streamrip-go/internal/jsonutil" "streamrip-go/internal/provider" ) // Developer smoke commands, listed by "rip dev-help". These exercise a single // provider call and print the result; they are not part of the supported CLI. func cmdSoundcloudSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 1 { fmt.Println("usage: rip soundcloud-smoke ") return 2 } meta, err := fetchSoundcloudOEmbed(ctx, cfg.Session.Downloads.VerifySSL, strings.TrimSpace(args[0])) if err != nil { fmt.Fprintf(os.Stderr, "soundcloud smoke error: %v\n", err) return 1 } fmt.Printf("soundcloud oembed ok: title=%q author=%q provider=%q\n", jsonutil.StringFromAny(meta["title"]), jsonutil.StringFromAny(meta["author_name"]), jsonutil.StringFromAny(meta["provider_name"])) return 0 } func cmdQobuzSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 1 { fmt.Println("usage: rip qobuz-smoke [quality]") return 2 } opts, err := parseSmokeOptions(args[1:], 1, 4) if err != nil { fmt.Fprintf(os.Stderr, "option error: %v\n", err) return 2 } if opts.qualitySet { cfg.Session.Qobuz.Quality = opts.quality } mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() provider, err := mainApp.GetLoggedInProvider(ctx, "qobuz") if err != nil { fmt.Fprintf(os.Stderr, "qobuz login error: %v\n", err) return 1 } trackID := args[0] meta, err := provider.GetMetadata(ctx, trackID, "track") if err != nil { fmt.Fprintf(os.Stderr, "metadata error: %v\n", err) return 1 } title, _ := meta["title"].(string) d, err := provider.GetDownloadable(ctx, trackID, cfg.Session.Qobuz.Quality) if err != nil { fmt.Fprintf(os.Stderr, "downloadable error: %v\n", err) return 1 } fmt.Printf("qobuz ok: title=%q quality=%d ext=%s\n", title, cfg.Session.Qobuz.Quality, d.Extension) fmt.Printf("stream_url=%s\n", d.URL) return 0 } func cmdQobuzConvertRipSmoke(ctx context.Context, cfg *config.Config, gopts globalOptions, args []string) int { if len(args) < 2 { fmt.Println("usage: rip qobuz-convert-rip-smoke [quality] [--force|--ignore-db]") return 2 } opts, err := parseSmokeOptions(args[2:], 1, 4) if err != nil { fmt.Fprintf(os.Stderr, "option error: %v\n", err) return 2 } if opts.qualitySet { cfg.Session.Qobuz.Quality = opts.quality } cfg.Session.Conversion.Enabled = true cfg.Session.Conversion.Codec = strings.ToUpper(strings.TrimSpace(args[1])) mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() mainApp.IgnoreDB = opts.ignoreDB || gopts.noDB trackID := args[0] if err = mainApp.AddByID(ctx, "qobuz", "track", trackID); err != nil { fmt.Fprintf(os.Stderr, "add error: %v\n", err) return 1 } if err = mainApp.Resolve(ctx); err != nil { fmt.Fprintf(os.Stderr, "resolve error: %v\n", err) return 1 } if err = mainApp.Rip(ctx); err != nil { fmt.Fprintf(os.Stderr, "rip error: %v\n", err) return 1 } fmt.Printf("qobuz convert rip smoke complete (codec=%s)\n", cfg.Session.Conversion.Codec) return 0 } func cmdQobuzSearchSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 1 { fmt.Println("usage: rip qobuz-search-smoke ") return 2 } mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() provider, err := mainApp.GetLoggedInProvider(ctx, "qobuz") if err != nil { fmt.Fprintf(os.Stderr, "qobuz login error: %v\n", err) return 1 } query := strings.Join(args, " ") pages, err := provider.Search(ctx, "album", query, 10) if err != nil { fmt.Fprintf(os.Stderr, "search error: %v\n", err) return 1 } for _, page := range pages { albums, ok := page["albums"].(map[string]any) if !ok { continue } items, ok := albums["items"].([]any) if !ok { continue } for _, raw := range items { item, ok := raw.(map[string]any) if !ok { continue } id := jsonutil.StringFromAny(item["id"]) title := jsonutil.StringFromAny(item["title"]) version := jsonutil.StringFromAny(item["version"]) bitDepth := jsonutil.StringFromAny(item["maximum_bit_depth"]) sr := jsonutil.StringFromAny(item["maximum_sampling_rate"]) if version != "" { title = title + " (" + version + ")" } fmt.Printf("album_id=%s | %s | %sB-%skHz\n", id, title, bitDepth, sr) } } return 0 } func cmdTidalSearchSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 1 { fmt.Println("usage: rip tidal-search-smoke ") return 2 } mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() provider, err := mainApp.GetLoggedInProvider(ctx, "tidal") if err != nil { fmt.Fprintf(os.Stderr, "tidal login error: %v\n", err) return 1 } query := strings.Join(args, " ") pages, err := provider.Search(ctx, "album", query, 10) if err != nil { fmt.Fprintf(os.Stderr, "search error: %v\n", err) return 1 } for _, page := range pages { items, ok := page["items"].([]any) if !ok { continue } for _, raw := range items { wrapper, ok := raw.(map[string]any) if !ok { continue } item, ok := wrapper["item"].(map[string]any) if !ok { item = wrapper } fmt.Printf("album_id=%s | %s\n", jsonutil.StringFromAny(item["id"]), jsonutil.StringFromAny(item["title"])) } } return 0 } func cmdTidalMetadataSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 2 { fmt.Println("usage: rip tidal-metadata-smoke ") return 2 } mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() provider, err := mainApp.GetLoggedInProvider(ctx, "tidal") if err != nil { fmt.Fprintf(os.Stderr, "tidal login error: %v\n", err) return 1 } mediaType := args[0] itemID := args[1] meta, err := provider.GetMetadata(ctx, itemID, mediaType) if err != nil { fmt.Fprintf(os.Stderr, "metadata error: %v\n", err) return 1 } title := jsonutil.StringFromAny(meta["title"]) if title == "" { title = jsonutil.StringFromAny(meta["name"]) } trackCount := 0 if tracksMap, ok := meta["tracks"].(map[string]any); ok { if items, ok := tracksMap["items"].([]map[string]any); ok { trackCount = len(items) } else if anyItems, ok := tracksMap["items"].([]any); ok { trackCount = len(anyItems) } } fmt.Printf("tidal metadata ok: type=%s id=%s title=%q tracks=%d\n", mediaType, itemID, title, trackCount) return 0 } func cmdTidalVideoSmoke(ctx context.Context, cfg *config.Config, _ globalOptions, args []string) int { if len(args) < 1 { fmt.Println("usage: rip tidal-video-smoke ") return 2 } mainApp, err := app.New(cfg) if err != nil { fmt.Fprintf(os.Stderr, "app init error: %v\n", err) return 1 } defer func() { _ = mainApp.Close() }() providerClient, err := mainApp.GetLoggedInProvider(ctx, "tidal") if err != nil { fmt.Fprintf(os.Stderr, "tidal login error: %v\n", err) return 1 } videoProvider, ok := providerClient.(interface { GetVideoDownloadable(context.Context, string) (*provider.Downloadable, error) }) if !ok { fmt.Fprintln(os.Stderr, "tidal provider does not support video downloadable") return 1 } videoID := strings.TrimSpace(args[0]) meta, err := providerClient.GetMetadata(ctx, videoID, "video") if err != nil { fmt.Fprintf(os.Stderr, "video metadata error: %v\n", err) return 1 } d, err := videoProvider.GetVideoDownloadable(ctx, videoID) if err != nil { fmt.Fprintf(os.Stderr, "video downloadable error: %v\n", err) return 1 } title := jsonutil.StringFromAny(meta["title"]) if title == "" { title = jsonutil.StringFromAny(meta["name"]) } fmt.Printf("tidal video ok: id=%s title=%q ext=%s\n", videoID, title, d.Extension) fmt.Printf("stream_url=%s\n", d.URL) return 0 }