diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index f1d7467..c674ca2 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; }; 1C7367FA10AE13598FDDE865 /* BMPlayerProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365F45D765A218FFC100F /* BMPlayerProtocols.swift */; }; 1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */; }; + 1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364709899FF62774B0199 /* VideoHelper.swift */; }; 1C73688D13E5A804880C8768 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */; }; 1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B794396F2E50387B8F2 /* stringutil.swift */; }; 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; }; @@ -77,6 +78,7 @@ 1C736253AB7A95EA41B605B7 /* ItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemModel.swift; sourceTree = ""; }; 1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = ""; }; 1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = ""; }; + 1C7364709899FF62774B0199 /* VideoHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoHelper.swift; sourceTree = ""; }; 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewControllerExtensions.swift; sourceTree = ""; }; 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDelegate.swift; sourceTree = ""; }; 1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerItem.swift; sourceTree = ""; }; @@ -181,6 +183,7 @@ 1C736B794396F2E50387B8F2 /* stringutil.swift */, 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */, 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */, + 1C7364709899FF62774B0199 /* VideoHelper.swift */, ); path = util; sourceTree = ""; @@ -496,6 +499,7 @@ 1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */, 1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */, 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */, + 1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/AppDelegate.swift b/kplayer/AppDelegate.swift index 1cf56a2..0207c7e 100644 --- a/kplayer/AppDelegate.swift +++ b/kplayer/AppDelegate.swift @@ -42,10 +42,10 @@ google MediaItem(name: "knk_archiv2", path:"", root: "/srv/samba/ren/knk_archiv2", type: ItemType.VIDEOROOT), MediaItem(name: "knk_archiv3", path:"", root: "/srv/samba/ren/knk_archiv3", type: ItemType.VIDEOROOT), MediaItem(name: "knk_archiv4", path:"", root: "/srv/samba/ren/knk_archiv4", type: ItemType.VIDEOROOT), + MediaItem(name: "real", path:"", root: "/srv/samba/ren/real", type: ItemType.VIDEOROOT), MediaItem(name: "fetish", path:"", root: "/srv/samba/ren/fetish", type: ItemType.VIDEOROOT), MediaItem(name: "fjoy", path:"", root: "/srv/samba/ren/fjoy", type: ItemType.PICROOT), MediaItem(name: "heg", path:"", root: "/srv/samba/ren/heg", type: ItemType.PICROOT), - MediaItem(name: "hegvid", path:"", root: "/srv/samba/ren/heg/videos", type: ItemType.VIDEOROOT), MediaItem(name: "ten", path:"", root: "/srv/samba/ren/ten", type: ItemType.PICROOT), MediaItem(name: "leg", path:"", root: "/srv/samba/ren/leg", type: ItemType.PICROOT), MediaItem(name: "ang", path:"", root: "/srv/samba/ren/ang", type: ItemType.PICROOT), @@ -56,6 +56,12 @@ google web, ] + NetworkManager.sharedInstance.alive() + + Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { (t) in + NetworkManager.sharedInstance.alive() + } + return true } diff --git a/kplayer/core/ItemModel.swift b/kplayer/core/ItemModel.swift index 26e3b39..02ddaa1 100644 --- a/kplayer/core/ItemModel.swift +++ b/kplayer/core/ItemModel.swift @@ -10,12 +10,19 @@ class ItemModel { var itemsMap = Dictionary() func addItem(_ item: MediaItem) { + if item.path == "" { + item.index = items.count + items.append(item) + return + } + let path = item.encodedDir! var folder = itemsMap[path] if folder == nil { folder = MediaItem(name: item.path, path: item.path, root: item.root, type: ItemType.FOLDER) folder!.loaded = true + folder!.local = item.local itemsMap[path] = folder! items.append(folder!) } @@ -30,4 +37,36 @@ class ItemModel { itemsMap.removeAll() } + func sortItems(selectedItem: MediaItem, children: [MediaItem]) { + for f in children { + addItem(f) + } + selectedItem.children = items + selectedItem.loaded = false + + if selectedItem.path.contains("*") { + selectedItem.loaded = true + } else + { + var all: [MediaItem]? + + for f in selectedItem.children { + print(f.path) + if f.type == ItemType.FOLDER && f.path == "" { + all = f.children + break + } + } + + if all != nil { + selectedItem.children = all! + selectedItem.loaded = false + + for f in all! { + f.loaded = false + f.parent = selectedItem + } + } + } + } } diff --git a/kplayer/core/MediaItem.swift b/kplayer/core/MediaItem.swift index 5005adb..7f074d1 100644 --- a/kplayer/core/MediaItem.swift +++ b/kplayer/core/MediaItem.swift @@ -42,12 +42,15 @@ class MediaItem: CustomDebugStringConvertible { var loop = true var thumbUrl: String? + var local = false + convenience init(model: MediaModel) { self.init(name: model.name, path: model.path, root: model.root, type: model.type) self.time = model.time self.length = model.length self.loop = model.loop + self.thumbUrl = model.thumbURL for m in model.children { let item = MediaItem(model: m) @@ -70,6 +73,8 @@ class MediaItem: CustomDebugStringConvertible { model.time = time ?? 0 model.loop = loop model.length = length ?? 0 + model.thumbURL = thumbUrl + return model } @@ -146,6 +151,15 @@ class MediaItem: CustomDebugStringConvertible { } var playerURL: URL? { + if local { + var file = FileHelper.getDocumentsDirectory() + + if (path != "") { + file = file.appendingPathComponent(path) + } + file = file.appendingPathComponent(name) + return file + } let enc = name.replacingOccurrences(of: " ", with: "%20") let index = encodedDir!.index(encodedDir!.startIndex, offsetBy: 10) let s = NetworkManager.sharedInstance.vidurl + encodedDir!.substring(from:index) @@ -189,6 +203,12 @@ class MediaItem: CustomDebugStringConvertible { children.sort(by: { $0.sortName < $1.sortName }) + + var j = 0 + for i in children { + i.index = j + j += 1 + } } func isWeb() -> Bool { @@ -200,14 +220,19 @@ class MediaItem: CustomDebugStringConvertible { } func isVideo() -> Bool { - return (superRoot().type == ItemType.VIDEOROOT || superRoot().type == ItemType.FAVROOT) && (type == ItemType.VIDEO || type == ItemType.SNAPSHOT) + return (superRoot().type == ItemType.VIDEOROOT || (superRoot().type == ItemType.VIDEO) || local) && (type == ItemType.VIDEO || type == ItemType.SNAPSHOT) } func isDetails() -> Bool { - return type == ItemType.DETAILS || type == ItemType.FAVROOT + if !children.isEmpty { + if (children[0].type == ItemType.VIDEO) { + return true; + } + } + return type == ItemType.DETAILS } func isFolder() -> Bool { - return type == ItemType.PICROOT || type == ItemType.VIDEOROOT || type == ItemType.FOLDER || type == ItemType.WEBROOT + return type == ItemType.PICROOT || type == ItemType.VIDEOROOT || type == ItemType.FOLDER || type == ItemType.WEBROOT || type == ItemType.FAVROOT } } diff --git a/kplayer/core/MediaModel.swift b/kplayer/core/MediaModel.swift index 5be2c21..1007750 100644 --- a/kplayer/core/MediaModel.swift +++ b/kplayer/core/MediaModel.swift @@ -13,6 +13,7 @@ public struct MediaModel : Codable { var time = 0.0 var length = 0.0 var loop = false + var thumbURL : String? var children: [MediaModel] let type: ItemType diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index 67f3440..fe0cab7 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -10,13 +10,13 @@ import Alamofire class NetworkManager { static let sharedInstance = NetworkManager() + let nodeurl = "http://linkstation:8081/" let baseurl = "http://linkstation:8080/tomcat/media" let vidurl = "http://linkstation:8089" - var authenticated = false + var authenticated = true var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT) - var currentFav = MediaItem(name: "current", path:"", root: "", type: ItemType.FOLDER) lazy var operationQueue: OperationQueue = { var queue = OperationQueue() @@ -41,16 +41,63 @@ class NetworkManager { } } + func loadFavFolders(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { + + } + func loadFavDirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { var res = [MediaItem]() + let url = FileHelper.getDocumentsDirectory() + if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) { + for case let fileURL as URL in enumerator { + do { + let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey]) + if fileAttributes.isRegularFile! { + if (fileURL.pathExtension == "mp4") { + print(fileURL.absoluteString) + + let p = fileURL.absoluteString.substringAfter("/Documents") + + var path = p.substringBefore("/") + + let m = MediaItem(name: fileURL.lastPathComponent, path: path, root: "1", type: ItemType.VIDEO) + m.local = true + + res.append(m) + + } + } + } catch { print(error, fileURL) } + } + } + + completionHandler(res) + } + + func loadFav2Dirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { + let files = FileHelper.listFiles(name: "1") + var res = [MediaItem]() + for f in files { + if f.pathExtension == "mp4" { + let m = MediaItem(name: f.lastPathComponent, path: "", root: "1", type: ItemType.VIDEO) + + m.local = true + res.append(m) + } + } + + completionHandler(res) + } + + func loadFav1Dirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { + var res = [MediaItem]() + do { let jsonData = try FileHelper.getData(name: "fav.json") let items = try JSONDecoder().decode(MediaModel.self, from: jsonData) let loaded = MediaItem(model: items) - currentFav.loaded = true res = loaded.children - // currentFav.children.append(loaded) } catch { print(error.localizedDescription) } @@ -229,6 +276,10 @@ class NetworkManager { } func loadItems(_ item: MediaItem) { + if item.local { + return + } + if (item.type != ItemType.FOLDER && item.type != ItemType.DETAILS) { return } @@ -311,7 +362,17 @@ class NetworkManager { } func deleteThumb(_ path: String) { - AF.request(NetworkManager.sharedInstance.baseurl + "/service/deletethumb\(path)").responseString { response in + let p = self.nodeurl + "deleteThumb\(path)" + print("Delete \(path)") + AF.request(p).responseString { response in + print(response) + } + } + + func alive() { + let p = self.nodeurl + "alive" + print("alive") + AF.request(p).responseString { response in print(response) } } @@ -325,6 +386,9 @@ class NetworkManager { func saveItem(_ item: MediaItem) { print(item.children) + if (item.local) { + return + } if (item.type != ItemType.VIDEO) { return @@ -356,13 +420,13 @@ class NetworkManager { print("contained") } else { if let id = c.image, let imageData = id.jpegData(compressionQuality: 1.0) { - let op = UploadOperation(baseUrl: self.baseurl + "/service/upload", data: imageData, path: p) + let op = UploadOperation(baseUrl: self.nodeurl + "upload", data: imageData, path: p) self.operationQueue.addOperation(op) let thumb = id.scaleToSize(15 * 16, height: 15 * 9) if let imageDataT = thumb.jpegData(compressionQuality: 1.0) { c.image = thumb - let opT = UploadOperation(baseUrl: self.baseurl + "/service/upload", data: imageDataT, path: pt) + let opT = UploadOperation(baseUrl: self.nodeurl + "upload", data: imageDataT, path: pt) self.operationQueue.addOperation(opT) } diff --git a/kplayer/core/ThumbnailCache.swift b/kplayer/core/ThumbnailCache.swift index c50a077..c7d5b3a 100644 --- a/kplayer/core/ThumbnailCache.swift +++ b/kplayer/core/ThumbnailCache.swift @@ -75,7 +75,7 @@ class ThumbnailCache { let size = (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + sizeHi index += 4 - if size > 0 { + if size > 0 && (d.count >= index + size){ // var buf = //UnsafeMutablePointer(&bytes[index]) let part = d.subdata(in: index ..< (index + size)) if let img = UIImage(data: part) { diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index a9b0e66..8800fc8 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -117,6 +117,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout 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 present(navController, animated: false, completion: nil) } @@ -138,9 +139,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout if let detail: MediaItem = self.detailItem { if i.type == ItemType.VIDEO { - if i.parent!.type != ItemType.DETAILS { - let path = IndexPath(item: index, section: i.parent!.index) - self.collectionView.reloadItems(at: [path]) + let set = IndexSet(integer: index) + if index < detail.children.count { + collectionView.reloadSections(set) } } else { if i.parent! !== detail { @@ -335,7 +336,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout func showVideo(selectedItem: MediaItem) { var pc: ItemController? - pc = AVPlayerController() + pc = VideoController() pc!.setCurrentItem(item: selectedItem) pc!.setItems(items: detailItem!.children) diff --git a/kplayer/detail/VideoController.swift b/kplayer/detail/VideoController.swift index ff338fe..9f942b0 100644 --- a/kplayer/detail/VideoController.swift +++ b/kplayer/detail/VideoController.swift @@ -14,7 +14,7 @@ protocol ItemController { func setCompletionHandler(handler: @escaping (() -> Void)) } -class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate { +class VideoController: UIViewController, ItemController, BMPlayerDelegate { var player = BMPlayer() var currentItem: MediaItem? var currentSnapshot: MediaItem? @@ -52,16 +52,16 @@ class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate { override func viewDidLoad() { super.viewDidLoad() - barbutton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(AVPlayerController.captureThumbnail)); + barbutton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(VideoController.captureThumbnail)); navigationItem.rightBarButtonItems = [barbutton!] - backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(AVPlayerController.back(_:))) - speedButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(AVPlayerController.speed(_:))) - loopButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(AVPlayerController.loop(_:))) - aspectButton = UIBarButtonItem(title:"1", style:UIBarButtonItem.Style.plain, target: self, action: #selector(AVPlayerController.aspect(_:))) - playButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(AVPlayerController.startstop(_:))) - favButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(AVPlayerController.favorite(_:))) - reviewButton = UIBarButtonItem(title:"Edit ", style:UIBarButtonItem.Style.plain, target: self, action: #selector(AVPlayerController.doEdit(_:))) + backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(VideoController.back(_:))) + speedButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.speed(_:))) + loopButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.loop(_:))) + aspectButton = UIBarButtonItem(title:"1", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.aspect(_:))) + playButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(VideoController.startstop(_:))) + favButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(VideoController.favorite(_:))) + reviewButton = UIBarButtonItem(title:"Edit ", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.doEdit(_:))) navigationItem.leftBarButtonItems = [backButton!, speedButton!, playButton!, loopButton!, aspectButton!, favButton!, reviewButton!] @@ -119,7 +119,16 @@ class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate { print("favorite") if let c = currentSnapshot { - NetworkManager.sharedInstance.currentFav.children.append(currentItem!) + do { + let file = FileHelper.getDocumentsDirectory().appendingPathComponent(c.name) + VideoHelper.export(item: player.avPlayer!.currentItem!, clipStart: c.time!, clipDuration: c.length!, file: file) { url in + print(url) + } + } catch { +print(error) + } + + // NetworkManager.sharedInstance.currentFav.children.append(currentItem!) } } @@ -214,6 +223,12 @@ print("play") var def = [BMPlayerResourceDefinition]() var index = 0; var count = 0; + + if allItems.isEmpty { + print("no items found") + return + } + for i in allItems { let r = BMPlayerResourceDefinition(url: i.playerURL!, definition: i.name); def.append(r) @@ -514,8 +529,6 @@ print("finish") func bmPlayer(player: BMPlayer, playTimeDidChange currentTime: TimeInterval, totalTime: TimeInterval) { if loopOption == 1 { if currentTime > loopEnd { - print("loop") - player.seek(loopStart) } } diff --git a/kplayer/master/MasterViewController.swift b/kplayer/master/MasterViewController.swift index 519aa9b..16d7962 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 Weiter) + func loadItem(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) } class MasterViewController: UITableViewController, UISearchResultsUpdating { @@ -131,7 +131,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { delegate!.loadFolder(selectedItem: selectedItem) { (neu) in - if neu.isDetails() { + if neu.isDetails() || neu.isVideo() { self.gotoDetails(neu) } else { self.gotoNextFolder(neu) @@ -151,48 +151,13 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { } fileprivate func gotoDetails(_ selectedItem: MediaItem) { - let weiter: NetworkManager.Weiter = { - (g) in + let weiter: (MediaItem) -> Void = { + (m) in - let model = ItemModel() - - for f in g { - model.addItem(f) - } - selectedItem.children = model.items - selectedItem.loaded = false - - if selectedItem.path.contains("*") { - selectedItem.loaded = true - } else - { - - var all: [MediaItem]? - - for f in selectedItem.children { - print(f.path) - if f.type == ItemType.FOLDER && f.path == "" { - all = f.children - break - } - } - - if all != nil { - selectedItem.children = all! - selectedItem.loaded = false - - for f in all! { - f.loaded = false - f.parent = selectedItem - } - } - } self.performSegue(withIdentifier: "showDetail", sender: self) - return } delegate!.loadItem(selectedItem: selectedItem, completionHandler: weiter) - } override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { @@ -215,12 +180,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { item.sort() - var j = 0 - for i in item.children { - i.index = j - j += 1 - } - NetworkManager.sharedInstance.loadItems(item) controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem controller.navigationItem.leftItemsSupplementBackButton = true diff --git a/kplayer/master/NetworkDelegate.swift b/kplayer/master/NetworkDelegate.swift index 5f0e42d..8321263 100644 --- a/kplayer/master/NetworkDelegate.swift +++ b/kplayer/master/NetworkDelegate.swift @@ -6,27 +6,53 @@ import Foundation class NetworkDelegate: MasterDelegate, DetailDelegate { - func loadItem(selectedItem: MediaItem, completionHandler: @escaping Weiter) { + func loadItem(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) { + if selectedItem.loaded { + completionHandler(selectedItem) + return + } + let p = selectedItem.superRoot() let dir = selectedItem.encodedDir! + let weiter:Weiter = { + (g) in + ItemModel().sortItems(selectedItem: selectedItem, children: g) + completionHandler(selectedItem) + } + if p.type == ItemType.FAVROOT { - NetworkManager.sharedInstance.loadFavDirs(dir, completionHandler: completionHandler) + NetworkManager.sharedInstance.loadFavDirs(dir, completionHandler: weiter) } else if p.type == ItemType.VIDEOROOT { - NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: completionHandler) + NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter) } else if p.type == ItemType.WEBROOT { - completionHandler(p.children) + weiter(p.children) } else { - NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: completionHandler) + NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter) } } func loadFolder(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) -> Void { + let weiter:Weiter = { + (g) in + ItemModel().sortItems(selectedItem: selectedItem, children: g) + completionHandler(selectedItem) + } + + if selectedItem.type == ItemType.FOLDER && selectedItem.loaded { + completionHandler(selectedItem) + return + } - NetworkManager.sharedInstance.listDirs(selectedItem.encodedDir!) { + if selectedItem.type == ItemType.FAVROOT { + NetworkManager.sharedInstance.loadFavDirs("dir", completionHandler: weiter) + return + } + + NetworkManager.sharedInstance.listDirs(selectedItem.encodedDir!, completionHandler: { (i, leaf) in let details = MediaItem(name: selectedItem.name, path: selectedItem.path, root: selectedItem.root, type: ItemType.DETAILS) @@ -66,13 +92,24 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { let isVideo = selectedItem.parent != nil && selectedItem.parent!.type == ItemType.VIDEOROOT if leaf || isVideo { - selectedItem.type = ItemType.DETAILS +// if selectedItem.type == ItemType.VIDEOROOT { +// NetworkManager.sharedInstance.loadVideoDirs(selectedItem.encodedDir!, completionHandler: { +// (i) in +// selectedItem.children = i +// completionHandler(selectedItem) +// return +// }) +// } + + if selectedItem.type == ItemType.FOLDER { + selectedItem.type = ItemType.DETAILS + } + completionHandler(selectedItem) - } - else { + } else { completionHandler(neu) } - } + }) } func loadDetails(selectedItem: MediaItem, completionHandler: @escaping () -> ()) { @@ -94,7 +131,7 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { let p = c.snapshotDirPathForVideo + "\(ms).jpg" let pt = c.snapshotDirPathForVideo + "\(ms)_thumb.jpg" - // NetworkManager.sharedInstance.deleteThumb(p) + NetworkManager.sharedInstance.deleteThumb(p) NetworkManager.sharedInstance.deleteThumb(pt) } } diff --git a/kplayer/photo/PhotoController.swift b/kplayer/photo/PhotoController.swift index e47dc71..b3d02cf 100644 --- a/kplayer/photo/PhotoController.swift +++ b/kplayer/photo/PhotoController.swift @@ -193,6 +193,22 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView } return } + + let controller = VideoController() + controller.edit = false + controller.allowEdit = false + + controller.currentItem = currentItem + controller.setItems(items: [currentItem]) + controller.navigationItem.leftItemsSupplementBackButton = true + navigationController!.navigationBar.barTintColor = UIColor.black + navigationController!.pushViewController(controller, animated: true) + + controller.completionHandler = { + () in +// NetworkManager.sharedInstance.saveItem(self.currentItem!) + self.dismiss(animated: true, completion: nil); + } } override func loadView() { diff --git a/kplayer/readme.md b/kplayer/readme.md index 9a5a0d6..e8b0c68 100644 --- a/kplayer/readme.md +++ b/kplayer/readme.md @@ -22,7 +22,7 @@ Ein Picture-Detail enthält Bilder. Ein Web-Detail enthält Bookmarks. Der MasterViewController enthält eine Liste von Roots. -Wird ein Root ausgewählt, werden die Children geladen. +Wird ein Root ausgewählt, werden die Folder geladen. Gibt es Folder, wird die nächste Ebene eingeblendet. Die Details-Liste wird im Details-Controller dargestellt. diff --git a/kplayer/util/FileHelper.swift b/kplayer/util/FileHelper.swift index 895ff2d..215618a 100644 --- a/kplayer/util/FileHelper.swift +++ b/kplayer/util/FileHelper.swift @@ -19,4 +19,31 @@ struct FileHelper { print(url) return try Data(contentsOf: url) } + + + static func createDir(name: String) throws -> URL { + let fileManager = FileManager.default + let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(name) + do { + try fileManager.createDirectory(at: documentsURL, withIntermediateDirectories: true) + } catch { + print(error) + } + print(documentsURL) + return documentsURL + } + + static func listFiles(name: String)-> [URL] { + let fileManager = FileManager.default + let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] //.appendingPathComponent(name) + print (documentsURL) + do { + let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil) + // process files + return fileURLs + } catch { + print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)") + } + return [] + } } diff --git a/kplayer/util/UploadOperation.swift b/kplayer/util/UploadOperation.swift index 5644c2b..e133b77 100644 --- a/kplayer/util/UploadOperation.swift +++ b/kplayer/util/UploadOperation.swift @@ -31,7 +31,9 @@ class UploadOperation: Operation { multipartFormData.append(self.path.data(using: String.Encoding.utf8)!, withName: "name") }, with: URLRequest(url: URL(string: baseUrl)!, method: HTTPMethod.post)) .response(completionHandler: { data in - print(data.data!) + if let d = data.data { + print(String(decoding: d, as: UTF8.self)) + } }) } catch { print("Upload Error") diff --git a/kplayer/util/VideoHelper.swift b/kplayer/util/VideoHelper.swift new file mode 100644 index 0000000..91b6ddf --- /dev/null +++ b/kplayer/util/VideoHelper.swift @@ -0,0 +1,64 @@ +// +// Created by Marco Schmickler on 30.04.21. +// Copyright (c) 2021 Marco Schmickler. All rights reserved. +// + +import Foundation +import AVFoundation + +class VideoHelper { + public static func export(item: AVPlayerItem, clipStart: Double, clipDuration: Double, file: URL, completion: @escaping (URL?) -> ()) { + guard item.asset.isExportable else { + completion(nil) + return + } + + let composition = AVMutableComposition() + let compositionVideoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid)) + let compositionAudioTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid)) + + let sourceVideoTrack = item.asset.tracks(withMediaType: AVMediaType.video).first! + let sourceAudioTrack = item.asset.tracks(withMediaType: AVMediaType.audio).first! + let start = CMTime(seconds: clipStart, preferredTimescale: 1); + let dur = CMTime(seconds: clipDuration, preferredTimescale: 100); + do { + + try compositionVideoTrack!.insertTimeRange(CMTimeRangeMake(start: start, duration: dur), of: sourceVideoTrack, at: start) + try compositionAudioTrack!.insertTimeRange(CMTimeRangeMake(start: start, duration: dur), of: sourceAudioTrack, at: start) + } catch(_) { + completion(nil) + return + } + + let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition) + var preset: String = AVAssetExportPresetPassthrough + // if compatiblePresets.contains(AVAssetExportPreset3840x2160) { preset = AVAssetExportPreset3840x2160 } + + guard + let exportSession = AVAssetExportSession(asset: composition, presetName: preset), + exportSession.supportedFileTypes.contains(AVFileType.mp4) else { + completion(nil) + return + } + + exportSession.outputURL = file + exportSession.outputFileType = AVFileType.mp4 + let startTime = CMTime(seconds: 0.0, preferredTimescale: 100); + let timeRange = CMTimeRangeMake(start: start, duration: dur) + exportSession.timeRange = timeRange + + print("Write file \(file)") + do { + try FileManager.default.removeItem(at: file) + } catch { + } + exportSession.exportAsynchronously { + print("ready \(exportSession.error)") + // let data = try? Data(contentsOf: tempFileUrl) + // _ = try? FileManager.default.removeItem(at: tempFileUrl) + let r = FileManager.default.fileExists(atPath: file.absoluteString) + print (r) + completion(file) + } + } +} diff --git a/kplayer/util/stringutil.swift b/kplayer/util/stringutil.swift index 81cdd13..6274409 100644 --- a/kplayer/util/stringutil.swift +++ b/kplayer/util/stringutil.swift @@ -38,6 +38,22 @@ public extension String { return substring(from: ix) } + func substringAfter(_ str: String) -> String { + if let range = range(of: str) { + let substring = self[range.upperBound.. String { + if let range = range(of: str, options: .backwards) { + let substring = self[.. Int { return count }