From 0bc1c2a01af862d564e00a48ccca08c6c24717f0 Mon Sep 17 00:00:00 2001 From: marcoschmickler Date: Mon, 10 May 2021 11:52:18 +0200 Subject: [PATCH] Web --- kplayer.xcodeproj/project.pbxproj | 8 ++ kplayer/AppDelegate.swift | 73 +++++++---- kplayer/core/HtmlParser.swift | 35 +++++ kplayer/core/ItemType.swift | 13 +- kplayer/core/MediaItem.swift | 11 +- kplayer/core/NetworkManager.swift | 91 +++++++++++++ kplayer/detail/BrowserController.swift | 152 +++++++++++++++++++++- kplayer/detail/DetailViewController.swift | 22 +++- kplayer/detail/ItemCell.swift | 16 ++- kplayer/detail/VideoController.swift | 49 +++++-- kplayer/master/NetworkDelegate.swift | 67 +--------- kplayer/video/BMPlayer.swift | 10 +- kplayer/video/BMPlayerLayerView.swift | 2 +- 13 files changed, 430 insertions(+), 119 deletions(-) create mode 100644 kplayer/core/HtmlParser.swift diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index f64d130..0e3dbd2 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; }; 1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; }; 1C7365885FAF292F2221ED44 /* PhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* PhotoController.swift */; }; + 1C736667B944A494666AEEAE /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73643B4BBE7251CCF189B2 /* Downloader.swift */; }; 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; }; 1C7366DAC06047DE335EFC37 /* BMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736927EA28AFBEB25D7487 /* BMPlayer.swift */; }; 1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; }; @@ -32,6 +33,7 @@ 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; }; 1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73602350ACE2436736F981 /* BrowserController.swift */; }; 1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */; }; + 1C736A06A2AD75B8C14EEBBE /* HtmlParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */; }; 1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */; }; 1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; }; 1C736CB96577F6A9A7BA03E8 /* BMPlayerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */; }; @@ -80,6 +82,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 = ""; }; + 1C73643B4BBE7251CCF189B2 /* Downloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Downloader.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 = ""; }; @@ -102,6 +105,7 @@ 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = ""; }; 1C736C7FFBDAC665AE04CB65 /* VideoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = ""; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = ""; }; + 1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParser.swift; sourceTree = ""; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; 1C736DFBD072763248412F74 /* BMPlayerClearityChooseButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerClearityChooseButton.swift; sourceTree = ""; }; 1C736E51F1A03E3A1200BDB6 /* BMPlayerControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerControlView.swift; sourceTree = ""; }; @@ -186,6 +190,7 @@ 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */, 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */, 1C7364709899FF62774B0199 /* VideoHelper.swift */, + 1C73643B4BBE7251CCF189B2 /* Downloader.swift */, ); path = util; sourceTree = ""; @@ -199,6 +204,7 @@ 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */, 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */, 1C73631C96E6C860833052CA /* ItemType.swift */, + 1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */, ); path = core; sourceTree = ""; @@ -506,6 +512,8 @@ 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */, 1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */, 1C7360F1D2CF83ECDD586B84 /* BMPlayerCompositionResourceDefinition.swift in Sources */, + 1C736A06A2AD75B8C14EEBBE /* HtmlParser.swift in Sources */, + 1C736667B944A494666AEEAE /* Downloader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/AppDelegate.swift b/kplayer/AppDelegate.swift index 231411f..9d27a85 100644 --- a/kplayer/AppDelegate.swift +++ b/kplayer/AppDelegate.swift @@ -26,32 +26,55 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele let controller = masterNavigationController.topViewController as! MasterViewController controller.delegate = NetworkDelegate() - let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT) - let google = MediaItem(name: "google", path:"", root: "www.google.de", type: ItemType.FOLDER) - let a = MediaItem(name: "google", path:"", root: "www.google.de", type: ItemType.WEB) - google.children = [ a ] - web.children = [ -google - ] + let url = URL(string: NetworkManager.sharedInstance.vidurl)?.appendingPathComponent("ren").appendingPathComponent("kplayer.txt") + + var roots = [MediaItem]() + do { + let remoteroots = try String(contentsOf: url!) + let components = remoteroots.split(separator: "\n") + for c in components { + let line = c.split(separator: " ") + + let name = String(line[0]) + let root = String(line[1]) - controller.model.items = [ - MediaItem(name: "sp", path:"", root: "/srv/samba/ren/sp/video", type: ItemType.VIDEOROOT), - MediaItem(name: "fav", path:"", root: "/srv/samba/ren/fav", type: ItemType.VIDEOROOT), - MediaItem(name: "knk", path:"", root: "/srv/samba/ren/knk", type: ItemType.VIDEOROOT), - MediaItem(name: "knk_archiv", path:"", root: "/srv/samba/ren/knk_archiv", type: ItemType.VIDEOROOT), - 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: "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), - MediaItem(name: "series", path:"", root: "/srv/samba/ren/series", type: ItemType.PICROOT), - MediaItem(name: "bm", path:"", root: "/srv/samba/ren/bm", type: ItemType.PICROOT), - MediaItem(name: "medieval", path:"", root: "/srv/samba/ren/medieval", type: ItemType.PICROOT), + let item = MediaItem(name: name, path:"", root: root, type: ItemType.REMOTEROOT) + roots.append(item) + } + } catch { + } + + let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT) +// let google = MediaItem(name: "google", path:"", root: "www.google.de", type: ItemType.FOLDER) +// let a = MediaItem(name: "google", path:"", root: "www.google.de", type: ItemType.WEB) +// google.children = [ a ] +// web.children = [ +//google +// ] + + roots.append(NetworkManager.sharedInstance.favorites) + roots.append(web) + + controller.model.items = roots + + let neues = [ + MediaItem(name: "sp", path:"", root: "/srv/samba/ren/sp/video", type: ItemType.REMOTEROOT), + MediaItem(name: "fav", path:"", root: "/srv/samba/ren/fav", type: ItemType.REMOTEROOT), + MediaItem(name: "knk", path:"", root: "/srv/samba/ren/knk", type: ItemType.REMOTEROOT), + MediaItem(name: "knk_archiv", path:"", root: "/srv/samba/ren/knk_archiv", type: ItemType.REMOTEROOT), + MediaItem(name: "knk_archiv2", path:"", root: "/srv/samba/ren/knk_archiv2", type: ItemType.REMOTEROOT), + MediaItem(name: "knk_archiv3", path:"", root: "/srv/samba/ren/knk_archiv3", type: ItemType.REMOTEROOT), + MediaItem(name: "knk_archiv4", path:"", root: "/srv/samba/ren/knk_archiv4", type: ItemType.REMOTEROOT), + MediaItem(name: "real", path:"", root: "/srv/samba/ren/real", type: ItemType.REMOTEROOT), + MediaItem(name: "fetish", path:"", root: "/srv/samba/ren/fetish", type: ItemType.REMOTEROOT), + MediaItem(name: "fjoy", path:"", root: "/srv/samba/ren/fjoy", type: ItemType.REMOTEROOT), + MediaItem(name: "heg", path:"", root: "/srv/samba/ren/heg", type: ItemType.REMOTEROOT), + MediaItem(name: "ten", path:"", root: "/srv/samba/ren/ten", type: ItemType.REMOTEROOT), + MediaItem(name: "leg", path:"", root: "/srv/samba/ren/leg", type: ItemType.REMOTEROOT), + MediaItem(name: "ang", path:"", root: "/srv/samba/ren/ang", type: ItemType.REMOTEROOT), + MediaItem(name: "series", path:"", root: "/srv/samba/ren/series", type: ItemType.REMOTEROOT), + MediaItem(name: "bm", path:"", root: "/srv/samba/ren/bm", type: ItemType.REMOTEROOT), + MediaItem(name: "medieval", path:"", root: "/srv/samba/ren/medieval", type: ItemType.REMOTEROOT), NetworkManager.sharedInstance.favorites, web, ] diff --git a/kplayer/core/HtmlParser.swift b/kplayer/core/HtmlParser.swift new file mode 100644 index 0000000..60a0bce --- /dev/null +++ b/kplayer/core/HtmlParser.swift @@ -0,0 +1,35 @@ +// +// Created by Marco Schmickler on 05.05.21. +// Copyright (c) 2021 Marco Schmickler. All rights reserved. +// + +import Foundation + +class HtmlParser : NSObject, XMLParserDelegate { + var currentItem : MediaItem? + var items = [MediaItem]() + let url : URL? + + init(url: URL?) { + self.url = url + } + + func parse() -> Bool { + let parser = XMLParser(contentsOf: url!) + parser?.delegate = self + + return parser!.parse() + } + + func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { + switch elementName { + case "a": + + if let value = attributeDict["href"] { + currentItem = MediaItem(name: value, path: value, root: "", type: ItemType.WEB) + items.append(currentItem!) + } + default: break + } + } +} \ No newline at end of file diff --git a/kplayer/core/ItemType.swift b/kplayer/core/ItemType.swift index b2fccbb..a766ec5 100644 --- a/kplayer/core/ItemType.swift +++ b/kplayer/core/ItemType.swift @@ -7,15 +7,13 @@ import Foundation enum ItemType: String, Codable, CustomStringConvertible { /** - Repräsentiert eine Wurzel, in der nur Bilder angezeigt werden. + Repräsentiert eine Wurzel, die vom Server geladen wird. */ - case PICROOT = "root" + case REMOTEROOT = "root" /** - Repräsentiert eine Wurzel, in der nur Videos angezeigt werden. + Repräsentiert eine Wurzel, die lokal gespeichert ist. */ - case VIDEOROOT = "videoroot" - case FAVROOT = "favroot" /** @@ -33,6 +31,11 @@ enum ItemType: String, Codable, CustomStringConvertible { */ case VIDEO = "video" + /** + Ein Download-Progress Stellvertreter. + */ + case DOWNLOAD = "download" + /** Web-Browser */ diff --git a/kplayer/core/MediaItem.swift b/kplayer/core/MediaItem.swift index f46cf12..af81641 100644 --- a/kplayer/core/MediaItem.swift +++ b/kplayer/core/MediaItem.swift @@ -41,9 +41,11 @@ class MediaItem: CustomDebugStringConvertible { var length: TimeInterval? var loop = true var thumbUrl: String? + var externalURL: String? var local = false var leaf = false + var cancelled = false convenience init(model: MediaModel) { self.init(name: model.name, path: model.path, root: model.root, type: model.type) @@ -136,6 +138,9 @@ class MediaItem: CustomDebugStringConvertible { Absolute URL, unter der das Thumbnail des Items abgerufen werden kann. */ var thumbUrlAbsolute: String { + if thumbUrl == nil { + return "" + } if thumbUrl!.starts(with: "file:") { return thumbUrl! } @@ -156,6 +161,10 @@ class MediaItem: CustomDebugStringConvertible { } var playerURL: URL? { + if let ext = externalURL { + return URL(string: ext) + } + if local { var file = FileHelper.getDocumentsDirectory() @@ -244,6 +253,6 @@ class MediaItem: CustomDebugStringConvertible { } func isFolder() -> Bool { - return type == ItemType.PICROOT || type == ItemType.VIDEOROOT || type == ItemType.FOLDER || type == ItemType.WEBROOT || type == ItemType.FAVROOT + return type == ItemType.REMOTEROOT || type == ItemType.FOLDER || type == ItemType.WEBROOT || type == ItemType.FAVROOT } } diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index ec50be8..ef067f1 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -17,6 +17,8 @@ class NetworkManager { var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT) + var currentDownloads = [String : MediaItem]() + lazy var operationQueue: OperationQueue = { var queue = OperationQueue() queue.name = "Backup queue" @@ -274,8 +276,35 @@ class NetworkManager { } } + func loadItems(_ item: MediaItem) { if item.local { + if item.name == "download" { + var nodes = [MediaItem]() + for c in item.children { + if c.type != ItemType.DOWNLOAD { + nodes.append(c) + } + + } + for d in NetworkManager.sharedInstance.currentDownloads { + nodes.append(d.value) + } + item.children = nodes + } + return + } + + if (item.type == ItemType.WEB) { + let url = URL(string: vidurl)!.appendingPathComponent(item.root.substringStartingFrom(10)).appendingPathComponent(item.path).appendingPathComponent("links.html") + + let parser = HtmlParser(url: url) + parser.parse() + + item.children = parser.items + + NotificationCenter.default.post(name: Notification.Name(rawValue: "loadedItems"), object: nil) + return } @@ -487,4 +516,66 @@ class NetworkManager { } } + func downloadFFMPEG(url: URL) { + let queryItems = [URLQueryItem(name: "url", value: url.absoluteString), URLQueryItem(name: "name", value: url.lastPathComponent + ".mp4")] + var urlComps = URLComponents(string: nodeurl + "ffmpeg")! + urlComps.queryItems = queryItems + let p = urlComps.url! + print("ffmpeg \(url.lastPathComponent)") + AF.request(p).responseString { response in + print(response) + } + } + + func download(url: URL) { + UIApplication.shared.isIdleTimerDisabled = true + + let destination: DownloadRequest.Destination = {temporaryURL, response in + if let suggestedFileName = response.suggestedFilename { + do { + let directory = try FileHelper.createDir(name: "download") + let downloadedFilePath = directory.appendingPathComponent(suggestedFileName) + // if downloadedFilePath.exists { try self.downloadedFilePath?.deleteFile() } + return (downloadedFilePath, [.removePreviousFile, .createIntermediateDirectories]) + + } catch let e { + print("Failed to get temporary directory - \(e)") + } + } + + let (downloadedFileURL, _) = DownloadRequest.suggestedDownloadDestination()(temporaryURL, response) + return (downloadedFileURL, [.removePreviousFile, .createIntermediateDirectories]) + } + + let request = AF.download(url, to: destination) + + request.downloadProgress(queue: .main, closure: { (progress) in + + let name = url.lastPathComponent + var dl = NetworkManager.sharedInstance.currentDownloads[name] + if dl == nil && !request.isCancelled { + dl = MediaItem(name: name, path: name, root: "", type: ItemType.DOWNLOAD) + NetworkManager.sharedInstance.currentDownloads[name] = dl + } + + if (dl!.cancelled) { + request.cancel() + NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: name) + } + + dl!.length = progress.fractionCompleted + //progress closure + print(progress.fractionCompleted) + }).responseData { response in + if let destinationUrl = response.fileURL { + print(destinationUrl) + + if let statusCode = (response as? HTTPURLResponse)?.statusCode { + NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: url.lastPathComponent) + print("Success: \(statusCode)") + } + } + } + } + } diff --git a/kplayer/detail/BrowserController.swift b/kplayer/detail/BrowserController.swift index 7532aa3..f175a4f 100644 --- a/kplayer/detail/BrowserController.swift +++ b/kplayer/detail/BrowserController.swift @@ -7,24 +7,63 @@ import Foundation import UIKit import WebBrowser import WebKit +import Alamofire -class BrowserController : UIViewController, ItemController, WebBrowserDelegate, UINavigationControllerDelegate { +class BrowserController : UIViewController, ItemController, WebBrowserDelegate, UINavigationControllerDelegate, WKScriptMessageHandler, WKHTTPCookieStoreObserver { var completionHandler: (() -> Void)? + var currentItem : MediaItem? + + var web : WebBrowserViewController? + func setItems(items: [MediaItem]) { } func setCurrentItem(item: MediaItem) { - + currentItem = item } func setCompletionHandler(handler: @escaping (() -> Void)) { completionHandler = handler } + func cookiesDidChange(in cookieStore: WKHTTPCookieStore) { + cookieStore.getAllCookies { cookies in + print(cookies) + } + } + override func viewDidLoad() { - let webBrowserViewController = WebBrowserViewController() + + let preferences = WKPreferences() + preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") + + let config = WKWebViewConfiguration() + config.preferences = preferences + config.allowsInlineMediaPlayback = true + config.allowsAirPlayForMediaPlayback = false + config.userContentController.add(self, name: "openDocument") + config.userContentController.add(self, name: "jsError") + config.setValue(true, forKey: "_allowUniversalAccessFromFileURLs") + + config.websiteDataStore = WKWebsiteDataStore.nonPersistent() + config.websiteDataStore.httpCookieStore.add(self) + + let cookiesStore = config.websiteDataStore.httpCookieStore + let cookieProps: [HTTPCookiePropertyKey : Any] = [ + HTTPCookiePropertyKey.domain: "URL", + HTTPCookiePropertyKey.path: "/", + HTTPCookiePropertyKey.name: "key", + HTTPCookiePropertyKey.value: "value", + HTTPCookiePropertyKey.secure: "TRUE", + HTTPCookiePropertyKey.expires: NSDate(timeIntervalSinceNow: 209000) + ] + cookiesStore.setCookie(HTTPCookie(properties: cookieProps)!) + + let webBrowserViewController = WebBrowserViewController(configuration: config) + web = webBrowserViewController + web?.getWKWebView().customUserAgent = "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"; webBrowserViewController.delegate = self @@ -38,19 +77,95 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate, webBrowserViewController.isShowPageTitleInNavigationBar = true // webBrowserViewController.customApplicationActivities = ... - webBrowserViewController.loadURLString("https://www.xvideos.com") + webBrowserViewController.loadURLString(currentItem!.name) webBrowserViewController.onOpenExternalAppHandler = { [weak self] _ in guard let `self` = self else { return } self.completionHandler!() //self.navigationController?.popViewController(animated: true) } + + let reviewButton = UIBarButtonItem(title:"download", style:UIBarButtonItem.Style.plain, target: self, action: #selector(BrowserController.doDownload(_:))) + + webBrowserViewController.navigationItem.rightBarButtonItems = [reviewButton] + navigationController?.delegate = self navigationController?.pushViewController(webBrowserViewController, animated: true) // present(webBrowserViewController, animated: false) } - func webBrowser(_ webBrowser: WebBrowserViewController, didStartLoad url: URL?) { + @objc public func doDownload(_ sender: AnyObject) { + executeDocumentDownloadScript(webView: web!.getWKWebView(), forAbsoluteUrl: "hello") + } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + debugPrint("did receive message \(message.name)") + + + if (message.name == "openDocument") { + do { + let data = (message.body as! String).data(using: .utf8) + let jsonObject = try JSONSerialization.jsonObject(with: data!, options: []) + handleDocument(jsonObject as! [String]) + } catch { + } + + + + } else if (message.name == "jsError") { + debugPrint(message.body as! String) + } + } + + private func handleDocument(_ strings: [String]) { + print(strings) + let alertController = UIAlertController(title: "Download", message: "Videos found", preferredStyle: .alert) + + for s in strings { + var name = s + if let u = URL(string: s) { + name = u.lastPathComponent + } + + let oneAction = UIAlertAction(title: name, style: .default) { (action) in + self.preview(url: s) + } + alertController.addAction(oneAction) + } + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in + } + + alertController.addAction(cancelAction) + + web!.present(alertController, animated: true) { + + } + } + + func preview(url: String) { + let vc = VideoController() + let name = URL(string: url)!.lastPathComponent + let item = MediaItem(name: name, path: name, root: "", type: ItemType.VIDEO) + item.externalURL = url + + vc.setItems(items: [item]) + vc.setCurrentItem(item: item) + + vc.setCompletionHandler(handler: { + self.dismiss(animated: true, completion: nil); + }) + + let navController = UINavigationController(rootViewController: (vc as UIViewController)) + navController.modalPresentationStyle = .fullScreen + navController.modalPresentationCapturesStatusBarAppearance = true + navController.navigationBar.barTintColor = UIColor.black + (vc as UIViewController).navigationItem.leftItemsSupplementBackButton = true + + self.present(navController, animated: false, completion: nil) + } + + func webBrowser(_ webBrowser: WebBrowserViewController, didStartLoad url: URL?) { +print(url?.absoluteString) } func webBrowser(_ webBrowser: WebBrowserViewController, didFinishLoad url: URL?) { @@ -74,4 +189,31 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate, } } +// func webBrowser(_ webBrowser: WebBrowserViewController, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) -> Bool { +// let url = navigationAction.request.url +// decisionHandler(.cancel) +// let closure = webBrowser.getWKWebView() +// executeDocumentDownloadScript(webView: closure, forAbsoluteUrl: url!.absoluteString) +// +// return true +// } + + /* + Intercept the download of documents in webView, trigger the download in JavaScript and pass the binary file to JavaScript handler in Swift code + */ + private func executeDocumentDownloadScript(webView: WKWebView, forAbsoluteUrl absoluteUrl : String) { + // TODO: Add more supported mime-types for missing content-disposition headers + do { + let js = try String(contentsOf: URL(string: "http://linkstation:8089/ren/web/download.js")!, encoding: .utf8) + let j = js.replacingOccurrences(of: "(absoluteUrl)", with: absoluteUrl) + webView.evaluateJavaScript(j) { (result, err) in + if (err != nil) { + debugPrint("JS ERR: \(String(describing: err))") + } + } + } catch { + print(error) + } + + } } diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index da90b02..919bf43 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -350,7 +350,24 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } else if sectionItem.isPic() { self.showPhotos(sectionItem.children) } else if sectionItem.isWeb() { - self.showWeb(sectionItem.children) + 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) } } @@ -372,8 +389,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout self.present(navController, animated: false, completion: nil) } - func showWeb(_ im: [MediaItem]) { + func showWeb(selectedItem: MediaItem) { let pc = BrowserController() + pc.setCurrentItem(item: selectedItem) pc.completionHandler = { self.dismiss(animated: true, completion: nil); diff --git a/kplayer/detail/ItemCell.swift b/kplayer/detail/ItemCell.swift index 49d494f..4f4cd2c 100644 --- a/kplayer/detail/ItemCell.swift +++ b/kplayer/detail/ItemCell.swift @@ -13,6 +13,7 @@ class ItemCell: UICollectionViewCell { var item: MediaItem? var image: UIImageView! + var progress: UIProgressView! required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -25,15 +26,26 @@ class ItemCell: UICollectionViewCell { image = UIImageView(frame: frame) image.contentMode = UIView.ContentMode.scaleAspectFit - autolayout(["imag": image], + progress = UIProgressView(progressViewStyle: .default) + progress.progressTintColor = .red + progress.trackTintColor = .lightGray + progress.progress = 0.0 + + autolayout(["imag": image, "prog" : progress], constraints: "H:|[imag]|", - "V:|[imag]|") + "H:|[prog]|", + "V:|[imag][prog]|") } func setItem(_ item: MediaItem) { self.item = item + if item.type == ItemType.DOWNLOAD { + progress.progress = Float(item.length!) + return + } + if let _ = item.thumbUrl, let nsurl = URL(string: item.thumbUrlAbsolute) { image.hnk_setImageFromURL(nsurl, placeholder: defaultImage) } else { diff --git a/kplayer/detail/VideoController.swift b/kplayer/detail/VideoController.swift index 7616ed6..ef01c38 100644 --- a/kplayer/detail/VideoController.swift +++ b/kplayer/detail/VideoController.swift @@ -14,6 +14,8 @@ protocol ItemController { func setCompletionHandler(handler: @escaping (() -> Void)) } +// Trimmer: https://github.com/Tomohiro-Yamashita/VideoTimelineView + class VideoController: UIViewController, ItemController, BMPlayerDelegate { var player = BMPlayer() var currentItem: MediaItem? @@ -137,11 +139,11 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate { @objc func favorite(_ sender: AnyObject) { print("favorite") + let inProgress = NetworkManager.sharedInstance.currentDownloads.count + let alertController = UIAlertController(title: "In Progress: \(inProgress)", message: "Download", preferredStyle: .alert) if let c = currentSnapshot { if (c.loop) { - let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert) - let oneAction = UIAlertAction(title: "One", style: .default) { (action) in self.save(currentSnapshot: c, name: "1") } @@ -151,28 +153,40 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate { let threeAction = UIAlertAction(title: "Three", style: .default) { (action) in self.save(currentSnapshot: c, name: "3") } - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in - } - alertController.addAction(oneAction) alertController.addAction(twoAction) alertController.addAction(threeAction) - alertController.addAction(cancelAction) - - self.present(alertController, animated: true) { + } + } - } + let downloadAction = UIAlertAction(title: "Download", style: .default) { (action) in + let url = self.currentItem!.playerURL + if url!.pathExtension == "m3u8" { + NetworkManager.sharedInstance.downloadFFMPEG(url: url!) } + else { + NetworkManager.sharedInstance.download(url: url!) + } + } + alertController.addAction(downloadAction) - // NetworkManager.sharedInstance.currentFav.children.append(currentItem!) + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in } + alertController.addAction(cancelAction) + + present(alertController, animated: true) } func save(currentSnapshot c: MediaItem, name: String) { do { try FileHelper.createDir(name: name) - let file = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(c.name) + var file = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(c.name) + + if file.pathExtension != "mp4" { + file = file.appendingPathExtension("mp4") + } + print (file) var dur = loopEnd - loopStart if (dur < 0) { return @@ -184,9 +198,16 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate { var s : MediaModel; if (c.type == ItemType.SNAPSHOT && c.parent != nil) { s = c.parent!.toMediaModel() - let local = try Data(contentsOf: URL(string: c.thumbUrlAbsolute)!) - let tfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(s.name).appendingPathExtension("jpg") - try local.write(to: tfile) + do { + if c.thumbUrlAbsolute != "" { + let local = try Data(contentsOf: URL(string: c.thumbUrlAbsolute)!) + let tfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(s.name).appendingPathExtension("jpg") + try local.write(to: tfile) + } + } + catch { + // ignore + } } else { s = c.toMediaModel() diff --git a/kplayer/master/NetworkDelegate.swift b/kplayer/master/NetworkDelegate.swift index b212a4f..e91ce76 100644 --- a/kplayer/master/NetworkDelegate.swift +++ b/kplayer/master/NetworkDelegate.swift @@ -21,17 +21,14 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { completionHandler(selectedItem) } - if p.type == ItemType.FAVROOT { - NetworkManager.sharedInstance.loadFavDirs(dir, completionHandler: weiter) - } - else if selectedItem.isVideo() { + if selectedItem.isVideo() { NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter) } else if selectedItem.isDetails() { NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter) } - else if p.type == ItemType.WEBROOT { - weiter(p.children) + else { + completionHandler(selectedItem) } } @@ -57,62 +54,8 @@ class NetworkDelegate: MasterDelegate, DetailDelegate { selectedItem.children = i selectedItem.sort() -// -// let details = MediaItem(name: selectedItem.name, path: selectedItem.path, root: selectedItem.root, type: ItemType.DETAILS) -// let neu = MediaItem(name: selectedItem.name, path: "", root: "", type: ItemType.FOLDER) -// let newFolder = MediaItem(name: ".", path: "", root: selectedItem.root, type: ItemType.DETAILS) -// newFolder.loaded = true -// -// var hasDetails = false -// -// for item in i { -// item.leaf = leaf -// if item.type != ItemType.FOLDER && item.isVideo() { -// item.parent = newFolder -// newFolder.children.append(item) -// } else { -// if item.path == "" { -// item.path = item.name -// item.name = "" -// } -// -// if item.type == ItemType.PICS { -// if !hasDetails { -// neu.children.append(details) -// hasDetails = true -// } -// } else { -//// item.type = ItemType.FOLDER -// item.parent = selectedItem -// neu.children.append(item) -// } -// } -// } -// -// if newFolder.children.count > 0 { -// newFolder.parent = selectedItem -// neu.children.append(newFolder) -// } -// -// let isVideo = selectedItem.parent != nil && selectedItem.parent!.type == ItemType.VIDEOROOT -// if leaf || isVideo { -// 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 { -// completionHandler(neu) -// } + + completionHandler(selectedItem) }) } diff --git a/kplayer/video/BMPlayer.swift b/kplayer/video/BMPlayer.swift index e5c2ccf..eb23097 100644 --- a/kplayer/video/BMPlayer.swift +++ b/kplayer/video/BMPlayer.swift @@ -143,7 +143,7 @@ open class BMPlayer: UIView { /// 用来保存时间状态 fileprivate var sumTime : TimeInterval = 0 - fileprivate var totalDuration : TimeInterval = 0 + var totalDuration : TimeInterval = 0 fileprivate var currentPosition : TimeInterval = 0 fileprivate var shouldSeekTo : TimeInterval = 0 @@ -706,7 +706,13 @@ extension BMPlayer: BMPlayerControlViewDelegate { case .touchDown: playerLayer?.onTimeSliderBegan() isSliderSliding = true - + + case .valueChanged: + let target = self.totalDuration * Double(slider.value) + + seekSmoothlyToTime(newChaseTime: CMTime(seconds: target, preferredTimescale: 10000)) + + break case .touchUpInside : isSliderSliding = false let target = self.totalDuration * Double(slider.value) diff --git a/kplayer/video/BMPlayerLayerView.swift b/kplayer/video/BMPlayerLayerView.swift index 2376de0..a32aaba 100644 --- a/kplayer/video/BMPlayerLayerView.swift +++ b/kplayer/video/BMPlayerLayerView.swift @@ -223,7 +223,7 @@ open class BMPlayerLayerView: UIView { let draggedTime = CMTime(value: Int64(secounds)*10000, timescale: 10000) // self.player!.cancelPendingPrerolls() self.playerItem!.cancelPendingSeeks() - self.player!.seek(to: draggedTime, toleranceBefore: CMTimeMake(value: 10, timescale: 1), toleranceAfter: CMTimeMake(value: 10, timescale: 1), completionHandler: { (finished) in + self.player!.seek(to: draggedTime, toleranceBefore: CMTimeMake(value: 1, timescale: 1), toleranceAfter: CMTimeMake(value: 1, timescale: 1), completionHandler: { (finished) in completion?() }) } else {