media.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package media // import "miniflux.app/v2/internal/reader/media"
  4. import (
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. )
  9. var textLinkRegex = regexp.MustCompile(`(?mi)(\bhttps?:\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])`)
  10. // Element represents XML media elements.
  11. type Element struct {
  12. MediaGroups []Group `xml:"http://search.yahoo.com/mrss/ group"`
  13. MediaContents []Content `xml:"http://search.yahoo.com/mrss/ content"`
  14. MediaThumbnails []Thumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
  15. MediaDescriptions DescriptionList `xml:"http://search.yahoo.com/mrss/ description"`
  16. MediaPeerLinks []PeerLink `xml:"http://search.yahoo.com/mrss/ peerLink"`
  17. }
  18. // AllMediaThumbnails returns all thumbnail elements merged together.
  19. func (e *Element) AllMediaThumbnails() []Thumbnail {
  20. var items []Thumbnail
  21. items = append(items, e.MediaThumbnails...)
  22. for _, mediaGroup := range e.MediaGroups {
  23. items = append(items, mediaGroup.MediaThumbnails...)
  24. }
  25. return items
  26. }
  27. // AllMediaContents returns all content elements merged together.
  28. func (e *Element) AllMediaContents() []Content {
  29. var items []Content
  30. items = append(items, e.MediaContents...)
  31. for _, mediaGroup := range e.MediaGroups {
  32. items = append(items, mediaGroup.MediaContents...)
  33. }
  34. return items
  35. }
  36. // AllMediaPeerLinks returns all peer link elements merged together.
  37. func (e *Element) AllMediaPeerLinks() []PeerLink {
  38. var items []PeerLink
  39. items = append(items, e.MediaPeerLinks...)
  40. for _, mediaGroup := range e.MediaGroups {
  41. items = append(items, mediaGroup.MediaPeerLinks...)
  42. }
  43. return items
  44. }
  45. // FirstMediaDescription returns the first description element.
  46. func (e *Element) FirstMediaDescription() string {
  47. description := e.MediaDescriptions.First()
  48. if description != "" {
  49. return description
  50. }
  51. for _, mediaGroup := range e.MediaGroups {
  52. description = mediaGroup.MediaDescriptions.First()
  53. if description != "" {
  54. return description
  55. }
  56. }
  57. return ""
  58. }
  59. // Group represents a XML element "media:group".
  60. type Group struct {
  61. MediaContents []Content `xml:"http://search.yahoo.com/mrss/ content"`
  62. MediaThumbnails []Thumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
  63. MediaDescriptions DescriptionList `xml:"http://search.yahoo.com/mrss/ description"`
  64. MediaPeerLinks []PeerLink `xml:"http://search.yahoo.com/mrss/ peerLink"`
  65. }
  66. // Content represents a XML element "media:content".
  67. type Content struct {
  68. URL string `xml:"url,attr"`
  69. Type string `xml:"type,attr"`
  70. FileSize string `xml:"fileSize,attr"`
  71. Medium string `xml:"medium,attr"`
  72. }
  73. // MimeType returns the attachment mime type.
  74. func (mc *Content) MimeType() string {
  75. switch {
  76. case mc.Type == "" && mc.Medium == "image":
  77. return "image/*"
  78. case mc.Type == "" && mc.Medium == "video":
  79. return "video/*"
  80. case mc.Type == "" && mc.Medium == "audio":
  81. return "audio/*"
  82. case mc.Type == "" && mc.Medium == "video":
  83. return "video/*"
  84. case mc.Type != "":
  85. return mc.Type
  86. default:
  87. return "application/octet-stream"
  88. }
  89. }
  90. // Size returns the attachment size.
  91. func (mc *Content) Size() int64 {
  92. if mc.FileSize == "" {
  93. return 0
  94. }
  95. size, _ := strconv.ParseInt(mc.FileSize, 10, 0)
  96. return size
  97. }
  98. // Thumbnail represents a XML element "media:thumbnail".
  99. type Thumbnail struct {
  100. URL string `xml:"url,attr"`
  101. }
  102. // MimeType returns the attachment mime type.
  103. func (t *Thumbnail) MimeType() string {
  104. return "image/*"
  105. }
  106. // Size returns the attachment size.
  107. func (t *Thumbnail) Size() int64 {
  108. return 0
  109. }
  110. // PeerLink represents a XML element "media:peerLink".
  111. type PeerLink struct {
  112. URL string `xml:"href,attr"`
  113. Type string `xml:"type,attr"`
  114. }
  115. // MimeType returns the attachment mime type.
  116. func (p *PeerLink) MimeType() string {
  117. if p.Type != "" {
  118. return p.Type
  119. }
  120. return "application/octet-stream"
  121. }
  122. // Size returns the attachment size.
  123. func (p *PeerLink) Size() int64 {
  124. return 0
  125. }
  126. // Description represents a XML element "media:description".
  127. type Description struct {
  128. Type string `xml:"type,attr"`
  129. Description string `xml:",chardata"`
  130. }
  131. // HTML returns the description as HTML.
  132. func (d *Description) HTML() string {
  133. if d.Type == "html" {
  134. return d.Description
  135. }
  136. content := strings.Replace(d.Description, "\n", "<br>", -1)
  137. return textLinkRegex.ReplaceAllString(content, `<a href="${1}">${1}</a>`)
  138. }
  139. // DescriptionList represents a list of "media:description" XML elements.
  140. type DescriptionList []Description
  141. // First returns the first non-empty description.
  142. func (dl DescriptionList) First() string {
  143. for _, description := range dl {
  144. contents := description.HTML()
  145. if contents != "" {
  146. return contents
  147. }
  148. }
  149. return ""
  150. }