marcoschmickler 5 years ago
parent
commit
0bc1c2a01a
  1. 8
      kplayer.xcodeproj/project.pbxproj
  2. 73
      kplayer/AppDelegate.swift
  3. 35
      kplayer/core/HtmlParser.swift
  4. 13
      kplayer/core/ItemType.swift
  5. 11
      kplayer/core/MediaItem.swift
  6. 91
      kplayer/core/NetworkManager.swift
  7. 152
      kplayer/detail/BrowserController.swift
  8. 22
      kplayer/detail/DetailViewController.swift
  9. 16
      kplayer/detail/ItemCell.swift
  10. 49
      kplayer/detail/VideoController.swift
  11. 67
      kplayer/master/NetworkDelegate.swift
  12. 10
      kplayer/video/BMPlayer.swift
  13. 2
      kplayer/video/BMPlayerLayerView.swift

8
kplayer.xcodeproj/project.pbxproj

@ -18,6 +18,7 @@
1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; }; 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; };
1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; }; 1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; };
1C7365885FAF292F2221ED44 /* PhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* PhotoController.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 */; }; 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; };
1C7366DAC06047DE335EFC37 /* BMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736927EA28AFBEB25D7487 /* BMPlayer.swift */; }; 1C7366DAC06047DE335EFC37 /* BMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736927EA28AFBEB25D7487 /* BMPlayer.swift */; };
1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.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 */; }; 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; };
1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73602350ACE2436736F981 /* BrowserController.swift */; }; 1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73602350ACE2436736F981 /* BrowserController.swift */; };
1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.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 */; }; 1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */; };
1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; }; 1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; };
1C736CB96577F6A9A7BA03E8 /* BMPlayerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F924BD979294C3EE4A /* BMPlayerItem.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 = "<group>"; }; 1C736253AB7A95EA41B605B7 /* ItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemModel.swift; sourceTree = "<group>"; };
1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = "<group>"; }; 1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = "<group>"; };
1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; }; 1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
1C73643B4BBE7251CCF189B2 /* Downloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; };
1C7364709899FF62774B0199 /* VideoHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoHelper.swift; sourceTree = "<group>"; }; 1C7364709899FF62774B0199 /* VideoHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoHelper.swift; sourceTree = "<group>"; };
1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewControllerExtensions.swift; sourceTree = "<group>"; }; 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewControllerExtensions.swift; sourceTree = "<group>"; };
1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDelegate.swift; sourceTree = "<group>"; }; 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDelegate.swift; sourceTree = "<group>"; };
@ -102,6 +105,7 @@
1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = "<group>"; }; 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = "<group>"; };
1C736C7FFBDAC665AE04CB65 /* VideoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = "<group>"; }; 1C736C7FFBDAC665AE04CB65 /* VideoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = "<group>"; };
1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; };
1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParser.swift; sourceTree = "<group>"; };
1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; };
1C736DFBD072763248412F74 /* BMPlayerClearityChooseButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerClearityChooseButton.swift; sourceTree = "<group>"; }; 1C736DFBD072763248412F74 /* BMPlayerClearityChooseButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerClearityChooseButton.swift; sourceTree = "<group>"; };
1C736E51F1A03E3A1200BDB6 /* BMPlayerControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerControlView.swift; sourceTree = "<group>"; }; 1C736E51F1A03E3A1200BDB6 /* BMPlayerControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerControlView.swift; sourceTree = "<group>"; };
@ -186,6 +190,7 @@
1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */, 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */,
1C7367ECBD369A2A0C94C499 /* FileHelper.swift */, 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */,
1C7364709899FF62774B0199 /* VideoHelper.swift */, 1C7364709899FF62774B0199 /* VideoHelper.swift */,
1C73643B4BBE7251CCF189B2 /* Downloader.swift */,
); );
path = util; path = util;
sourceTree = "<group>"; sourceTree = "<group>";
@ -199,6 +204,7 @@
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */, 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */,
1C736B41C6AC33F3FA592C63 /* MediaModel.swift */, 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */,
1C73631C96E6C860833052CA /* ItemType.swift */, 1C73631C96E6C860833052CA /* ItemType.swift */,
1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */,
); );
path = core; path = core;
sourceTree = "<group>"; sourceTree = "<group>";
@ -506,6 +512,8 @@
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */, 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */,
1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */, 1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */,
1C7360F1D2CF83ECDD586B84 /* BMPlayerCompositionResourceDefinition.swift in Sources */, 1C7360F1D2CF83ECDD586B84 /* BMPlayerCompositionResourceDefinition.swift in Sources */,
1C736A06A2AD75B8C14EEBBE /* HtmlParser.swift in Sources */,
1C736667B944A494666AEEAE /* Downloader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

73
kplayer/AppDelegate.swift

@ -26,32 +26,55 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
let controller = masterNavigationController.topViewController as! MasterViewController let controller = masterNavigationController.topViewController as! MasterViewController
controller.delegate = NetworkDelegate() 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, NetworkManager.sharedInstance.favorites,
web, web,
] ]

35
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
}
}
}

13
kplayer/core/ItemType.swift

@ -7,15 +7,13 @@ import Foundation
enum ItemType: String, Codable, CustomStringConvertible { 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" case FAVROOT = "favroot"
/** /**
@ -33,6 +31,11 @@ enum ItemType: String, Codable, CustomStringConvertible {
*/ */
case VIDEO = "video" case VIDEO = "video"
/**
Ein Download-Progress Stellvertreter.
*/
case DOWNLOAD = "download"
/** /**
Web-Browser Web-Browser
*/ */

11
kplayer/core/MediaItem.swift

@ -41,9 +41,11 @@ class MediaItem: CustomDebugStringConvertible {
var length: TimeInterval? var length: TimeInterval?
var loop = true var loop = true
var thumbUrl: String? var thumbUrl: String?
var externalURL: String?
var local = false var local = false
var leaf = false var leaf = false
var cancelled = false
convenience init(model: MediaModel) { convenience init(model: MediaModel) {
self.init(name: model.name, path: model.path, root: model.root, type: model.type) 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. Absolute URL, unter der das Thumbnail des Items abgerufen werden kann.
*/ */
var thumbUrlAbsolute: String { var thumbUrlAbsolute: String {
if thumbUrl == nil {
return ""
}
if thumbUrl!.starts(with: "file:") { if thumbUrl!.starts(with: "file:") {
return thumbUrl! return thumbUrl!
} }
@ -156,6 +161,10 @@ class MediaItem: CustomDebugStringConvertible {
} }
var playerURL: URL? { var playerURL: URL? {
if let ext = externalURL {
return URL(string: ext)
}
if local { if local {
var file = FileHelper.getDocumentsDirectory() var file = FileHelper.getDocumentsDirectory()
@ -244,6 +253,6 @@ class MediaItem: CustomDebugStringConvertible {
} }
func isFolder() -> Bool { 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
} }
} }

91
kplayer/core/NetworkManager.swift

@ -17,6 +17,8 @@ class NetworkManager {
var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT) var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT)
var currentDownloads = [String : MediaItem]()
lazy var operationQueue: OperationQueue = { lazy var operationQueue: OperationQueue = {
var queue = OperationQueue() var queue = OperationQueue()
queue.name = "Backup queue" queue.name = "Backup queue"
@ -274,8 +276,35 @@ class NetworkManager {
} }
} }
func loadItems(_ item: MediaItem) { func loadItems(_ item: MediaItem) {
if item.local { 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 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)")
}
}
}
}
} }

152
kplayer/detail/BrowserController.swift

@ -7,24 +7,63 @@ import Foundation
import UIKit import UIKit
import WebBrowser import WebBrowser
import WebKit import WebKit
import Alamofire
class BrowserController : UIViewController, ItemController, WebBrowserDelegate, UINavigationControllerDelegate {
class BrowserController : UIViewController, ItemController, WebBrowserDelegate, UINavigationControllerDelegate, WKScriptMessageHandler, WKHTTPCookieStoreObserver {
var completionHandler: (() -> Void)? var completionHandler: (() -> Void)?
var currentItem : MediaItem?
var web : WebBrowserViewController?
func setItems(items: [MediaItem]) { func setItems(items: [MediaItem]) {
} }
func setCurrentItem(item: MediaItem) { func setCurrentItem(item: MediaItem) {
currentItem = item
} }
func setCompletionHandler(handler: @escaping (() -> Void)) { func setCompletionHandler(handler: @escaping (() -> Void)) {
completionHandler = handler completionHandler = handler
} }
func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
cookieStore.getAllCookies { cookies in
print(cookies)
}
}
override func viewDidLoad() { 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 webBrowserViewController.delegate = self
@ -38,19 +77,95 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
webBrowserViewController.isShowPageTitleInNavigationBar = true webBrowserViewController.isShowPageTitleInNavigationBar = true
// webBrowserViewController.customApplicationActivities = ... // webBrowserViewController.customApplicationActivities = ...
webBrowserViewController.loadURLString("https://www.xvideos.com")
webBrowserViewController.loadURLString(currentItem!.name)
webBrowserViewController.onOpenExternalAppHandler = { [weak self] _ in webBrowserViewController.onOpenExternalAppHandler = { [weak self] _ in
guard let `self` = self else { return } guard let `self` = self else { return }
self.completionHandler!() self.completionHandler!()
//self.navigationController?.popViewController(animated: true) //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?.delegate = self
navigationController?.pushViewController(webBrowserViewController, animated: true) navigationController?.pushViewController(webBrowserViewController, animated: true)
// present(webBrowserViewController, animated: false) // 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?) { 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)
}
}
} }

22
kplayer/detail/DetailViewController.swift

@ -350,7 +350,24 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
} else if sectionItem.isPic() { } else if sectionItem.isPic() {
self.showPhotos(sectionItem.children) self.showPhotos(sectionItem.children)
} else if sectionItem.isWeb() { } 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) self.present(navController, animated: false, completion: nil)
} }
func showWeb(_ im: [MediaItem]) {
func showWeb(selectedItem: MediaItem) {
let pc = BrowserController() let pc = BrowserController()
pc.setCurrentItem(item: selectedItem)
pc.completionHandler = { pc.completionHandler = {
self.dismiss(animated: true, completion: nil); self.dismiss(animated: true, completion: nil);

16
kplayer/detail/ItemCell.swift

@ -13,6 +13,7 @@ class ItemCell: UICollectionViewCell {
var item: MediaItem? var item: MediaItem?
var image: UIImageView! var image: UIImageView!
var progress: UIProgressView!
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
@ -25,15 +26,26 @@ class ItemCell: UICollectionViewCell {
image = UIImageView(frame: frame) image = UIImageView(frame: frame)
image.contentMode = UIView.ContentMode.scaleAspectFit 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: constraints:
"H:|[imag]|", "H:|[imag]|",
"V:|[imag]|")
"H:|[prog]|",
"V:|[imag][prog]|")
} }
func setItem(_ item: MediaItem) { func setItem(_ item: MediaItem) {
self.item = item self.item = item
if item.type == ItemType.DOWNLOAD {
progress.progress = Float(item.length!)
return
}
if let _ = item.thumbUrl, let nsurl = URL(string: item.thumbUrlAbsolute) { if let _ = item.thumbUrl, let nsurl = URL(string: item.thumbUrlAbsolute) {
image.hnk_setImageFromURL(nsurl, placeholder: defaultImage) image.hnk_setImageFromURL(nsurl, placeholder: defaultImage)
} else { } else {

49
kplayer/detail/VideoController.swift

@ -14,6 +14,8 @@ protocol ItemController {
func setCompletionHandler(handler: @escaping (() -> Void)) func setCompletionHandler(handler: @escaping (() -> Void))
} }
// Trimmer: https://github.com/Tomohiro-Yamashita/VideoTimelineView
class VideoController: UIViewController, ItemController, BMPlayerDelegate { class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var player = BMPlayer() var player = BMPlayer()
var currentItem: MediaItem? var currentItem: MediaItem?
@ -137,11 +139,11 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
@objc func favorite(_ sender: AnyObject) { @objc func favorite(_ sender: AnyObject) {
print("favorite") print("favorite")
let inProgress = NetworkManager.sharedInstance.currentDownloads.count
let alertController = UIAlertController(title: "In Progress: \(inProgress)", message: "Download", preferredStyle: .alert)
if let c = currentSnapshot { if let c = currentSnapshot {
if (c.loop) { if (c.loop) {
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
let oneAction = UIAlertAction(title: "One", style: .default) { (action) in let oneAction = UIAlertAction(title: "One", style: .default) { (action) in
self.save(currentSnapshot: c, name: "1") self.save(currentSnapshot: c, name: "1")
} }
@ -151,28 +153,40 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
let threeAction = UIAlertAction(title: "Three", style: .default) { (action) in let threeAction = UIAlertAction(title: "Three", style: .default) { (action) in
self.save(currentSnapshot: c, name: "3") self.save(currentSnapshot: c, name: "3")
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
}
alertController.addAction(oneAction) alertController.addAction(oneAction)
alertController.addAction(twoAction) alertController.addAction(twoAction)
alertController.addAction(threeAction) 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) { func save(currentSnapshot c: MediaItem, name: String) {
do { do {
try FileHelper.createDir(name: name) 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 var dur = loopEnd - loopStart
if (dur < 0) { if (dur < 0) {
return return
@ -184,9 +198,16 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var s : MediaModel; var s : MediaModel;
if (c.type == ItemType.SNAPSHOT && c.parent != nil) { if (c.type == ItemType.SNAPSHOT && c.parent != nil) {
s = c.parent!.toMediaModel() 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 { else {
s = c.toMediaModel() s = c.toMediaModel()

67
kplayer/master/NetworkDelegate.swift

@ -21,17 +21,14 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
completionHandler(selectedItem) 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) NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter)
} }
else if selectedItem.isDetails() { else if selectedItem.isDetails() {
NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter) 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.children = i
selectedItem.sort() 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)
}) })
} }

10
kplayer/video/BMPlayer.swift

@ -143,7 +143,7 @@ open class BMPlayer: UIView {
/// ///
fileprivate var sumTime : TimeInterval = 0 fileprivate var sumTime : TimeInterval = 0
fileprivate var totalDuration : TimeInterval = 0
var totalDuration : TimeInterval = 0
fileprivate var currentPosition : TimeInterval = 0 fileprivate var currentPosition : TimeInterval = 0
fileprivate var shouldSeekTo : TimeInterval = 0 fileprivate var shouldSeekTo : TimeInterval = 0
@ -706,7 +706,13 @@ extension BMPlayer: BMPlayerControlViewDelegate {
case .touchDown: case .touchDown:
playerLayer?.onTimeSliderBegan() playerLayer?.onTimeSliderBegan()
isSliderSliding = true isSliderSliding = true
case .valueChanged:
let target = self.totalDuration * Double(slider.value)
seekSmoothlyToTime(newChaseTime: CMTime(seconds: target, preferredTimescale: 10000))
break
case .touchUpInside : case .touchUpInside :
isSliderSliding = false isSliderSliding = false
let target = self.totalDuration * Double(slider.value) let target = self.totalDuration * Double(slider.value)

2
kplayer/video/BMPlayerLayerView.swift

@ -223,7 +223,7 @@ open class BMPlayerLayerView: UIView {
let draggedTime = CMTime(value: Int64(secounds)*10000, timescale: 10000) let draggedTime = CMTime(value: Int64(secounds)*10000, timescale: 10000)
// self.player!.cancelPendingPrerolls() // self.player!.cancelPendingPrerolls()
self.playerItem!.cancelPendingSeeks() 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?() completion?()
}) })
} else { } else {

Loading…
Cancel
Save