Browse Source

Refactor VIDEOFOLDER

master
marcoschmickler 4 years ago
parent
commit
4d0844d5e2
  1. 8
      kplayer.xcodeproj/project.pbxproj
  2. 2
      kplayer/AppDelegate.swift
  3. 7
      kplayer/core/DatabaseManager.swift
  4. 2
      kplayer/core/ItemModel.swift
  5. 15
      kplayer/core/ItemType.swift
  6. 47
      kplayer/core/LocalManager.swift
  7. 7
      kplayer/core/MediaItem.swift
  8. 6
      kplayer/core/NetworkManager.swift
  9. 12
      kplayer/detail/BrowserController.swift
  10. 143
      kplayer/detail/DetailViewController+Show.swift
  11. 180
      kplayer/detail/DetailViewController.swift
  12. 201
      kplayer/master/MasterViewController.swift
  13. 13
      kplayer/master/NetworkDelegate.swift
  14. 21
      kplayer/util/UIViewController+Alert.swift

8
kplayer.xcodeproj/project.pbxproj

@ -25,6 +25,7 @@
1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */; }; 1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */; };
1C73666A07CF2416B1B8D3F0 /* KSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C94157754DE1C808173 /* KSettingsModel.swift */; }; 1C73666A07CF2416B1B8D3F0 /* KSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C94157754DE1C808173 /* KSettingsModel.swift */; };
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; }; 1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; };
1C7367084839D2E8B180DB74 /* UIViewController+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360295486647982CFEACF /* UIViewController+Alert.swift */; };
1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; }; 1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; };
1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; }; 1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; };
1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73625012D50E457D18A785 /* kplayer.js */; }; 1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73625012D50E457D18A785 /* kplayer.js */; };
@ -46,6 +47,7 @@
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; }; 1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; };
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; }; 1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; };
1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; }; 1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; };
1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */; };
1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */; }; 1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */; };
1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */; }; 1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */; };
1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736677D4EF2437358B2387 /* Utility.swift */; }; 1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736677D4EF2437358B2387 /* Utility.swift */; };
@ -82,6 +84,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1C73602350ACE2436736F981 /* BrowserController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserController.swift; sourceTree = "<group>"; }; 1C73602350ACE2436736F981 /* BrowserController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserController.swift; sourceTree = "<group>"; };
1C7360295486647982CFEACF /* UIViewController+Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Alert.swift"; sourceTree = "<group>"; };
1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = "<group>"; }; 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = "<group>"; };
1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = "<group>"; }; 1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = "<group>"; };
1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = "<group>"; }; 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = "<group>"; };
@ -118,6 +121,7 @@
1C7369F53095B7A4D65679C2 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; }; 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsView.swift; sourceTree = "<group>"; }; 1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsView.swift; sourceTree = "<group>"; };
1C736AE5021E3D985FE3402D /* KSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettings.swift; sourceTree = "<group>"; }; 1C736AE5021E3D985FE3402D /* KSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettings.swift; sourceTree = "<group>"; };
1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DetailViewController+Show.swift"; sourceTree = "<group>"; };
1C736B41C6AC33F3FA592C63 /* MediaModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaModel.swift; sourceTree = "<group>"; }; 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaModel.swift; sourceTree = "<group>"; };
1C736B794396F2E50387B8F2 /* stringutil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = stringutil.swift; sourceTree = "<group>"; }; 1C736B794396F2E50387B8F2 /* stringutil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = stringutil.swift; sourceTree = "<group>"; };
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>"; };
@ -179,6 +183,7 @@
1C7369F53095B7A4D65679C2 /* DetailViewController.swift */, 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */,
1C73602350ACE2436736F981 /* BrowserController.swift */, 1C73602350ACE2436736F981 /* BrowserController.swift */,
1C7366C09381DC0052B52B69 /* EditItemView.swift */, 1C7366C09381DC0052B52B69 /* EditItemView.swift */,
1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */,
); );
path = detail; path = detail;
sourceTree = "<group>"; sourceTree = "<group>";
@ -233,6 +238,7 @@
1C736DCD945ABAE984FF43EF /* KNetworkProtocol.swift */, 1C736DCD945ABAE984FF43EF /* KNetworkProtocol.swift */,
1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */, 1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */,
1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */, 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */,
1C7360295486647982CFEACF /* UIViewController+Alert.swift */,
); );
path = util; path = util;
sourceTree = "<group>"; sourceTree = "<group>";
@ -574,6 +580,8 @@
1C7365C59F72C29EA41C8717 /* SVideoModel.swift in Sources */, 1C7365C59F72C29EA41C8717 /* SVideoModel.swift in Sources */,
1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */, 1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */,
1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */, 1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */,
1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */,
1C7367084839D2E8B180DB74 /* UIViewController+Alert.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

2
kplayer/AppDelegate.swift

@ -49,7 +49,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT) let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT)
web.loaded = true web.loaded = true
let google = MediaItem(name: "google", path:"www.google.de", root: "", type: ItemType.DETAILS)
let google = MediaItem(name: "google", path:"www.google.de", root: "", type: ItemType.PICFOLDER)
google.loaded = true google.loaded = true
web.children = [ google ] web.children = [ google ]
let urlPath = Bundle.main.url(forResource: "links", withExtension: "html") let urlPath = Bundle.main.url(forResource: "links", withExtension: "html")

7
kplayer/core/DatabaseManager.swift

@ -158,13 +158,16 @@ class DatabaseManager {
let results = try! managedObjectContext.fetch(fetchRequest) let results = try! managedObjectContext.fetch(fetchRequest)
for t in results { for t in results {
let tag = MediaItem(name: t.name!, path: t.name!, root: "tags", type: ItemType.VIDEO)
let tag = MediaItem(name: t.name!, path: t.name!, root: "tags", type: ItemType.TAG)
tag.loaded = true tag.loaded = true
let snapshots = t.tagged as! Set<KSnapshot> let snapshots = t.tagged as! Set<KSnapshot>
for s in snapshots { for s in snapshots {
let i = s.item! let i = s.item!
let sitem = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.VIDEO) let sitem = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.VIDEO)
sitem.loaded = true
sitem.parent = tag
let c = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.SNAPSHOT) let c = MediaItem(name: i.name!, path: i.path!, root: i.root!, type: ItemType.SNAPSHOT)
c.time = s.time c.time = s.time
c.length = s.length c.length = s.length
@ -176,6 +179,8 @@ class DatabaseManager {
c.scale = s.scale c.scale = s.scale
c.rating = Int(s.rating) c.rating = Int(s.rating)
c.thumbUrl = s.thumb c.thumbUrl = s.thumb
c.indexId = Int(s.index)
c.parent = sitem
sitem.children.append(c) sitem.children.append(c)
tag.children.append(sitem) tag.children.append(sitem)

2
kplayer/core/ItemModel.swift

@ -32,7 +32,7 @@ class ItemModel {
item.parent = folder item.parent = folder
if (item.isVideo()) { if (item.isVideo()) {
item.parent?.type = ItemType.VIDEO
item.parent?.type = ItemType.VIDEOFOLDER
} }
} }

15
kplayer/core/ItemType.swift

@ -31,6 +31,16 @@ enum ItemType: String, Codable, CustomStringConvertible {
*/ */
case FOLDER = "folder" case FOLDER = "folder"
/**
Repräsentiert ein Video. Hat keine Unterordner. Die Children sind Snapshots.
*/
case VIDEOFOLDER = "videofolder"
/**
Repräsentiert einen Ordner, der Videos oder Bilder-Ordner enthält. Wird im DetailViewController angezeigt.
*/
case PICFOLDER = "picfolder"
/** /**
Repräsentiert ein Video. Hat keine Unterordner. Die Children sind Snapshots. Repräsentiert ein Video. Hat keine Unterordner. Die Children sind Snapshots.
*/ */
@ -61,11 +71,6 @@ enum ItemType: String, Codable, CustomStringConvertible {
*/ */
case PICS = "pics" case PICS = "pics"
/**
Repräsentiert einen Ordner, der Videos oder Bilder-Ordner enthält. Wird im DetailViewController angezeigt.
*/
case DETAILS = "details"
var description: String { var description: String {
return self.rawValue return self.rawValue
} }

47
kplayer/core/LocalManager.swift

@ -51,6 +51,8 @@ class LocalManager {
} }
func saveFavDir(url: URL, item: MediaItem) -> Void { func saveFavDir(url: URL, item: MediaItem) -> Void {
DatabaseManager.sharedInstance.saveItemMetaData(item)
for c in item.children { for c in item.children {
let t = c.time let t = c.time
let ms = Int(t * 1000) let ms = Int(t * 1000)
@ -105,7 +107,7 @@ class LocalManager {
var itemsMap = Dictionary<String, MediaItem>() var itemsMap = Dictionary<String, MediaItem>()
let fi = MediaItem(name: "images", path: "", root: "fav", type: ItemType.DETAILS)
let fi = MediaItem(name: "images", path: "", root: "fav", type: ItemType.PICFOLDER)
fi.local = true fi.local = true
fi.loaded = true fi.loaded = true
@ -222,6 +224,49 @@ class LocalManager {
completionHandler(res) completionHandler(res)
} }
/*
exclude mp4 from backup
create images folder
extract zip to images folder
*/
func prepareLocalFolder() {
do {
let i = try FileHelper.createDir(name: "incoming")
let files = FileHelper.listFiles(name: "")
for f in files {
if f.pathExtension == "mp4" {
var dest = i.appendingPathComponent(f.lastPathComponent)
try FileManager.default.moveItem(at: f, to: dest)
try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true)
}
}
} catch {
}
do {
let i = try FileHelper.createDir(name: "images")
let files = FileHelper.listFiles(name: "download")
for f in files {
print(f)
if f.pathExtension == "zip" {
var dest = i.appendingPathComponent(f.lastPathComponent)
do {
try FileManager.default.createDirectory(at: dest, withIntermediateDirectories: true, attributes: nil)
try FileManager.default.unzipItem(at: f, to: dest)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
try FileManager.default.removeItem(at: f)
try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true)
}
}
} catch {
}
}
// old // old
func loadFav2Dirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void { func loadFav2Dirs(_ rootParam: String, completionHandler: @escaping Weiter) -> Void {
let files = FileHelper.listFiles(name: "1") let files = FileHelper.listFiles(name: "1")

7
kplayer/core/MediaItem.swift

@ -303,15 +303,16 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
} }
func isPic() -> Bool { func isPic() -> Bool {
type == ItemType.PICS || type == ItemType.DETAILS
type == ItemType.PICS || type == ItemType.PICFOLDER
} }
func isVideo() -> Bool { func isVideo() -> Bool {
type == ItemType.VIDEO || type == ItemType.SNAPSHOT
type == ItemType.VIDEOFOLDER || type == ItemType.VIDEO || type == ItemType.SNAPSHOT
} }
func isDetails() -> Bool { func isDetails() -> Bool {
isWeb() || isPic() || isVideo()
type == ItemType.VIDEOFOLDER || type == ItemType.PICFOLDER || type == ItemType.WEB || type == ItemType.TAG
// isWeb() || isPic() || isVideo()
} }
func isFolder() -> Bool { func isFolder() -> Bool {

6
kplayer/core/NetworkManager.swift

@ -169,10 +169,10 @@ class NetworkManager {
var t = ItemType.FOLDER var t = ItemType.FOLDER
if (type == "video") { if (type == "video") {
t = ItemType.VIDEO
t = ItemType.VIDEOFOLDER
} }
if (type == "pictures") { if (type == "pictures") {
t = ItemType.DETAILS
t = ItemType.PICFOLDER
} }
if (type == "web") { if (type == "web") {
t = ItemType.WEB t = ItemType.WEB
@ -222,7 +222,7 @@ class NetworkManager {
return return
} }
if (item.type != ItemType.VIDEO && item.type != ItemType.DETAILS) {
if (item.type != ItemType.VIDEOFOLDER && item.type != ItemType.PICFOLDER) {
return return
} }

12
kplayer/detail/BrowserController.swift

@ -333,16 +333,4 @@ print(url?.absoluteString)
} }
} }
func showAlert(title:String, message:String ) {
let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alertVC.addAction(okAction)
DispatchQueue.main.async() { () -> Void in
self.present(alertVC, animated: true, completion: nil)
}
}
} }

143
kplayer/detail/DetailViewController+Show.swift

@ -0,0 +1,143 @@
//
// Created by Marco Schmickler on 23.01.22.
// Copyright (c) 2022 Marco Schmickler. All rights reserved.
//
import Foundation
import UIKit
import SwiftUI
extension DetailViewController {
func showDetails(sectionItem: MediaItem, selectedItem: MediaItem) {
if sectionItem.isVideo() {
showVideo(selectedItem: selectedItem)
} else if sectionItem.isPic() {
showPhotos(sectionItem.children)
} else if sectionItem.isWeb() {
showWeb(selectedItem: selectedItem)
} else if sectionItem.type == ItemType.DOWNLOAD {
showDownload(sectionItem: sectionItem)
}
}
func showPhotos(_ im: [MediaItem]) {
let pc = MediaPhotoController()
pc.items = im
pc.completionHandler = {
self.dismiss(animated: true, completion: nil);
}
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack.
navController.modalPresentationStyle = .fullScreen
self.present(navController, animated: false, completion: nil)
}
func showWeb(selectedItem: MediaItem) {
if selectedItem.name.contains("strip") {
let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://")
UIApplication.shared.open(URL(string: chromeURL)!)
return
}
let pc = BrowserController()
pc.setCurrentItem(item: selectedItem)
pc.completionHandler = {
self.dismiss(animated: true, completion: nil);
}
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack.
navController.modalPresentationStyle = .fullScreen
self.present(navController, animated: false, completion: nil)
}
func showVideo(selectedItem: MediaItem) {
var se = selectedItem
var children = [MediaItem]()
var clonedChildren = [MediaItem]()
var baseItem = selectedItem
if baseItem.type == ItemType.SNAPSHOT {
baseItem = selectedItem.parent!
}
children = baseItem.children
clonedChildren = baseItem.clone().children
let model = SVideoModel(allItems: children, currentSnapshot: se, baseItem: baseItem)
model.edit = delegate!.settings().edit
model.loop = delegate!.settings().autoloop
model.zoomed = delegate!.settings().zoomed
let player = SVideoPlayer(completionHandler: { saved in
if saved {
baseItem.children = model.allItems
self.delegate!.saveItem(selectedItem: baseItem)
}
else {
baseItem.children = clonedChildren
}
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
self.dismiss(animated: true, completion: nil);
}, model: model)
player
let pc = UIHostingController(rootView: player)
pc.view.backgroundColor = .black
getWindow().rootViewController!.definesPresentationContext = true
pc.modalPresentationStyle = .overCurrentContext
getWindow().rootViewController!.present(pc, animated: true)
}
func showDownload(sectionItem: MediaItem) {
let cancelAlert = UIAlertController(title: "Abort Download", message: "All data will be lost.", preferredStyle: UIAlertController.Style.alert)
cancelAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
sectionItem.cancelled = true
if sectionItem.length == 1.0 {
NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: sectionItem.name)
}
}))
cancelAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in
print("Handle Cancel Logic here")
}))
self.present(cancelAlert, animated: true)
}
func showAllVideos(_ item: MediaItem) {
let composition = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO)
for d in item.children {
if d.children.isEmpty {
let clone = d.clone()
clone.parent = composition
clone.type = ItemType.SNAPSHOT
composition.children.append(clone)
}
else {
for c in d.children {
let clone = c.clone()
clone.parent = composition
composition.children.append(clone)
}
}
}
showVideo(selectedItem: composition.children[0])
}
func getWindow() -> UIWindow {
let delegate2 = UIApplication.shared.delegate!
return delegate2.window as! UIWindow
}
}

180
kplayer/detail/DetailViewController.swift

@ -105,41 +105,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
@objc func fileBrowser() { @objc func fileBrowser() {
let d = FileHelper.getDocumentsDirectory() let d = FileHelper.getDocumentsDirectory()
do {
let i = try FileHelper.createDir(name: "incoming")
let files = FileHelper.listFiles(name: "")
for f in files {
if f.pathExtension == "mp4" {
var dest = i.appendingPathComponent(f.lastPathComponent)
try FileManager.default.moveItem(at: f, to: dest)
try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true)
}
}
} catch {
}
do {
let i = try FileHelper.createDir(name: "images")
let files = FileHelper.listFiles(name: "download")
for f in files {
print(f)
if f.pathExtension == "zip" {
var dest = i.appendingPathComponent(f.lastPathComponent)
do {
try FileManager.default.createDirectory(at: dest, withIntermediateDirectories: true, attributes: nil)
try FileManager.default.unzipItem(at: f, to: dest)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
try FileManager.default.removeItem(at: f)
try FileHelper.setExcludeFromiCloudBackup(&dest, isExcluded: true)
}
}
} catch {
}
LocalManager.sharedInstance.prepareLocalFolder()
let fileBrowser = FileBrowser(initialPath: d, allowEditing: true) let fileBrowser = FileBrowser(initialPath: d, allowEditing: true)
fileBrowser.modalPresentationStyle = .fullScreen fileBrowser.modalPresentationStyle = .fullScreen
@ -147,11 +113,13 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
present(fileBrowser, animated: true, completion: nil) present(fileBrowser, animated: true, completion: nil)
} }
// Favorites Button
@objc func favorites() { @objc func favorites() {
showFavoritesOnly = !showFavoritesOnly showFavoritesOnly = !showFavoritesOnly
collectionView.reloadData() collectionView.reloadData()
} }
// Edit Settings Button
@objc func settings() { @objc func settings() {
let kv = KSettingsView(kSettings: LocalManager.sharedInstance.settings, completionHandler: { let kv = KSettingsView(kSettings: LocalManager.sharedInstance.settings, completionHandler: {
self.dismiss(animated: true, completion: nil); self.dismiss(animated: true, completion: nil);
@ -163,12 +131,12 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
present(navController, animated: false, completion: nil) present(navController, animated: false, completion: nil)
} }
// Overview Button
@objc func overview() { @objc func overview() {
var i = [MediaItem]() var i = [MediaItem]()
if let d = detailItem { if let d = detailItem {
showAll(d)
showAllVideos(d)
return return
let pc = MediaPhotoController() let pc = MediaPhotoController()
@ -192,30 +160,8 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
} }
} }
private func showAll(_ item: MediaItem) {
let composition = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO)
for d in item.children {
if d.children.isEmpty {
let clone = d.clone()
clone.parent = composition
clone.type = ItemType.SNAPSHOT
composition.children.append(clone)
}
else {
for c in d.children {
let clone = c.clone()
clone.parent = composition
composition.children.append(clone)
}
}
}
showVideo(selectedItem: composition.children[0])
}
@objc func refreshItems(_ notification: Notification) { @objc func refreshItems(_ notification: Notification) {
if notification.object == nil { if notification.object == nil {
if self.collectionView != nil { if self.collectionView != nil {
self.collectionView.reloadData() self.collectionView.reloadData()
@ -265,6 +211,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
} }
} }
// delete item
@objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { @objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
if (gestureRecognizer.state != UIGestureRecognizer.State.ended) { if (gestureRecognizer.state != UIGestureRecognizer.State.ended) {
return; return;
@ -332,6 +279,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
return 0 return 0
} }
// show cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ItemCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ItemCell
@ -351,6 +299,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
return cell return cell
} }
// show Section header
func collectionView(_ collectionView: UICollectionView, func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String, viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView { at indexPath: IndexPath) -> UICollectionReusableView {
@ -370,17 +319,17 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
} }
} }
// select item
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let detail: MediaItem = self.detailItem { if let detail: MediaItem = self.detailItem {
print(detail.toJSON()) print(detail.toJSON())
// NetworkManager.sharedInstance.saveFavDir(name: "fav.json", item: detail)
let sectionItem = detail.children[indexPath.section] let sectionItem = detail.children[indexPath.section]
currentItem = sectionItem currentItem = sectionItem
var selectedItem = currentItem! var selectedItem = currentItem!
let weiter: () -> Void = { let weiter: () -> Void = {
if (sectionItem.type == ItemType.VIDEO || sectionItem.loaded) { if (sectionItem.type == ItemType.VIDEO || sectionItem.loaded) {
if indexPath.item >= sectionItem.children.count { if indexPath.item >= sectionItem.children.count {
print(sectionItem.name) print(sectionItem.name)
@ -389,119 +338,14 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
print(selectedItem.name) print(selectedItem.name)
} }
} }
if sectionItem.isVideo() {
self.showVideo(selectedItem: selectedItem)
} else if sectionItem.isPic() {
self.showPhotos(sectionItem.children)
} else if sectionItem.isWeb() {
self.showWeb(selectedItem: selectedItem)
}
if sectionItem.type == ItemType.DOWNLOAD {
let cancelAlert = UIAlertController(title: "Abort Download", message: "All data will be lost.", preferredStyle: UIAlertController.Style.alert)
cancelAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
sectionItem.cancelled = true
if sectionItem.length == 1.0 {
NetworkManager.sharedInstance.currentDownloads.removeValue(forKey: sectionItem.name)
}
}))
cancelAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in
print("Handle Cancel Logic here")
}))
self.present(cancelAlert, animated: true)
}
self.showDetails(sectionItem: sectionItem, selectedItem: selectedItem)
} }
delegate!.loadDetails(selectedItem: currentItem!, completionHandler: weiter) delegate!.loadDetails(selectedItem: currentItem!, completionHandler: weiter)
} }
} }
func showPhotos(_ im: [MediaItem]) {
let pc = MediaPhotoController()
pc.items = im
pc.completionHandler = {
self.dismiss(animated: true, completion: nil);
}
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack.
navController.modalPresentationStyle = .fullScreen
self.present(navController, animated: false, completion: nil)
}
func showWeb(selectedItem: MediaItem) {
if selectedItem.name.contains("strip") {
let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://")
UIApplication.shared.open(URL(string: chromeURL)!)
return
}
let pc = BrowserController()
pc.setCurrentItem(item: selectedItem)
pc.completionHandler = {
self.dismiss(animated: true, completion: nil);
}
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack.
navController.modalPresentationStyle = .fullScreen
self.present(navController, animated: false, completion: nil)
}
func getWindow() -> UIWindow {
let delegate2 = UIApplication.shared.delegate!
return delegate2.window as! UIWindow
}
func showVideo(selectedItem: MediaItem) {
var se = selectedItem
var children = [MediaItem]()
var clonedChildren = [MediaItem]()
var baseItem = selectedItem
if baseItem.type == ItemType.SNAPSHOT {
baseItem = selectedItem.parent!
}
children = baseItem.children
clonedChildren = baseItem.clone().children
let model = SVideoModel(allItems: children, currentSnapshot: se, baseItem: baseItem)
model.edit = delegate!.settings().edit
model.loop = delegate!.settings().autoloop
model.zoomed = delegate!.settings().zoomed
let player = SVideoPlayer(completionHandler: { saved in
if saved {
baseItem.children = model.allItems
self.delegate!.saveItem(selectedItem: baseItem)
}
else {
baseItem.children = clonedChildren
}
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
self.dismiss(animated: true, completion: nil);
}, model: model)
player
let pc = UIHostingController(rootView: player)
pc.view.backgroundColor = .black
getWindow().rootViewController!.definesPresentationContext = true
pc.modalPresentationStyle = .overCurrentContext
getWindow().rootViewController!.present(pc, animated: true)
}
// Drag and drop
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
if let detail: MediaItem = self.detailItem { if let detail: MediaItem = self.detailItem {

201
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 (MediaItem) -> Void)
func loadItemDetails(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void)
} }
class MasterViewController: UITableViewController, UISearchResultsUpdating, UITableViewDropDelegate, UIDocumentPickerDelegate { class MasterViewController: UITableViewController, UISearchResultsUpdating, UITableViewDropDelegate, UIDocumentPickerDelegate {
@ -58,7 +58,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
if str.hasSuffix("*") { if str.hasSuffix("*") {
let txt = str[str.startIndex ..< str.index(str.endIndex, offsetBy: -1)] let txt = str[str.startIndex ..< str.index(str.endIndex, offsetBy: -1)]
if let p = model.items[0].parent { if let p = model.items[0].parent {
let neu = MediaItem(name: String(txt), path: p.path + "*" + txt, root: p.root, type: ItemType.DETAILS)
let neu = MediaItem(name: String(txt), path: p.path + "*" + txt, root: p.root, type: ItemType.PICFOLDER)
model.items.append(neu) model.items.append(neu)
neu.parent = p neu.parent = p
self.tableView.reloadData() self.tableView.reloadData()
@ -66,68 +66,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
} }
} }
func doAuthenticate() {
let authenticationContext = LAContext()
var error : NSError?
guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
self.showAlert(title: "Error", message: "This device does not have a TouchID sensor.")
return;
}
authenticationContext.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Only awesome people are allowed",
reply: { [unowned self] (success, error) -> Void in
LocalManager.sharedInstance.authenticated = success
})
}
func showAlert(title:String, message:String ) {
let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alertVC.addAction(okAction)
DispatchQueue.main.async() { () -> Void in
self.present(alertVC, animated: true, completion: nil)
}
}
func createFolder(_ item: MediaItem) {
let ac = UIAlertController(title: "Enter new folder name", message: nil, preferredStyle: .alert)
ac.addTextField()
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
let answer = ac.textFields![0].text!
if (item.root == "/tags") {
DatabaseManager.sharedInstance.createTag(answer)
let m = MediaItem(name: answer, path: answer, root: "tags", type: ItemType.TAG)
m.local = true
item.children.append(m)
m.parent = item
}
else {
do {
try FileHelper.createDir(name: answer)
item.name = answer
item.path = answer
self.tableView.reloadData()
} catch {
print(error)
}
}
}
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "cancel", style: .cancel))
present(ac, animated: true)
}
/** /**
* Item ausgewaehlt * Item ausgewaehlt
*/ */
@ -180,39 +118,7 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
} }
} }
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print("hello")
if urls.count > 0 {
let url = urls[0]
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
currentSelection?.externalURL = url.absoluteString
LocalManager.sharedInstance.externalURL = url.absoluteString
delegate!.loadFolder(selectedItem: currentSelection!) {
(neu) in
if neu.isFolder() {
self.gotoNextFolder(neu)
} else {
self.gotoDetails(neu)
}
}
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
print("hello")
}
fileprivate func gotoNextFolder(_ selectedItem: MediaItem) {
func gotoNextFolder(_ selectedItem: MediaItem) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "mastertable") as! MasterViewController let vc = mainStoryboard.instantiateViewController(withIdentifier: "mastertable") as! MasterViewController
vc.navigationItem.title = selectedItem.name; vc.navigationItem.title = selectedItem.name;
@ -221,21 +127,53 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
navigationController!.pushViewController(vc, animated: true) navigationController!.pushViewController(vc, animated: true)
} }
fileprivate func gotoDetails(_ selectedItem: MediaItem) {
func gotoDetails(_ selectedItem: MediaItem) {
let weiter: (MediaItem) -> Void = { let weiter: (MediaItem) -> Void = {
(m) in (m) in
self.performSegue(withIdentifier: "showDetail", sender: self) self.performSegue(withIdentifier: "showDetail", sender: self)
} }
delegate!.loadItem(selectedItem: selectedItem, completionHandler: weiter)
delegate!.loadItemDetails(selectedItem: selectedItem, completionHandler: weiter)
}
func createFolder(_ item: MediaItem) {
let ac = UIAlertController(title: "Enter new folder name", message: nil, preferredStyle: .alert)
ac.addTextField()
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
let answer = ac.textFields![0].text!
if (item.root == "/tags") {
DatabaseManager.sharedInstance.createTag(answer)
let m = MediaItem(name: answer, path: answer, root: "tags", type: ItemType.TAG)
m.local = true
item.children.append(m)
m.parent = item
}
else {
do {
try FileHelper.createDir(name: answer)
item.name = answer
item.path = answer
self.tableView.reloadData()
} catch {
print(error)
}
}
}
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "cancel", style: .cancel))
present(ac, animated: true)
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return false; return false;
} }
// MARK: - Segues
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" { if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow { if let indexPath = self.tableView.indexPathForSelectedRow {
@ -269,10 +207,18 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
return model.items.count return model.items.count
} }
// Show Cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
self.configureCell(cell, atIndexPath: indexPath)
let item = model.items[indexPath.row]
if (item.path.count == 0) {
cell.textLabel!.text = "'" + item.name
} else {
cell.textLabel!.text = item.path
}
cell.accessoryType = UITableViewCell.AccessoryType.none
return cell return cell
} }
@ -287,17 +233,40 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
} }
} }
func configureCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) {
let object = model.items[indexPath.row]
if (object.path.count == 0) {
cell.textLabel!.text = "'" + object.name
// Document Picker
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print("hello")
if urls.count > 0 {
let url = urls[0]
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
currentSelection?.externalURL = url.absoluteString
LocalManager.sharedInstance.externalURL = url.absoluteString
delegate!.loadFolder(selectedItem: currentSelection!) {
(neu) in
if neu.isFolder() {
self.gotoNextFolder(neu)
} else { } else {
cell.textLabel!.text = object.path
self.gotoDetails(neu)
}
}
}
} }
cell.accessoryType = UITableViewCell.AccessoryType.none
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
print("hello")
} }
// Drag and drop
func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal { func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
if tableView.indexPathForSelectedRow == destinationIndexPath { if tableView.indexPathForSelectedRow == destinationIndexPath {
@ -398,5 +367,23 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
} }
// Authentication
func doAuthenticate() {
let authenticationContext = LAContext()
var error : NSError?
guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
showAlert(title: "Error", message: "This device does not have a TouchID sensor.")
return;
}
authenticationContext.evaluatePolicy(
.deviceOwnerAuthentication,
localizedReason: "Only awesome people are allowed",
reply: { [unowned self] (success, error) -> Void in
LocalManager.sharedInstance.authenticated = success
})
}
} }

13
kplayer/master/NetworkDelegate.swift

@ -6,13 +6,12 @@
import Foundation import Foundation
class NetworkDelegate: MasterDelegate, DetailDelegate { class NetworkDelegate: MasterDelegate, DetailDelegate {
func loadItem(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) {
func loadItemDetails(selectedItem: MediaItem, completionHandler: @escaping (MediaItem) -> Void) {
if selectedItem.loaded { if selectedItem.loaded {
completionHandler(selectedItem) completionHandler(selectedItem)
return return
} }
let p = selectedItem.superRoot()
let dir = selectedItem.encodedDir! let dir = selectedItem.encodedDir!
let weiter:Weiter = { let weiter:Weiter = {
@ -21,10 +20,10 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
completionHandler(selectedItem) completionHandler(selectedItem)
} }
if selectedItem.isVideo() {
if selectedItem.type == ItemType.VIDEOFOLDER {
NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter) NetworkManager.sharedInstance.loadVideoDirs(dir, completionHandler: weiter)
} }
else if selectedItem.isDetails() {
else if selectedItem.type == ItemType.PICFOLDER {
NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter) NetworkManager.sharedInstance.loadPicDirs(dir, completionHandler: weiter)
} }
else { else {
@ -54,7 +53,11 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
if selectedItem.type == ItemType.TAGROOT { if selectedItem.type == ItemType.TAGROOT {
DatabaseManager.sharedInstance.loadTags(completionHandler: weiter)
DatabaseManager.sharedInstance.loadTags(completionHandler: {
c in
selectedItem.children = c
completionHandler(selectedItem)
})
return return
} }

21
kplayer/util/UIViewController+Alert.swift

@ -0,0 +1,21 @@
//
// Created by Marco Schmickler on 23.01.22.
// Copyright (c) 2022 Marco Schmickler. All rights reserved.
//
import Foundation
import UIKit
extension UIViewController {
func showAlert(title:String, message:String ) {
let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alertVC.addAction(okAction)
DispatchQueue.main.async() { () -> Void in
self.present(alertVC, animated: true, completion: nil)
}
}
}
Loading…
Cancel
Save