Implements GET for images
This commit is contained in:
4
album.go
4
album.go
@@ -106,8 +106,8 @@ func (a *Album) String() string {
|
|||||||
|
|
||||||
var rxExt = regexp.MustCompile(`\.([0-9]+)([a-zA-Z]+)\z`)
|
var rxExt = regexp.MustCompile(`\.([0-9]+)([a-zA-Z]+)\z`)
|
||||||
|
|
||||||
func (a *Album) CoverExt() string {
|
func AlbumCoverExt(URL string) string {
|
||||||
ext := path.Ext(a.CoverURL)
|
ext := path.Ext(URL)
|
||||||
m := rxExt.FindStringSubmatch(ext)
|
m := rxExt.FindStringSubmatch(ext)
|
||||||
if m != nil {
|
if m != nil {
|
||||||
ext = "." + m[2]
|
ext = "." + m[2]
|
||||||
|
|||||||
@@ -34,21 +34,22 @@ func NewAlbumCoverCache(basepath string, maxRequest uint, window time.Duration)
|
|||||||
return res
|
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]
|
URL, ok := c.URLs[ID]
|
||||||
if ok == false {
|
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)
|
resp, err := c.getter.Get(URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, ext, err
|
||||||
}
|
}
|
||||||
defer closeOrPanic(resp.Body, "GET "+URL)
|
defer closeOrPanic(resp.Body, "GET "+URL)
|
||||||
|
|
||||||
err = c.dv.WriteStream(c.dataKey(ID), resp.Body, false)
|
err = c.dv.WriteStream(c.dataKey(ID), resp.Body, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, ext, err
|
||||||
}
|
}
|
||||||
|
|
||||||
md := coverMetadata{
|
md := coverMetadata{
|
||||||
@@ -58,11 +59,11 @@ func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
err = c.dv.Write(c.metadataKey(ID), mdbyte)
|
err = c.dv.Write(c.metadataKey(ID), mdbyte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, ext, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rc, err := c.dv.ReadStream(c.dataKey(ID), false)
|
||||||
return c.dv.ReadStream(c.dataKey(ID), false)
|
return rc, ext, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type coverMetadata struct {
|
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
|
// 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()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
|
|
||||||
@@ -107,5 +108,8 @@ func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, error) {
|
|||||||
return c.fetch(ID)
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) {
|
|||||||
var resData = []bytes.Buffer{}
|
var resData = []bytes.Buffer{}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for _, a := range albumsDataTest {
|
for _, a := range albumsDataTest {
|
||||||
cover, err := s.cache.GetCover(a.ID)
|
cover, _, err := s.cache.GetCover(a.ID)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if c.Check(err, IsNil) == true {
|
if c.Check(err, IsNil) == true {
|
||||||
_, err := io.Copy(&buf, cover)
|
_, 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
|
// now we check that we get it again, but from the disk, not
|
||||||
// hitting the web
|
// hitting the web
|
||||||
for i, a := range albumsDataTest {
|
for i, a := range albumsDataTest {
|
||||||
cover, err := s.cache.GetCover(a.ID)
|
cover, _, err := s.cache.GetCover(a.ID)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if c.Check(err, IsNil) == true {
|
if c.Check(err, IsNil) == true {
|
||||||
_, err := io.Copy(&buf, cover)
|
_, err := io.Copy(&buf, cover)
|
||||||
@@ -68,7 +68,7 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) {
|
|||||||
s.cache.TTL = 0
|
s.cache.TTL = 0
|
||||||
|
|
||||||
for _, a := range albumsDataTest {
|
for _, a := range albumsDataTest {
|
||||||
cover, err := s.cache.GetCover(a.ID)
|
cover, _, err := s.cache.GetCover(a.ID)
|
||||||
c.Check(cover, IsNil)
|
c.Check(cover, IsNil)
|
||||||
c.Check(err, ErrorMatches, "I will always have an error")
|
c.Check(err, ErrorMatches, "I will always have an error")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,19 +41,20 @@ func (s *AlbumSuite) TestAuthors(c *C) {
|
|||||||
|
|
||||||
func (s *AlbumSuite) TestExt(c *C) {
|
func (s *AlbumSuite) TestExt(c *C) {
|
||||||
type TestData struct {
|
type TestData struct {
|
||||||
A *Album
|
URL string
|
||||||
Res string
|
Res string
|
||||||
}
|
}
|
||||||
data := []TestData{
|
data := []TestData{
|
||||||
{&Album{CoverURL: "foo/bar/test.jpg"}, ".jpg"},
|
{URL: "foo/bar/test.jpg", Res: ".jpg"},
|
||||||
{&Album{CoverURL: "foo/bar/foo.JPG"}, ".jpg"},
|
{URL: "foo/bar/foo.JPG", Res: ".jpg"},
|
||||||
{&Album{CoverURL: "foo/bar/foo.JPEG"}, ".jpeg"},
|
{URL: "foo/bar/foo.JPEG", Res: ".jpeg"},
|
||||||
{&Album{CoverURL: "foo/bar/foo.023879jpg"}, ".jpg"},
|
{URL: "foo/bar/foo.023879jpg", Res: ".jpg"},
|
||||||
{&Album{CoverURL: "foo/bar/foo.PNG"}, ".png"},
|
{URL: "foo/bar/foo.PNG", Res: ".png"},
|
||||||
{&Album{CoverURL: "foo/bar/foo.gif"}, ".gif"},
|
{URL: "http://example.com/foo/bar/foo.gif", Res: ".gif"},
|
||||||
|
{URL: "", Res: ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range data {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
router.go
33
router.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ponyo.epfl.ch/gitlab/alexandre.tuleu/narco"
|
"ponyo.epfl.ch/gitlab/alexandre.tuleu/narco"
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ func (a *appData) buildRouter() http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
album := *albumUnsafe
|
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)
|
enc := json.NewEncoder(w)
|
||||||
if err := enc.Encode(album); err != nil {
|
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"}
|
dirs := []string{"css", "js", "img"}
|
||||||
for _, d := range dirs {
|
for _, d := range dirs {
|
||||||
router.ServeFiles(path.Join("/", d, "/*filepath"), http.Dir(filepath.Join("static", d)))
|
router.ServeFiles(path.Join("/", d, "/*filepath"), http.Dir(filepath.Join("static", d)))
|
||||||
|
|||||||
Reference in New Issue
Block a user