VLCMediaDataSource.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*****************************************************************************
  2. * VLCMediaDataSource.swift
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2017 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Carola Nitz <caro # videolan.org>
  9. *
  10. * Refer to the COPYING file of the official project for license.
  11. *****************************************************************************/
  12. import Foundation
  13. @objc enum VLCMediaCategory: Int {
  14. case unknown
  15. case audio
  16. case video
  17. }
  18. @objc enum VLCMediaSubcategory: Int {
  19. case unknown
  20. case movies
  21. case episodes
  22. case artists
  23. case albums
  24. case tracks
  25. case genres
  26. case videoPlaylists
  27. case audioPlaylists
  28. case allVideos
  29. }
  30. struct VLCMediaType {
  31. let category: VLCMediaCategory
  32. var subcategory: VLCMediaSubcategory
  33. }
  34. @objc class VLCMediaDataSource: NSObject {
  35. var foundVideos = [MLFile]()
  36. var foundAudio = [MLFile]()
  37. var movies = [MLFile]()
  38. var episodes = [MLShowEpisode]()
  39. var artists = [String]()
  40. var albums = [MLAlbum]()
  41. var genres = [String]()
  42. var audioPlaylist = [MLLabel]()
  43. var videoPlaylist = [MLLabel]()
  44. override init() {
  45. super.init()
  46. getAllVideos()
  47. getAllAudio()
  48. }
  49. @objc
  50. func numberOfFiles(subcategory: VLCMediaSubcategory) -> Int {
  51. return array(for: subcategory).count
  52. }
  53. private func array(for subcategory: VLCMediaSubcategory ) -> [Any] {
  54. switch subcategory {
  55. case .unknown:
  56. preconditionFailure("No")
  57. case .movies:
  58. return movies
  59. case .episodes:
  60. return episodes
  61. case .artists:
  62. return artists
  63. case .albums:
  64. return albums
  65. case .tracks:
  66. return foundAudio
  67. case .genres:
  68. return genres
  69. case .audioPlaylists:
  70. return audioPlaylist
  71. case .videoPlaylists:
  72. return videoPlaylist
  73. case .allVideos:
  74. return foundVideos
  75. }
  76. }
  77. func indicatorInfo(for subcategory: VLCMediaSubcategory) -> IndicatorInfo {
  78. switch subcategory {
  79. case .unknown:
  80. preconditionFailure("No")
  81. case .movies:
  82. return IndicatorInfo(title: NSLocalizedString("MOVIES", comment: ""))
  83. case .episodes:
  84. return IndicatorInfo(title: NSLocalizedString("EPISODES", comment: ""))
  85. case .artists:
  86. return IndicatorInfo(title: NSLocalizedString("ARTISTS", comment: ""))
  87. case .albums:
  88. return IndicatorInfo(title: NSLocalizedString("ALBUMS", comment: ""))
  89. case .tracks:
  90. return IndicatorInfo(title: NSLocalizedString("SONGS", comment: ""))
  91. case .genres:
  92. return IndicatorInfo(title: NSLocalizedString("GENRES", comment: ""))
  93. case .audioPlaylists:
  94. return IndicatorInfo(title: NSLocalizedString("AUDIO_PLAYLISTS", comment: ""))
  95. case .videoPlaylists:
  96. return IndicatorInfo(title: NSLocalizedString("VIDEO_PLAYLISTS", comment: ""))
  97. case .allVideos:
  98. return IndicatorInfo(title: NSLocalizedString("VIDEOS", comment: ""))
  99. }
  100. }
  101. @objc func object(at index: Int, subcategory: VLCMediaSubcategory) -> Any {
  102. guard index >= 0 else {
  103. preconditionFailure("a negative value ? I don't think so!")
  104. }
  105. let categoryArray = array(for: subcategory)
  106. if index < categoryArray.count {
  107. return categoryArray[Int(index)]
  108. }
  109. preconditionFailure("index is taller than count")
  110. }
  111. func allObjects(for subcategory: VLCMediaSubcategory) -> [Any] {
  112. return array(for:subcategory)
  113. }
  114. internal func removeObject(at index: Int, subcategory: VLCMediaSubcategory) {
  115. guard index >= 0 else {
  116. preconditionFailure("a negative value ? I don't think so!")
  117. }
  118. var categoryArray = array(for: subcategory)
  119. if index < categoryArray.count {
  120. categoryArray.remove(at: index)
  121. }
  122. preconditionFailure("index is taller than count")
  123. }
  124. internal func insert(_ item: MLFile, at index: Int, subcategory: VLCMediaSubcategory) {
  125. guard index >= 0 else {
  126. preconditionFailure("a negative value ? I don't think so!")
  127. }
  128. var categoryArray = array(for: subcategory)
  129. if index < categoryArray.count {
  130. categoryArray.insert(item, at: index)
  131. }
  132. categoryArray.append(item)
  133. }
  134. private func getAllVideos() {
  135. let files = MLFile.allFiles() as! [MLFile]
  136. foundVideos = files.filter {
  137. ($0 as MLFile).isKind(ofType: kMLFileTypeMovie) ||
  138. ($0 as MLFile).isKind(ofType: kMLFileTypeTVShowEpisode) ||
  139. ($0 as MLFile).isKind(ofType: kMLFileTypeClip)
  140. }
  141. moviesFromVideos()
  142. episodesFromVideos()
  143. videoPlaylistsFromVideos()
  144. }
  145. private func getAllAudio() {
  146. let files = MLFile.allFiles() as! [MLFile]
  147. foundAudio = files.filter { $0.isSupportedAudioFile() }
  148. artistsFromAudio()
  149. albumsFromAudio()
  150. audioPlaylistsFromAudio()
  151. genresFromAudio()
  152. }
  153. private func artistsFromAudio() {
  154. let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
  155. let tracksWithArtist = albumtracks.filter { $0.artist != nil && $0.artist != "" }
  156. artists = tracksWithArtist.map { $0.artist }
  157. }
  158. private func genresFromAudio() {
  159. let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
  160. let tracksWithArtist = albumtracks.filter { $0.genre != nil && $0.genre != "" }
  161. genres = tracksWithArtist.map { $0.genre }
  162. }
  163. private func episodesFromVideos() {
  164. episodes = MLShowEpisode.allEpisodes() as! [MLShowEpisode]
  165. }
  166. private func albumsFromAudio() {
  167. albums = MLAlbum.allAlbums() as! [MLAlbum]
  168. }
  169. private func audioPlaylistsFromAudio() {
  170. let labels = MLLabel.allLabels() as! [MLLabel]
  171. audioPlaylist = labels.filter {
  172. let audioFiles = $0.files.filter {
  173. if let file = $0 as? MLFile {
  174. return file.isSupportedAudioFile()
  175. }
  176. return false
  177. }
  178. return !audioFiles.isEmpty
  179. }
  180. }
  181. private func videoPlaylistsFromVideos() {
  182. let labels = MLLabel.allLabels() as! [MLLabel]
  183. audioPlaylist = labels.filter {
  184. let audioFiles = $0.files.filter {
  185. if let file = $0 as? MLFile {
  186. return file.isShowEpisode() || file.isMovie() || file.isClip()
  187. }
  188. return false
  189. }
  190. return !audioFiles.isEmpty
  191. }
  192. }
  193. private func moviesFromVideos() {
  194. movies = foundVideos.filter { $0.isMovie() }
  195. }
  196. }
  197. // Todo: implement the remove
  198. // - (void)removeMediaObjectFromFolder:(NSManagedObject *)managedObject
  199. // {
  200. // 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");
  201. //
  202. // if (![managedObject isKindOfClass:[MLFile class]]) return;
  203. //
  204. // MLFile *mediaFile = (MLFile *)managedObject;
  205. // [self rearrangeFolderTrackNumbersForRemovedItem:mediaFile];
  206. // mediaFile.labels = nil;
  207. // mediaFile.folderTrackNumber = nil;
  208. // }
  209. //
  210. // - (void)removeMediaObject:(NSManagedObject *)managedObject
  211. // {
  212. // if ([managedObject isKindOfClass:[MLAlbum class]]) {
  213. // MLAlbum *album = (MLAlbum *)managedObject;
  214. // NSSet *iterAlbumTrack = [NSSet setWithSet:album.tracks];
  215. //
  216. // for (MLAlbumTrack *track in iterAlbumTrack) {
  217. // NSSet *iterFiles = [NSSet setWithSet:track.files];
  218. //
  219. // for (MLFile *file in iterFiles)
  220. // [self _deleteMediaObject:file];
  221. // }
  222. // [[MLMediaLibrary sharedMediaLibrary] removeObject: album];
  223. // // delete all episodes from a show
  224. // } else if ([managedObject isKindOfClass:[MLShow class]]) {
  225. // MLShow *show = (MLShow *)managedObject;
  226. // NSSet *iterShowEpisodes = [NSSet setWithSet:show.episodes];
  227. //
  228. // for (MLShowEpisode *episode in iterShowEpisodes) {
  229. // NSSet *iterFiles = [NSSet setWithSet:episode.files];
  230. //
  231. // for (MLFile *file in iterFiles)
  232. // [self _deleteMediaObject:file];
  233. // }
  234. // [[MLMediaLibrary sharedMediaLibrary] removeObject: show];
  235. // // delete all files from an episode
  236. // } else if ([managedObject isKindOfClass:[MLShowEpisode class]]) {
  237. // MLShowEpisode *episode = (MLShowEpisode *)managedObject;
  238. // NSSet *iterFiles = [NSSet setWithSet:episode.files];
  239. //
  240. // for (MLFile *file in iterFiles)
  241. // [self _deleteMediaObject:file];
  242. // // delete all files from a track
  243. // [[MLMediaLibrary sharedMediaLibrary] removeObject: episode];
  244. // } else if ([managedObject isKindOfClass:[MLAlbumTrack class]]) {
  245. // MLAlbumTrack *track = (MLAlbumTrack *)managedObject;
  246. // NSSet *iterFiles = [NSSet setWithSet:track.files];
  247. //
  248. // for (MLFile *file in iterFiles)
  249. // [self _deleteMediaObject:file];
  250. // } else if ([managedObject isKindOfClass:[MLLabel class]]) {
  251. // MLLabel *folder = (MLLabel *)managedObject;
  252. // NSSet *iterFiles = [NSSet setWithSet:folder.files];
  253. // [folder removeFiles:folder.files];
  254. // for (MLFile *file in iterFiles)
  255. // [self _deleteMediaObject:file];
  256. // [[MLMediaLibrary sharedMediaLibrary] removeObject:folder];
  257. // }
  258. // else
  259. // [self _deleteMediaObject:(MLFile *)managedObject];
  260. // }
  261. //
  262. // - (void)_deleteMediaObject:(MLFile *)mediaObject
  263. // {
  264. // [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
  265. //
  266. // /* stop playback if needed */
  267. // VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  268. // VLCMedia *media = [vpc currentlyPlayingMedia];
  269. // MLFile *currentlyPlayingFile = [MLFile fileForURL:media.url].firstObject;
  270. // if (currentlyPlayingFile && currentlyPlayingFile == mediaObject) {
  271. // [vpc stopPlayback];
  272. // }
  273. //
  274. // NSFileManager *fileManager = [NSFileManager defaultManager];
  275. // NSString *folderLocation = [[mediaObject.url path] stringByDeletingLastPathComponent];
  276. // NSArray *allfiles = [fileManager contentsOfDirectoryAtPath:folderLocation error:nil];
  277. // NSString *fileName = [mediaObject.path.lastPathComponent stringByDeletingPathExtension];
  278. // if (!fileName)
  279. // return;
  280. // NSIndexSet *indexSet = [allfiles indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
  281. // return ([obj rangeOfString:fileName].location != NSNotFound);
  282. // }];
  283. // NSUInteger count = indexSet.count;
  284. // NSString *additionalFilePath;
  285. // NSUInteger currentIndex = [indexSet firstIndex];
  286. // for (unsigned int x = 0; x < count; x++) {
  287. // additionalFilePath = allfiles[currentIndex];
  288. // if ([additionalFilePath isSupportedSubtitleFormat])
  289. // [fileManager removeItemAtPath:[folderLocation stringByAppendingPathComponent:additionalFilePath] error:nil];
  290. // currentIndex = [indexSet indexGreaterThanIndex:currentIndex];
  291. // }
  292. // [fileManager removeItemAtURL:mediaObject.url error:nil];
  293. // }
  294. //
  295. // - (void)rearrangeFolderTrackNumbersForRemovedItem:(MLFile *) mediaObject
  296. // {
  297. // MLLabel *label = [mediaObject.labels anyObject];
  298. // NSSet *allFiles = label.files;
  299. // for (MLFile *file in allFiles) {
  300. // if (file.folderTrackNumber > mediaObject.folderTrackNumber) {
  301. // int value = [file.folderTrackNumber intValue];
  302. // file.folderTrackNumber = [NSNumber numberWithInt:value - 1];
  303. // }
  304. // }
  305. // }
  306. // @end