Browse Source

Delegates

master
marcoschmickler 5 years ago
parent
commit
4b1ffa6193
  1. 4
      kplayer.xcodeproj/project.pbxproj
  2. 8
      kplayer/AppDelegate.swift
  3. 39
      kplayer/core/ItemModel.swift
  4. 31
      kplayer/core/MediaItem.swift
  5. 1
      kplayer/core/MediaModel.swift
  6. 78
      kplayer/core/NetworkManager.swift
  7. 2
      kplayer/core/ThumbnailCache.swift
  8. 9
      kplayer/detail/DetailViewController.swift
  9. 37
      kplayer/detail/VideoController.swift
  10. 49
      kplayer/master/MasterViewController.swift
  11. 59
      kplayer/master/NetworkDelegate.swift
  12. 16
      kplayer/photo/PhotoController.swift
  13. 2
      kplayer/readme.md
  14. 27
      kplayer/util/FileHelper.swift
  15. 4
      kplayer/util/UploadOperation.swift
  16. 64
      kplayer/util/VideoHelper.swift
  17. 16
      kplayer/util/stringutil.swift

4
kplayer.xcodeproj/project.pbxproj

@ -25,6 +25,7 @@
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; }; 1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; };
1C7367FA10AE13598FDDE865 /* BMPlayerProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365F45D765A218FFC100F /* BMPlayerProtocols.swift */; }; 1C7367FA10AE13598FDDE865 /* BMPlayerProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365F45D765A218FFC100F /* BMPlayerProtocols.swift */; };
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.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 */; }; 1C73688D13E5A804880C8768 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */; };
1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B794396F2E50387B8F2 /* stringutil.swift */; }; 1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B794396F2E50387B8F2 /* stringutil.swift */; };
1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.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 = "<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>"; };
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>"; };
1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerItem.swift; sourceTree = "<group>"; }; 1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerItem.swift; sourceTree = "<group>"; };
@ -181,6 +183,7 @@
1C736B794396F2E50387B8F2 /* stringutil.swift */, 1C736B794396F2E50387B8F2 /* stringutil.swift */,
1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */, 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */,
1C7367ECBD369A2A0C94C499 /* FileHelper.swift */, 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */,
1C7364709899FF62774B0199 /* VideoHelper.swift */,
); );
path = util; path = util;
sourceTree = "<group>"; sourceTree = "<group>";
@ -496,6 +499,7 @@
1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */, 1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */,
1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */, 1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */,
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */, 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */,
1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

8
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_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_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: "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: "fetish", path:"", root: "/srv/samba/ren/fetish", type: ItemType.VIDEOROOT),
MediaItem(name: "fjoy", path:"", root: "/srv/samba/ren/fjoy", type: ItemType.PICROOT), 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: "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: "ten", path:"", root: "/srv/samba/ren/ten", type: ItemType.PICROOT),
MediaItem(name: "leg", path:"", root: "/srv/samba/ren/leg", 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: "ang", path:"", root: "/srv/samba/ren/ang", type: ItemType.PICROOT),
@ -56,6 +56,12 @@ google
web, web,
] ]
NetworkManager.sharedInstance.alive()
Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { (t) in
NetworkManager.sharedInstance.alive()
}
return true return true
} }

39
kplayer/core/ItemModel.swift

@ -10,12 +10,19 @@ class ItemModel {
var itemsMap = Dictionary<String, MediaItem>() var itemsMap = Dictionary<String, MediaItem>()
func addItem(_ item: MediaItem) { func addItem(_ item: MediaItem) {
if item.path == "" {
item.index = items.count
items.append(item)
return
}
let path = item.encodedDir! let path = item.encodedDir!
var folder = itemsMap[path] var folder = itemsMap[path]
if folder == nil { if folder == nil {
folder = MediaItem(name: item.path, path: item.path, root: item.root, type: ItemType.FOLDER) folder = MediaItem(name: item.path, path: item.path, root: item.root, type: ItemType.FOLDER)
folder!.loaded = true folder!.loaded = true
folder!.local = item.local
itemsMap[path] = folder! itemsMap[path] = folder!
items.append(folder!) items.append(folder!)
} }
@ -30,4 +37,36 @@ class ItemModel {
itemsMap.removeAll() 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
}
}
}
}
} }

31
kplayer/core/MediaItem.swift

@ -42,12 +42,15 @@ class MediaItem: CustomDebugStringConvertible {
var loop = true var loop = true
var thumbUrl: String? var thumbUrl: String?
var local = 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)
self.time = model.time self.time = model.time
self.length = model.length self.length = model.length
self.loop = model.loop self.loop = model.loop
self.thumbUrl = model.thumbURL
for m in model.children { for m in model.children {
let item = MediaItem(model: m) let item = MediaItem(model: m)
@ -70,6 +73,8 @@ class MediaItem: CustomDebugStringConvertible {
model.time = time ?? 0 model.time = time ?? 0
model.loop = loop model.loop = loop
model.length = length ?? 0 model.length = length ?? 0
model.thumbURL = thumbUrl
return model return model
} }
@ -146,6 +151,15 @@ class MediaItem: CustomDebugStringConvertible {
} }
var playerURL: URL? { 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 enc = name.replacingOccurrences(of: " ", with: "%20")
let index = encodedDir!.index(encodedDir!.startIndex, offsetBy: 10) let index = encodedDir!.index(encodedDir!.startIndex, offsetBy: 10)
let s = NetworkManager.sharedInstance.vidurl + encodedDir!.substring(from:index) let s = NetworkManager.sharedInstance.vidurl + encodedDir!.substring(from:index)
@ -189,6 +203,12 @@ class MediaItem: CustomDebugStringConvertible {
children.sort(by: { children.sort(by: {
$0.sortName < $1.sortName $0.sortName < $1.sortName
}) })
var j = 0
for i in children {
i.index = j
j += 1
}
} }
func isWeb() -> Bool { func isWeb() -> Bool {
@ -200,14 +220,19 @@ class MediaItem: CustomDebugStringConvertible {
} }
func isVideo() -> Bool { 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 { 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 { 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
} }
} }

1
kplayer/core/MediaModel.swift

@ -13,6 +13,7 @@ public struct MediaModel : Codable {
var time = 0.0 var time = 0.0
var length = 0.0 var length = 0.0
var loop = false var loop = false
var thumbURL : String?
var children: [MediaModel] var children: [MediaModel]
let type: ItemType let type: ItemType

78
kplayer/core/NetworkManager.swift

@ -10,13 +10,13 @@ import Alamofire
class NetworkManager { class NetworkManager {
static let sharedInstance = NetworkManager() static let sharedInstance = NetworkManager()
let nodeurl = "http://linkstation:8081/"
let baseurl = "http://linkstation:8080/tomcat/media" let baseurl = "http://linkstation:8080/tomcat/media"
let vidurl = "http://linkstation:8089" let vidurl = "http://linkstation:8089"
var authenticated = false
var authenticated = true
var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT) var favorites = MediaItem(name: "fav", path:"", root: "", type: ItemType.FAVROOT)
var currentFav = MediaItem(name: "current", path:"", root: "", type: ItemType.FOLDER)
lazy var operationQueue: OperationQueue = { lazy var operationQueue: OperationQueue = {
var queue = 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 { func loadFavDirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void {
var res = [MediaItem]() 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 { do {
let jsonData = try FileHelper.getData(name: "fav.json") let jsonData = try FileHelper.getData(name: "fav.json")
let items = try JSONDecoder().decode(MediaModel.self, from: jsonData) let items = try JSONDecoder().decode(MediaModel.self, from: jsonData)
let loaded = MediaItem(model: items) let loaded = MediaItem(model: items)
currentFav.loaded = true
res = loaded.children res = loaded.children
// currentFav.children.append(loaded)
} catch { } catch {
print(error.localizedDescription) print(error.localizedDescription)
} }
@ -229,6 +276,10 @@ class NetworkManager {
} }
func loadItems(_ item: MediaItem) { func loadItems(_ item: MediaItem) {
if item.local {
return
}
if (item.type != ItemType.FOLDER && item.type != ItemType.DETAILS) { if (item.type != ItemType.FOLDER && item.type != ItemType.DETAILS) {
return return
} }
@ -311,7 +362,17 @@ class NetworkManager {
} }
func deleteThumb(_ path: String) { 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) print(response)
} }
} }
@ -325,6 +386,9 @@ class NetworkManager {
func saveItem(_ item: MediaItem) { func saveItem(_ item: MediaItem) {
print(item.children) print(item.children)
if (item.local) {
return
}
if (item.type != ItemType.VIDEO) { if (item.type != ItemType.VIDEO) {
return return
@ -356,13 +420,13 @@ class NetworkManager {
print("contained") print("contained")
} else { } else {
if let id = c.image, let imageData = id.jpegData(compressionQuality: 1.0) { 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) self.operationQueue.addOperation(op)
let thumb = id.scaleToSize(15 * 16, height: 15 * 9) let thumb = id.scaleToSize(15 * 16, height: 15 * 9)
if let imageDataT = thumb.jpegData(compressionQuality: 1.0) { if let imageDataT = thumb.jpegData(compressionQuality: 1.0) {
c.image = thumb 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) self.operationQueue.addOperation(opT)
} }

2
kplayer/core/ThumbnailCache.swift

@ -75,7 +75,7 @@ class ThumbnailCache {
let size = (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + sizeHi let size = (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + sizeHi
index += 4 index += 4
if size > 0 {
if size > 0 && (d.count >= index + size){
// var buf = //UnsafeMutablePointer<UInt8>(&bytes[index]) // var buf = //UnsafeMutablePointer<UInt8>(&bytes[index])
let part = d.subdata(in: index ..< (index + size)) let part = d.subdata(in: index ..< (index + size))
if let img = UIImage(data: part) { if let img = UIImage(data: part) {

9
kplayer/detail/DetailViewController.swift

@ -117,6 +117,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
self.dismiss(animated: true, completion: nil); self.dismiss(animated: true, completion: nil);
} }
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. 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) present(navController, animated: false, completion: nil)
} }
@ -138,9 +139,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
if let detail: MediaItem = self.detailItem { if let detail: MediaItem = self.detailItem {
if i.type == ItemType.VIDEO { 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 { } else {
if i.parent! !== detail { if i.parent! !== detail {
@ -335,7 +336,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
func showVideo(selectedItem: MediaItem) { func showVideo(selectedItem: MediaItem) {
var pc: ItemController? var pc: ItemController?
pc = AVPlayerController()
pc = VideoController()
pc!.setCurrentItem(item: selectedItem) pc!.setCurrentItem(item: selectedItem)
pc!.setItems(items: detailItem!.children) pc!.setItems(items: detailItem!.children)

37
kplayer/detail/VideoController.swift

@ -14,7 +14,7 @@ protocol ItemController {
func setCompletionHandler(handler: @escaping (() -> Void)) func setCompletionHandler(handler: @escaping (() -> Void))
} }
class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate {
class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var player = BMPlayer() var player = BMPlayer()
var currentItem: MediaItem? var currentItem: MediaItem?
var currentSnapshot: MediaItem? var currentSnapshot: MediaItem?
@ -52,16 +52,16 @@ class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate {
override func viewDidLoad() { override func viewDidLoad() {
super.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!] 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!] navigationItem.leftBarButtonItems = [backButton!, speedButton!, playButton!, loopButton!, aspectButton!, favButton!, reviewButton!]
@ -119,7 +119,16 @@ class AVPlayerController: UIViewController, ItemController, BMPlayerDelegate {
print("favorite") print("favorite")
if let c = currentSnapshot { 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 def = [BMPlayerResourceDefinition]()
var index = 0; var index = 0;
var count = 0; var count = 0;
if allItems.isEmpty {
print("no items found")
return
}
for i in allItems { for i in allItems {
let r = BMPlayerResourceDefinition(url: i.playerURL!, definition: i.name); let r = BMPlayerResourceDefinition(url: i.playerURL!, definition: i.name);
def.append(r) def.append(r)
@ -514,8 +529,6 @@ print("finish")
func bmPlayer(player: BMPlayer, playTimeDidChange currentTime: TimeInterval, totalTime: TimeInterval) { func bmPlayer(player: BMPlayer, playTimeDidChange currentTime: TimeInterval, totalTime: TimeInterval) {
if loopOption == 1 { if loopOption == 1 {
if currentTime > loopEnd { if currentTime > loopEnd {
print("loop")
player.seek(loopStart) player.seek(loopStart)
} }
} }

49
kplayer/master/MasterViewController.swift

@ -15,7 +15,7 @@ typealias Weiter = ([MediaItem]) -> Void
protocol MasterDelegate : DetailDelegate { protocol MasterDelegate : DetailDelegate {
func loadFolder(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) 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 { class MasterViewController: UITableViewController, UISearchResultsUpdating {
@ -131,7 +131,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating {
delegate!.loadFolder(selectedItem: selectedItem) { delegate!.loadFolder(selectedItem: selectedItem) {
(neu) in (neu) in
if neu.isDetails() {
if neu.isDetails() || neu.isVideo() {
self.gotoDetails(neu) self.gotoDetails(neu)
} else { } else {
self.gotoNextFolder(neu) self.gotoNextFolder(neu)
@ -151,48 +151,13 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating {
} }
fileprivate func gotoDetails(_ selectedItem: MediaItem) { 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) self.performSegue(withIdentifier: "showDetail", sender: self)
return
} }
delegate!.loadItem(selectedItem: selectedItem, completionHandler: weiter) delegate!.loadItem(selectedItem: selectedItem, completionHandler: weiter)
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
@ -215,12 +180,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating {
item.sort() item.sort()
var j = 0
for i in item.children {
i.index = j
j += 1
}
NetworkManager.sharedInstance.loadItems(item) NetworkManager.sharedInstance.loadItems(item)
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true controller.navigationItem.leftItemsSupplementBackButton = true

59
kplayer/master/NetworkDelegate.swift

@ -6,27 +6,53 @@
import Foundation import Foundation
class NetworkDelegate: MasterDelegate, DetailDelegate { 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 p = selectedItem.superRoot()
let dir = selectedItem.encodedDir! let dir = selectedItem.encodedDir!
let weiter:Weiter = {
(g) in
ItemModel().sortItems(selectedItem: selectedItem, children: g)
completionHandler(selectedItem)
}
if p.type == ItemType.FAVROOT { if p.type == ItemType.FAVROOT {
NetworkManager.sharedInstance.loadFavDirs(dir, completionHandler: completionHandler)
NetworkManager.sharedInstance.loadFavDirs(dir, completionHandler: weiter)
} }
else if p.type == ItemType.VIDEOROOT { else if p.type == ItemType.VIDEOROOT {
NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: completionHandler)
NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter)
} }
else if p.type == ItemType.WEBROOT { else if p.type == ItemType.WEBROOT {
completionHandler(p.children)
weiter(p.children)
} }
else { else {
NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: completionHandler)
NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter)
} }
} }
func loadFolder(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) -> Void { 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 (i, leaf) in
let details = MediaItem(name: selectedItem.name, path: selectedItem.path, root: selectedItem.root, type: ItemType.DETAILS) 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 let isVideo = selectedItem.parent != nil && selectedItem.parent!.type == ItemType.VIDEOROOT
if leaf || isVideo { 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) completionHandler(selectedItem)
}
else {
} else {
completionHandler(neu) completionHandler(neu)
} }
}
})
} }
func loadDetails(selectedItem: MediaItem, completionHandler: @escaping () -> ()) { func loadDetails(selectedItem: MediaItem, completionHandler: @escaping () -> ()) {
@ -94,7 +131,7 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
let p = c.snapshotDirPathForVideo + "\(ms).jpg" let p = c.snapshotDirPathForVideo + "\(ms).jpg"
let pt = c.snapshotDirPathForVideo + "\(ms)_thumb.jpg" let pt = c.snapshotDirPathForVideo + "\(ms)_thumb.jpg"
// NetworkManager.sharedInstance.deleteThumb(p)
NetworkManager.sharedInstance.deleteThumb(p)
NetworkManager.sharedInstance.deleteThumb(pt) NetworkManager.sharedInstance.deleteThumb(pt)
} }
} }

16
kplayer/photo/PhotoController.swift

@ -193,6 +193,22 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
} }
return 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() { override func loadView() {

2
kplayer/readme.md

@ -22,7 +22,7 @@ Ein Picture-Detail enthält Bilder.
Ein Web-Detail enthält Bookmarks. Ein Web-Detail enthält Bookmarks.
Der MasterViewController enthält eine Liste von Roots. 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. Gibt es Folder, wird die nächste Ebene eingeblendet.
Die Details-Liste wird im Details-Controller dargestellt. Die Details-Liste wird im Details-Controller dargestellt.

27
kplayer/util/FileHelper.swift

@ -19,4 +19,31 @@ struct FileHelper {
print(url) print(url)
return try Data(contentsOf: 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 []
}
} }

4
kplayer/util/UploadOperation.swift

@ -31,7 +31,9 @@ class UploadOperation: Operation {
multipartFormData.append(self.path.data(using: String.Encoding.utf8)!, withName: "name") multipartFormData.append(self.path.data(using: String.Encoding.utf8)!, withName: "name")
}, with: URLRequest(url: URL(string: baseUrl)!, method: HTTPMethod.post)) }, with: URLRequest(url: URL(string: baseUrl)!, method: HTTPMethod.post))
.response(completionHandler: { data in .response(completionHandler: { data in
print(data.data!)
if let d = data.data {
print(String(decoding: d, as: UTF8.self))
}
}) })
} catch { } catch {
print("Upload Error") print("Upload Error")

64
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)
}
}
}

16
kplayer/util/stringutil.swift

@ -38,6 +38,22 @@ public extension String {
return substring(from: ix) return substring(from: ix)
} }
func substringAfter(_ str: String) -> String {
if let range = range(of: str) {
let substring = self[range.upperBound..<endIndex] // ab
return String(substring)
}
return self
}
func substringBefore(_ str: String) -> String {
if let range = range(of: str, options: .backwards) {
let substring = self[..<range.lowerBound] // ab
return String(substring)
}
return self
}
func length() -> Int { func length() -> Int {
return count return count
} }

Loading…
Cancel
Save