VLCMediaLibraryManager.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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. class VLCMediaLibraryManager: NSObject {
  13. private static let databaseName: String = "medialibrary.db"
  14. private var databasePath: String!
  15. private var thumbnailPath: String!
  16. private lazy var medialibrary: VLCMediaLibrary = {
  17. let medialibrary = VLCMediaLibrary()
  18. medialibrary.delegate = self
  19. return medialibrary
  20. }()
  21. override init() {
  22. super.init()
  23. setupMediaLibrary()
  24. }
  25. // MARK: Private
  26. private func setupMediaLibrary() {
  27. guard let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first,
  28. let dbPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first else {
  29. preconditionFailure("VLCMediaLibraryManager: Unable to init medialibrary.")
  30. }
  31. medialibrary.setVerbosity(.info)
  32. databasePath = dbPath + "/" + VLCMediaLibraryManager.databaseName
  33. thumbnailPath = documentPath
  34. let medialibraryStatus = medialibrary.setupMediaLibrary(databasePath: databasePath,
  35. thumbnailPath: thumbnailPath)
  36. switch medialibraryStatus {
  37. case .success:
  38. guard medialibrary.start() else {
  39. assertionFailure("VLCMediaLibraryManager: Medialibrary failed to start.")
  40. return
  41. }
  42. medialibrary.reload()
  43. medialibrary.discover(onEntryPoint: "file://" + documentPath)
  44. break
  45. case .alreadyInitialized:
  46. assertionFailure("VLCMediaLibraryManager: Medialibrary already initialized.")
  47. break
  48. case .failed:
  49. preconditionFailure("VLCMediaLibraryManager: Failed to setup medialibrary.")
  50. break
  51. case .dbReset:
  52. // should still start and discover but warn the user that the db has been wipped
  53. assertionFailure("VLCMediaLibraryManager: The database was resetted, please re-configure.")
  54. break
  55. }
  56. }
  57. // MARK: Internal
  58. /// Returns number of *ALL* files(audio and video) present in the medialibrary database
  59. func numberOfFiles() -> Int {
  60. var media = medialibrary.audioFiles(with: .filename, desc: false)
  61. media += medialibrary.videoFiles(with: .filename, desc: false)
  62. return media.count
  63. }
  64. /// Returns *ALL* file found for a specified VLCMLMediaType
  65. ///
  66. /// - Parameter type: Type of the media
  67. /// - Returns: Array of VLCMLMedia
  68. func media(ofType type: VLCMLMediaType) -> [VLCMLMedia] {
  69. return type == .video ? medialibrary.videoFiles(with: .filename, desc: false) : medialibrary.audioFiles(with: .filename, desc: false)
  70. }
  71. func addMedia(withMrl mrl: URL) {
  72. medialibrary.addMedia(withMrl: mrl)
  73. }
  74. }
  75. // MARK: MediaDataSource - Other methods
  76. extension VLCMediaLibraryManager {
  77. // @objc enum VLCMediaCategory: Int {
  78. // case unknown
  79. // case audio
  80. // case video
  81. // }
  82. //
  83. // @objc enum VLCMediaSubcategory: Int {
  84. // case unknown
  85. // case movies
  86. // case episodes
  87. // case artists
  88. // case albums
  89. // case tracks
  90. // case genres
  91. // case videoPlaylists
  92. // case audioPlaylists
  93. // case allVideos
  94. // }
  95. //
  96. // struct VLCMediaType {
  97. // let category: VLCMediaCategory
  98. // var subcategory: VLCMediaSubcategory
  99. // }
  100. //
  101. //
  102. // var foundVideos = [VLCMLMedia]()
  103. // var foundAudio = [VLCMLMedia]()
  104. //
  105. // var movies = [VLCMLMedia]()
  106. // var episodes = [VLCMLMedia]()
  107. // var artists = [String]()
  108. // var albums = [MLAlbum]()
  109. // var genres = [String]()
  110. // var audioPlaylist = [MLLabel]()
  111. // var videoPlaylist = [MLLabel]()
  112. //
  113. // @objc func numberOfFiles(subcategory: VLCMediaSubcategory) -> Int {
  114. // return array(for: subcategory).countSources/MediaViewController.swift
  115. // }
  116. //
  117. // private func array(for subcategory: VLCMediaSubcategory) -> [Any] {
  118. // switch subcategory {
  119. // case .unknown:
  120. // preconditionFailure("No")
  121. // case .movies:
  122. // return movies
  123. // case .episodes:
  124. // return episodes
  125. // case .artists:
  126. // return artists
  127. // case .albums:
  128. // return albums
  129. // case .tracks:
  130. // return foundAudio
  131. // case .genres:
  132. // return genres
  133. // case .audioPlaylists:
  134. // return audioPlaylist
  135. // case .videoPlaylists:
  136. // return videoPlaylist
  137. // case .allVideos:
  138. // return foundVideos
  139. // }
  140. // }
  141. //
  142. // func indicatorInfo(for subcategory: VLCMediaSubcategory) -> IndicatorInfo {
  143. // switch subcategory {
  144. // case .unknown:
  145. // preconditionFailure("No")
  146. // case .movies:
  147. // return IndicatorInfo(title: NSLocalizedString("MOVIES", comment: ""))
  148. // case .episodes:
  149. // return IndicatorInfo(title: NSLocalizedString("EPISODES", comment: ""))
  150. // case .artists:
  151. // return IndicatorInfo(title: NSLocalizedString("ARTISTS", comment: ""))
  152. // case .albums:
  153. // return IndicatorInfo(title: NSLocalizedString("ALBUMS", comment: ""))
  154. // case .tracks:
  155. // return IndicatorInfo(title: NSLocalizedString("SONGS", comment: ""))
  156. // case .genres:
  157. // return IndicatorInfo(title: NSLocalizedString("GENRES", comment: ""))
  158. // case .audioPlaylists:
  159. // return IndicatorInfo(title: NSLocalizedString("AUDIO_PLAYLISTS", comment: ""))
  160. // case .videoPlaylists:
  161. // return IndicatorInfo(title: NSLocalizedString("VIDEO_PLAYLISTS", comment: ""))
  162. // case .allVideos:
  163. // return IndicatorInfo(title: NSLocalizedString("VIDEOS", comment: ""))
  164. // }
  165. //
  166. // }
  167. //
  168. // @objc func object(at index: Int, subcategory: VLCMediaSubcategory) -> Any {
  169. //
  170. // guard index >= 0 else {
  171. // preconditionFailure("a negative value ? I don't think so!")
  172. // }
  173. //
  174. // let categoryArray = array(for: subcategory)
  175. // if index < categoryArray.count {
  176. // return categoryArray[Int(index)]
  177. // }
  178. // preconditionFailure("index is taller than count")
  179. // }
  180. //
  181. // func allObjects(for subcategory: VLCMediaSubcategory) -> [Any] {
  182. // return array(for:subcategory)
  183. // }
  184. //
  185. // func removeObject(at index: Int, subcategory: VLCMediaSubcategory) {
  186. // guard index >= 0 else {
  187. // preconditionFailure("a negative value ? I don't think so!")
  188. // }
  189. // var categoryArray = array(for: subcategory)
  190. // if index < categoryArray.count {
  191. // categoryArray.remove(at: index)
  192. // }
  193. // preconditionFailure("index is taller than count")
  194. // }
  195. //
  196. // func insert(_ item: MLFile, at index: Int, subcategory: VLCMediaSubcategory) {
  197. // guard index >= 0 else {
  198. // preconditionFailure("a negative value ? I don't think so!")
  199. // }
  200. // var categoryArray = array(for: subcategory)
  201. // if index < categoryArray.count {
  202. // categoryArray.insert(item, at: index)
  203. // }
  204. // categoryArray.append(item)
  205. // }
  206. }
  207. // MARK: MediaDataSource - Audio methods
  208. extension VLCMediaLibraryManager {
  209. private func getAllAudio() {
  210. // foundAudio = medialibrary.media(ofType: .audio)
  211. // artistsFromAudio()
  212. // albumsFromAudio()
  213. // audioPlaylistsFromAudio()
  214. // genresFromAudio()
  215. }
  216. private func getArtists() {
  217. // let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
  218. // let tracksWithArtist = albumtracks.filter { $0.artist != nil && $0.artist != "" }
  219. // artists = tracksWithArtist.map { $0.artist }
  220. }
  221. private func getAlbums() {
  222. // albums = MLAlbum.allAlbums() as! [MLAlbum]
  223. }
  224. private func getAudioPlaylists() {
  225. // let labels = MLLabel.allLabels() as! [MLLabel]
  226. // audioPlaylist = labels.filter {
  227. // let audioFiles = $0.files.filter {
  228. // if let file = $0 as? MLFile {
  229. // return file.isSupportedAudioFile()
  230. // }
  231. // return false
  232. // }
  233. // return !audioFiles.isEmpty
  234. // }
  235. }
  236. private func genresFromAudio() {
  237. // let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
  238. // let tracksWithArtist = albumtracks.filter { $0.genre != nil && $0.genre != "" }
  239. // genres = tracksWithArtist.map { $0.genre }
  240. }
  241. }
  242. // MARK: MediaDataSource - Video methods
  243. extension VLCMediaLibraryManager {
  244. private func getAllVideos() {
  245. // foundVideos = medialibrary.media(ofType: .video)
  246. // moviesFromVideos()
  247. // episodesFromVideos()
  248. // videoPlaylistsFromVideos()
  249. }
  250. private func getMovies() {
  251. // movies = foundVideos.filter { $0.subtype() == .movie }
  252. }
  253. private func getShowEpisodes() {
  254. // episodes = foundVideos.filter { $0.subtype() == .showEpisode }
  255. }
  256. private func getVideoPlaylists() {
  257. // let labels = MLLabel.allLabels() as! [MLLabel]
  258. // audioPlaylist = labels.filter {
  259. // let audioFiles = $0.files.filter {
  260. // if let file = $0 as? MLFile {
  261. // return file.isShowEpisode() || file.isMovie() || file.isClip()
  262. // }
  263. // return false
  264. // }
  265. // return !audioFiles.isEmpty
  266. // }
  267. }
  268. }
  269. // MARK: VLCMediaLibraryDelegate
  270. extension VLCMediaLibraryManager: VLCMediaLibraryDelegate {
  271. func medialibrary(_ medialibrary: VLCMediaLibrary, didAddMedia media: [VLCMLMedia]) {
  272. NotificationCenter.default.post(name: .VLCVideosDidChangeNotification, object: media)
  273. }
  274. func medialibrary(_ medialibrary: VLCMediaLibrary, didStartDiscovery entryPoint: String) {
  275. }
  276. func medialibrary(_ medialibrary: VLCMediaLibrary, didCompleteDiscovery entryPoint: String) {
  277. }
  278. func medialibrary(_ medialibrary: VLCMediaLibrary, didProgressDiscovery entryPoint: String) {
  279. }
  280. func medialibrary(_ medialibrary: VLCMediaLibrary, didUpdateParsingStatsWithPercent percent: UInt32) {
  281. }
  282. }
  283. // MARK: Future MediaDataSource extension
  284. // Todo: implement the remove
  285. // - (void)removeMediaObjectFromFolder:(NSManagedObject *)managedObject
  286. // {
  287. // NSAssert(([managedObject isKindOfClass:[MLFile class]] && ((MLFile *)managedObject).labels.count > 0), @"All media in a folder should be of type MLFile and it should be in a folder");
  288. //
  289. // if (![managedObject isKindOfClass:[MLFile class]]) return;
  290. //
  291. // MLFile *mediaFile = (MLFile *)managedObject;
  292. // [self rearrangeFolderTrackNumbersForRemovedItem:mediaFile];
  293. // mediaFile.labels = nil;
  294. // mediaFile.folderTrackNumber = nil;
  295. // }
  296. //
  297. // - (void)removeMediaObject:(NSManagedObject *)managedObject
  298. // {
  299. // if ([managedObject isKindOfClass:[MLAlbum class]]) {
  300. // MLAlbum *album = (MLAlbum *)managedObject;
  301. // NSSet *iterAlbumTrack = [NSSet setWithSet:album.tracks];
  302. //
  303. // for (MLAlbumTrack *track in iterAlbumTrack) {
  304. // NSSet *iterFiles = [NSSet setWithSet:track.files];
  305. //
  306. // for (MLFile *file in iterFiles)
  307. // [self _deleteMediaObject:file];
  308. // }
  309. // [[MLMediaLibrary sharedMediaLibrary] removeObject: album];
  310. // // delete all episodes from a show
  311. // } else if ([managedObject isKindOfClass:[MLShow class]]) {
  312. // MLShow *show = (MLShow *)managedObject;
  313. // NSSet *iterShowEpisodes = [NSSet setWithSet:show.episodes];
  314. //
  315. // for (MLShowEpisode *episode in iterShowEpisodes) {
  316. // NSSet *iterFiles = [NSSet setWithSet:episode.files];
  317. //
  318. // for (MLFile *file in iterFiles)
  319. // [self _deleteMediaObject:file];
  320. // }
  321. // [[MLMediaLibrary sharedMediaLibrary] removeObject: show];
  322. // // delete all files from an episode
  323. // } else if ([managedObject isKindOfClass:[MLShowEpisode class]]) {
  324. // MLShowEpisode *episode = (MLShowEpisode *)managedObject;
  325. // NSSet *iterFiles = [NSSet setWithSet:episode.files];
  326. //
  327. // for (MLFile *file in iterFiles)
  328. // [self _deleteMediaObject:file];
  329. // // delete all files from a track
  330. // [[MLMediaLibrary sharedMediaLibrary] removeObject: episode];
  331. // } else if ([managedObject isKindOfClass:[MLAlbumTrack class]]) {
  332. // MLAlbumTrack *track = (MLAlbumTrack *)managedObject;
  333. // NSSet *iterFiles = [NSSet setWithSet:track.files];
  334. //
  335. // for (MLFile *file in iterFiles)
  336. // [self _deleteMediaObject:file];
  337. // } else if ([managedObject isKindOfClass:[MLLabel class]]) {
  338. // MLLabel *folder = (MLLabel *)managedObject;
  339. // NSSet *iterFiles = [NSSet setWithSet:folder.files];
  340. // [folder removeFiles:folder.files];
  341. // for (MLFile *file in iterFiles)
  342. // [self _deleteMediaObject:file];
  343. // [[MLMediaLibrary sharedMediaLibrary] removeObject:folder];
  344. // }
  345. // else
  346. // [self _deleteMediaObject:(MLFile *)managedObject];
  347. // }
  348. //
  349. // - (void)_deleteMediaObject:(MLFile *)mediaObject
  350. // {
  351. // [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
  352. //
  353. // /* stop playback if needed */
  354. // VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  355. // VLCMedia *media = [vpc currentlyPlayingMedia];
  356. // MLFile *currentlyPlayingFile = [MLFile fileForURL:media.url].firstObject;
  357. // if (currentlyPlayingFile && currentlyPlayingFile == mediaObject) {
  358. // [vpc stopPlayback];
  359. // }
  360. //
  361. // NSFileManager *fileManager = [NSFileManager defaultManager];
  362. // NSString *folderLocation = [[mediaObject.url path] stringByDeletingLastPathComponent];
  363. // NSArray *allfiles = [fileManager contentsOfDirectoryAtPath:folderLocation error:nil];
  364. // NSString *fileName = [mediaObject.path.lastPathComponent stringByDeletingPathExtension];
  365. // if (!fileName)
  366. // return;
  367. // NSIndexSet *indexSet = [allfiles indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
  368. // return ([obj rangeOfString:fileName].location != NSNotFound);
  369. // }];
  370. // NSUInteger count = indexSet.count;
  371. // NSString *additionalFilePath;
  372. // NSUInteger currentIndex = [indexSet firstIndex];
  373. // for (unsigned int x = 0; x < count; x++) {
  374. // additionalFilePath = allfiles[currentIndex];
  375. // if ([additionalFilePath isSupportedSubtitleFormat])
  376. // [fileManager removeItemAtPath:[folderLocation stringByAppendingPathComponent:additionalFilePath] error:nil];
  377. // currentIndex = [indexSet indexGreaterThanIndex:currentIndex];
  378. // }
  379. // [fileManager removeItemAtURL:mediaObject.url error:nil];
  380. // }
  381. //
  382. // - (void)rearrangeFolderTrackNumbersForRemovedItem:(MLFile *) mediaObject
  383. // {
  384. // MLLabel *label = [mediaObject.labels anyObject];
  385. // NSSet *allFiles = label.files;
  386. // for (MLFile *file in allFiles) {
  387. // if (file.folderTrackNumber > mediaObject.folderTrackNumber) {
  388. // int value = [file.folderTrackNumber intValue];
  389. // file.folderTrackNumber = [NSNumber numberWithInt:value - 1];
  390. // }
  391. // }
  392. // }
  393. // @end