diff --git a/album.go b/album.go index c2354d8..325a537 100644 --- a/album.go +++ b/album.go @@ -106,8 +106,8 @@ func (a *Album) String() string { var rxExt = regexp.MustCompile(`\.([0-9]+)([a-zA-Z]+)\z`) -func (a *Album) CoverExt() string { - ext := path.Ext(a.CoverURL) +func AlbumCoverExt(URL string) string { + ext := path.Ext(URL) m := rxExt.FindStringSubmatch(ext) if m != nil { ext = "." + m[2] diff --git a/album_cover_cache.go b/album_cover_cache.go index 759b44d..a813a41 100644 --- a/album_cover_cache.go +++ b/album_cover_cache.go @@ -34,21 +34,22 @@ func NewAlbumCoverCache(basepath string, maxRequest uint, window time.Duration) return res } -func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, error) { +func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, string, error) { URL, ok := c.URLs[ID] if ok == false { - return nil, fmt.Errorf("Cover URL for Album %d is not registered", ID) + return nil, "", fmt.Errorf("Cover URL for Album %d is not registered", ID) } + ext := AlbumCoverExt(URL) resp, err := c.getter.Get(URL) if err != nil { - return nil, err + return nil, ext, err } defer closeOrPanic(resp.Body, "GET "+URL) err = c.dv.WriteStream(c.dataKey(ID), resp.Body, false) if err != nil { - return nil, err + return nil, ext, err } md := coverMetadata{ @@ -58,11 +59,11 @@ func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, error) { if err == nil { err = c.dv.Write(c.metadataKey(ID), mdbyte) if err != nil { - return nil, err + return nil, ext, err } } - - return c.dv.ReadStream(c.dataKey(ID), false) + rc, err := c.dv.ReadStream(c.dataKey(ID), false) + return rc, ext, err } type coverMetadata struct { @@ -84,7 +85,7 @@ func (c *AlbumCoverCache) RegisterCover(ID AlbumID, URL string) { } // GetCover retrieves from the cache or either from www.bedetheque.com the Cover of an album -func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, error) { +func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, string, error) { c.lock.RLock() defer c.lock.RUnlock() @@ -107,5 +108,8 @@ func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, error) { return c.fetch(ID) } - return c.dv.ReadStream(c.dataKey(ID), false) + URL := c.URLs[ID] + rc, err := c.dv.ReadStream(c.dataKey(ID), false) + + return rc, AlbumCoverExt(URL), err } diff --git a/album_cover_cache_test.go b/album_cover_cache_test.go index 1f43ab8..c906b90 100644 --- a/album_cover_cache_test.go +++ b/album_cover_cache_test.go @@ -38,7 +38,7 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { var resData = []bytes.Buffer{} start := time.Now() for _, a := range albumsDataTest { - cover, err := s.cache.GetCover(a.ID) + cover, _, err := s.cache.GetCover(a.ID) var buf bytes.Buffer if c.Check(err, IsNil) == true { _, err := io.Copy(&buf, cover) @@ -53,7 +53,7 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { // now we check that we get it again, but from the disk, not // hitting the web for i, a := range albumsDataTest { - cover, err := s.cache.GetCover(a.ID) + cover, _, err := s.cache.GetCover(a.ID) var buf bytes.Buffer if c.Check(err, IsNil) == true { _, err := io.Copy(&buf, cover) @@ -68,7 +68,7 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { s.cache.TTL = 0 for _, a := range albumsDataTest { - cover, err := s.cache.GetCover(a.ID) + cover, _, err := s.cache.GetCover(a.ID) c.Check(cover, IsNil) c.Check(err, ErrorMatches, "I will always have an error") } diff --git a/album_test.go b/album_test.go index ca0e9eb..4707701 100644 --- a/album_test.go +++ b/album_test.go @@ -41,19 +41,20 @@ func (s *AlbumSuite) TestAuthors(c *C) { func (s *AlbumSuite) TestExt(c *C) { type TestData struct { - A *Album + URL string Res string } data := []TestData{ - {&Album{CoverURL: "foo/bar/test.jpg"}, ".jpg"}, - {&Album{CoverURL: "foo/bar/foo.JPG"}, ".jpg"}, - {&Album{CoverURL: "foo/bar/foo.JPEG"}, ".jpeg"}, - {&Album{CoverURL: "foo/bar/foo.023879jpg"}, ".jpg"}, - {&Album{CoverURL: "foo/bar/foo.PNG"}, ".png"}, - {&Album{CoverURL: "foo/bar/foo.gif"}, ".gif"}, + {URL: "foo/bar/test.jpg", Res: ".jpg"}, + {URL: "foo/bar/foo.JPG", Res: ".jpg"}, + {URL: "foo/bar/foo.JPEG", Res: ".jpeg"}, + {URL: "foo/bar/foo.023879jpg", Res: ".jpg"}, + {URL: "foo/bar/foo.PNG", Res: ".png"}, + {URL: "http://example.com/foo/bar/foo.gif", Res: ".gif"}, + {URL: "", Res: ""}, } for _, d := range data { - c.Check(d.A.CoverExt(), Equals, d.Res, Commentf("With URL %s", d.A.CoverURL)) + c.Check(AlbumCoverExt(d.URL), Equals, d.Res, Commentf("With URL %s", d.URL)) } } diff --git a/router.go b/router.go index f6dde5b..0110882 100644 --- a/router.go +++ b/router.go @@ -9,6 +9,7 @@ import ( "path" "path/filepath" "strconv" + "strings" "ponyo.epfl.ch/gitlab/alexandre.tuleu/narco" @@ -75,7 +76,7 @@ func (a *appData) buildRouter() http.Handler { } album := *albumUnsafe - album.CoverURL = fmt.Sprintf("/covers/%d%s", album.ID, album.CoverExt()) + album.CoverURL = fmt.Sprintf("/covers/%d%s", album.ID, AlbumCoverExt(album.CoverURL)) enc := json.NewEncoder(w) if err := enc.Encode(album); err != nil { @@ -83,6 +84,36 @@ func (a *appData) buildRouter() http.Handler { } }))) + router.GET("/covers/:name", narco.EndChain(ch, narco.HandlerFunc( + func(ctx context.Context, w http.ResponseWriter, req *http.Request, ps httprouter.Params) { + var ID uint64 + var err error = fmt.Errorf("Not Found") + var requestedExt string + name := ps.ByName("name") + if len(name) > 0 { + requestedExt = path.Ext(name) + ID, err = strconv.ParseUint(strings.TrimSuffix(name, requestedExt), 10, 64) + } + + if err != nil { + narco.Error(ctx, w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + } + + rc, ext, err := a.cover.GetCover(AlbumID(ID)) + if rc != nil { + defer closeOrPanic(rc, "Album cover "+name) + } + if err != nil { + narco.Error(ctx, w, err, http.StatusInternalServerError) + } + if ext != requestedExt { + narco.Error(ctx, w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + } + + io.Copy(w, rc) + + }))) + dirs := []string{"css", "js", "img"} for _, d := range dirs { router.ServeFiles(path.Join("/", d, "/*filepath"), http.Dir(filepath.Join("static", d)))