Browse Source

Coredata

master
marcoschmickler 4 years ago
parent
commit
048303351b
  1. 38
      kplayer.xcodeproj/project.pbxproj
  2. 142
      kplayer/core/DatabaseManager.swift
  3. 16
      kplayer/core/KItem+CoreDataClass.swift
  4. 48
      kplayer/core/KItem+CoreDataProperties.swift
  5. 6
      kplayer/core/KSettings.swift
  6. 1
      kplayer/core/KSettingsModel.swift
  7. 16
      kplayer/core/KSnapshot+CoreDataClass.swift
  8. 56
      kplayer/core/KSnapshot+CoreDataProperties.swift
  9. 16
      kplayer/core/KTag+CoreDataClass.swift
  10. 44
      kplayer/core/KTag+CoreDataProperties.swift
  11. 12
      kplayer/core/LocalManager.swift
  12. 14
      kplayer/core/MediaItem.swift
  13. 11
      kplayer/core/NetworkManager.swift
  14. 6
      kplayer/detail/DetailViewController.swift
  15. 33
      kplayer/kplayer.xcdatamodeld/kplayer.xcdatamodel/contents
  16. 3
      kplayer/master/KSettingsView.swift
  17. 35
      kplayer/server/kplayer.js
  18. 4
      kplayer/svideo/SVideoModel.swift
  19. 81
      kplayer/svideo/SVideoPlayer.swift
  20. 16
      kplayer/svideo/VideoPlayerView.swift
  21. 13
      kplayer/util/KPersistentContainer.swift
  22. 52
      kplayer/util/nspersistentcontainer-defaults-swift.swift

38
kplayer.xcodeproj/project.pbxproj

@ -52,12 +52,14 @@
1C736A622876405F3EE2D043 /* EditItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366C09381DC0052B52B69 /* EditItemView.swift */; };
1C736A78C1F8F41E2AEEF278 /* KVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736E2CD0C1780F4F5AE0C4 /* KVideoPlayer.swift */; };
1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; };
1C736B4B0889BD35DC566124 /* nspersistentcontainer-defaults-swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */; };
1C736CB96577F6A9A7BA03E8 /* BMPlayerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */; };
1C736CD0E54786D3A2405E51 /* BMPlayerLayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366D766CDE0C9872E86F5 /* BMPlayerLayerView.swift */; };
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; };
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; };
1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; };
1C736D895B75BDCDB35937C1 /* BMTimeSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360AE55EB115762C42EB9 /* BMTimeSlider.swift */; };
1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */; };
1C736DB41BD06D359E6A0DEE /* BMSubtitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366AAB82A46086690E164 /* BMSubtitles.swift */; };
1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736677D4EF2437358B2387 /* Utility.swift */; };
1C736E21B246C0BE7E123FD3 /* MediaModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */; };
@ -68,10 +70,17 @@
1C736F3570EADA086682E6BC /* BMPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360D6580FB5D09C2BBCCB /* BMPlayerManager.swift */; };
1C736F3D082067948BA4DE84 /* FrameImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736A5140D9B25BEC266B72 /* FrameImagesView.swift */; };
1C736F6A223D4ADB2E1BA733 /* ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736069C214E9522BB1BD97 /* ItemCell.swift */; };
1C736F7D29B76C7037CEF778 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73647019E6C2E822127BA3 /* DatabaseManager.swift */; };
1C736FAE5D3E5D3BA3C1FAE5 /* links.html in Resources */ = {isa = PBXBuildFile; fileRef = 1C73645DBD6499A726D34973 /* links.html */; };
1C736FB92B19FE17E4357C85 /* MediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73688DAB88F9360FB62A49 /* MediaItem.swift */; };
1C736FF8FF423F01F880F94D /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73624617102E0DEB001C25 /* SVideoPlayer.swift */; };
AA74B07A01F0E99E6DEC7D1B /* Pods_kplayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B75159FFCD5A882E6F167FE /* Pods_kplayer.framework */; };
C91E05892795AC5C0003AB79 /* KTag+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05832795AC5C0003AB79 /* KTag+CoreDataClass.swift */; };
C91E058A2795AC5C0003AB79 /* KTag+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05842795AC5C0003AB79 /* KTag+CoreDataProperties.swift */; };
C91E058B2795AC5C0003AB79 /* KItem+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05852795AC5C0003AB79 /* KItem+CoreDataClass.swift */; };
C91E058C2795AC5C0003AB79 /* KItem+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05862795AC5C0003AB79 /* KItem+CoreDataProperties.swift */; };
C91E058D2795AC5C0003AB79 /* KSnapshot+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05872795AC5C0003AB79 /* KSnapshot+CoreDataClass.swift */; };
C91E058E2795AC5C0003AB79 /* KSnapshot+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E05882795AC5C0003AB79 /* KSnapshot+CoreDataProperties.swift */; };
C98AF5D51B124D6A00D196CC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98AF5D41B124D6A00D196CC /* AppDelegate.swift */; };
C98AF5D81B124D6A00D196CC /* kplayer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C98AF5D61B124D6A00D196CC /* kplayer.xcdatamodeld */; };
C98AF5DF1B124D6A00D196CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C98AF5DD1B124D6A00D196CC /* Main.storyboard */; };
@ -104,16 +113,19 @@
1C73611D226B48C24DB37535 /* MasterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = "<group>"; };
1C73615FFA2AA98BD1C56CD4 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = "<group>"; };
1C7361C26ED27AB54594317D /* CenterLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CenterLine.swift; path = timeline/CenterLine.swift; sourceTree = "<group>"; };
1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "nspersistentcontainer-defaults-swift.swift"; sourceTree = "<group>"; };
1C73620D01687FB4F1811C5C /* NetworkHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkHelper.swift; sourceTree = "<group>"; };
1C73624617102E0DEB001C25 /* SVideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SVideoPlayer.swift; path = svideo/SVideoPlayer.swift; sourceTree = "<group>"; };
1C73625012D50E457D18A785 /* kplayer.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = kplayer.js; sourceTree = "<group>"; };
1C736253AB7A95EA41B605B7 /* ItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemModel.swift; sourceTree = "<group>"; };
1C7362603E8588B4D1A8C617 /* SVideoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SVideoModel.swift; path = svideo/SVideoModel.swift; sourceTree = "<group>"; };
1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = "<group>"; };
1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KPersistentContainer.swift; sourceTree = "<group>"; };
1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
1C736362946D7A8585B0D875 /* TimelineScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TimelineScroller.swift; path = timeline/TimelineScroller.swift; sourceTree = "<group>"; };
1C7363E0DDA5854D55F8836E /* scratch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = scratch.txt; sourceTree = "<group>"; };
1C73645DBD6499A726D34973 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = "<group>"; };
1C73647019E6C2E822127BA3 /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.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>"; };
1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDelegate.swift; sourceTree = "<group>"; };
@ -145,6 +157,7 @@
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>"; };
1C736C94157754DE1C808173 /* KSettingsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsModel.swift; sourceTree = "<group>"; };
1C736CF935C2A6AB916BE494 /* scratch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = scratch.txt; 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>"; };
@ -157,6 +170,12 @@
1C736FC4180B42C3A357E9BF /* VideoTimelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VideoTimelineView.swift; path = timeline/VideoTimelineView.swift; sourceTree = "<group>"; };
6D522F61736592330F481B4F /* Pods-kplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-kplayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-kplayer/Pods-kplayer.debug.xcconfig"; sourceTree = "<group>"; };
8B75159FFCD5A882E6F167FE /* Pods_kplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_kplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C91E05832795AC5C0003AB79 /* KTag+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KTag+CoreDataClass.swift"; sourceTree = "<group>"; };
C91E05842795AC5C0003AB79 /* KTag+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KTag+CoreDataProperties.swift"; sourceTree = "<group>"; };
C91E05852795AC5C0003AB79 /* KItem+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KItem+CoreDataClass.swift"; sourceTree = "<group>"; };
C91E05862795AC5C0003AB79 /* KItem+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KItem+CoreDataProperties.swift"; sourceTree = "<group>"; };
C91E05872795AC5C0003AB79 /* KSnapshot+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KSnapshot+CoreDataClass.swift"; sourceTree = "<group>"; };
C91E05882795AC5C0003AB79 /* KSnapshot+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KSnapshot+CoreDataProperties.swift"; sourceTree = "<group>"; };
C98AF5CF1B124D6A00D196CC /* kplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kplayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
C98AF5D31B124D6A00D196CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C98AF5D41B124D6A00D196CC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -209,6 +228,7 @@
1C73625012D50E457D18A785 /* kplayer.js */,
1C73615FFA2AA98BD1C56CD4 /* links.html */,
1C73645DBD6499A726D34973 /* links.html */,
1C736CF935C2A6AB916BE494 /* scratch.txt */,
);
path = server;
sourceTree = "<group>";
@ -250,6 +270,8 @@
1C736677D4EF2437358B2387 /* Utility.swift */,
1C7360B6D0757D4FB6433E7B /* AsyncImage.swift */,
1C736DCD945ABAE984FF43EF /* KNetworkProtocol.swift */,
1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */,
1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */,
);
path = util;
sourceTree = "<group>";
@ -257,6 +279,12 @@
1C736DC8C3AFB991541A2079 /* core */ = {
isa = PBXGroup;
children = (
C91E05832795AC5C0003AB79 /* KTag+CoreDataClass.swift */,
C91E05842795AC5C0003AB79 /* KTag+CoreDataProperties.swift */,
C91E05852795AC5C0003AB79 /* KItem+CoreDataClass.swift */,
C91E05862795AC5C0003AB79 /* KItem+CoreDataProperties.swift */,
C91E05872795AC5C0003AB79 /* KSnapshot+CoreDataClass.swift */,
C91E05882795AC5C0003AB79 /* KSnapshot+CoreDataProperties.swift */,
1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */,
1C73688DAB88F9360FB62A49 /* MediaItem.swift */,
1C736253AB7A95EA41B605B7 /* ItemModel.swift */,
@ -267,6 +295,7 @@
1C736AE5021E3D985FE3402D /* KSettings.swift */,
1C736C94157754DE1C808173 /* KSettingsModel.swift */,
1C73659CC9B523B957E58DC6 /* LocalManager.swift */,
1C73647019E6C2E822127BA3 /* DatabaseManager.swift */,
);
path = core;
sourceTree = "<group>";
@ -568,6 +597,7 @@
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */,
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */,
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */,
C91E058C2795AC5C0003AB79 /* KItem+CoreDataProperties.swift in Sources */,
1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */,
1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */,
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */,
@ -577,6 +607,7 @@
1C7366DAC06047DE335EFC37 /* BMPlayer.swift in Sources */,
1C73673F39A34C3275D0230A /* BMPlayerClearityChooseButton.swift in Sources */,
1C736F278DDC77F40C8CB1D4 /* BMPlayerControlView.swift in Sources */,
C91E05892795AC5C0003AB79 /* KTag+CoreDataClass.swift in Sources */,
1C736CB96577F6A9A7BA03E8 /* BMPlayerItem.swift in Sources */,
1C736CD0E54786D3A2405E51 /* BMPlayerLayerView.swift in Sources */,
1C7367FA10AE13598FDDE865 /* BMPlayerProtocols.swift in Sources */,
@ -592,13 +623,17 @@
1C736A06A2AD75B8C14EEBBE /* HtmlParser.swift in Sources */,
1C736048BFA120F5C7D36874 /* readme.md in Sources */,
1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */,
C91E058D2795AC5C0003AB79 /* KSnapshot+CoreDataClass.swift in Sources */,
1C7361B3AF46CEB30D3F4FA0 /* KSettings.swift in Sources */,
1C73600CB93F16F4F28C116F /* KSettingsView.swift in Sources */,
1C73666A07CF2416B1B8D3F0 /* KSettingsModel.swift in Sources */,
1C736F1C31D1EC23F59125F0 /* VideoTimelineView.swift in Sources */,
1C73672CEAE1B9DA7805D4F2 /* CenterLine.swift in Sources */,
C91E058E2795AC5C0003AB79 /* KSnapshot+CoreDataProperties.swift in Sources */,
1C736F3D082067948BA4DE84 /* FrameImagesView.swift in Sources */,
C91E058A2795AC5C0003AB79 /* KTag+CoreDataProperties.swift in Sources */,
1C7361D3BA77C40275F89D4A /* TimelineMeasure.swift in Sources */,
C91E058B2795AC5C0003AB79 /* KItem+CoreDataClass.swift in Sources */,
1C736ECAE78F5C722423D7ED /* TimelineScroller.swift in Sources */,
1C7365BEFFB35E8DE8F04CCF /* TimelineView.swift in Sources */,
1C7361F376DA11F17CD3250B /* TrimView.swift in Sources */,
@ -612,6 +647,9 @@
1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */,
1C736998044A9A7D89411892 /* AsyncImage.swift in Sources */,
1C73633C00C18FDA2E9F0A2F /* KNetworkProtocol.swift in Sources */,
1C736B4B0889BD35DC566124 /* nspersistentcontainer-defaults-swift.swift in Sources */,
1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */,
1C736F7D29B76C7037CEF778 /* DatabaseManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

142
kplayer/core/DatabaseManager.swift

@ -0,0 +1,142 @@
//
// Created by Marco Schmickler on 18.01.22.
// Copyright (c) 2022 Marco Schmickler. All rights reserved.
//
import Foundation
import CoreData
import CoreGraphics
class DatabaseManager {
static let sharedInstance = DatabaseManager()
let managedObjectContext : NSManagedObjectContext
let persistentContainer : NSPersistentContainer
init() {
self.persistentContainer = KPersistentContainer(defaultContainerWithName: "kplayer")
self.managedObjectContext = self.persistentContainer.viewContext
}
func enrichItem(_ item: MediaItem) {
let fetchRequest = KItem.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %@ AND path == %@ AND root == %@", item.name, item.path, item.root)
let results = try! managedObjectContext.fetch(fetchRequest)
var saved = false
for r in results {
print(r.name)
saved = true
for s in r.snapshots as! Set<KSnapshot> {
print("DB -- Found snapshot at \(s.index)")
for c in item.children {
if s.index == c.indexId {
c.time = s.time
c.length = s.length
c.loop = s.loop
c.size.width = CGFloat(s.sizex)
c.size.height = CGFloat(s.sizey)
c.offset.x = CGFloat(s.offx)
c.offset.y = CGFloat(s.offy)
c.scale = s.scale
}
}
print(s)
}
return
}
}
func getKItem(_ item: MediaItem) -> KItem {
let fetchRequest = KItem.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %@ AND path == %@ AND root == %@", item.name, item.path, item.root)
let results = try! managedObjectContext.fetch(fetchRequest)
var saved = false
for r in results {
print(r.name)
saved = true
return r
}
var new = KItem(context: managedObjectContext)
new.name = item.name
new.root = item.root
new.path = item.path
new.local = item.local
new.type = item.type.rawValue
do {
try managedObjectContext.save()
} catch {
print("Error")
}
return new;
}
func saveItemMetaData(_ item: MediaItem) {
if (item.type == ItemType.VIDEO) {
let k = getKItem(item)
var kstimes = Set<Int32>()
var ksitems = k.snapshots as! Set<KSnapshot>
for snap in ksitems {
print(snap)
kstimes.insert(snap.index)
}
for c in item.children {
if !kstimes.contains(Int32(c.indexId)) {
let snap = KSnapshot(context: managedObjectContext)
snap.time = c.time
snap.index = Int32(c.indexId)
snap.length = c.length
snap.loop = c.loop
snap.offx = Int16(c.offset.x)
snap.offy = Int16(c.offset.y)
snap.sizex = Int16(c.size.width)
snap.sizey = Int16(c.size.height)
snap.scale = c.scale
snap.thumb = c.thumbUrl
k.addToSnapshots(snap)
print("DB -- Insert snapshot at \(c.indexId)")
}
else {
for snap in ksitems {
if snap.index == c.indexId {
snap.time = c.time
snap.length = c.length
snap.loop = c.loop
snap.offx = Int16(c.offset.x)
snap.offy = Int16(c.offset.y)
snap.sizex = Int16(c.size.width)
snap.sizey = Int16(c.size.height)
snap.scale = c.scale
snap.thumb = c.thumbUrl
print("DB -- Update snapshot at \(c.indexId)")
}
}
}
}
do {
try managedObjectContext.save()
} catch {
print("Error")
}
}
}
}

16
kplayer/core/KItem+CoreDataClass.swift

@ -0,0 +1,16 @@
//
// KItem+CoreDataClass.swift
// kplayer
//
// Created by Marco Schmickler on 17.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
public class KItem: NSManagedObject {
}

48
kplayer/core/KItem+CoreDataProperties.swift

@ -0,0 +1,48 @@
//
// KItem+CoreDataProperties.swift
// kplayer
//
// Created by Marco Schmickler on 17.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
extension KItem {
@nonobjc public class func fetchRequest() -> NSFetchRequest<KItem> {
return NSFetchRequest<KItem>(entityName: "KItem")
}
@NSManaged public var local: Bool
@NSManaged public var name: String?
@NSManaged public var path: String?
@NSManaged public var root: String?
@NSManaged public var type: String?
@NSManaged public var snapshots: NSSet?
}
// MARK: Generated accessors for snapshots
extension KItem {
@objc(addSnapshotsObject:)
@NSManaged public func addToSnapshots(_ value: KSnapshot)
@objc(removeSnapshotsObject:)
@NSManaged public func removeFromSnapshots(_ value: KSnapshot)
@objc(addSnapshots:)
@NSManaged public func addToSnapshots(_ values: NSSet)
@objc(removeSnapshots:)
@NSManaged public func removeFromSnapshots(_ values: NSSet)
}
extension KItem : Identifiable {
}

6
kplayer/core/KSettings.swift

@ -12,6 +12,9 @@ class KSettings: ObservableObject {
@Published
var autoloop = false
@Published
var zoomed = false
@Published
var edit = false
@ -22,10 +25,11 @@ class KSettings: ObservableObject {
self.init()
scale = model.scale
autoloop = model.autoloop
zoomed = model.zoomed
}
func toModel() -> KSettingsModel {
KSettingsModel(scale: scale, autoloop: autoloop)
KSettingsModel(scale: scale, autoloop: autoloop, zoomed: zoomed)
}
func toJSON() -> String {

1
kplayer/core/KSettingsModel.swift

@ -8,4 +8,5 @@ import Foundation
struct KSettingsModel: Codable {
var scale = Float(0.5)
var autoloop = false
var zoomed = false
}

16
kplayer/core/KSnapshot+CoreDataClass.swift

@ -0,0 +1,16 @@
//
// KSnapshot+CoreDataClass.swift
// kplayer
//
// Created by Marco Schmickler on 17.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
public class KSnapshot: NSManagedObject {
}

56
kplayer/core/KSnapshot+CoreDataProperties.swift

@ -0,0 +1,56 @@
//
// KSnapshot+CoreDataProperties.swift
// kplayer
//
// Created by Marco Schmickler on 18.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
extension KSnapshot {
@nonobjc public class func fetchRequest() -> NSFetchRequest<KSnapshot> {
return NSFetchRequest<KSnapshot>(entityName: "KSnapshot")
}
@NSManaged public var length: Double
@NSManaged public var loop: Bool
@NSManaged public var offx: Int16
@NSManaged public var offy: Int16
@NSManaged public var rating: Int16
@NSManaged public var scale: Double
@NSManaged public var sizex: Int16
@NSManaged public var sizey: Int16
@NSManaged public var thumb: String?
@NSManaged public var time: Double
@NSManaged public var timeStamp: Date?
@NSManaged public var index: Int32
@NSManaged public var item: KItem?
@NSManaged public var tags: NSSet?
}
// MARK: Generated accessors for tags
extension KSnapshot {
@objc(addTagsObject:)
@NSManaged public func addToTags(_ value: KTag)
@objc(removeTagsObject:)
@NSManaged public func removeFromTags(_ value: KTag)
@objc(addTags:)
@NSManaged public func addToTags(_ values: NSSet)
@objc(removeTags:)
@NSManaged public func removeFromTags(_ values: NSSet)
}
extension KSnapshot : Identifiable {
}

16
kplayer/core/KTag+CoreDataClass.swift

@ -0,0 +1,16 @@
//
// KTag+CoreDataClass.swift
// kplayer
//
// Created by Marco Schmickler on 17.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
public class KTag: NSManagedObject {
}

44
kplayer/core/KTag+CoreDataProperties.swift

@ -0,0 +1,44 @@
//
// KTag+CoreDataProperties.swift
// kplayer
//
// Created by Marco Schmickler on 17.01.22.
// Copyright © 2022 Marco Schmickler. All rights reserved.
//
//
import Foundation
import CoreData
extension KTag {
@nonobjc public class func fetchRequest() -> NSFetchRequest<KTag> {
return NSFetchRequest<KTag>(entityName: "KTag")
}
@NSManaged public var name: String?
@NSManaged public var tagged: NSSet?
}
// MARK: Generated accessors for tagged
extension KTag {
@objc(addTaggedObject:)
@NSManaged public func addToTagged(_ value: KSnapshot)
@objc(removeTaggedObject:)
@NSManaged public func removeFromTagged(_ value: KSnapshot)
@objc(addTagged:)
@NSManaged public func addToTagged(_ values: NSSet)
@objc(removeTagged:)
@NSManaged public func removeFromTagged(_ values: NSSet)
}
extension KTag : Identifiable {
}

12
kplayer/core/LocalManager.swift

@ -144,11 +144,13 @@ class LocalManager {
let jsonURL = fileURL.absoluteString.replacingOccurrences(of: ".jpg", with: ".json")
do {
let jsonData = try Data(contentsOf: URL(string: jsonURL)!)
let item = try JSONDecoder().decode(MediaModel.self, from: jsonData)
m.scale = item.scale
m.offset = item.offset
m.size = item.size
if let u = URL(string: jsonURL) {
let jsonData = try Data(contentsOf: u)
let item = try JSONDecoder().decode(MediaModel.self, from: jsonData)
m.scale = item.scale
m.offset = item.offset
m.size = item.size
}
} catch {
}

14
kplayer/core/MediaItem.swift

@ -27,6 +27,7 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
Position innerhalb der Children
*/
var index = 0
var indexId = 0
/**
true, wenn die Children geladen sind
@ -72,7 +73,7 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
var cancelled = false
@Published
var scale = 1.0
var scale = 0.0
@Published
var offset = CGPoint()
@ -187,7 +188,7 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
}
let enc = thumbUrl!.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
return NetworkManager.sharedInstance.baseurl + "/service/download" + enc
return NetworkManager.sharedInstance.vidurl + enc.substringStartingFrom(10)
}
var nameWithoutExtension: String {
@ -309,6 +310,13 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
}
func clone() -> MediaItem {
MediaItem(model: toMediaModel())
let m = MediaItem(model: toMediaModel())
m.local = local
for c in m.children {
c.local = local
}
return m
}
}

11
kplayer/core/NetworkManager.swift

@ -252,7 +252,7 @@ class NetworkManager {
return
}
let url = baseurl + "/service/listfiles" + item.snapshotDirPathForVideo
let url = nodeurl + "listfiles" + item.snapshotDirPathForVideo
print(url)
@ -286,13 +286,17 @@ class NetworkManager {
let snap = MediaItem(name: item.name, path: item.path, root: item.root, type: .SNAPSHOT)
snap.time = t
snap.indexId = (ts as NSString).integerValue
snap.thumbUrl = p
snap.parent = item
snap.loaded = true
item.children.append(snap)
}
DatabaseManager.sharedInstance.enrichItem(item)
item.loaded = true
NotificationCenter.default.post(name: Notification.Name(rawValue: "loadedItems"), object: item)
}
@ -338,7 +342,8 @@ class NetworkManager {
}
func saveItem(_ item: MediaItem) {
print(item.children)
var ch = item.children
print(ch)
if (item.local) {
return
@ -348,6 +353,8 @@ class NetworkManager {
return
}
DatabaseManager.sharedInstance.saveItemMetaData(item)
let url = baseurl + "/service/listfiles" + item.snapshotDirPathForVideo
print(url)

6
kplayer/detail/DetailViewController.swift

@ -94,7 +94,10 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
let browserButton = UIBarButtonItem(barButtonSystemItem: .organize, target: self, action: #selector(fileBrowser));
navigationItem.rightBarButtonItems = [settingsButton, favButton, overviewButton,browserButton]
if detailItem != nil {
print("Details \(detailItem!.children)")
if detailItem!.children != nil {
var d = detailItem!.children
print("Details \(d)")
}
}
}
@ -515,6 +518,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
model.edit = delegate!.settings().edit
model.loop = delegate!.settings().autoloop
model.zoomed = delegate!.settings().zoomed
let player = SVideoPlayer(completionHandler: { saved in
if saved {

33
kplayer/kplayer.xcdatamodeld/kplayer.xcdatamodel/contents

@ -1,9 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="Test1.xcdatamodel" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Event">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="20G165" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="KItem" representedClassName=".KItem" syncable="YES">
<attribute name="local" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="path" optional="YES" attributeType="String"/>
<attribute name="root" optional="YES" attributeType="String"/>
<attribute name="type" optional="YES" attributeType="String"/>
<relationship name="snapshots" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="KSnapshot" inverseName="item" inverseEntity="KSnapshot"/>
</entity>
<entity name="KSnapshot" representedClassName=".KSnapshot" syncable="YES">
<attribute name="index" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="length" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="loop" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="offx" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="offy" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rating" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="scale" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="sizex" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sizey" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="thumb" optional="YES" attributeType="String"/>
<attribute name="time" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="timeStamp" optional="YES" attributeType="Date"/>
<relationship name="item" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="KItem" inverseName="snapshots" inverseEntity="KItem"/>
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="KTag" inverseName="tagged" inverseEntity="KTag"/>
</entity>
<entity name="KTag" representedClassName=".KTag" syncable="YES">
<attribute name="name" optional="YES" attributeType="String"/>
<relationship name="tagged" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="KSnapshot" inverseName="tags" inverseEntity="KSnapshot"/>
</entity>
<elements>
<element name="Event" positionX="261" positionY="189" width="128" height="60"/>
<element name="KItem" positionX="-59.99090576171875" positionY="213.7807769775391" width="128" height="133"/>
<element name="KSnapshot" positionX="266.1280517578125" positionY="32.68440246582031" width="128" height="253"/>
<element name="KTag" positionX="592.757568359375" positionY="115.8440093994141" width="128" height="73"/>
</elements>
</model>

3
kplayer/master/KSettingsView.swift

@ -21,6 +21,9 @@ struct KSettingsView: View {
Toggle(isOn: $kSettings.autoloop, label: {
Text("Autoloop")
})
Toggle(isOn: $kSettings.zoomed, label: {
Text("Zoomed")
})
Toggle(isOn: $kSettings.edit, label: {
Text("Edit")
})

35
kplayer/server/kplayer.js

@ -49,11 +49,44 @@ function hasPics(address) {
}
function getFiles(address) {
return fs.readdirSync(address, {withFileTypes: true})
try {
return fs.readdirSync(address, {withFileTypes: true})
}
catch {
return []
}
// .filter(dirent => dirent.isDirectory())
// .map(dirent => dirent.name);
}
app.get('/listfiles/*', function (req, res) {
var address = req.path.substr(10)
console.log("listdirs " + address);
if (!req.path.startsWith("/listfiles/srv/samba/ren/")) {
res.end("Access denied")
return
}
var result = [];
try {
var files = getFiles(address)
files.forEach(function(file){
var filename = path.resolve(address, file.name)
console.log(filename)
result.push(filename)
})
}
catch {
}
res.end(JSON.stringify(result))
})
app.get('/listdirs/*', function (req, res) {
var address = req.path.substr(9)
console.log("listdirs " + address);

4
kplayer/svideo/SVideoModel.swift

@ -5,6 +5,7 @@
import Foundation
import AVKit
import SwiftUI
class SVideoModel : ObservableObject {
@Published var allItems : [MediaItem]
@ -22,6 +23,7 @@ class SVideoModel : ObservableObject {
@Published var edit = false
@Published var loop = false
@Published var zoomed = false
@Published var speed: Float = 1.0
@ -30,6 +32,8 @@ class SVideoModel : ObservableObject {
@Published var scale: CGFloat = 1.0
@Published var dragOffset: CGSize = CGSize.zero
@Published var proxy: GeometryProxy?
var observer: Any?
init(allItems: [MediaItem], currentSnapshot: MediaItem, baseItem: MediaItem) {

81
kplayer/svideo/SVideoPlayer.swift

@ -27,6 +27,12 @@ struct SVideoPlayer: View, EditItemDelegate {
@State var smoothSeekTime = -1.0
@State var timeCounter = 0
@State var orientation = UIDevice.current.orientation
let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
.makeConnectable()
.autoconnect()
let steps : [Float] = [0.25, 0.5, 1.0, 2.0 ]
init(completionHandler: ((Bool) -> ())?, model: SVideoModel) {
@ -36,13 +42,22 @@ struct SVideoPlayer: View, EditItemDelegate {
}
func cleanup() {
player.removeTimeObserver(model.observer)
if model.observer != nil {
player.removeTimeObserver(model.observer)
model.observer = nil
}
}
var body: some View {
VStack {
HStack {
Button(action: {completionHandler!(model.edit)}, label: {
Button(action: {
player.pause()
player.replaceCurrentItem(with: nil)
model.currentURL = nil
cleanup()
completionHandler!(model.edit)
}, label: {
Text("cancel")
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
@ -50,6 +65,11 @@ struct SVideoPlayer: View, EditItemDelegate {
}, label: {
Text("loop")
}).foregroundColor(model.loop ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle())
Button(action: {
model.zoomed.toggle()
}, label: {
Text("zoom")
}).foregroundColor(model.zoomed ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle())
Button(action: {
for s in steps {
@ -111,7 +131,7 @@ struct SVideoPlayer: View, EditItemDelegate {
}).buttonStyle(BorderlessButtonStyle());
}.frame(height: 50)
GeometryReader { geometry in
VStack {
let v = VideoPlayerView(model: model,
player: player)
@ -137,24 +157,28 @@ struct SVideoPlayer: View, EditItemDelegate {
self.lastScaleValue = val
self.model.scale = self.model.scale * delta
//... anything else e.g. clamping the newScale
}.onEnded { val in
}
.onEnded { val in
// without this the next gesture will be broken
self.lastScaleValue = 1.0
}).contentShape(Rectangle())
})
.contentShape(Rectangle())
if model.edit && model.videoDuration != Double.nan && model.videoDuration > 0.0 {
v.overlay(EditItemView(item: model.currentSnapshot, len: model.videoDuration, delegate: self)
.frame(width: 400, alignment: .top).offset(x: 0, y: -50), alignment: .topTrailing)
}
else {
} else {
v
}
VideoPlayerControlsView(model: model,
player: player)
}.clipped()
}.onAppear {
model.proxy = geometry
print ("geo \(geometry.size.height)");
}
.clipped()
}
}
.onAppear() {
@ -175,13 +199,15 @@ struct SVideoPlayer: View, EditItemDelegate {
let item = model.currentPlayerItem()
item.preferredForwardBufferDuration = 2.0
player.removeAllItems()
self.player.replaceCurrentItem(with: item)
model.currentURL = model.currentSnapshot.playerURL
print(model.currentURL)
// player.removeAllItems()
// player.insert(model.currentPlayerItem(), after: nil)
// Start the player going, otherwise controls don't appear
// player.play()
player.play()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
gotoSnapshot(model.currentSnapshot)
@ -194,6 +220,9 @@ struct SVideoPlayer: View, EditItemDelegate {
cleanup()
self.player.replaceCurrentItem(with: nil)
}
.onReceive(orientationChanged) { _ in
self.orientation = UIDevice.current.orientation
}
}
func doSnapshot() {
@ -218,6 +247,7 @@ struct SVideoPlayer: View, EditItemDelegate {
let newItem = MediaItem(name: currentItem.name, path: currentItem.path, root: currentItem.root, type: ItemType.SNAPSHOT)
newItem.image = thumbnail
newItem.time = time.seconds
newItem.indexId = Int(newItem.time * 1000)
newItem.parent = currentItem
newItem.local = currentItem.local
newItem.external = currentItem.external
@ -300,11 +330,11 @@ struct SVideoPlayer: View, EditItemDelegate {
// player.cancelPendingPrerolls()
if let item = player.currentItem {
// item.cancelPendingSeeks()
item.cancelPendingSeeks()
}
player.seek(to: CMTime(seconds: time, preferredTimescale: CMTimeScale(10000)),
toleranceBefore: CMTime.zero,
toleranceBefore: CMTime(seconds: 0.3, preferredTimescale: CMTimeScale(10000)),
toleranceAfter: CMTime(seconds: 1.0, preferredTimescale: CMTimeScale(10000))){ _ in
player.play()
player.rate = model.speed
@ -350,12 +380,25 @@ struct SVideoPlayer: View, EditItemDelegate {
else {
seekTime(currentSnapshot.time)
}
// if loopMode && currentSnapshot.scale > 0 {
// player.zoom = Float(currentSnapshot.scale)
// player.xpos = currentSnapshot.offset.x
// player.ypos = currentSnapshot.offset.y
// player.transformLayer()
// }
let height = player.currentItem!.presentationSize.height
let heightSpace = model.proxy!.size.height * 2
var f = height / heightSpace
print("h \(height) \(heightSpace) \(f)")
if model.zoomed && currentSnapshot.scale > 0 {
model.scale = CGFloat(currentSnapshot.scale)
model.dragOffset.width = currentSnapshot.offset.x
model.dragOffset.height = currentSnapshot.offset.y
// player.transformLayer()
}
else {
if model.zoomed && f > 0.0 {
if f < 0.5 {
f = f * 1.5
}
model.scale = f
}
}
}

16
kplayer/svideo/VideoPlayerView.swift

@ -54,6 +54,22 @@ class VideoPlayerUIView: UIView {
}
}
}
//
// override func viewDidLayoutSubviews() {
// playerLayer.frame = view.bounds
// let deviceOrientation = UIDevice.current.orientation
// switch deviceOrientation {
// case .landscapeLeft:
// playerLayer.connection!.videoOrientation = .landscapeRight
// case .landscapeRight:
// playerLayer.connection!.videoOrientation = .landscapeLeft
// case .portraitUpsideDown:
// playerLayer.connection!.videoOrientation = .portraitUpsideDown
// case .portrait:
// playerLayer.connection!.videoOrientation = .portrait
// default:
// player.connection!.videoOrientation = .portrait
// }
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")

13
kplayer/util/KPersistentContainer.swift

@ -0,0 +1,13 @@
//
// Created by Marco Schmickler on 17.01.22.
// Copyright (c) 2022 Marco Schmickler. All rights reserved.
//
import Foundation
import CoreData
class KPersistentContainer : NSPersistentContainer {
override class func defaultDirectoryURL() -> URL {
FileHelper.getDocumentsDirectory()
}
}

52
kplayer/util/nspersistentcontainer-defaults-swift.swift

@ -0,0 +1,52 @@
import Foundation
import CoreData
extension NSPersistentContainer {
typealias ErrorHandler = (NSError) -> Void
convenience init(defaultContainerWithName name : String, inMemory : Bool = false, errorHandler : ErrorHandler? = nil) {
self.init(name: name)
if inMemory {
let description = NSPersistentStoreDescription()
description.type = NSInMemoryStoreType
self.persistentStoreDescriptions = [description]
}
self.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let url = storeDescription.url {
debugPrint("Core Data database URL", url.path) //kann mit sqlite3 im Terminal aufgerufen werden
}
if let error = error as NSError? {
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
if let errorHandler = errorHandler {
errorHandler(error)
} else {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
})
}
func saveViewContext() throws {
assert(Thread.isMainThread)
let context = self.viewContext
if context.hasChanges {
try context.save()
}
}
}
Loading…
Cancel
Save