VLCMediaLibraryManager.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*****************************************************************************
  2. * VLCMediaLibraryManager.swift
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright © 2018 VideoLAN. All rights reserved.
  6. * Copyright © 2018 Videolabs
  7. *
  8. * Authors: Soomin Lee <bubu # mikan.io>
  9. *
  10. * Refer to the COPYING file of the official project for license.
  11. *****************************************************************************/
  12. extension Notification.Name {
  13. static let VLCNewFileAddedNotification = Notification.Name("NewFileAddedNotification")
  14. }
  15. // For objc
  16. extension NSNotification {
  17. @objc static let VLCNewFileAddedNotification = Notification.Name.VLCNewFileAddedNotification
  18. }
  19. @objc protocol MediaLibraryObserver: class {
  20. // Video
  21. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  22. didModifyVideo video: [VLCMLMedia])
  23. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  24. didDeleteMediaWithIds ids: [NSNumber])
  25. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  26. didAddVideos videos: [VLCMLMedia])
  27. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  28. didAddShowEpisodes showEpisodes: [VLCMLMedia])
  29. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  30. thumbnailReady media: VLCMLMedia)
  31. // Audio
  32. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  33. didAddAudios audios: [VLCMLMedia])
  34. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  35. didAddArtists artists: [VLCMLArtist])
  36. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  37. didDeleteArtistsWithIds artistsIds: [NSNumber])
  38. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  39. didAddAlbums albums: [VLCMLAlbum])
  40. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  41. didDeleteAlbumsWithIds albumsIds: [NSNumber])
  42. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  43. didAddAlbumTracks albumTracks: [VLCMLMedia])
  44. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  45. didAddGenres genres: [VLCMLGenre])
  46. // Playlist
  47. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  48. didAddPlaylists playlists: [VLCMLPlaylist])
  49. @objc optional func medialibrary(_ medialibrary: VLCMediaLibraryManager,
  50. didDeletePlaylistsWithIds playlistsIds: [NSNumber])
  51. }
  52. class VLCMediaLibraryManager: NSObject {
  53. private static let databaseName: String = "medialibrary.db"
  54. // Using ObjectIdentifier to avoid duplication and facilitate
  55. // identification of observing object
  56. private var observers = [ObjectIdentifier: Observer]()
  57. private lazy var medialib: VLCMediaLibrary = {
  58. let medialibrary = VLCMediaLibrary()
  59. medialibrary.delegate = self
  60. return medialibrary
  61. }()
  62. override init() {
  63. super.init()
  64. setupMediaLibrary()
  65. NotificationCenter.default.addObserver(self, selector: #selector(reload),
  66. name: .VLCNewFileAddedNotification, object: nil)
  67. }
  68. // MARK: Private
  69. private func setupMediaLibrary() {
  70. guard let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first,
  71. let libraryPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first else {
  72. preconditionFailure("VLCMediaLibraryManager: Unable to init medialibrary.")
  73. }
  74. let databasePath = libraryPath + "/MediaLibrary/" + VLCMediaLibraryManager.databaseName
  75. let thumbnailPath = libraryPath + "/MediaLibrary/Thumbnails"
  76. do {
  77. try FileManager.default.createDirectory(atPath: thumbnailPath,
  78. withIntermediateDirectories: true)
  79. } catch let error as NSError {
  80. assertionFailure("Failed to create directory: \(error.localizedDescription)")
  81. }
  82. let medialibraryStatus = medialib.setupMediaLibrary(databasePath: databasePath,
  83. thumbnailPath: thumbnailPath)
  84. switch medialibraryStatus {
  85. case .success:
  86. guard medialib.start() else {
  87. assertionFailure("VLCMediaLibraryManager: Medialibrary failed to start.")
  88. return
  89. }
  90. medialib.reload()
  91. medialib.discover(onEntryPoint: "file://" + documentPath)
  92. case .alreadyInitialized:
  93. assertionFailure("VLCMediaLibraryManager: Medialibrary already initialized.")
  94. case .failed:
  95. preconditionFailure("VLCMediaLibraryManager: Failed to setup medialibrary.")
  96. case .dbReset:
  97. // should still start and discover but warn the user that the db has been wipped
  98. assertionFailure("VLCMediaLibraryManager: The database was resetted, please re-configure.")
  99. }
  100. }
  101. // MARK: Internal
  102. @objc func reload() {
  103. medialib.reload()
  104. }
  105. /// Returns number of *ALL* files(audio and video) present in the medialibrary database
  106. func numberOfFiles() -> Int {
  107. var media = medialib.audioFiles(with: .filename, desc: false)
  108. media += medialib.videoFiles(with: .filename, desc: false)
  109. return media.count
  110. }
  111. /// Returns *ALL* file found for a specified VLCMLMediaType
  112. ///
  113. /// - Parameter type: Type of the media
  114. /// - Returns: Array of VLCMLMedia
  115. func media(ofType type: VLCMLMediaType, sortingCriteria sort: VLCMLSortingCriteria = .filename, desc: Bool = false) -> [VLCMLMedia] {
  116. return type == .video ? medialib.videoFiles(with: sort, desc: desc) : medialib.audioFiles(with: sort, desc: desc)
  117. }
  118. func genre(sortingCriteria sort: VLCMLSortingCriteria = .default, desc: Bool = false) -> [VLCMLGenre] {
  119. return medialib.genres(with: sort, desc: desc)
  120. }
  121. }
  122. // MARK: - Observer
  123. private extension VLCMediaLibraryManager {
  124. struct Observer {
  125. weak var observer: MediaLibraryObserver?
  126. }
  127. }
  128. extension VLCMediaLibraryManager {
  129. func addObserver(_ observer: MediaLibraryObserver) {
  130. let identifier = ObjectIdentifier(observer)
  131. observers[identifier] = Observer(observer: observer)
  132. }
  133. func removeObserver(_ observer: MediaLibraryObserver) {
  134. let identifier = ObjectIdentifier(observer)
  135. observers.removeValue(forKey: identifier)
  136. }
  137. }
  138. // MARK: MediaLibrary - Audio methods
  139. extension VLCMediaLibraryManager {
  140. func getArtists(sortingCriteria sort: VLCMLSortingCriteria = .artist, desc: Bool = false) -> [VLCMLArtist] {
  141. return medialib.artists(with: sort, desc: desc, all: true)
  142. }
  143. func getAlbums(sortingCriteria sort: VLCMLSortingCriteria = .album, desc: Bool = false) -> [VLCMLAlbum] {
  144. return medialib.albums(with: sort, desc: desc)
  145. }
  146. }
  147. // MARK: MediaLibrary - Video methods
  148. extension VLCMediaLibraryManager {
  149. func requestThumbnail(for media: [VLCMLMedia]) {
  150. media.forEach() {
  151. // FIXME: Remind Chouquette! In the medialibrary thumbnails uses absolute paths. Workaround:
  152. // - Regenerate path is a thumbnail is detected
  153. // - Request a new thumbnail
  154. // if $0.isThumbnailGenerated() { return }
  155. if !medialib.requestThumbnail(for: $0) {
  156. assertionFailure("VLCMediaLibraryManager: Failed to generate thumbnail for: \($0.identifier())")
  157. }
  158. }
  159. }
  160. }
  161. // MARK: MediaLibrary - Playlist methods
  162. extension VLCMediaLibraryManager {
  163. func createPlaylist(with name: String) -> VLCMLPlaylist {
  164. return medialib.createPlaylist(withName: name)
  165. }
  166. func deletePlaylist(with identifier: VLCMLIdentifier) -> Bool {
  167. return medialib.deletePlaylist(withIdentifier: identifier)
  168. }
  169. func getPlaylists(sortingCriteria sort: VLCMLSortingCriteria = .default, desc: Bool = false) -> [VLCMLPlaylist] {
  170. return medialib.playlists(with: sort, desc: desc)
  171. }
  172. }
  173. // MARK: - VLCMediaLibraryDelegate - Media
  174. extension VLCMediaLibraryManager: VLCMediaLibraryDelegate {
  175. func medialibrary(_ medialibrary: VLCMediaLibrary, didAddMedia media: [VLCMLMedia]) {
  176. let videos = media.filter {( $0.type() == .video )}
  177. let audio = media.filter {( $0.type() == .audio )}
  178. // thumbnails only for videos
  179. requestThumbnail(for: videos)
  180. for observer in observers {
  181. observer.value.observer?.medialibrary?(self, didAddVideos: videos)
  182. observer.value.observer?.medialibrary?(self, didAddAudios: audio)
  183. }
  184. }
  185. func medialibrary(_ medialibrary: VLCMediaLibrary, didModifyMedia media: [VLCMLMedia]) {
  186. let showEpisodes = media.filter {( $0.subtype() == .showEpisode )}
  187. let albumTrack = media.filter {( $0.subtype() == .albumTrack )}
  188. for observer in observers {
  189. observer.value.observer?.medialibrary?(self, didAddShowEpisodes: showEpisodes)
  190. observer.value.observer?.medialibrary?(self, didAddAlbumTracks: albumTrack)
  191. }
  192. }
  193. func medialibrary(_ medialibrary: VLCMediaLibrary, didDeleteMediaWithIds mediaIds: [NSNumber]) {
  194. for observer in observers {
  195. observer.value.observer?.medialibrary?(self, didDeleteMediaWithIds: mediaIds)
  196. }
  197. }
  198. func medialibrary(_ medialibrary: VLCMediaLibrary, thumbnailReadyFor media: VLCMLMedia, withSuccess success: Bool) {
  199. for observer in observers {
  200. observer.value.observer?.medialibrary?(self, thumbnailReady: media)
  201. }
  202. }
  203. }
  204. // MARK: - VLCMediaLibraryDelegate - Artists
  205. extension VLCMediaLibraryManager {
  206. func medialibrary(_ medialibrary: VLCMediaLibrary, didAdd artists: [VLCMLArtist]) {
  207. for observer in observers {
  208. observer.value.observer?.medialibrary?(self, didAddArtists: artists)
  209. }
  210. }
  211. func medialibrary(_ medialibrary: VLCMediaLibrary, didDeleteArtistsWithIds artistsIds: [NSNumber]) {
  212. for observer in observers {
  213. observer.value.observer?.medialibrary?(self, didDeleteArtistsWithIds: artistsIds)
  214. }
  215. }
  216. }
  217. // MARK: - VLCMediaLibraryDelegate - Albums
  218. extension VLCMediaLibraryManager {
  219. func medialibrary(_ medialibrary: VLCMediaLibrary, didAdd albums: [VLCMLAlbum]) {
  220. for observer in observers {
  221. observer.value.observer?.medialibrary?(self, didAddAlbums: albums)
  222. }
  223. }
  224. func medialibrary(_ medialibrary: VLCMediaLibrary, didDeleteAlbumsWithIds albumsIds: [NSNumber]) {
  225. for observer in observers {
  226. observer.value.observer?.medialibrary?(self, didDeleteAlbumsWithIds: albumsIds)
  227. }
  228. }
  229. }
  230. // MARK: - VLCMediaLibraryDelegate - Playlists
  231. extension VLCMediaLibraryManager {
  232. func medialibrary(_ medialibrary: VLCMediaLibrary, didAdd playlists: [VLCMLPlaylist]) {
  233. for observer in observers {
  234. observer.value.observer?.medialibrary?(self, didAddPlaylists: playlists)
  235. }
  236. }
  237. func medialibrary(_ medialibrary: VLCMediaLibrary, didDeletePlaylistsWithIds playlistsIds: [NSNumber]) {
  238. for observer in observers {
  239. observer.value.observer?.medialibrary?(self, didDeletePlaylistsWithIds: playlistsIds)
  240. }
  241. }
  242. }
  243. // MARK: - VLCMediaLibraryDelegate - Discovery
  244. extension VLCMediaLibraryManager {
  245. func medialibrary(_ medialibrary: VLCMediaLibrary, didStartDiscovery entryPoint: String) {
  246. }
  247. func medialibrary(_ medialibrary: VLCMediaLibrary, didCompleteDiscovery entryPoint: String) {
  248. }
  249. func medialibrary(_ medialibrary: VLCMediaLibrary, didProgressDiscovery entryPoint: String) {
  250. }
  251. func medialibrary(_ medialibrary: VLCMediaLibrary, didUpdateParsingStatsWithPercent percent: UInt32) {
  252. }
  253. }