[WIP] music platform user data scraper
teal-fm atproto

embed fs working

Changed files
+51 -45
cmd
pages
+3 -2
cmd/handlers.go
··· 9 9 10 10 "github.com/teal-fm/piper/db" 11 11 "github.com/teal-fm/piper/models" 12 + pages "github.com/teal-fm/piper/pages" 12 13 "github.com/teal-fm/piper/service/musicbrainz" 13 14 "github.com/teal-fm/piper/service/spotify" 14 15 "github.com/teal-fm/piper/session" 15 16 ) 16 17 17 - func home(database *db.DB, pages *Pages) http.HandlerFunc { 18 + func home(database *db.DB, pages *pages.Pages) http.HandlerFunc { 18 19 return func(w http.ResponseWriter, r *http.Request) { 19 20 20 21 w.Header().Set("Content-Type", "text/html") ··· 138 139 </body> 139 140 </html> 140 141 ` 141 - pages.execute("home", w, nil) 142 + pages.Execute("home", w, nil) 142 143 143 144 //w.Write([]byte(html)) 144 145 }
+3 -2
cmd/main.go
··· 16 16 "github.com/teal-fm/piper/db" 17 17 "github.com/teal-fm/piper/oauth" 18 18 "github.com/teal-fm/piper/oauth/atproto" 19 + pages "github.com/teal-fm/piper/pages" 19 20 apikeyService "github.com/teal-fm/piper/service/apikey" 20 21 "github.com/teal-fm/piper/service/musicbrainz" 21 22 "github.com/teal-fm/piper/service/spotify" ··· 31 32 mbService *musicbrainz.MusicBrainzService 32 33 atprotoService *atproto.ATprotoAuthService 33 34 playingNowService *playingnow.PlayingNowService 34 - pages *Pages 35 + pages *pages.Pages 35 36 } 36 37 37 38 // JSON API handlers ··· 106 107 spotifyService: spotifyService, 107 108 atprotoService: atprotoService, 108 109 playingNowService: playingNowService, 109 - pages: NewPages(true), 110 + pages: pages.NewPages(false), 110 111 } 111 112 112 113 trackerInterval := time.Duration(viper.GetInt("tracker.interval")) * time.Second
+10 -41
cmd/pages.go pages/pages.go
··· 1 - package main 1 + package pages 2 2 3 3 import ( 4 4 "embed" ··· 7 7 "io/fs" 8 8 "os" 9 9 "strings" 10 - "sync" 11 10 "time" 12 11 ) 13 12 ··· 33 32 if pages.dev { 34 33 pages.embedFS = os.DirFS(pages.templateDir) 35 34 } else { 36 - //pages.embedFS = Files 35 + pages.embedFS = Files 37 36 } 38 37 39 38 return pages ··· 41 40 42 41 func (p *Pages) fragmentPaths() ([]string, error) { 43 42 var fragmentPaths []string 43 + // When using os.DirFS("templates"), the FS root is already the templates directory. 44 + // Walk from "." and use relative paths (no "templates/" prefix). 44 45 err := fs.WalkDir(p.embedFS, "templates", func(path string, d fs.DirEntry, err error) error { 45 46 if err != nil { 46 47 return err ··· 51 52 if !strings.HasSuffix(path, ".gohtml") { 52 53 return nil 53 54 } 54 - if !strings.Contains(path, "fragments/") { 55 - return nil 56 - } 55 + //if !strings.Contains(path, "fragments/") { 56 + // return nil 57 + //} 57 58 fragmentPaths = append(fragmentPaths, path) 58 59 return nil 59 60 }) ··· 65 66 } 66 67 67 68 func (p *Pages) pathToName(s string) string { 68 - return strings.TrimSuffix(strings.TrimPrefix(s, "templates/"), ".html") 69 + return strings.TrimSuffix(strings.TrimPrefix(s, "templates/"), ".gohtml") 69 70 } 70 71 71 72 // reverse of pathToName 72 73 func (p *Pages) nameToPath(s string) string { 73 - return "templates/" + s + ".html" 74 + return "templates/" + s + ".gohtml" 74 75 } 75 76 76 77 // parse without memoization ··· 140 141 return tpl.Execute(w, params) 141 142 } 142 143 143 - func (p *Pages) execute(name string, w io.Writer, params any) error { 144 + func (p *Pages) Execute(name string, w io.Writer, params any) error { 144 145 tpl, err := p.parseBase(name) 145 146 if err != nil { 146 147 return err ··· 148 149 149 150 return tpl.ExecuteTemplate(w, "layouts/base", params) 150 151 } 151 - 152 - /// Cache for pages 153 - 154 - type TmplCache[K comparable, V any] struct { 155 - data map[K]V 156 - mutex sync.RWMutex 157 - } 158 - 159 - func NewTmplCache[K comparable, V any]() *TmplCache[K, V] { 160 - return &TmplCache[K, V]{ 161 - data: make(map[K]V), 162 - } 163 - } 164 - 165 - func (c *TmplCache[K, V]) Get(key K) (V, bool) { 166 - c.mutex.RLock() 167 - defer c.mutex.RUnlock() 168 - val, exists := c.data[key] 169 - return val, exists 170 - } 171 - 172 - func (c *TmplCache[K, V]) Set(key K, value V) { 173 - c.mutex.Lock() 174 - defer c.mutex.Unlock() 175 - c.data[key] = value 176 - } 177 - 178 - func (c *TmplCache[K, V]) Size() int { 179 - c.mutex.RLock() 180 - defer c.mutex.RUnlock() 181 - return len(c.data) 182 - }
+35
pages/cache.go
··· 1 + package pages 2 + 3 + import "sync" 4 + 5 + /// Cache for pages 6 + 7 + type TmplCache[K comparable, V any] struct { 8 + data map[K]V 9 + mutex sync.RWMutex 10 + } 11 + 12 + func NewTmplCache[K comparable, V any]() *TmplCache[K, V] { 13 + return &TmplCache[K, V]{ 14 + data: make(map[K]V), 15 + } 16 + } 17 + 18 + func (c *TmplCache[K, V]) Get(key K) (V, bool) { 19 + c.mutex.RLock() 20 + defer c.mutex.RUnlock() 21 + val, exists := c.data[key] 22 + return val, exists 23 + } 24 + 25 + func (c *TmplCache[K, V]) Set(key K, value V) { 26 + c.mutex.Lock() 27 + defer c.mutex.Unlock() 28 + c.data[key] = value 29 + } 30 + 31 + func (c *TmplCache[K, V]) Size() int { 32 + c.mutex.RLock() 33 + defer c.mutex.RUnlock() 34 + return len(c.data) 35 + }
templates/home.gohtml pages/templates/home.gohtml
templates/layouts/base.gohtml pages/templates/layouts/base.gohtml