You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

200 lines
3.3 KiB

  1. package media
  2. import (
  3. "image"
  4. "io"
  5. "net/http"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "sort"
  10. "time"
  11. // for processing images
  12. _ "image/gif"
  13. _ "image/png"
  14. "github.com/tajtiattila/metadata"
  15. )
  16. const (
  17. TypeImage = "image"
  18. TypeVideo = "video"
  19. )
  20. type MediaSource struct {
  21. StorageDir string
  22. ThumbDir string
  23. set Set
  24. }
  25. type Media struct {
  26. Type string
  27. Name string
  28. Size image.Rectangle
  29. FullName string
  30. ms *MediaSource
  31. metadata *metadata.Metadata
  32. }
  33. func (m Media) ThumbPath(size image.Rectangle) string {
  34. return "/" + thumbPath(size, m.Name)
  35. }
  36. func (m Media) ThumbFilename(size image.Rectangle) string {
  37. size = m.NormalizeSize(size)
  38. return thumbFilename(m.ms.ThumbDir, size, m.Name)
  39. }
  40. func (ms *MediaSource) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  41. m, err := ms.ByName(path.Base(r.URL.Path))
  42. if err != nil {
  43. http.Error(w, err.Error(), http.StatusInternalServerError)
  44. return
  45. }
  46. sizeRequested, err := SizeRequested(r.URL.Path, m.Size)
  47. if err != nil {
  48. http.Error(w, err.Error(), http.StatusBadRequest)
  49. return
  50. }
  51. size, err := ms.Thumb(*m, sizeRequested)
  52. if err != nil {
  53. http.Error(w, err.Error(), http.StatusBadRequest)
  54. return
  55. }
  56. http.ServeFile(w, r, m.ThumbFilename(size))
  57. }
  58. func (ms *MediaSource) ReceiveNewMedia(name string, r io.Reader) error {
  59. ms.set = nil
  60. dest := path.Join(ms.StorageDir, name)
  61. f, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, 0644)
  62. if err != nil {
  63. return err
  64. }
  65. _, err = io.Copy(f, r)
  66. if err != nil {
  67. return err
  68. }
  69. return f.Close()
  70. }
  71. func (m *Media) Date() time.Time {
  72. m.getMetadata()
  73. if m.metadata != nil {
  74. raw := m.metadata.Get(metadata.DateTimeOriginal)
  75. if raw != "" {
  76. d, err := time.Parse("2006-01-02T15:04:05", raw)
  77. if err == nil {
  78. return d
  79. }
  80. }
  81. }
  82. fi, err := os.Stat(m.FullName)
  83. if err == nil {
  84. return fi.ModTime()
  85. }
  86. return time.Time{}
  87. }
  88. func (m *Media) getMetadata() error {
  89. if m.metadata != nil {
  90. return nil
  91. }
  92. f, err := os.Open(m.FullName)
  93. if err != nil {
  94. return err
  95. }
  96. md, err := metadata.Parse(f)
  97. if err != nil {
  98. return err
  99. }
  100. m.metadata = md
  101. return nil
  102. }
  103. func (ms *MediaSource) Size(name string) (image.Rectangle, error) {
  104. switch filepath.Ext(name) {
  105. case ".mp4":
  106. return VideoSize(name)
  107. }
  108. return imageSize(name)
  109. }
  110. func (ms *MediaSource) ByName(name string) (*Media, error) {
  111. ext := filepath.Ext(name)
  112. typ := TypeImage
  113. switch ext {
  114. case ".mp4":
  115. typ = TypeVideo
  116. }
  117. fullName := path.Join(ms.StorageDir, name)
  118. size, _ := ms.Size(fullName)
  119. return &Media{
  120. Type: typ,
  121. Name: name,
  122. Size: size,
  123. FullName: fullName,
  124. ms: ms,
  125. }, nil
  126. }
  127. func (ms *MediaSource) Walk() ([]*Media, error) {
  128. if ms.set == nil {
  129. var media []*Media
  130. err := filepath.Walk(ms.StorageDir, func(name string, fi os.FileInfo, err error) error {
  131. if err != nil {
  132. return err
  133. }
  134. if fi.IsDir() {
  135. return nil
  136. }
  137. m, err := ms.ByName(path.Base(name))
  138. if err != nil {
  139. return nil
  140. }
  141. media = append(media, m)
  142. return nil
  143. })
  144. if err != nil {
  145. return media, err
  146. }
  147. ms.set = media
  148. }
  149. return ms.set, nil
  150. }
  151. type Set []*Media
  152. func (s Set) ByDate() Set {
  153. sort.Slice(s, func(i, j int) bool {
  154. return s[i].Date().After(s[j].Date())
  155. })
  156. return s
  157. }
  158. func removeExtension(name string) string {
  159. ext := path.Ext(name)
  160. return name[:len(name)-len(ext)]
  161. }