refactor(cli): use FaviconService exports instead of file cache
- Remove old FaviconCache usage from browse.go
- Update dmenu to get favicons from FaviconService exports
- Dmenu queries {dataDir}/webkit/favicons-export/{hash}.png
- Remove legacy cache cleanup from root.go initialization
- Use 7-day expiry check for favicon freshness- commit
- 0acc806d6777822bfc3ac2b11cb5659fdae2a8ce
- author
- bnema · · 2025-10-18 10:54 UTC
- parent
- c8f47a3
- signature
- GPG Unverified
2B82207D370127A0The key is unknown
3 files · +21 -33
M internal/cli/browse.go +0 -7
| @@ -9,7 +9,6 @@ import ( | |||
| 9 | 9 | "os/exec" | |
| 10 | 10 | "strings" | |
| 11 | 11 | ||
| 12 | - | "github.com/bnema/dumber/internal/cache" | |
| 13 | 12 | "github.com/bnema/dumber/internal/config" | |
| 14 | 13 | ||
| 15 | 14 | "github.com/spf13/cobra" | |
| @@ -161,12 +160,6 @@ func updateFavicon(ctx context.Context, cli *CLI, pageURL string) { | |||
| 161 | 160 | // Update in database using the new sqlc-generated method | |
| 162 | 161 | faviconNullString := sql.NullString{String: faviconURL, Valid: true} | |
| 163 | 162 | if err := cli.Queries.UpdateHistoryFavicon(ctx, faviconNullString, pageURL); err != nil { | |
| 164 | - | // Silently fail - favicon is not critical | |
| 165 | 163 | return | |
| 166 | 164 | } | |
| 167 | - | ||
| 168 | - | // Also cache the favicon for dmenu use | |
| 169 | - | if faviconCache, err := cache.NewFaviconCache(); err == nil { | |
| 170 | - | faviconCache.CacheAsync(faviconURL) | |
| 171 | - | } | |
| 172 | 165 | } | |
| @@ -9,7 +9,6 @@ import ( | |||||
| 9 | "os/exec" | 9 | "os/exec" | ||
| 10 | "strings" | 10 | "strings" | ||
| 11 | 11 | ||||
| 12 | - | "github.com/bnema/dumber/internal/cache" | |||
| 13 | "github.com/bnema/dumber/internal/config" | 12 | "github.com/bnema/dumber/internal/config" | ||
| 14 | 13 | ||||
| 15 | "github.com/spf13/cobra" | 14 | "github.com/spf13/cobra" | ||
| @@ -161,12 +160,6 @@ func updateFavicon(ctx context.Context, cli *CLI, pageURL string) { | |||||
| 161 | // Update in database using the new sqlc-generated method | 160 | // Update in database using the new sqlc-generated method | ||
| 162 | faviconNullString := sql.NullString{String: faviconURL, Valid: true} | 161 | faviconNullString := sql.NullString{String: faviconURL, Valid: true} | ||
| 163 | if err := cli.Queries.UpdateHistoryFavicon(ctx, faviconNullString, pageURL); err != nil { | 162 | if err := cli.Queries.UpdateHistoryFavicon(ctx, faviconNullString, pageURL); err != nil { | ||
| 164 | - | // Silently fail - favicon is not critical | |||
| 165 | return | 163 | return | ||
| 166 | } | 164 | } | ||
| 167 | - | ||||
| 168 | - | // Also cache the favicon for dmenu use | |||
| 169 | - | if faviconCache, err := cache.NewFaviconCache(); err == nil { | |||
| 170 | - | faviconCache.CacheAsync(faviconURL) | |||
| 171 | - | } | |||
| 172 | } | 165 | } | ||
M internal/cli/dmenu.go +21 -18
| @@ -8,8 +8,11 @@ import ( | |||
| 8 | 8 | "os" | |
| 9 | 9 | "sort" | |
| 10 | 10 | "strings" | |
| 11 | + | "time" | |
| 11 | 12 | ||
| 12 | 13 | "github.com/bnema/dumber/internal/cache" | |
| 14 | + | "github.com/bnema/dumber/internal/config" | |
| 15 | + | "github.com/bnema/dumber/internal/services" | |
| 13 | 16 | "github.com/spf13/cobra" | |
| 14 | 17 | ) | |
| 15 | 18 | ||
| @@ -63,11 +66,10 @@ func generateOptions(cli *CLI) error { | |||
| 63 | 66 | cacheConfig.MaxResults = 50 // Match the old maxHistoryEntries | |
| 64 | 67 | cacheManager := cache.NewCacheManager(cli.Queries, cacheConfig) | |
| 65 | 68 | ||
| 66 | - | // Initialize favicon cache | |
| 67 | - | faviconCache, err := cache.NewFaviconCache() | |
| 69 | + | // Get data directory for favicon exports | |
| 70 | + | dataDir, err := config.GetDataDir() | |
| 68 | 71 | if err != nil { | |
| 69 | - | // Continue without favicon cache if it fails | |
| 70 | - | faviconCache = nil | |
| 72 | + | dataDir = "" // Continue without favicons if data dir fails | |
| 71 | 73 | } | |
| 72 | 74 | ||
| 73 | 75 | // Get top entries from cache (this is blazingly fast!) | |
| @@ -139,7 +141,7 @@ func generateOptions(cli *CLI) error { | |||
| 139 | 141 | ||
| 140 | 142 | // Output options to stdout with icon specifications | |
| 141 | 143 | for _, option := range options { | |
| 142 | - | iconName := getIconName(option, faviconCache) | |
| 144 | + | iconName := getIconName(option, dataDir) | |
| 143 | 145 | if iconName != "" { | |
| 144 | 146 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | |
| 145 | 147 | } else { | |
| @@ -157,11 +159,10 @@ func generateOptions(cli *CLI) error { | |||
| 157 | 159 | func generateOptionsFallback(cli *CLI) error { | |
| 158 | 160 | ctx := context.Background() | |
| 159 | 161 | ||
| 160 | - | // Initialize favicon cache | |
| 161 | - | faviconCache, err := cache.NewFaviconCache() | |
| 162 | + | // Get data directory for favicon exports | |
| 163 | + | dataDir, err := config.GetDataDir() | |
| 162 | 164 | if err != nil { | |
| 163 | - | // Continue without favicon cache if it fails | |
| 164 | - | faviconCache = nil | |
| 165 | + | dataDir = "" // Continue without favicons if data dir fails | |
| 165 | 166 | } | |
| 166 | 167 | ||
| 167 | 168 | const maxHistoryEntries = 50 | |
| @@ -240,7 +241,7 @@ func generateOptionsFallback(cli *CLI) error { | |||
| 240 | 241 | ||
| 241 | 242 | // Output options to stdout with icon specifications | |
| 242 | 243 | for _, option := range options { | |
| 243 | - | iconName := getIconName(option, faviconCache) | |
| 244 | + | iconName := getIconName(option, dataDir) | |
| 244 | 245 | if iconName != "" { | |
| 245 | 246 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | |
| 246 | 247 | } else { | |
| @@ -344,19 +345,21 @@ func parseSelection(selection string) string { | |||
| 344 | 345 | } | |
| 345 | 346 | ||
| 346 | 347 | // getIconName determines the appropriate icon name for a dmenu option | |
| 347 | - | func getIconName(option DmenuOption, faviconCache *cache.FaviconCache) string { | |
| 348 | + | func getIconName(option DmenuOption, dataDir string) string { | |
| 348 | 349 | if option.Type != historyType { | |
| 349 | 350 | return "" | |
| 350 | 351 | } | |
| 351 | 352 | ||
| 352 | - | // Only use cached favicons - no system theme fallbacks for consistent sizing | |
| 353 | - | if option.FaviconURL != "" && faviconCache != nil { | |
| 354 | - | cachedPath := faviconCache.GetCachedPath(option.FaviconURL) | |
| 355 | - | if cachedPath != "" { | |
| 356 | - | return cachedPath | |
| 353 | + | // Get exported favicon path from FaviconService | |
| 354 | + | // FaviconService exports all favicons as 32x32 PNG files for CLI access | |
| 355 | + | if option.Value != "" { | |
| 356 | + | faviconPath := services.GetExportedFaviconPath(dataDir, option.Value) | |
| 357 | + | // Check if file exists and is recent (within 7 days) | |
| 358 | + | if info, err := os.Stat(faviconPath); err == nil { | |
| 359 | + | if time.Since(info.ModTime()) < 7*24*time.Hour { | |
| 360 | + | return faviconPath | |
| 361 | + | } | |
| 357 | 362 | } | |
| 358 | - | // Start async download for next time | |
| 359 | - | faviconCache.CacheAsync(option.FaviconURL) | |
| 360 | 363 | } | |
| 361 | 364 | ||
| 362 | 365 | // No favicon available - return empty string to show no icon | |
| @@ -8,8 +8,11 @@ import ( | |||||
| 8 | "os" | 8 | "os" | ||
| 9 | "sort" | 9 | "sort" | ||
| 10 | "strings" | 10 | "strings" | ||
| 11 | + | "time" | |||
| 11 | 12 | ||||
| 12 | "github.com/bnema/dumber/internal/cache" | 13 | "github.com/bnema/dumber/internal/cache" | ||
| 14 | + | "github.com/bnema/dumber/internal/config" | |||
| 15 | + | "github.com/bnema/dumber/internal/services" | |||
| 13 | "github.com/spf13/cobra" | 16 | "github.com/spf13/cobra" | ||
| 14 | ) | 17 | ) | ||
| 15 | 18 | ||||
| @@ -63,11 +66,10 @@ func generateOptions(cli *CLI) error { | |||||
| 63 | cacheConfig.MaxResults = 50 // Match the old maxHistoryEntries | 66 | cacheConfig.MaxResults = 50 // Match the old maxHistoryEntries | ||
| 64 | cacheManager := cache.NewCacheManager(cli.Queries, cacheConfig) | 67 | cacheManager := cache.NewCacheManager(cli.Queries, cacheConfig) | ||
| 65 | 68 | ||||
| 66 | - | // Initialize favicon cache | 69 | + | // Get data directory for favicon exports |
| 67 | - | faviconCache, err := cache.NewFaviconCache() | 70 | + | dataDir, err := config.GetDataDir() |
| 68 | if err != nil { | 71 | if err != nil { | ||
| 69 | - | // Continue without favicon cache if it fails | 72 | + | dataDir = "" // Continue without favicons if data dir fails |
| 70 | - | faviconCache = nil | |||
| 71 | } | 73 | } | ||
| 72 | 74 | ||||
| 73 | // Get top entries from cache (this is blazingly fast!) | 75 | // Get top entries from cache (this is blazingly fast!) | ||
| @@ -139,7 +141,7 @@ func generateOptions(cli *CLI) error { | |||||
| 139 | 141 | ||||
| 140 | // Output options to stdout with icon specifications | 142 | // Output options to stdout with icon specifications | ||
| 141 | for _, option := range options { | 143 | for _, option := range options { | ||
| 142 | - | iconName := getIconName(option, faviconCache) | 144 | + | iconName := getIconName(option, dataDir) |
| 143 | if iconName != "" { | 145 | if iconName != "" { | ||
| 144 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | 146 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | ||
| 145 | } else { | 147 | } else { | ||
| @@ -157,11 +159,10 @@ func generateOptions(cli *CLI) error { | |||||
| 157 | func generateOptionsFallback(cli *CLI) error { | 159 | func generateOptionsFallback(cli *CLI) error { | ||
| 158 | ctx := context.Background() | 160 | ctx := context.Background() | ||
| 159 | 161 | ||||
| 160 | - | // Initialize favicon cache | 162 | + | // Get data directory for favicon exports |
| 161 | - | faviconCache, err := cache.NewFaviconCache() | 163 | + | dataDir, err := config.GetDataDir() |
| 162 | if err != nil { | 164 | if err != nil { | ||
| 163 | - | // Continue without favicon cache if it fails | 165 | + | dataDir = "" // Continue without favicons if data dir fails |
| 164 | - | faviconCache = nil | |||
| 165 | } | 166 | } | ||
| 166 | 167 | ||||
| 167 | const maxHistoryEntries = 50 | 168 | const maxHistoryEntries = 50 | ||
| @@ -240,7 +241,7 @@ func generateOptionsFallback(cli *CLI) error { | |||||
| 240 | 241 | ||||
| 241 | // Output options to stdout with icon specifications | 242 | // Output options to stdout with icon specifications | ||
| 242 | for _, option := range options { | 243 | for _, option := range options { | ||
| 243 | - | iconName := getIconName(option, faviconCache) | 244 | + | iconName := getIconName(option, dataDir) |
| 244 | if iconName != "" { | 245 | if iconName != "" { | ||
| 245 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | 246 | fmt.Printf("%s\x00icon\x1f%s\n", option.Display, iconName) | ||
| 246 | } else { | 247 | } else { | ||
| @@ -344,19 +345,21 @@ func parseSelection(selection string) string { | |||||
| 344 | } | 345 | } | ||
| 345 | 346 | ||||
| 346 | // getIconName determines the appropriate icon name for a dmenu option | 347 | // getIconName determines the appropriate icon name for a dmenu option | ||
| 347 | - | func getIconName(option DmenuOption, faviconCache *cache.FaviconCache) string { | 348 | + | func getIconName(option DmenuOption, dataDir string) string { |
| 348 | if option.Type != historyType { | 349 | if option.Type != historyType { | ||
| 349 | return "" | 350 | return "" | ||
| 350 | } | 351 | } | ||
| 351 | 352 | ||||
| 352 | - | // Only use cached favicons - no system theme fallbacks for consistent sizing | 353 | + | // Get exported favicon path from FaviconService |
| 353 | - | if option.FaviconURL != "" && faviconCache != nil { | 354 | + | // FaviconService exports all favicons as 32x32 PNG files for CLI access |
| 354 | - | cachedPath := faviconCache.GetCachedPath(option.FaviconURL) | 355 | + | if option.Value != "" { |
| 355 | - | if cachedPath != "" { | 356 | + | faviconPath := services.GetExportedFaviconPath(dataDir, option.Value) |
| 356 | - | return cachedPath | 357 | + | // Check if file exists and is recent (within 7 days) |
| 358 | + | if info, err := os.Stat(faviconPath); err == nil { | |||
| 359 | + | if time.Since(info.ModTime()) < 7*24*time.Hour { | |||
| 360 | + | return faviconPath | |||
| 361 | + | } | |||
| 357 | } | 362 | } | ||
| 358 | - | // Start async download for next time | |||
| 359 | - | faviconCache.CacheAsync(option.FaviconURL) | |||
| 360 | } | 363 | } | ||
| 361 | 364 | ||||
| 362 | // No favicon available - return empty string to show no icon | 365 | // No favicon available - return empty string to show no icon | ||
M internal/cli/root.go +0 -8
| @@ -8,7 +8,6 @@ import ( | |||
| 8 | 8 | "os" | |
| 9 | 9 | "path/filepath" | |
| 10 | 10 | ||
| 11 | - | "github.com/bnema/dumber/internal/cache" | |
| 12 | 11 | "github.com/bnema/dumber/internal/config" | |
| 13 | 12 | "github.com/bnema/dumber/internal/db" | |
| 14 | 13 | "github.com/bnema/dumber/internal/services" | |
| @@ -77,13 +76,6 @@ func NewCLI() (*CLI, error) { | |||
| 77 | 76 | queries := db.New(database) | |
| 78 | 77 | parserService := services.NewParserService(cfg, queries) | |
| 79 | 78 | ||
| 80 | - | // Clean old cached favicons on startup (async, non-blocking) | |
| 81 | - | go func() { | |
| 82 | - | if faviconCache, err := cache.NewFaviconCache(); err == nil { | |
| 83 | - | faviconCache.CleanOld() | |
| 84 | - | } | |
| 85 | - | }() | |
| 86 | - | ||
| 87 | 79 | return &CLI{ | |
| 88 | 80 | DB: database, | |
| 89 | 81 | Queries: queries, | |
| @@ -8,7 +8,6 @@ import ( | |||||
| 8 | "os" | 8 | "os" | ||
| 9 | "path/filepath" | 9 | "path/filepath" | ||
| 10 | 10 | ||||
| 11 | - | "github.com/bnema/dumber/internal/cache" | |||
| 12 | "github.com/bnema/dumber/internal/config" | 11 | "github.com/bnema/dumber/internal/config" | ||
| 13 | "github.com/bnema/dumber/internal/db" | 12 | "github.com/bnema/dumber/internal/db" | ||
| 14 | "github.com/bnema/dumber/internal/services" | 13 | "github.com/bnema/dumber/internal/services" | ||
| @@ -77,13 +76,6 @@ func NewCLI() (*CLI, error) { | |||||
| 77 | queries := db.New(database) | 76 | queries := db.New(database) | ||
| 78 | parserService := services.NewParserService(cfg, queries) | 77 | parserService := services.NewParserService(cfg, queries) | ||
| 79 | 78 | ||||
| 80 | - | // Clean old cached favicons on startup (async, non-blocking) | |||
| 81 | - | go func() { | |||
| 82 | - | if faviconCache, err := cache.NewFaviconCache(); err == nil { | |||
| 83 | - | faviconCache.CleanOld() | |||
| 84 | - | } | |||
| 85 | - | }() | |||
| 86 | - | ||||
| 87 | return &CLI{ | 79 | return &CLI{ | ||
| 88 | DB: database, | 80 | DB: database, | ||
| 89 | Queries: queries, | 81 | Queries: queries, | ||