/music/album.go

https://gitlab.com/barsanuphe/radis · Go · 183 lines · 144 code · 13 blank · 26 comment · 36 complexity · c8011ff71704fe4cb9b3364ac61be637 MD5 · raw file

  1. // Package music deals with album folders and m3u playlists.
  2. package music
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "regexp"
  9. "sort"
  10. "strings"
  11. "github.com/barsanuphe/radis/config"
  12. "github.com/barsanuphe/radis/directory"
  13. )
  14. var albumPattern = regexp.MustCompile(`^([\pL\pP\pS\pN\d\pZ]+) \(([0-9]{4})\) ([\pL\pP\pS\pN\d\pZ]+?)(\[MP3\])?$`)
  15. // Album holds the information of an album directory.
  16. // An album follows the pattern: Artist (year) Album title
  17. // Or: Various Artists (year) Compilation title
  18. type Album struct {
  19. Root string // absolute
  20. Path string // absolute
  21. NewPath string // absolute
  22. artist string
  23. mainAlias string
  24. year string
  25. title string
  26. IsMP3 bool
  27. }
  28. // String gives a representation of an AlbumFolder.
  29. func (a *Album) String() (albumName string) {
  30. albumName = a.mainAlias + "/" + a.artist + " (" + a.year + ") " + a.title
  31. if a.IsMP3 {
  32. albumName += "[MP3]"
  33. }
  34. return
  35. }
  36. // IsValidAlbum indicates if a directory name has the proper template to be an album.
  37. func (a *Album) IsValidAlbum() bool {
  38. if a.artist != "" {
  39. // directory name already parsed, no need to do it again
  40. return true
  41. }
  42. if err := a.extractInfo(); err != nil {
  43. // fmt.Println(err)
  44. return false
  45. }
  46. return true
  47. }
  48. // IsNew checks if the album was found in the INCOMING directory.
  49. func (a *Album) IsNew(c config.Config) bool {
  50. return strings.Contains(a.Path, filepath.Join(c.Paths.Root, c.Paths.IncomingSubdir))
  51. }
  52. // extractInfo parses an AlbumFolder's basepath to extract information.
  53. func (a *Album) extractInfo() (err error) {
  54. matches := albumPattern.FindStringSubmatch(filepath.Base(a.Path))
  55. if len(matches) > 0 {
  56. a.artist = matches[1]
  57. a.mainAlias = a.artist
  58. a.year = matches[2]
  59. a.title = matches[3]
  60. a.IsMP3 = matches[4] != ""
  61. } else {
  62. err = errors.New("Not an album!")
  63. }
  64. return
  65. }
  66. // FindNewPath for an album according to configuration.
  67. func (a *Album) FindNewPath(c config.Config) (hasGenre bool, err error) {
  68. if !a.IsValidAlbum() {
  69. err = errors.New("Not an album!")
  70. return
  71. }
  72. // see if artist has known alias
  73. for _, alias := range c.Aliases {
  74. if alias.HasAlias(a.artist) {
  75. a.mainAlias = alias.MainAlias
  76. break
  77. }
  78. }
  79. // find which genre the artist or main alias belongs to
  80. hasGenre = false
  81. directoryName := filepath.Base(a.Path)
  82. for _, genre := range c.Genres {
  83. var found bool
  84. if a.mainAlias == "Various Artists" {
  85. found = genre.HasCompilation(a.title)
  86. } else {
  87. found = genre.HasArtist(a.mainAlias)
  88. }
  89. // if artist is known, it belongs to genre.Name
  90. if found {
  91. a.NewPath = filepath.Join(a.Root, genre.Name, a.mainAlias, directoryName)
  92. hasGenre = true
  93. break
  94. }
  95. }
  96. if !hasGenre {
  97. a.NewPath = filepath.Join(a.Root, c.Paths.UnsortedSubdir, a.mainAlias, directoryName)
  98. }
  99. return
  100. }
  101. // MoveToNewPath moves an album directory to its new home in another genre.
  102. func (a *Album) MoveToNewPath(doNothing bool) (hasMoved bool, err error) {
  103. hasMoved = false
  104. if a.NewPath == "" {
  105. return false, errors.New("FindNewPath first.")
  106. }
  107. // comparer avec l'ancien
  108. if a.NewPath != a.Path {
  109. // if different, move folder
  110. if !doNothing {
  111. newPathParent := filepath.Dir(a.NewPath)
  112. if _, err = os.Stat(newPathParent); os.IsNotExist(err) {
  113. // newPathParent does not exist, creating
  114. err = os.MkdirAll(newPathParent, 0777)
  115. if err != nil {
  116. panic(err)
  117. }
  118. }
  119. // move
  120. err = os.Rename(a.Path, a.NewPath)
  121. if err == nil {
  122. hasMoved = true
  123. }
  124. } else {
  125. // would have moved, but must do nothing
  126. hasMoved = true
  127. }
  128. }
  129. return
  130. }
  131. // GetMusicFiles returns flac or mp3 files of the album.
  132. func (a *Album) GetMusicFiles() (contents []string, err error) {
  133. fileList, err := directory.GetFiles(a.NewPath)
  134. if err != nil {
  135. return []string{}, err
  136. }
  137. // check for music files
  138. for _, file := range fileList {
  139. switch filepath.Ext(file) {
  140. case ".flac", ".mp3":
  141. // accepted extensions
  142. contents = append(contents, filepath.Join(a.NewPath, file))
  143. }
  144. }
  145. sort.Strings(contents)
  146. return
  147. }
  148. // HasNonFlacFiles returns true if an album contains files other than flac songs and cover pictures.
  149. func (a *Album) HasNonFlacFiles() (bool, error) {
  150. fileList, err := directory.GetFiles(a.Path)
  151. if err != nil {
  152. return false, err
  153. }
  154. // check for suspicious files
  155. hasNonFlac := false
  156. for _, file := range fileList {
  157. switch filepath.Ext(file) {
  158. case ".flac", ".jpg", ".jpeg", ".png":
  159. // accepted extensions
  160. case ".mp3", ".wma", ".m4a":
  161. hasNonFlac = true
  162. break
  163. default:
  164. fmt.Println("Found suspicious file ", file, " in ", a.Path)
  165. hasNonFlac = true
  166. break
  167. }
  168. }
  169. return hasNonFlac, err
  170. }