// linksexp -- Texinfo/XBEL/OPML/urls autogeneration from recfile bookmark // Copyright (C) 2021-2024 Sergey Matveev // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package main import ( "flag" "fmt" "io" "os" "sort" "strings" "time" "go.cypherpunks.ru/recfile" ) type ByTitle []map[string][]string func (a ByTitle) Len() int { return len(a) } func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByTitle) Less(i, j int) bool { return strings.Compare(a[i]["Title"][0], a[j]["Title"][0]) < 0 } func main() { doXBEL := flag.Bool("xbel", false, "Make XBEL") doOPML := flag.Bool("opml", false, "Make OPML") doURLS := flag.Bool("urls", false, "Make newsboat urls") flag.Parse() r := recfile.NewReader(os.Stdin) if *doURLS { for { m, err := r.NextMapWithSlice() if err == io.EOF { break } if err != nil { panic(err) } if m["%rec"] != nil { continue } sort.Strings(m["Category"]) cats := strings.Join(m["Category"], " ") for _, f := range m["Feed"] { if strings.HasPrefix(f, "gemini://") { f = "https://gemini/" + f } fmt.Println(f, cats) } } os.Exit(0) } if *doOPML { data := make([]map[string][]string, 0) for { m, err := r.NextMapWithSlice() if err == io.EOF { break } if err != nil { panic(err) } if m["%rec"] != nil { continue } sort.Strings(m["Category"]) data = append(data, m) } sort.Sort(ByTitle(data)) opml(data) os.Exit(0) } data := make(map[string][]map[string][]string) for { m, err := r.NextMapWithSlice() if err == io.EOF { break } if err != nil { panic(err) } if m["%rec"] != nil { continue } sort.Strings(m["Category"]) if cs := m["Category"]; len(cs) == 0 { data["Uncategorized"] = append(data["Uncategorized"], m) } else { for _, cat := range cs { data[cat] = append(data[cat], m) } } } cats := make([]string, 0, len(data)) for c := range data { cats = append(cats, c) sort.Sort(ByTitle(data[c])) } sort.Strings(cats) if *doXBEL { xbel(cats, data) os.Exit(0) } fmt.Println("Updated:", time.Now().Format(time.RFC3339)) fmt.Println("@menu") fmt.Println("Categories:") for _, cat := range cats { fmt.Printf("* %s (%d items): LinksCat%s\n", cat, len(data[cat]), cat) } fmt.Println("@end menu") for _, cat := range cats { fmt.Println("@node", "LinksCat"+cat) fmt.Println("@section Links category:", cat) fmt.Println("@multitable @columnfractions .05 .8 .1 .05") fmt.Println("@headitem @tab @tab Other categories @tab Feed URLs") ents := data[cat] for n, ent := range ents { catsOther := make([]string, 0) for _, c := range ent["Category"] { if c != cat { catsOther = append(catsOther, c) } } var note string if len(ent["Note"]) > 0 { note = "(" + strings.Trim(ent["Note"][0], " \n") + ")" } fmt.Printf( " @item %d @tab @url{%s,, %s} %s @tab %s @tab\n", n, strings.ReplaceAll(ent["URL"][0], "@", "@@"), strings.ReplaceAll(ent["Title"][0], "@", "@@"), note, strings.Join(catsOther, ", "), ) switch feeds := ent["Feed"]; len(feeds) { case 0: fmt.Printf(" @emph{STATIC}\n") case 1: fmt.Printf( " @url{%s, feed}\n", strings.ReplaceAll(feeds[0], "@", "@@"), ) default: for i, feed := range feeds { fmt.Printf( " @url{%s, feed%d}\n", strings.ReplaceAll(feed, "@", "@@"), i, ) } } } fmt.Println("@end multitable") } }