From 4d0844d5e2447fc99552064d1d60d5f8027ee3a1 Mon Sep 17 00:00:00 2001 From: marcoschmickler Date: Mon, 24 Jan 2022 19:49:05 +0100 Subject: [PATCH] Refactor VIDEOFOLDER --- kplayer.xcodeproj/project.pbxproj | 8 + kplayer/AppDelegate.swift | 2 +- kplayer/core/DatabaseManager.swift | 7 +- kplayer/core/ItemModel.swift | 2 +- kplayer/core/ItemType.swift | 15 +- kplayer/core/LocalManager.swift | 47 +++- kplayer/core/MediaItem.swift | 7 +- kplayer/core/NetworkManager.swift | 6 +- kplayer/detail/BrowserController.swift | 12 -- .../detail/DetailViewController+Show.swift | 143 ++++++++++++ kplayer/detail/DetailViewController.swift | 180 ++-------------- kplayer/master/MasterViewController.swift | 203 ++++++++---------- kplayer/master/NetworkDelegate.swift | 13 +- kplayer/util/UIViewController+Alert.swift | 21 ++ 14 files changed, 358 insertions(+), 308 deletions(-) create mode 100644 kplayer/detail/DetailViewController+Show.swift create mode 100644 kplayer/util/UIViewController+Alert.swift diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index f30919a..654a324 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */; }; 1C73666A07CF2416B1B8D3F0 /* KSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C94157754DE1C808173 /* KSettingsModel.swift */; }; 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; }; + 1C7367084839D2E8B180DB74 /* UIViewController+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360295486647982CFEACF /* UIViewController+Alert.swift */; }; 1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; }; 1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; }; 1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73625012D50E457D18A785 /* kplayer.js */; }; @@ -46,6 +47,7 @@ 1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; }; 1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; }; 1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; }; + 1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */; }; 1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */; }; 1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */; }; 1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736677D4EF2437358B2387 /* Utility.swift */; }; @@ -82,6 +84,7 @@ /* Begin PBXFileReference section */ 1C73602350ACE2436736F981 /* BrowserController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserController.swift; sourceTree = ""; }; + 1C7360295486647982CFEACF /* UIViewController+Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Alert.swift"; sourceTree = ""; }; 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = ""; }; 1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; }; 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = ""; }; @@ -118,6 +121,7 @@ 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsView.swift; sourceTree = ""; }; 1C736AE5021E3D985FE3402D /* KSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettings.swift; sourceTree = ""; }; + 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DetailViewController+Show.swift"; sourceTree = ""; }; 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaModel.swift; sourceTree = ""; }; 1C736B794396F2E50387B8F2 /* stringutil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = stringutil.swift; sourceTree = ""; }; 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = ""; }; @@ -179,6 +183,7 @@ 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */, 1C73602350ACE2436736F981 /* BrowserController.swift */, 1C7366C09381DC0052B52B69 /* EditItemView.swift */, + 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */, ); path = detail; sourceTree = ""; @@ -233,6 +238,7 @@ 1C736DCD945ABAE984FF43EF /* KNetworkProtocol.swift */, 1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */, 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */, + 1C7360295486647982CFEACF /* UIViewController+Alert.swift */, ); path = util; sourceTree = ""; @@ -574,6 +580,8 @@ 1C7365C59F72C29EA41C8717 /* SVideoModel.swift in Sources */, 1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */, 1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */, + 1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */, + 1C7367084839D2E8B180DB74 /* UIViewController+Alert.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/AppDelegate.swift b/kplayer/AppDelegate.swift index e1a669c..eca8e0d 100644 --- a/kplayer/AppDelegate.swift +++ b/kplayer/AppDelegate.swift @@ -49,7 +49,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT) web.loaded = true - let google = MediaItem(name: "google", path:"www.google.de", root: "", type: ItemType.DETAILS) + let google = MediaItem(name: "google", path:"www.google.de", root: "", type: ItemType.PICFOLDER) google.loaded = true web.children = [ google ] let urlPath = Bundle.main.url(forResource: "links", withExtension: "html") diff --git a/kplayer/core/DatabaseManager.swift b/kplayer/core/DatabaseManager.swift index 021d021..6c3d214 100644 --- a/kplayer/core/DatabaseManager.swift +++ b/kplayer/core/DatabaseManager.swift @@ -158,13 +158,16 @@ class DatabaseManager { let results = try! managedObjectContext.fetch(fetchRequest) for t in results { - let tag = MediaItem(name: t.name!, path: t.name!, root: "tags", type: ItemType.VIDEO) + let tag = MediaItem(name: t.name!, path: t.name!, root: "tags", type: ItemType.TAG) tag.loaded = true let snapshots = t.tagged as! Set for s in snapshots { let i = s.item! let sitem = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.VIDEO) + sitem.loaded = true + sitem.parent = tag + let c = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.SNAPSHOT) c.time = s.time c.length = s.length @@ -176,6 +179,8 @@ class DatabaseManager { c.scale = s.scale c.rating = Int(s.rating) c.thumbUrl = s.thumb + c.indexId = Int(s.index) + c.parent = sitem sitem.children.append(c) tag.children.append(sitem) diff --git a/kplayer/core/ItemModel.swift b/kplayer/core/ItemModel.swift index f0dfa43..93e3ec3 100644 --- a/kplayer/core/ItemModel.swift +++ b/kplayer/core/ItemModel.swift @@ -32,7 +32,7 @@ class ItemModel { item.parent = folder if (item.isVideo()) { - item.parent?.type = ItemType.VIDEO + item.parent?.type = ItemType.VIDEOFOLDER } } diff --git a/kplayer/core/ItemType.swift b/kplayer/core/ItemType.swift index fe7a854..c045761 100644 --- a/kplayer/core/ItemType.swift +++ b/kplayer/core/ItemType.swift @@ -31,6 +31,16 @@ enum ItemType: String, Codable, CustomStringConvertible { */ case FOLDER = "folder" + /** + Repräsentiert ein Video. Hat keine Unterordner. Die Children sind Snapshots. + */ + case VIDEOFOLDER = "videofolder" + + /** + Repräsentiert einen Ordner, der Videos oder Bilder-Ordner enthält. Wird im DetailViewController angezeigt. + */ + case PICFOLDER = "picfolder" + /** Repräsentiert ein Video. Hat keine Unterordner. Die Children sind Snapshots. */ @@ -61,11 +71,6 @@ enum ItemType: String, Codable, CustomStringConvertible { */ case PICS = "pics" - /** - Repräsentiert einen Ordner, der Videos oder Bilder-Ordner enthält. Wird im DetailViewController angezeigt. - */ - case DETAILS = "details" - var description: String { return self.rawValue } diff --git a/kplayer/core/LocalManager.swift b/kplayer/core/LocalManager.swift index e7e3114..9a54ea9 100644 --- a/kplayer/core/LocalManager.swift +++ b/kplayer/core/LocalManager.swift @@ -51,6 +51,8 @@ class LocalManager { } func saveFavDir(url: URL, item: MediaItem) -> Void { + DatabaseManager.sharedInstance.saveItemMetaData(item) + for c in item.children { let t = c.time let ms = Int(t * 1000) @@ -105,7 +107,7 @@ class LocalManager { var itemsMap = Dictionary() - let fi = MediaItem(name: "images", path: "", root: "fav", type: ItemType.DETAILS) + let fi = MediaItem(name: "images", path: "", root: "fav", type: ItemType.PICFOLDER) fi.local = true fi.loaded = true @@ -222,6 +224,49 @@ class LocalManager { completionHandler(res) } + /* + exclude mp4 from backup + create images folder + extract zip to images folder + */ + func prepareLocalFolder() { + do { + let i = try FileHelper.createDir(name: "incoming") + + let files = FileHelper.listFiles(name: "") + for f in files { + if f.pathExtension == "mp4" { + var dest = i.appendingPathComponent(f.lastPathComponent) + try FileManager.default.moveItem(at: f, to: dest) + try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true) + } + } + } catch { + } + + do { + let i = try FileHelper.createDir(name: "images") + + let files = FileHelper.listFiles(name: "download") + for f in files { + print(f) + if f.pathExtension == "zip" { + var dest = i.appendingPathComponent(f.lastPathComponent) + + do { + try FileManager.default.createDirectory(at: dest, withIntermediateDirectories: true, attributes: nil) + try FileManager.default.unzipItem(at: f, to: dest) + } catch { + print("Extraction of ZIP archive failed with error:\(error)") + } + try FileManager.default.removeItem(at: f) + try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true) + } + } + } catch { + } + } + // old func loadFav2Dirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { let files = FileHelper.listFiles(name: "1") diff --git a/kplayer/core/MediaItem.swift b/kplayer/core/MediaItem.swift index 21c0b4d..4d42fd0 100644 --- a/kplayer/core/MediaItem.swift +++ b/kplayer/core/MediaItem.swift @@ -303,15 +303,16 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable { } func isPic() -> Bool { - type == ItemType.PICS || type == ItemType.DETAILS + type == ItemType.PICS || type == ItemType.PICFOLDER } func isVideo() -> Bool { - type == ItemType.VIDEO || type == ItemType.SNAPSHOT + type == ItemType.VIDEOFOLDER || type == ItemType.VIDEO || type == ItemType.SNAPSHOT } func isDetails() -> Bool { - isWeb() || isPic() || isVideo() + type == ItemType.VIDEOFOLDER || type == ItemType.PICFOLDER || type == ItemType.WEB || type == ItemType.TAG + // isWeb() || isPic() || isVideo() } func isFolder() -> Bool { diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index d6a0af2..781cd7c 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -169,10 +169,10 @@ class NetworkManager { var t = ItemType.FOLDER if (type == "video") { - t = ItemType.VIDEO + t = ItemType.VIDEOFOLDER } if (type == "pictures") { - t = ItemType.DETAILS + t = ItemType.PICFOLDER } if (type == "web") { t = ItemType.WEB @@ -222,7 +222,7 @@ class NetworkManager { return } - if (item.type != ItemType.VIDEO && item.type != ItemType.DETAILS) { + if (item.type != ItemType.VIDEOFOLDER && item.type != ItemType.PICFOLDER) { return } diff --git a/kplayer/detail/BrowserController.swift b/kplayer/detail/BrowserController.swift index 3e00c01..4c39137 100644 --- a/kplayer/detail/BrowserController.swift +++ b/kplayer/detail/BrowserController.swift @@ -333,16 +333,4 @@ print(url?.absoluteString) } } - - func showAlert(title:String, message:String ) { - - let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) - - let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil) - alertVC.addAction(okAction) - - DispatchQueue.main.async() { () -> Void in - self.present(alertVC, animated: true, completion: nil) - } - } } diff --git a/kplayer/detail/DetailViewController+Show.swift b/kplayer/detail/DetailViewController+Show.swift new file mode 100644 index 0000000..367ebdb --- /dev/null +++ b/kplayer/detail/DetailViewController+Show.swift @@ -0,0 +1,143 @@ +// +// Created by Marco Schmickler on 23.01.22. +// Copyright (c) 2022 Marco Schmickler. All rights reserved. +// + +import Foundation +import UIKit +import SwiftUI + +extension DetailViewController { + + func showDetails(sectionItem: MediaItem, selectedItem: MediaItem) { + if sectionItem.isVideo() { + showVideo(selectedItem: selectedItem) + } else if sectionItem.isPic() { + showPhotos(sectionItem.children) + } else if sectionItem.isWeb() { + showWeb(selectedItem: selectedItem) + } else if sectionItem.type == ItemType.DOWNLOAD { + showDownload(sectionItem: sectionItem) + } + } + + func showPhotos(_ im: [MediaItem]) { + let pc = MediaPhotoController() + + pc.items = im + pc.completionHandler = { + self.dismiss(animated: true, completion: nil); + } + let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. + navController.modalPresentationStyle = .fullScreen + + self.present(navController, animated: false, completion: nil) + } + + func showWeb(selectedItem: MediaItem) { + if selectedItem.name.contains("strip") { + + let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://") + UIApplication.shared.open(URL(string: chromeURL)!) + + return + } + let pc = BrowserController() + pc.setCurrentItem(item: selectedItem) + + pc.completionHandler = { + self.dismiss(animated: true, completion: nil); + } + let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. + navController.modalPresentationStyle = .fullScreen + + self.present(navController, animated: false, completion: nil) + } + + func showVideo(selectedItem: MediaItem) { + var se = selectedItem + var children = [MediaItem]() + var clonedChildren = [MediaItem]() + var baseItem = selectedItem + + if baseItem.type == ItemType.SNAPSHOT { + baseItem = selectedItem.parent! + } + + children = baseItem.children + clonedChildren = baseItem.clone().children + + let model = SVideoModel(allItems: children, currentSnapshot: se, baseItem: baseItem) + + model.edit = delegate!.settings().edit + model.loop = delegate!.settings().autoloop + model.zoomed = delegate!.settings().zoomed + + let player = SVideoPlayer(completionHandler: { saved in + if saved { + baseItem.children = model.allItems + self.delegate!.saveItem(selectedItem: baseItem) + } + else { + baseItem.children = clonedChildren + } + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + + self.dismiss(animated: true, completion: nil); + }, model: model) + player + + let pc = UIHostingController(rootView: player) + pc.view.backgroundColor = .black + + getWindow().rootViewController!.definesPresentationContext = true + pc.modalPresentationStyle = .overCurrentContext + getWindow().rootViewController!.present(pc, animated: true) + } + + func showDownload(sectionItem: MediaItem) { + let cancelAlert = UIAlertController(title: "Abort Download", message: "All data will be lost.", preferredStyle: UIAlertController.Style.alert) + + cancelAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in + sectionItem.cancelled = true + if sectionItem.length == 1.0 { + NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: sectionItem.name) + } + })) + + cancelAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in + print("Handle Cancel Logic here") + })) + + self.present(cancelAlert, animated: true) + } + + func showAllVideos(_ item: MediaItem) { + let composition = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO) + + for d in item.children { + if d.children.isEmpty { + let clone = d.clone() + clone.parent = composition + clone.type = ItemType.SNAPSHOT + composition.children.append(clone) + } + else { + for c in d.children { + let clone = c.clone() + clone.parent = composition + composition.children.append(clone) + } + } + } + + showVideo(selectedItem: composition.children[0]) + } + + func getWindow() -> UIWindow { + let delegate2 = UIApplication.shared.delegate! + return delegate2.window as! UIWindow + } +} \ No newline at end of file diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index 9bcf04e..7d48af9 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -105,41 +105,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout @objc func fileBrowser() { let d = FileHelper.getDocumentsDirectory() - do { - let i = try FileHelper.createDir(name: "incoming") - - let files = FileHelper.listFiles(name: "") - for f in files { - if f.pathExtension == "mp4" { - var dest = i.appendingPathComponent(f.lastPathComponent) - try FileManager.default.moveItem(at: f, to: dest) - try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true) - } - } - } catch { - } - - do { - let i = try FileHelper.createDir(name: "images") - - let files = FileHelper.listFiles(name: "download") - for f in files { - print(f) - if f.pathExtension == "zip" { - var dest = i.appendingPathComponent(f.lastPathComponent) - - do { - try FileManager.default.createDirectory(at: dest, withIntermediateDirectories: true, attributes: nil) - try FileManager.default.unzipItem(at: f, to: dest) - } catch { - print("Extraction of ZIP archive failed with error:\(error)") - } - try FileManager.default.removeItem(at: f) - try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true) - } - } - } catch { - } + LocalManager.sharedInstance.prepareLocalFolder() let fileBrowser = FileBrowser(initialPath: d, allowEditing: true) fileBrowser.modalPresentationStyle = .fullScreen @@ -147,11 +113,13 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout present(fileBrowser, animated: true, completion: nil) } + // Favorites Button @objc func favorites() { showFavoritesOnly = !showFavoritesOnly collectionView.reloadData() } + // Edit Settings Button @objc func settings() { let kv = KSettingsView(kSettings: LocalManager.sharedInstance.settings, completionHandler: { self.dismiss(animated: true, completion: nil); @@ -163,12 +131,12 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout present(navController, animated: false, completion: nil) } + // Overview Button @objc func overview() { - var i = [MediaItem]() if let d = detailItem { - showAll(d) + showAllVideos(d) return let pc = MediaPhotoController() @@ -192,30 +160,8 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } } - private func showAll(_ item: MediaItem) { - let composition = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO) - - for d in item.children { - if d.children.isEmpty { - let clone = d.clone() - clone.parent = composition - clone.type = ItemType.SNAPSHOT - composition.children.append(clone) - } - else { - for c in d.children { - let clone = c.clone() - clone.parent = composition - composition.children.append(clone) - } - } - } - - showVideo(selectedItem: composition.children[0]) - } @objc func refreshItems(_ notification: Notification) { - if notification.object == nil { if self.collectionView != nil { self.collectionView.reloadData() @@ -265,6 +211,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } } + // delete item @objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { if (gestureRecognizer.state != UIGestureRecognizer.State.ended) { return; @@ -332,6 +279,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout return 0 } + // show cell func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ItemCell @@ -351,6 +299,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout return cell } + // show Section header func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { @@ -370,17 +319,17 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } } + // select item + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let detail: MediaItem = self.detailItem { print(detail.toJSON()) - // NetworkManager.sharedInstance.saveFavDir(name: "fav.json", item: detail) let sectionItem = detail.children[indexPath.section] currentItem = sectionItem var selectedItem = currentItem! let weiter: () -> Void = { - if (sectionItem.type == ItemType.VIDEO || sectionItem.loaded) { if indexPath.item >= sectionItem.children.count { print(sectionItem.name) @@ -389,119 +338,14 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout print(selectedItem.name) } } - - if sectionItem.isVideo() { - self.showVideo(selectedItem: selectedItem) - } else if sectionItem.isPic() { - self.showPhotos(sectionItem.children) - } else if sectionItem.isWeb() { - self.showWeb(selectedItem: selectedItem) - } - - if sectionItem.type == ItemType.DOWNLOAD { - let cancelAlert = UIAlertController(title: "Abort Download", message: "All data will be lost.", preferredStyle: UIAlertController.Style.alert) - - cancelAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in - sectionItem.cancelled = true - if sectionItem.length == 1.0 { - NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: sectionItem.name) - } - })) - - cancelAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in - print("Handle Cancel Logic here") - })) - - self.present(cancelAlert, animated: true) - } + self.showDetails(sectionItem: sectionItem, selectedItem: selectedItem) } delegate!.loadDetails(selectedItem: currentItem!, completionHandler: weiter) - } } - func showPhotos(_ im: [MediaItem]) { - let pc = MediaPhotoController() - - pc.items = im - pc.completionHandler = { - self.dismiss(animated: true, completion: nil); - } - let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. - navController.modalPresentationStyle = .fullScreen - - self.present(navController, animated: false, completion: nil) - } - - func showWeb(selectedItem: MediaItem) { - if selectedItem.name.contains("strip") { - - let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://") - UIApplication.shared.open(URL(string: chromeURL)!) - - return - } - let pc = BrowserController() - pc.setCurrentItem(item: selectedItem) - - pc.completionHandler = { - self.dismiss(animated: true, completion: nil); - } - let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. - navController.modalPresentationStyle = .fullScreen - - self.present(navController, animated: false, completion: nil) - } - - func getWindow() -> UIWindow { - - let delegate2 = UIApplication.shared.delegate! - return delegate2.window as! UIWindow - } - - func showVideo(selectedItem: MediaItem) { - var se = selectedItem - var children = [MediaItem]() - var clonedChildren = [MediaItem]() - var baseItem = selectedItem - - if baseItem.type == ItemType.SNAPSHOT { - baseItem = selectedItem.parent! - } - - children = baseItem.children - clonedChildren = baseItem.clone().children - - let model = SVideoModel(allItems: children, currentSnapshot: se, baseItem: baseItem) - - model.edit = delegate!.settings().edit - model.loop = delegate!.settings().autoloop - model.zoomed = delegate!.settings().zoomed - - let player = SVideoPlayer(completionHandler: { saved in - if saved { - baseItem.children = model.allItems - self.delegate!.saveItem(selectedItem: baseItem) - } - else { - baseItem.children = clonedChildren - } - - self.collectionView.reloadData() - self.collectionView.collectionViewLayout.invalidateLayout() - - self.dismiss(animated: true, completion: nil); - }, model: model) - player - - let pc = UIHostingController(rootView: player) - pc.view.backgroundColor = .black - - getWindow().rootViewController!.definesPresentationContext = true - pc.modalPresentationStyle = .overCurrentContext - getWindow().rootViewController!.present(pc, animated: true) - } + // Drag and drop func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { if let detail: MediaItem = self.detailItem { diff --git a/kplayer/master/MasterViewController.swift b/kplayer/master/MasterViewController.swift index 58c3ff6..6befdc6 100644 --- a/kplayer/master/MasterViewController.swift +++ b/kplayer/master/MasterViewController.swift @@ -15,7 +15,7 @@ typealias Weiter = ([MediaItem]) -> Void protocol MasterDelegate : DetailDelegate { func loadFolder(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) - func loadItem(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) + func loadItemDetails(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) } class MasterViewController: UITableViewController, UISearchResultsUpdating, UITableViewDropDelegate, UIDocumentPickerDelegate { @@ -58,7 +58,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa if str.hasSuffix("*") { let txt = str[str.startIndex ..< str.index(str.endIndex, offsetBy: -1)] if let p = model.items[0].parent { - let neu = MediaItem(name: String(txt), path: p.path + "*" + txt, root: p.root, type: ItemType.DETAILS) + let neu = MediaItem(name: String(txt), path: p.path + "*" + txt, root: p.root, type: ItemType.PICFOLDER) model.items.append(neu) neu.parent = p self.tableView.reloadData() @@ -66,68 +66,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa } } - func doAuthenticate() { - let authenticationContext = LAContext() - var error : NSError? - - guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { - self.showAlert(title: "Error", message: "This device does not have a TouchID sensor.") - return; - } - - authenticationContext.evaluatePolicy( - .deviceOwnerAuthenticationWithBiometrics, - localizedReason: "Only awesome people are allowed", - reply: { [unowned self] (success, error) -> Void in - LocalManager.sharedInstance.authenticated = success - }) - } - - func showAlert(title:String, message:String ) { - - let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) - - let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil) - alertVC.addAction(okAction) - - DispatchQueue.main.async() { () -> Void in - self.present(alertVC, animated: true, completion: nil) - } - - } - - func createFolder(_ item: MediaItem) { - let ac = UIAlertController(title: "Enter new folder name", message: nil, preferredStyle: .alert) - ac.addTextField() - - let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in - let answer = ac.textFields![0].text! - - if (item.root == "/tags") { - DatabaseManager.sharedInstance.createTag(answer) - let m = MediaItem(name: answer, path: answer, root: "tags", type: ItemType.TAG) - m.local = true - item.children.append(m) - m.parent = item - } - else { - do { - try FileHelper.createDir(name: answer) - item.name = answer - item.path = answer - self.tableView.reloadData() - } catch { - print(error) - } - } - } - - ac.addAction(submitAction) - ac.addAction(UIAlertAction(title: "cancel", style: .cancel)) - - present(ac, animated: true) - } - /** * Item ausgewaehlt */ @@ -180,39 +118,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa } } - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - print("hello") - - if urls.count > 0 { - let url = urls[0] - - guard url.startAccessingSecurityScopedResource() else { - // Handle the failure here. - return - } - - currentSelection?.externalURL = url.absoluteString - LocalManager.sharedInstance.externalURL = url.absoluteString - - delegate!.loadFolder(selectedItem: currentSelection!) { - (neu) in - if neu.isFolder() { - self.gotoNextFolder(neu) - } else { - self.gotoDetails(neu) - } - } - } - } - - func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { - } - - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { - print("hello") - } - - fileprivate func gotoNextFolder(_ selectedItem: MediaItem) { + func gotoNextFolder(_ selectedItem: MediaItem) { let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) let vc = mainStoryboard.instantiateViewController(withIdentifier: "mastertable") as! MasterViewController vc.navigationItem.title = selectedItem.name; @@ -221,21 +127,53 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa navigationController!.pushViewController(vc, animated: true) } - fileprivate func gotoDetails(_ selectedItem: MediaItem) { + func gotoDetails(_ selectedItem: MediaItem) { let weiter: (MediaItem) -> Void = { (m) in self.performSegue(withIdentifier: "showDetail", sender: self) } - delegate!.loadItem(selectedItem: selectedItem, completionHandler: weiter) + delegate!.loadItemDetails(selectedItem: selectedItem, completionHandler: weiter) + } + + func createFolder(_ item: MediaItem) { + let ac = UIAlertController(title: "Enter new folder name", message: nil, preferredStyle: .alert) + ac.addTextField() + + let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in + let answer = ac.textFields![0].text! + + if (item.root == "/tags") { + DatabaseManager.sharedInstance.createTag(answer) + let m = MediaItem(name: answer, path: answer, root: "tags", type: ItemType.TAG) + m.local = true + item.children.append(m) + m.parent = item + } + else { + do { + try FileHelper.createDir(name: answer) + item.name = answer + item.path = answer + self.tableView.reloadData() + } catch { + print(error) + } + } + } + + ac.addAction(submitAction) + ac.addAction(UIAlertAction(title: "cancel", style: .cancel)) + + present(ac, animated: true) } override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { return false; } - // MARK: - Segues + // MARK: - Segues override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let indexPath = self.tableView.indexPathForSelectedRow { @@ -269,10 +207,18 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa return model.items.count } + // Show Cell override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) - self.configureCell(cell, atIndexPath: indexPath) + let item = model.items[indexPath.row] + + if (item.path.count == 0) { + cell.textLabel!.text = "'" + item.name + } else { + cell.textLabel!.text = item.path + } + cell.accessoryType = UITableViewCell.AccessoryType.none return cell } @@ -287,17 +233,40 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa } } - func configureCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) { - let object = model.items[indexPath.row] - if (object.path.count == 0) { - cell.textLabel!.text = "'" + object.name - } else { - cell.textLabel!.text = object.path + // Document Picker + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + print("hello") + + if urls.count > 0 { + let url = urls[0] + + guard url.startAccessingSecurityScopedResource() else { + // Handle the failure here. + return + } + + currentSelection?.externalURL = url.absoluteString + LocalManager.sharedInstance.externalURL = url.absoluteString + + delegate!.loadFolder(selectedItem: currentSelection!) { + (neu) in + if neu.isFolder() { + self.gotoNextFolder(neu) + } else { + self.gotoDetails(neu) + } + } } + } - cell.accessoryType = UITableViewCell.AccessoryType.none + func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { + print("hello") } + // Drag and drop func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal { if tableView.indexPathForSelectedRow == destinationIndexPath { @@ -398,5 +367,23 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa } + + // Authentication + func doAuthenticate() { + let authenticationContext = LAContext() + var error : NSError? + + guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { + showAlert(title: "Error", message: "This device does not have a TouchID sensor.") + return; + } + + authenticationContext.evaluatePolicy( + .deviceOwnerAuthentication, + localizedReason: "Only awesome people are allowed", + reply: { [unowned self] (success, error) -> Void in + LocalManager.sharedInstance.authenticated = success + }) + } } diff --git a/kplayer/master/NetworkDelegate.swift b/kplayer/master/NetworkDelegate.swift index ce0629f..4bff6bb 100644 --- a/kplayer/master/NetworkDelegate.swift +++ b/kplayer/master/NetworkDelegate.swift @@ -6,13 +6,12 @@ import Foundation class NetworkDelegate: MasterDelegate, DetailDelegate { - func loadItem(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) { + func loadItemDetails(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) { if selectedItem.loaded { completionHandler(selectedItem) return } - let p = selectedItem.superRoot() let dir = selectedItem.encodedDir! let weiter:Weiter = { @@ -21,10 +20,10 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { completionHandler(selectedItem) } - if selectedItem.isVideo() { + if selectedItem.type == ItemType.VIDEOFOLDER { NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter) } - else if selectedItem.isDetails() { + else if selectedItem.type == ItemType.PICFOLDER { NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter) } else { @@ -54,7 +53,11 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { if selectedItem.type == ItemType.TAGROOT { - DatabaseManager.sharedInstance.loadTags(completionHandler: weiter) + DatabaseManager.sharedInstance.loadTags(completionHandler: { + c in + selectedItem.children = c + completionHandler(selectedItem) + }) return } diff --git a/kplayer/util/UIViewController+Alert.swift b/kplayer/util/UIViewController+Alert.swift new file mode 100644 index 0000000..478a9a8 --- /dev/null +++ b/kplayer/util/UIViewController+Alert.swift @@ -0,0 +1,21 @@ +// +// Created by Marco Schmickler on 23.01.22. +// Copyright (c) 2022 Marco Schmickler. All rights reserved. +// + +import Foundation +import UIKit + +extension UIViewController { + func showAlert(title:String, message:String ) { + + let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) + + let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil) + alertVC.addAction(okAction) + + DispatchQueue.main.async() { () -> Void in + self.present(alertVC, animated: true, completion: nil) + } + } +}