initial commit
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"bargain-scraper/internal/exporter"
|
||||
"bargain-scraper/internal/models"
|
||||
"bargain-scraper/internal/scraper"
|
||||
"bargain-scraper/internal/tui"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
if len(os.Args) > 1 && os.Args[1] == "--help" {
|
||||
printHelp()
|
||||
return
|
||||
}
|
||||
|
||||
app := newApp()
|
||||
if err := app.run(); err != nil {
|
||||
log.Fatalf("Error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp() {
|
||||
fmt.Println("Bargain Hardware Storage Scraper")
|
||||
fmt.Println("")
|
||||
fmt.Println("Usage:")
|
||||
fmt.Println(" bargain-scraper Start the interactive TUI")
|
||||
fmt.Println(" bargain-scraper --help Show this help message")
|
||||
fmt.Println("")
|
||||
fmt.Println("In the TUI:")
|
||||
fmt.Println(" ↑/↓ or j/k Navigate")
|
||||
fmt.Println(" Enter Select/scrape category")
|
||||
fmt.Println(" / Start search")
|
||||
fmt.Println(" E Export data")
|
||||
fmt.Println(" R Rescrape current category")
|
||||
fmt.Println(" Esc Go back")
|
||||
}
|
||||
|
||||
type app struct {
|
||||
model *tui.Model
|
||||
scraper *scraper.Scraper
|
||||
exp *exporter.Exporter
|
||||
progressChan chan scraper.ScrapeProgress
|
||||
doneChan chan struct{}
|
||||
}
|
||||
|
||||
func newApp() *app {
|
||||
return &app{
|
||||
model: tui.New(),
|
||||
exp: exporter.New(),
|
||||
progressChan: make(chan scraper.ScrapeProgress, 100),
|
||||
doneChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) run() error {
|
||||
s, err := scraper.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create scraper: %w", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
a.scraper = s
|
||||
|
||||
a.model.ScrapeCallback = func(category models.Category) {
|
||||
go a.scrapeCategory(category)
|
||||
}
|
||||
|
||||
a.model.ExportCallback = func(format string) error {
|
||||
switch format {
|
||||
case "json":
|
||||
return a.exp.ExportToJSON(a.model.GetFilteredProducts(), "")
|
||||
case "csv":
|
||||
return a.exp.ExportToCSV(a.model.GetFilteredProducts(), "")
|
||||
case "print":
|
||||
fmt.Print(a.exp.PrintToConsole(a.model.GetFilteredProducts()))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
p := tea.NewProgram(a.model, tea.WithAltScreen())
|
||||
|
||||
go a.handleProgress(p)
|
||||
|
||||
if _, err := p.Run(); err != nil {
|
||||
return fmt.Errorf("error running TUI: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *app) handleProgress(p *tea.Program) {
|
||||
for {
|
||||
select {
|
||||
case progress := <-a.progressChan:
|
||||
a.model.SetProgress(progress)
|
||||
p.Send(progress)
|
||||
case <-a.doneChan:
|
||||
p.Send(ScrapingDoneMsg{})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ScrapingDoneMsg struct{}
|
||||
|
||||
func (a *app) scrapeCategory(category models.Category) {
|
||||
products, err := a.scraper.ScrapeCategory(a.model.GetContext(), category, a.progressChan)
|
||||
|
||||
a.model.SetScraping(false)
|
||||
|
||||
if err != nil {
|
||||
a.model.SetError(err)
|
||||
}
|
||||
|
||||
a.model.SetProducts(products)
|
||||
a.doneChan <- struct{}{}
|
||||
}
|
||||
Reference in New Issue
Block a user