commit d127bfea684b80ced83693d029c8a3b38e9e3546 from: Rohan Verma date: Thu Jul 2 22:30:03 2026 UTC update table width, move to librey commit - edc1831cb3d3372a55ed8721a2ee5f1e854d6967 commit + d127bfea684b80ced83693d029c8a3b38e9e3546 blob - e9d1da12f19339f89a364299cb8ebe2152dde288 blob + b2da7b9ec6e57de642ba2dfa18436e27338b55e7 --- main.go +++ main.go @@ -4,6 +4,7 @@ import ( _ "embed" "encoding/json" "find/utils" + "html" "html/template" "io" "net/http" @@ -44,8 +45,10 @@ type Page struct { Web []WebRes News []NewsRes Images []ImgRes + ImgRows [][]ImgRes Count int - ShowTabs, HasPrev, HasNext bool + ShowTabs, ShowNews bool + HasPrev, HasNext bool Page, PrevPage, NextPage int PrevNPT, PrevH, NextH template.URL } @@ -76,6 +79,7 @@ var ( ramMu sync.RWMutex ram = map[string]Page{} cdir, base string + backend string // "4get" or "librey" ) // --- helpers --- @@ -233,6 +237,98 @@ func fetchParse(tab, q, npt string, p *Page) error { return nil } +// --- librey backend --- + +type libItem struct { + Title, URL, Description string + Thumbnail, Alt string + Special *struct{ Response, Source, Image string } `json:"special_response"` +} + +// LibreY's api.php returns a plain JSON array, except when PHP string keys +// ("results_source", "error") are mixed in, in which case it becomes an +// object with numeric string keys. Handle both. +func libreyItems(body []byte) ([]libItem, string, error) { + var arr []libItem + if json.Unmarshal(body, &arr) == nil { return arr, "", nil } + var obj map[string]json.RawMessage + if err := json.Unmarshal(body, &obj); err != nil { return nil, "", err } + if e, ok := obj["error"]; ok { + var em struct{ Message string } + _ = json.Unmarshal(e, &em) + if em.Message == "" { em.Message = "backend error" } + return nil, em.Message, nil + } + var out []libItem + for i := 0; ; i++ { + raw, ok := obj[strconv.Itoa(i)] + if !ok { break } + var it libItem + if json.Unmarshal(raw, &it) == nil { out = append(out, it) } + } + return out, "", nil +} + +func fetchLibrey(tab, q string, pg int, p *Page) error { + t := "0" + if tab == "images" { t = "1" } + ck := "librey" + t + pgs := strconv.Itoa(pg) + var body []byte; cached := false + if b, ok := diskGet(ck, q, pgs); ok { + body, cached = b, true + } else { + req, _ := http.NewRequest("GET", base+"/api.php?q="+esc(q)+"&t="+t+"&p="+strconv.Itoa(pg-1), nil) + req.Header.Set("Accept", "application/json") + resp, err := client.Do(req); if err != nil { return err } + body, _ = io.ReadAll(resp.Body); resp.Body.Close() + } + items, apiErr, err := libreyItems(body) + if err != nil { return err } + if apiErr != "" { p.Error = apiErr; return nil } + if !cached && len(items) > 0 { diskSet(ck, q, pgs, body) } + + un := html.UnescapeString // librey htmlspecialchars()-escapes everything + for _, it := range items { + if it.Special != nil { + r, s := trim(un(it.Special.Response)), trim(it.Special.Source) + if r != "" { + var b strings.Builder + b.WriteString(template.HTMLEscapeString(r)) + if s != "" { + b.WriteString(`
` + template.HTMLEscapeString(s) + ``) + } + p.Answer = template.HTML(b.String()) + } + continue + } + switch tab { + case "images": + ti, u, th := trim(un(it.Alt)), trim(un(it.URL)), trim(un(it.Thumbnail)) + if u == "" && th == "" { continue } + if ti == "" { ti = u } + p.Images = append(p.Images, ImgRes{Title: ti, URL: u, Thumb: th}) + default: + ti, u, d := trim(un(it.Title)), trim(un(it.URL)), trim(un(it.Description)) + if ti == "" && u == "" { continue } + if ti == "" { ti = u } + p.Web = append(p.Web, WebRes{Title: ti, URL: u, Desc: d}) + } + } + if tab == "images" { p.Count = len(p.Images) } else { p.Count = len(p.Web) } + p.HasNext = p.Count > 0 + return nil +} + +func chunkImgs(in []ImgRes, n int) [][]ImgRes { + var rows [][]ImgRes + for i := 0; i < len(in); i += n { + j := i + n; if j > len(in) { j = len(in) } + rows = append(rows, in[i:j]) + } + return rows +} + // --- handlers --- func home(w http.ResponseWriter, r *http.Request) { @@ -249,6 +345,7 @@ func search(w http.ResponseWriter, r *http.Request) { tab := trim(r.URL.Query().Get("tab")) pg := atoi(r.URL.Query().Get("page"), 1) if tab != "images" && tab != "news" { tab = "web" } + if backend == "librey" && tab == "news" { tab = "web" } // librey has no news var conv utils.Conv if amtStr := r.URL.Query().Get("amt"); amtStr != "" { @@ -265,7 +362,8 @@ func search(w http.ResponseWriter, r *http.Request) { p := Page{ Tab: tab, Query: q, EscQ: template.URL(esc(q)), EscNPT: template.URL(esc(npt)), - ShowTabs: true, Page: pg, PrevPage: pg - 1, NextPage: pg + 1, HasPrev: pg > 1, + ShowTabs: true, ShowNews: backend != "librey", + Page: pg, PrevPage: pg - 1, NextPage: pg + 1, HasPrev: pg > 1, Conv: conv, Calc: calc, Dict: dict, } if p.HasPrev && len(h) > 0 { @@ -286,7 +384,10 @@ func search(w http.ResponseWriter, r *http.Request) { _ = tpl.ExecuteTemplate(w, "head", p) if f, ok := w.(http.Flusher); ok { f.Flush() } - if err := fetchParse(tab, q, npt, &p); err != nil { p.Error = err.Error() } + var ferr error + if backend == "librey" { ferr = fetchLibrey(tab, q, pg, &p) } else { ferr = fetchParse(tab, q, npt, &p) } + if ferr != nil { p.Error = ferr.Error() } + p.ImgRows = chunkImgs(p.Images, 4) if p.HasNext { nh := append(append([]string{}, h...), npt); if npt == "" { nh = h } p.NextH = template.URL(esc(strings.Join(nh, ","))) @@ -297,12 +398,15 @@ func search(w http.ResponseWriter, r *http.Request) { func main() { cdir = env("CACHE_DIR", "/cache") - base = strings.TrimRight(env("FOURGET_URL", "http://10.88.0.134"), "/") + backend = env("BACKEND", "4get") // "4get" or "librey" + def := "http://10.88.0.134" + if backend == "librey" { def = "http://127.0.0.1:8080" } + base = strings.TrimRight(env("BACKEND_URL", env("FOURGET_URL", def)), "/") _ = os.MkdirAll(cdir, 0o755) utils.ConvInit() var b strings.Builder - _ = tpl.ExecuteTemplate(&b, "head", Page{Tab: "web"}) + _ = tpl.ExecuteTemplate(&b, "head", Page{Tab: "web", ShowNews: backend != "librey"}) _ = tpl.ExecuteTemplate(&b, "tail", Page{Tab: "web"}) homeHTML = []byte(b.String()) blob - a2d18a4578c6027b8664f021ed4f7cda7376427a blob + 2b83a6fddcc6f37ef0bd4ee723b5b148520553de --- utils/units.go +++ utils/units.go @@ -1,8 +1,5 @@ package utils -// Unit catalog — conversion factors are relative to the base unit of each category. -// Currency factors are unused (fetched live); temperature uses special-case math. - type udef struct{ id, label string; fac float64; aka string } type cdef struct{ id, label string; units []udef }