瀏覽代碼

VLCMediaSubcategory: added generic Subcategory and adjusted classes

Carola Nitz 7 年之前
父節點
當前提交
6809c30186

+ 220 - 0
Sources/MediaSubcategory/VLCMediaSubcategory.swift

@@ -0,0 +1,220 @@
+/*****************************************************************************
+ * VLCMediaSubcategory.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2017 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Carola Nitz <caro # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+import Foundation
+
+extension Notification.Name {
+    static let VLCMoviesDidChangeNotification = Notification.Name("MoviesDidChangeNotfication")
+    static let VLCEpisodesDidChangeNotification = Notification.Name("EpisodesDidChangeNotfication")
+    static let VLCArtistsDidChangeNotification = Notification.Name("ArtistsDidChangeNotfication")
+    static let VLCAlbumsDidChangeNotification = Notification.Name("AlbumsDidChangeNotfication")
+    static let VLCTracksDidChangeNotification = Notification.Name("TracksDidChangeNotfication")
+    static let VLCGenresDidChangeNotification = Notification.Name("GenresDidChangeNotfication")
+    static let VLCAudioPlaylistsDidChangeNotification = Notification.Name("AudioPlaylistsDidChangeNotfication")
+    static let VLCVideoPlaylistsDidChangeNotification = Notification.Name("VideoPlaylistsDidChangeNotfication")
+    static let VLCVideosDidChangeNotification = Notification.Name("VideosDidChangeNotfication")
+}
+
+enum VLCDataUnit {
+    case file(MLFile)
+    case episode(MLShowEpisode)
+    case album(MLAlbum)
+    case label(MLLabel)
+}
+
+class VLCMediaSubcategory<T>: NSObject {
+    var files: [T]
+    var indicatorInfoName: String
+    var notificationName: Notification.Name
+    var includesFunc: (VLCDataUnit) -> Bool
+    var appendFunc: (VLCDataUnit) -> Void
+
+    var indicatorInfo: IndicatorInfo {
+        return IndicatorInfo(title: indicatorInfoName)
+    }
+
+    init(files: [T],
+         indicatorInfoName: String,
+         notificationName: Notification.Name,
+         includesFunc: @escaping (VLCDataUnit) -> Bool,
+         appendFunc: @escaping (VLCDataUnit) -> Void) {
+        self.files = files
+        self.indicatorInfoName = indicatorInfoName
+        self.notificationName = notificationName
+        self.includesFunc = includesFunc
+        self.appendFunc = appendFunc
+    }
+}
+
+struct VLCMediaSubcategories {
+    static var movies = VLCMediaSubcategory<MLFile>(
+        files: {
+            (MLFile.allFiles() as! [MLFile]).filter {
+            ($0 as MLFile).isKind(ofType: kMLFileTypeMovie) ||
+                ($0 as MLFile).isKind(ofType: kMLFileTypeTVShowEpisode) ||
+                ($0 as MLFile).isKind(ofType: kMLFileTypeClip)
+            }
+        }(),
+        indicatorInfoName: NSLocalizedString("MOVIES", comment: ""),
+        notificationName: .VLCMoviesDidChangeNotification,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .file(let f) = dataUnit {
+                return f.isMovie()
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var episodes = VLCMediaSubcategory<MLShowEpisode>(
+        files: MLShowEpisode.allEpisodes() as! [MLShowEpisode],
+        indicatorInfoName: NSLocalizedString("EPISODES", comment: ""),
+        notificationName: .VLCEpisodesDidChangeNotification,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .episode(let f) = dataUnit {
+                return true
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var artists = VLCMediaSubcategory<String>(
+        files: {
+            let tracksWithArtist = (MLAlbumTrack.allTracks() as! [MLAlbumTrack]).filter { $0.artist != nil && $0.artist != "" }
+            return tracksWithArtist.map { $0.artist } as! [String]
+        }(),
+        indicatorInfoName: NSLocalizedString("ARTISTS", comment: ""),
+        notificationName: .VLCArtistsDidChangeNotification,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .file(let f) = dataUnit {
+                return f.artist != nil
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var albums = VLCMediaSubcategory<MLAlbum>(
+        files: MLAlbum.allAlbums() as! [MLAlbum],
+        indicatorInfoName: NSLocalizedString("ALBUMS", comment: ""),
+        notificationName: .VLCAlbumsDidChangeNotification,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .album(let f) = dataUnit {
+                return true
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var tracks = VLCMediaSubcategory<MLFile>(
+        files: (MLFile.allFiles() as! [MLFile]).filter { $0.isSupportedAudioFile()},
+        indicatorInfoName: NSLocalizedString("SONGS", comment: ""),
+        notificationName: .VLCTracksDidChangeNotification,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .file(let f) = dataUnit {
+                return f.isSupportedAudioFile()
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var genres = VLCMediaSubcategory<String>(
+        files: {
+            let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
+            let tracksWithArtist = albumtracks.filter { $0.genre != nil && $0.genre != "" }
+            return tracksWithArtist.map { $0.genre }
+        }(),
+        indicatorInfoName: NSLocalizedString("GENRES", comment: ""),
+        notificationName: .VLCGenresDidChangeNotification ,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .file(let f) = dataUnit {
+                return f.genre != nil
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var audioPlaylists = VLCMediaSubcategory<MLLabel>(
+        files: {
+            let labels = MLLabel.allLabels() as! [MLLabel]
+            let audioPlaylist = labels.filter {
+                let audioFiles = $0.files.filter {
+                    if let file = $0 as? MLFile {
+                        return file.isSupportedAudioFile()
+                    }
+                    return false
+                }
+                return !audioFiles.isEmpty
+            }
+            return audioPlaylist
+        }(),
+        indicatorInfoName: NSLocalizedString("AUDIO_PLAYLISTS", comment: ""),
+        notificationName: .VLCAudioPlaylistsDidChangeNotification ,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .label(let l) = dataUnit {
+                let audioFiles = l.files.filter {
+                    if let file = $0 as? MLFile {
+                        return file.isSupportedAudioFile()
+                    } else {
+                        return false
+                    }
+                }
+                return !audioFiles.isEmpty
+            }
+            return false
+        },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+        })
+
+    static var videoPlaylists = VLCMediaSubcategory<MLLabel>(
+        files: {
+            let labels = MLLabel.allLabels() as! [MLLabel]
+            let audioPlaylist = labels.filter {
+                let audioFiles = $0.files.filter {
+                    if let file = $0 as? MLFile {
+                        return file.isShowEpisode() || file.isMovie() || file.isClip()
+                    }
+                    return false
+                }
+                return !audioFiles.isEmpty
+            }
+            return audioPlaylist
+        }(),
+        indicatorInfoName: NSLocalizedString("VIDEO_PLAYLISTS", comment: ""),
+        notificationName: .VLCVideoPlaylistsDidChangeNotification ,
+        includesFunc: { (dataUnit: VLCDataUnit) in
+            if case .label(let l) = dataUnit {
+                let videoFiles = l.files.filter {
+                    if let file = $0 as? MLFile {
+                        return file.isShowEpisode() || file.isMovie() || file.isClip()
+                    } else {
+                        return false
+                    }
+                }
+                return !videoFiles.isEmpty
+            }
+            return false
+    },
+        appendFunc: { (dataUnit: VLCDataUnit) in
+
+    })
+}

+ 18 - 14
Sources/MediaSubcategoryViewController.swift

@@ -14,25 +14,29 @@ import UIKit
 
 class VLCVideoSubcategoryViewController: VLCMediaSubcategoryViewController {
     override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
-        let movies = VLCMediaViewController(services: services, type: VLCMediaType(category: .video, subcategory: .allVideos))
-        let episodes = VLCMediaViewController(services: services, type: VLCMediaType(category: .video, subcategory: .episodes))
-        let playlists = VLCMediaViewController(services: services, type: VLCMediaType(category: .video, subcategory: .videoPlaylists))
-        let viewControllers = [movies, episodes, playlists]
-        viewControllers.forEach { $0.delegate = mediaDelegate }
-        return viewControllers
+        let movies = VLCMediaViewController<MLFile>(services: services, subcategory: VLCMediaSubcategories.movies)
+        movies.delegate = mediaDelegate
+        let episodes = VLCMediaViewController<MLShowEpisode>(services: services, subcategory: VLCMediaSubcategories.episodes)
+        episodes.delegate = mediaDelegate
+        let playlists = VLCMediaViewController<MLLabel>(services: services, subcategory: VLCMediaSubcategories.videoPlaylists)
+        playlists.delegate = mediaDelegate
+        return [movies, episodes, playlists]
     }
 }
 
 class VLCAudioSubcategoryViewController: VLCMediaSubcategoryViewController {
     override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
-        let tracks = VLCMediaViewController(services: services, type: VLCMediaType(category: .audio, subcategory: .tracks))
-        let genres = VLCMediaViewController(services: services, type: VLCMediaType(category: .audio, subcategory: .genres))
-        let artists = VLCMediaViewController(services: services, type: VLCMediaType(category: .audio, subcategory: .artists))
-        let albums = VLCMediaViewController(services: services, type: VLCMediaType(category: .audio, subcategory: .albums))
-        let playlists = VLCMediaViewController(services: services, type: VLCMediaType(category: .audio, subcategory: .audioPlaylists))
-        let viewControllers = [tracks, genres, artists, albums, playlists]
-        viewControllers.forEach { $0.delegate = mediaDelegate }
-        return viewControllers
+        let tracks = VLCMediaViewController<MLFile>(services: services, subcategory: VLCMediaSubcategories.tracks)
+        tracks.delegate = mediaDelegate
+        let genres = VLCMediaViewController<String>(services: services, subcategory: VLCMediaSubcategories.genres)
+        genres.delegate = mediaDelegate
+        let artists = VLCMediaViewController<String>(services: services, subcategory: VLCMediaSubcategories.artists)
+        artists.delegate = mediaDelegate
+        let albums = VLCMediaViewController<MLAlbum>(services: services, subcategory: VLCMediaSubcategories.albums)
+        albums.delegate = mediaDelegate
+        let playlists = VLCMediaViewController<MLLabel>(services: services, subcategory: VLCMediaSubcategories.audioPlaylists)
+        playlists.delegate = mediaDelegate
+        return [tracks, genres, artists, albums, playlists]
     }
 }
 

+ 0 - 70
Sources/MediaViewController+DataSourceDelegate.swift

@@ -1,70 +0,0 @@
-/*****************************************************************************
- * MediaViewController+DataSourceDelegate.swift
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2018 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Carola Nitz <nitz.carola # gmail.com>
- *          Mike JS. Choi <mkchoi212 # icloud.com>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
-
-extension Notification.Name {
-    static let VLCTracksDidChangeNotification = Notification.Name("kTracksDidChangeNotification")
-    static let VLCAllVideosDidChangeNotification = Notification.Name("kAllVideosDidChangeNotification")
-}
-
-// These are the delegate and datasource calls from the MediaViewController
-extension VLCMediaViewController: UICollectionViewDelegateFlowLayout {
-
-    // MARK: - UICollectionViewDataSource
-    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        return Int(services.mediaDataSource.numberOfFiles(subcategory: mediaType.subcategory))
-    }
-
-    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-        if let playlistCell = collectionView.dequeueReusableCell(withReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier(), for: indexPath) as? VLCPlaylistCollectionViewCell {
-            if let mediaObject = services.mediaDataSource.object(at: indexPath.row, subcategory: mediaType.subcategory) as? NSManagedObject {
-                playlistCell.mediaObject = mediaObject
-            }
-            return playlistCell
-        }
-        return UICollectionViewCell()
-    }
-
-    // MARK: - UICollectionViewDelegate
-    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-        if let mediaObject = services.mediaDataSource.object(at: indexPath.row, subcategory: mediaType.subcategory) as? NSManagedObject {
-            delegate?.mediaViewControllerDidSelectMediaObject(self, mediaObject: mediaObject)
-        }
-    }
-
-    // MARK: - UICollectionViewDelegateFlowLayout
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
-
-        let numberOfCells: CGFloat = collectionView.traitCollection.horizontalSizeClass == .regular ? 3.0 : 2.0
-        let aspectRatio: CGFloat = 10.0 / 16.0
-
-        // We have the number of cells and we always have numberofCells + 1 padding spaces. -pad-[Cell]-pad-[Cell]-pad-
-        // we then have the entire padding, we divide the entire padding by the number of Cells to know how much needs to be substracted from ech cell
-        // since this might be an uneven number we ceil
-        var cellWidth = collectionView.bounds.size.width / numberOfCells
-        cellWidth = cellWidth - ceil(((numberOfCells + 1) * cellPadding) / numberOfCells)
-
-        return CGSize(width: cellWidth, height: cellWidth * aspectRatio)
-    }
-
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
-        return UIEdgeInsets(top: cellPadding, left: cellPadding, bottom: cellPadding, right: cellPadding)
-    }
-
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
-        return cellPadding
-    }
-
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
-        return cellPadding
-    }
-}

+ 63 - 21
Sources/MediaViewController.swift

@@ -13,23 +13,22 @@
 
 import Foundation
 
-@objc protocol VLCMediaViewControllerDelegate: class {
-    func mediaViewControllerDidSelectMediaObject(_ mediaViewController: VLCMediaViewController, mediaObject: NSManagedObject)
+protocol VLCMediaViewControllerDelegate: class {
+    func mediaViewControllerDidSelectMediaObject(_ mediaViewController: UIViewController, mediaObject: NSManagedObject)
 }
 
-class VLCMediaViewController: UICollectionViewController, UISearchResultsUpdating, UISearchControllerDelegate, IndicatorInfoProvider {
+class VLCMediaViewController<T>: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating, UISearchControllerDelegate, IndicatorInfoProvider {
     let cellPadding: CGFloat = 5.0
-    var services: Services
-    var mediaType: VLCMediaType
-    weak var delegate: VLCMediaViewControllerDelegate?
+    private var services: Services
     private var searchController: UISearchController?
     private let searchDataSource = VLCLibrarySearchDisplayDataSource()
+    var subcategory: VLCMediaSubcategory<T>
+    weak var delegate: VLCMediaViewControllerDelegate?
 
     @available(iOS 11.0, *)
-    lazy var dragAndDropManager: VLCDragAndDropManager = {
-        let dragAndDropManager = VLCDragAndDropManager(type: mediaType)
-        dragAndDropManager.delegate = services.mediaDataSource
-        return dragAndDropManager
+    lazy var dragAndDropManager: VLCDragAndDropManager = { () -> VLCDragAndDropManager<T> in
+        VLCDragAndDropManager<T>(subcategory: subcategory)
+
     }()
 
     lazy var emptyView: VLCEmptyLibraryView = {
@@ -44,17 +43,13 @@ class VLCMediaViewController: UICollectionViewController, UISearchResultsUpdatin
         fatalError()
     }
 
-    init(services: Services, type: VLCMediaType) {
+    init(services: Services, subcategory: VLCMediaSubcategory<T>) {
         self.services = services
-        mediaType = type
+        self.subcategory = subcategory
 
         super.init(collectionViewLayout: UICollectionViewFlowLayout())
         NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .VLCThemeDidChangeNotification, object: nil)
-        if mediaType.category == .video {
-            NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: .VLCAllVideosDidChangeNotification, object: nil)
-        } else {
-            NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: .VLCTracksDidChangeNotification, object: nil)
-        }
+        NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: subcategory.notificationName, object: nil)
     }
 
     override var preferredStatusBarStyle: UIStatusBarStyle {
@@ -98,8 +93,6 @@ class VLCMediaViewController: UICollectionViewController, UISearchResultsUpdatin
         collectionView?.register(playlistnib, forCellWithReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier())
         collectionView?.backgroundColor = PresentationTheme.current.colors.background
         collectionView?.alwaysBounceVertical = true
-        collectionView?.dataSource = self
-        collectionView?.delegate = self
         if #available(iOS 11.0, *) {
             collectionView?.dragDelegate = dragAndDropManager
             collectionView?.dropDelegate = dragAndDropManager
@@ -149,7 +142,7 @@ class VLCMediaViewController: UICollectionViewController, UISearchResultsUpdatin
     // MARK: - Search
 
     func updateSearchResults(for searchController: UISearchController) {
-        searchDataSource.shouldReloadTable(forSearch: searchController.searchBar.text, searchableFiles: services.mediaDataSource.allObjects(for: mediaType.subcategory))
+        searchDataSource.shouldReloadTable(forSearch: searchController.searchBar.text, searchableFiles: subcategory.files)
         collectionView?.reloadData()
     }
 
@@ -162,6 +155,55 @@ class VLCMediaViewController: UICollectionViewController, UISearchResultsUpdatin
     }
 
     func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
-        return  services.mediaDataSource.indicatorInfo(for:mediaType.subcategory)
+        return IndicatorInfo(title:subcategory.indicatorInfoName)
+    }
+
+    // MARK: - UICollectionViewDataSource
+    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return subcategory.files.count
+    }
+
+    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        if let playlistCell = collectionView.dequeueReusableCell(withReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier(), for: indexPath) as? VLCPlaylistCollectionViewCell {
+            if let mediaObject = subcategory.files[indexPath.row] as? NSManagedObject {
+                playlistCell.mediaObject = mediaObject
+            }
+            return playlistCell
+        }
+        return UICollectionViewCell()
+    }
+
+    // MARK: - UICollectionViewDelegate
+    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        if let mediaObject = subcategory.files[indexPath.row] as? NSManagedObject {
+            delegate?.mediaViewControllerDidSelectMediaObject(self, mediaObject: mediaObject)
+        }
+    }
+
+    // MARK: - UICollectionViewDelegateFlowLayout
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+
+        let numberOfCells: CGFloat = collectionView.traitCollection.horizontalSizeClass == .regular ? 3.0 : 2.0
+        let aspectRatio: CGFloat = 10.0 / 16.0
+
+        // We have the number of cells and we always have numberofCells + 1 padding spaces. -pad-[Cell]-pad-[Cell]-pad-
+        // we then have the entire padding, we divide the entire padding by the number of Cells to know how much needs to be substracted from ech cell
+        // since this might be an uneven number we ceil
+        var cellWidth = collectionView.bounds.size.width / numberOfCells
+        cellWidth = cellWidth - ceil(((numberOfCells + 1) * cellPadding) / numberOfCells)
+
+        return CGSize(width: cellWidth, height: cellWidth * aspectRatio)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
+        return UIEdgeInsets(top: cellPadding, left: cellPadding, bottom: cellPadding, right: cellPadding)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        return cellPadding
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        return cellPadding
     }
 }

+ 28 - 29
Sources/VLCDragAndDropManager.swift

@@ -24,20 +24,19 @@ struct DropError: Error {
 }
 
 @available(iOS 11.0, *)
-@objc protocol VLCDragAndDropManagerDelegate: NSObjectProtocol {
-    func dragAndDropManagerRequestsFile(manager: VLCDragAndDropManager, atIndexPath indexPath: IndexPath) -> Any?
-    func dragAndDropManagerInsertItem(manager: VLCDragAndDropManager, item: NSManagedObject, atIndexPath indexPath: IndexPath)
-    func dragAndDropManagerDeleteItem(manager: VLCDragAndDropManager, atIndexPath indexPath: IndexPath)
-    func dragAndDropManagerRemoveFileFromFolder(manager: VLCDragAndDropManager, file: NSManagedObject)
-    func dragAndDropManagerCurrentSelection(manager: VLCDragAndDropManager) -> AnyObject?
+protocol VLCDragAndDropManagerDelegate: NSObjectProtocol {
+    func dragAndDropManagerRequestsFile(manager: NSObject, atIndexPath indexPath: IndexPath) -> Any?
+    func dragAndDropManagerInsertItem(manager: NSObject, item: NSManagedObject, atIndexPath indexPath: IndexPath)
+    func dragAndDropManagerDeleteItem(manager: NSObject, atIndexPath indexPath: IndexPath)
+    func dragAndDropManagerRemoveFileFromFolder(manager: NSObject, file: NSManagedObject)
+    func dragAndDropManagerCurrentSelection(manager: NSObject) -> AnyObject?
 }
 
 @available(iOS 11.0, *)
-class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableViewDragDelegate, UICollectionViewDropDelegate, UITableViewDropDelegate, UIDropInteractionDelegate {
-    @objc weak var delegate: VLCDragAndDropManagerDelegate?
+class VLCDragAndDropManager<T>: NSObject, UICollectionViewDragDelegate, UITableViewDragDelegate, UICollectionViewDropDelegate, UITableViewDropDelegate, UIDropInteractionDelegate {
 
     let utiTypeIdentifiers: [String] = VLCDragAndDropManager.supportedTypeIdentifiers()
-    var mediaType: VLCMediaType
+    var subcategory: VLCMediaSubcategory<T>
     /// Returns the supported type identifiers that VLC can process.
     /// It fetches the identifiers in LSItemContentTypes from all the CFBundleDocumentTypes in the info.plist.
     /// Video, Audio and Subtitle formats
@@ -60,8 +59,8 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
         fatalError()
     }
 
-    init(type: VLCMediaType) {
-        mediaType = type
+    init(subcategory: VLCMediaSubcategory<T>) {
+        self.subcategory = subcategory
         super.init()
     }
 
@@ -116,7 +115,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
                 if let file = file {
                     placeholderContext.commitInsertion() {
                         insertionIndexPath in
-                        strongSelf.delegate?.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
+                        strongSelf.subcategory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
                     }
                 }
                 if let error = error as? DropError {
@@ -128,15 +127,15 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
     }
 
     private func inFolder() -> Bool {
-        return delegate?.dragAndDropManagerCurrentSelection(manager: self) as? MLLabel != nil
+        return subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLLabel != nil
     }
 
     private func moveItem(tableView: UITableView, item: UITableViewDropItem, toIndexPath destinationPath: IndexPath) {
         if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
             tableView.performBatchUpdates({
                 tableView.insertRows(at: [destinationPath], with: .automatic)
-                delegate?.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
-                delegate?.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
+                subcategory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
+                subcategory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
             }, completion: nil)
         }
     }
@@ -144,10 +143,10 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
     private func addDragItem(tableView: UITableView, dragItem item: UITableViewDropItem, toFolderAt index: IndexPath) {
         if let sourcepath = item.sourceIndexPath { // local file that just needs to be moved
             tableView.performBatchUpdates({
-                if let file = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
+                if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
                     tableView.deleteRows(at: [sourcepath], with: .automatic)
                     addFile(file: file, toFolderAt: index)
-                    delegate?.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
+                    subcategory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
                 }
             }, completion: nil)
             return
@@ -209,7 +208,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
                 if let file = file {
                     placeholderContext.commitInsertion() {
                         insertionIndexPath in
-                        strongSelf.delegate?.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
+                        strongSelf.subcategory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
                     }
                 }
                 if let error = error as? DropError {
@@ -224,8 +223,8 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
         if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
             collectionView.performBatchUpdates({
                 collectionView.insertItems(at: [destinationPath])
-                delegate?.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
-                delegate?.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
+                subcategory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
+                subcategory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
             }, completion: nil)
         }
     }
@@ -234,10 +233,10 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
         if let sourcepath = item.sourceIndexPath {
             // local file that just needs to be moved
             collectionView.performBatchUpdates({
-                if let file = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
+                if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
                     collectionView.deleteItems(at: [sourcepath])
                     addFile(file: file, toFolderAt: index)
-                    delegate?.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
+                    subcategory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
                 }
             }, completion: nil)
         } else {
@@ -290,8 +289,8 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
     ///   - item: UIDragItem from session
     /// - Returns: UIDropOperation
     private func dropOperation(hasActiveDrag: Bool, firstSessionItem item: AnyObject?, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UIDropOperation {
-        let inAlbum = delegate?.dragAndDropManagerCurrentSelection(manager: self) as? MLAlbum != nil
-        let inShow = delegate?.dragAndDropManagerCurrentSelection(manager: self) as? MLShow != nil
+        let inAlbum = subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLAlbum != nil
+        let inShow = subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLShow != nil
         // you can move files into a folder or copy from anothr app into a folder
         if fileIsFolder(atIndexPath: destinationIndexPath) {
             // no dragging entire shows and albums into folders
@@ -337,7 +336,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
 
     private func fileIsFolder(atIndexPath indexPath: IndexPath?) -> Bool {
         if let indexPath = indexPath {
-            let file = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath)
+            let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath)
             return file as? MLLabel != nil
         }
         return false
@@ -352,7 +351,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
 
     private func fileIsCollection(atIndexPath indexPath: IndexPath?) -> Bool {
         if let indexPath = indexPath {
-            if let file = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
+            if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
                 return fileIsCollection(file:file)
             }
         }
@@ -361,7 +360,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
 
     // creating dragItems for the file at indexpath
     private func dragItems(forIndexPath indexPath: IndexPath) -> [UIDragItem] {
-        if let file = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
+        if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
             if fileIsCollection(atIndexPath: indexPath) {
                 return dragItemsforCollection(file: file)
             }
@@ -445,7 +444,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
     }
 
     private func addFile(file: MLFile, toFolderAt folderIndex: IndexPath) {
-        let label = delegate?.dragAndDropManagerRequestsFile(manager: self, atIndexPath: folderIndex) as! MLLabel
+        let label = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: folderIndex) as! MLLabel
         DispatchQueue.main.async {
             _ = label.files.insert(file)
             file.labels = [label]
@@ -483,7 +482,7 @@ class VLCDragAndDropManager: NSObject, UICollectionViewDragDelegate, UITableView
                 if let file = MLFile.file(for: destinationURL).first as? MLFile {
                     DispatchQueue.main.async {
                         // we dragged into a folder
-                        if let selection = strongSelf.delegate?.dragAndDropManagerCurrentSelection(manager: strongSelf) as? MLLabel {
+                        if let selection = strongSelf.subcategory.dragAndDropManagerCurrentSelection(manager: strongSelf) as? MLLabel {
                             file.labels = [selection]
                         }
                         completion(file, nil)

+ 0 - 44
Sources/VLCMediaData+VLCDragAndDrop.swift

@@ -1,44 +0,0 @@
-/*****************************************************************************
- * VLCMediaData+VLCDragAndDrop.swift
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2017 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Carola Nitz <caro # videolan.org>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
-
-import Foundation
-
-@available(iOS 11.0, *)
-extension VLCMediaDataSource: VLCDragAndDropManagerDelegate {
-
-    func dragAndDropManagerRequestsFile(manager: VLCDragAndDropManager, atIndexPath indexPath: IndexPath) -> Any? {
-        return object(at: indexPath.row, subcategory: manager.mediaType.subcategory)
-    }
-
-    func dragAndDropManagerInsertItem(manager: VLCDragAndDropManager, item: NSManagedObject, atIndexPath indexPath: IndexPath) {
-        if item as? MLLabel != nil && indexPath.row < numberOfFiles(subcategory: manager.mediaType.subcategory) {
-            removeObject(at: indexPath.row, subcategory: manager.mediaType.subcategory)
-        }
-        insert(item as! MLFile, at: indexPath.row, subcategory: manager.mediaType.subcategory)
-
-    }
-
-    func dragAndDropManagerDeleteItem(manager: VLCDragAndDropManager, atIndexPath indexPath: IndexPath) {
-        return removeObject(at: indexPath.row, subcategory: manager.mediaType.subcategory)
-    }
-
-    func dragAndDropManagerCurrentSelection(manager: VLCDragAndDropManager) -> AnyObject? {
-
-        //  TODO: Handle playlists and Collections
-        fatalError()
-    }
-
-    func dragAndDropManagerRemoveFileFromFolder(manager: VLCDragAndDropManager, file: NSManagedObject) {
-        // TODO: handle removing from playlists and Collections
-        fatalError()
-    }
-}

+ 2 - 204
Sources/VLCMediaDataSource.swift

@@ -12,216 +12,14 @@
 
 import Foundation
 
-@objc enum VLCMediaCategory: Int {
-    case unknown
-    case audio
-    case video
-}
-
-@objc enum VLCMediaSubcategory: Int {
-    case unknown
-    case movies
-    case episodes
-    case artists
-    case albums
-    case tracks
-    case genres
-    case videoPlaylists
-    case audioPlaylists
-    case allVideos
-}
-
-struct VLCMediaType {
-    let category: VLCMediaCategory
-    var subcategory: VLCMediaSubcategory
-}
-
 @objc class VLCMediaDataSource: NSObject {
 
-    var foundVideos = [MLFile]()
-    var foundAudio = [MLFile]()
-
-    var movies = [MLFile]()
-    var episodes = [MLShowEpisode]()
-    var artists = [String]()
-    var albums = [MLAlbum]()
-    var genres = [String]()
-    var audioPlaylist = [MLLabel]()
-    var videoPlaylist = [MLLabel]()
-
     override init() {
         super.init()
-        getAllVideos()
-        getAllAudio()
-    }
-
-    @objc
-    func numberOfFiles(subcategory: VLCMediaSubcategory) -> Int {
-        return array(for: subcategory).count
-    }
-
-    private func array(for subcategory: VLCMediaSubcategory ) -> [Any] {
-        switch subcategory {
-        case .unknown:
-            preconditionFailure("No")
-        case .movies:
-            return movies
-        case .episodes:
-            return episodes
-        case .artists:
-            return artists
-        case .albums:
-            return albums
-        case .tracks:
-            return foundAudio
-        case .genres:
-            return genres
-        case .audioPlaylists:
-            return audioPlaylist
-        case .videoPlaylists:
-            return videoPlaylist
-        case .allVideos:
-            return foundVideos
-        }
-    }
-
-    func indicatorInfo(for subcategory: VLCMediaSubcategory) -> IndicatorInfo {
-        switch subcategory {
-        case .unknown:
-            preconditionFailure("No")
-        case .movies:
-            return IndicatorInfo(title: NSLocalizedString("MOVIES", comment: ""))
-        case .episodes:
-            return IndicatorInfo(title: NSLocalizedString("EPISODES", comment: ""))
-        case .artists:
-            return IndicatorInfo(title: NSLocalizedString("ARTISTS", comment: ""))
-        case .albums:
-             return IndicatorInfo(title: NSLocalizedString("ALBUMS", comment: ""))
-        case .tracks:
-            return IndicatorInfo(title: NSLocalizedString("SONGS", comment: ""))
-        case .genres:
-            return IndicatorInfo(title: NSLocalizedString("GENRES", comment: ""))
-        case .audioPlaylists:
-            return IndicatorInfo(title: NSLocalizedString("AUDIO_PLAYLISTS", comment: ""))
-        case .videoPlaylists:
-            return IndicatorInfo(title: NSLocalizedString("VIDEO_PLAYLISTS", comment: ""))
-        case .allVideos:
-            return IndicatorInfo(title: NSLocalizedString("VIDEOS", comment: ""))
-        }
-
-    }
-
-    @objc func object(at index: Int, subcategory: VLCMediaSubcategory) -> Any {
-
-        guard index >= 0 else {
-            preconditionFailure("a negative value ? I don't think so!")
-        }
-
-        let categoryArray = array(for: subcategory)
-        if index < categoryArray.count {
-            return categoryArray[Int(index)]
-        }
-        preconditionFailure("index is taller than count")
-    }
-
-    func allObjects(for subcategory: VLCMediaSubcategory) -> [Any] {
-        return array(for:subcategory)
-    }
-
-    internal func removeObject(at index: Int, subcategory: VLCMediaSubcategory) {
-        guard index >= 0 else {
-            preconditionFailure("a negative value ? I don't think so!")
-        }
-        var categoryArray = array(for: subcategory)
-        if index < categoryArray.count {
-            categoryArray.remove(at: index)
-        }
-        preconditionFailure("index is taller than count")
-    }
-
-    internal func insert(_ item: MLFile, at index: Int, subcategory: VLCMediaSubcategory) {
-        guard index >= 0 else {
-            preconditionFailure("a negative value ? I don't think so!")
-        }
-        var categoryArray = array(for: subcategory)
-        if index < categoryArray.count {
-            categoryArray.insert(item, at: index)
-        }
-        categoryArray.append(item)
-    }
-
-    private func getAllVideos() {
-        let files = MLFile.allFiles() as! [MLFile]
-        foundVideos = files.filter {
-            ($0 as MLFile).isKind(ofType: kMLFileTypeMovie) ||
-                ($0 as MLFile).isKind(ofType: kMLFileTypeTVShowEpisode) ||
-                ($0 as MLFile).isKind(ofType: kMLFileTypeClip)
-        }
-        moviesFromVideos()
-        episodesFromVideos()
-        videoPlaylistsFromVideos()
-    }
-
-    private func getAllAudio() {
-        let files = MLFile.allFiles() as! [MLFile]
-        foundAudio = files.filter { $0.isSupportedAudioFile() }
-
-        artistsFromAudio()
-        albumsFromAudio()
-        audioPlaylistsFromAudio()
-        genresFromAudio()
-    }
-
-    private func artistsFromAudio() {
-        let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
-        let tracksWithArtist = albumtracks.filter { $0.artist != nil && $0.artist != "" }
-        artists = tracksWithArtist.map { $0.artist }
-    }
-
-    private func genresFromAudio() {
-        let albumtracks = MLAlbumTrack.allTracks() as! [MLAlbumTrack]
-        let tracksWithArtist = albumtracks.filter { $0.genre != nil && $0.genre != "" }
-        genres = tracksWithArtist.map { $0.genre }
-    }
-
-    private func episodesFromVideos() {
-        episodes = MLShowEpisode.allEpisodes() as! [MLShowEpisode]
-    }
-
-    private func albumsFromAudio() {
-        albums = MLAlbum.allAlbums() as! [MLAlbum]
-    }
-
-    private func audioPlaylistsFromAudio() {
-        let labels = MLLabel.allLabels() as! [MLLabel]
-        audioPlaylist = labels.filter {
-            let audioFiles = $0.files.filter {
-                if let file = $0 as? MLFile {
-                    return file.isSupportedAudioFile()
-                }
-                return false
-            }
-            return !audioFiles.isEmpty
-        }
-    }
-
-    private func videoPlaylistsFromVideos() {
-        let labels = MLLabel.allLabels() as! [MLLabel]
-        audioPlaylist = labels.filter {
-            let audioFiles = $0.files.filter {
-                if let file = $0 as? MLFile {
-                    return file.isShowEpisode() || file.isMovie() || file.isClip()
-                }
-                return false
-            }
-            return !audioFiles.isEmpty
-        }
-    }
-
-    private func moviesFromVideos() {
-        movies = foundVideos.filter { $0.isMovie() }
     }
 }
+//Todo: Move discoverer code here 
+
 
 // Todo: implement the remove
 //    - (void)removeMediaObjectFromFolder:(NSManagedObject *)managedObject

+ 44 - 0
Sources/VLCMediaSubcategory+VLCDragAndDrop.swift

@@ -0,0 +1,44 @@
+/*****************************************************************************
+ * VLCMediaSubcategory+VLCDragAndDrop.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2017 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Carola Nitz <caro # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+import Foundation
+
+@available(iOS 11.0, *)
+extension VLCMediaSubcategory: VLCDragAndDropManagerDelegate {
+
+    func dragAndDropManagerRequestsFile(manager: NSObject, atIndexPath indexPath: IndexPath) -> Any? {
+        return files[indexPath.row]
+    }
+
+    func dragAndDropManagerInsertItem(manager: NSObject, item: NSManagedObject, atIndexPath indexPath: IndexPath) {
+        if item as? MLLabel != nil && indexPath.row < files.count {
+            files.remove(at: indexPath.row)
+        }
+        // TODO: handle insertion
+        //files.insert(item, at: indexPath.row)
+    }
+
+    func dragAndDropManagerDeleteItem(manager: NSObject, atIndexPath indexPath: IndexPath) {
+        files.remove(at: indexPath.row)
+    }
+
+    func dragAndDropManagerCurrentSelection(manager: NSObject) -> AnyObject? {
+
+        //TODO: Handle playlists and Collections
+        fatalError()
+    }
+
+    func dragAndDropManagerRemoveFileFromFolder(manager: NSObject, file: NSManagedObject) {
+        //TODO: handle removing from playlists and Collections
+        fatalError()
+    }
+}

+ 1 - 1
Sources/VLCTabBarCoordinator.swift

@@ -98,7 +98,7 @@ class VLCTabbarCooordinator: NSObject, VLCMediaViewControllerDelegate {
 
     // MARK: - VLCMediaViewControllerDelegate
 
-    func mediaViewControllerDidSelectMediaObject(_ VLCMediaViewController: VLCMediaViewController, mediaObject: NSManagedObject) {
+    func mediaViewControllerDidSelectMediaObject(_ viewcontroller: UIViewController, mediaObject: NSManagedObject) {
         playMedia(media: mediaObject)
     }
 

+ 17 - 8
VLC.xcodeproj/project.pbxproj

@@ -11,6 +11,7 @@
 		26F1BFD01A770408001DF30C /* libMediaVLC.xml in Resources */ = {isa = PBXBuildFile; fileRef = 26F1BFCF1A770408001DF30C /* libMediaVLC.xml */; };
 		29125E5617492219003F03E5 /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 29125E5417492219003F03E5 /* index.html */; };
 		2915544317490D4A00B86CAD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2915544217490D4A00B86CAD /* Security.framework */; };
+		411DC0FD20F650B10044305E /* VLCMediaSubcategory+VLCDragAndDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412BE7521FC4947400ACCC42 /* VLCMediaSubcategory+VLCDragAndDrop.swift */; };
 		41251ED01FD0CF7900099110 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41251ECE1FD0CF7900099110 /* AppCoordinator.swift */; };
 		41273A3C1A955C4100A2EF77 /* VLCMigrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 41273A3A1A955C4100A2EF77 /* VLCMigrationViewController.m */; };
 		41273A3D1A955C4100A2EF77 /* VLCMigrationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 41273A3B1A955C4100A2EF77 /* VLCMigrationViewController.xib */; };
@@ -36,13 +37,12 @@
 		418B144720179C00000447AA /* MediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B144620179C00000447AA /* MediaViewController.swift */; };
 		418B144D20179C75000447AA /* VLCTabBarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B144C20179C74000447AA /* VLCTabBarCoordinator.swift */; };
 		418B145020179CB9000447AA /* LayoutAnchorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B144F20179CB9000447AA /* LayoutAnchorContainer.swift */; };
-		418B145620179DF2000447AA /* VLCMediaData+VLCDragAndDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B145520179DF2000447AA /* VLCMediaData+VLCDragAndDrop.swift */; };
 		418B145920179E50000447AA /* VLCDragAndDropManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B145820179E50000447AA /* VLCDragAndDropManager.swift */; };
 		4195747D206A92ED00393A42 /* RemoteNetworkDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4195747C206A92ED00393A42 /* RemoteNetworkDataSource.swift */; };
 		419A2C661F37A4B70069D224 /* VLCStringsForLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */; };
 		419A2C681F37A4B70069D224 /* VLCStringsForLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */; };
 		419D7F051F54176900AF69A2 /* VLCTimeNavigationTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 419D7F041F54176900AF69A2 /* VLCTimeNavigationTitleView.xib */; };
-		41A6948320EFAC090070151D /* MediaViewController+DataSourceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A6948220EFAC090070151D /* MediaViewController+DataSourceDelegate.swift */; };
+		41B0948520E6851200DE38AD /* VLCMediaSubcategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41B0948420E6851200DE38AD /* VLCMediaSubcategory.swift */; };
 		41B93C011A53833B00102E8B /* VLCProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B93C001A53833B00102E8B /* VLCProgressView.m */; };
 		41B93C051A53835300102E8B /* VLCCloudServiceCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B93C031A53835300102E8B /* VLCCloudServiceCell.m */; };
 		41B93C081A53853B00102E8B /* VLCCloudServiceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 41B93C071A53853B00102E8B /* VLCCloudServiceCell.xib */; };
@@ -494,7 +494,7 @@
 		41273A391A955C4100A2EF77 /* VLCMigrationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCMigrationViewController.h; path = Sources/VLCMigrationViewController.h; sourceTree = SOURCE_ROOT; };
 		41273A3A1A955C4100A2EF77 /* VLCMigrationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCMigrationViewController.m; path = Sources/VLCMigrationViewController.m; sourceTree = SOURCE_ROOT; };
 		41273A3B1A955C4100A2EF77 /* VLCMigrationViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = VLCMigrationViewController.xib; path = Sources/VLCMigrationViewController.xib; sourceTree = SOURCE_ROOT; };
-		412BE7521FC4947400ACCC42 /* VLCMediaData+VLCDragAndDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "VLCMediaData+VLCDragAndDrop.swift"; path = "Sources/VLCMediaData+VLCDragAndDrop.swift"; sourceTree = SOURCE_ROOT; };
+		412BE7521FC4947400ACCC42 /* VLCMediaSubcategory+VLCDragAndDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "VLCMediaSubcategory+VLCDragAndDrop.swift"; path = "Sources/VLCMediaSubcategory+VLCDragAndDrop.swift"; sourceTree = SOURCE_ROOT; };
 		413EC986201A329D00BF412F /* SortOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SortOption.swift; path = Sources/Coordinators/SortOption.swift; sourceTree = SOURCE_ROOT; };
 		413EC98A201B4F2B00BF412F /* PresentationTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationTheme.swift; sourceTree = "<group>"; };
 		4142AB4420A31EC500039380 /* VLCMediaDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VLCMediaDataSource.swift; path = Sources/VLCMediaDataSource.swift; sourceTree = "<group>"; };
@@ -569,7 +569,7 @@
 		4195747C206A92ED00393A42 /* RemoteNetworkDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNetworkDataSource.swift; sourceTree = "<group>"; };
 		419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCStringsForLocalization.m; sourceTree = "<group>"; };
 		419D7F041F54176900AF69A2 /* VLCTimeNavigationTitleView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = VLCTimeNavigationTitleView.xib; path = Resources/VLCTimeNavigationTitleView.xib; sourceTree = SOURCE_ROOT; };
-		41A6948220EFAC090070151D /* MediaViewController+DataSourceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "MediaViewController+DataSourceDelegate.swift"; path = "Sources/MediaViewController+DataSourceDelegate.swift"; sourceTree = SOURCE_ROOT; };
+		41B0948420E6851200DE38AD /* VLCMediaSubcategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCMediaSubcategory.swift; sourceTree = "<group>"; };
 		41B0BC861F73ED7D0063BA26 /* VLC for iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "VLC for iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		41B0BC8A1F73ED7D0063BA26 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		41B93BFF1A53833B00102E8B /* VLCProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCProgressView.h; path = Sources/VLCProgressView.h; sourceTree = SOURCE_ROOT; };
@@ -1249,7 +1249,7 @@
 				DD35678B1B67619700338947 /* WatchKit.framework */,
 				DD3567891B67618F00338947 /* CoreData.framework */,
 				416DEFF51FEEA76A00F4FC59 /* LayoutAnchorContainer.swift */,
-				412BE7521FC4947400ACCC42 /* VLCMediaData+VLCDragAndDrop.swift */,
+				412BE7521FC4947400ACCC42 /* VLCMediaSubcategory+VLCDragAndDrop.swift */,
 			);
 			name = "Recovered References";
 			sourceTree = "<group>";
@@ -1265,6 +1265,15 @@
 			name = Coordinators;
 			sourceTree = "<group>";
 		};
+		41B0948320E6844700DE38AD /* MediaSubcategory */ = {
+			isa = PBXGroup;
+			children = (
+				41B0948420E6851200DE38AD /* VLCMediaSubcategory.swift */,
+			);
+			name = MediaSubcategory;
+			path = Sources/MediaSubcategory;
+			sourceTree = "<group>";
+		};
 		41B0BC871F73ED7D0063BA26 /* VLC-iOS-UITests */ = {
 			isa = PBXGroup;
 			children = (
@@ -1537,6 +1546,7 @@
 		7D5F7AB81752658E006CCCFA /* Everything Playlist */ = {
 			isa = PBXGroup;
 			children = (
+				41B0948320E6844700DE38AD /* MediaSubcategory */,
 				4142AB4420A31EC500039380 /* VLCMediaDataSource.swift */,
 				418B145520179DF2000447AA /* VLCMediaData+VLCDragAndDrop.swift */,
 				418B145820179E50000447AA /* VLCDragAndDropManager.swift */,
@@ -1941,7 +1951,6 @@
 				7D37849D183A98DD009EE944 /* VLCThumbnailsCache.m */,
 				DD7110EE1AF38B2B00854776 /* MLMediaLibrary+playlist.h */,
 				DD7110EF1AF38B2B00854776 /* MLMediaLibrary+playlist.m */,
-				41A6948220EFAC090070151D /* MediaViewController+DataSourceDelegate.swift */,
 			);
 			name = Library;
 			sourceTree = "<group>";
@@ -3161,6 +3170,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				411DC0FD20F650B10044305E /* VLCMediaSubcategory+VLCDragAndDrop.swift in Sources */,
 				4144C4661A0ED6C700918C89 /* Reachability.m in Sources */,
 				DD846BDF1D1FCCCD00699D17 /* VLCNetworkLoginDataSource.m in Sources */,
 				41251ED01FD0CF7900099110 /* AppCoordinator.swift in Sources */,
@@ -3188,7 +3198,6 @@
 				9BADAF45185FBD9D00108BD8 /* VLCFrostedGlasView.m in Sources */,
 				DDC10BE41AEE8EA700890DC3 /* VLCTimeNavigationTitleView.m in Sources */,
 				DD870E9A1CEF929A00BBD4FE /* VLCNetworkLoginViewFieldCell.m in Sources */,
-				418B145620179DF2000447AA /* VLCMediaData+VLCDragAndDrop.swift in Sources */,
 				4195747D206A92ED00393A42 /* RemoteNetworkDataSource.swift in Sources */,
 				DD3EAC041BE153B4003668DA /* VLCNetworkImageView.m in Sources */,
 				E0C04F951A25B4410080331A /* VLCDocumentPickerController.m in Sources */,
@@ -3214,6 +3223,7 @@
 				7D378492183A98BF009EE944 /* VLCExternalDisplayController.m in Sources */,
 				4170152C209A1D3600802E44 /* MediaSubcategoryViewController.swift in Sources */,
 				41F9BC7C1F4F20E400268461 /* VLCTrackSelectorView.m in Sources */,
+				41B0948520E6851200DE38AD /* VLCMediaSubcategory.swift in Sources */,
 				7D378499183A98D1009EE944 /* VLCPlaylistCollectionViewCell.m in Sources */,
 				DD8F84311B00EB3B0009138A /* VLCPlaybackController+MediaLibrary.m in Sources */,
 				418B144720179C00000447AA /* MediaViewController.swift in Sources */,
@@ -3234,7 +3244,6 @@
 				7D3784AE183A9906009EE944 /* VLCDropboxTableViewController.m in Sources */,
 				7D3784BE183A9938009EE944 /* UIBarButtonItem+Theme.m in Sources */,
 				DD3EFF3B1BDEBCE500B68579 /* VLCNetworkServerBrowserVLCMedia.m in Sources */,
-				41A6948320EFAC090070151D /* MediaViewController+DataSourceDelegate.swift in Sources */,
 				41B93C051A53835300102E8B /* VLCCloudServiceCell.m in Sources */,
 				7D4625881A5614A1001A80B4 /* VLCEqualizerView.m in Sources */,
 				7DF9352F1958AB0600E60FD4 /* UIColor+Presets.m in Sources */,