package media import ( "image" "io" "net/http" "os" "path" "path/filepath" "sort" "time" // for processing images _ "image/gif" _ "image/png" "github.com/tajtiattila/metadata" ) const ( TypeImage = "image" TypeVideo = "video" ) type MediaSource struct { StorageDir string ThumbDir string set Set } type Media struct { Type string Name string Size image.Rectangle FullName string ms *MediaSource metadata *metadata.Metadata } func (m Media) ThumbPath(size image.Rectangle) string { return "/" + thumbPath(size, m.Name) } func (m Media) ThumbFilename(size image.Rectangle) string { size = m.NormalizeSize(size) return thumbFilename(m.ms.ThumbDir, size, m.Name) } func (ms *MediaSource) ServeHTTP(w http.ResponseWriter, r *http.Request) { m, err := ms.ByName(path.Base(r.URL.Path)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } sizeRequested, err := SizeRequested(r.URL.Path, m.Size) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } size, err := ms.Thumb(*m, sizeRequested) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } http.ServeFile(w, r, m.ThumbFilename(size)) } func (ms *MediaSource) ReceiveNewMedia(name string, r io.Reader) error { ms.set = nil dest := path.Join(ms.StorageDir, name) f, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return err } _, err = io.Copy(f, r) if err != nil { return err } return f.Close() } func (m *Media) Date() time.Time { m.getMetadata() if m.metadata != nil { raw := m.metadata.Get(metadata.DateTimeOriginal) if raw != "" { d, err := time.Parse("2006-01-02T15:04:05", raw) if err == nil { return d } } } fi, err := os.Stat(m.FullName) if err == nil { return fi.ModTime() } return time.Time{} } func (m *Media) getMetadata() error { if m.metadata != nil { return nil } f, err := os.Open(m.FullName) if err != nil { return err } md, err := metadata.Parse(f) if err != nil { return err } m.metadata = md return nil } func (ms *MediaSource) Size(name string) (image.Rectangle, error) { switch filepath.Ext(name) { case ".mp4": return VideoSize(name) } return imageSize(name) } func (ms *MediaSource) ByName(name string) (*Media, error) { ext := filepath.Ext(name) typ := TypeImage switch ext { case ".mp4": typ = TypeVideo } fullName := path.Join(ms.StorageDir, name) size, _ := ms.Size(fullName) return &Media{ Type: typ, Name: name, Size: size, FullName: fullName, ms: ms, }, nil } func (ms *MediaSource) Walk() ([]*Media, error) { if ms.set == nil { var media []*Media err := filepath.Walk(ms.StorageDir, func(name string, fi os.FileInfo, err error) error { if err != nil { return err } if fi.IsDir() { return nil } m, err := ms.ByName(path.Base(name)) if err != nil { return nil } media = append(media, m) return nil }) if err != nil { return media, err } ms.set = media } return ms.set, nil } type Set []*Media func (s Set) ByDate() Set { sort.Slice(s, func(i, j int) bool { return s[i].Date().After(s[j].Date()) }) return s } func removeExtension(name string) string { ext := path.Ext(name) return name[:len(name)-len(ext)] }