Skip to content

Commit

Permalink
v1.4.2(54)
Browse files Browse the repository at this point in the history
Add network cache option.
Fix ffplayer bug in position when MPEG-TS wrap around occured.
  • Loading branch information
lithium0003 committed Nov 20, 2019
1 parent ad8be7d commit 8de1db1
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 26 deletions.
18 changes: 18 additions & 0 deletions RemoteCloud/RemoteCloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
3F3ABABD2273119D002A8D16 /* pCloudStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F3ABABC2273119D002A8D16 /* pCloudStorage.swift */; };
3F92E7052336BFBA00D1FF9B /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F92E7042336BFBA00D1FF9B /* Media.xcassets */; };
3F987DEC2277608A0020A796 /* Cryptomator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F987DEB2277608A0020A796 /* Cryptomator.swift */; };
3FA46CE223849615007ACC82 /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FA46CE123849615007ACC82 /* FileCache.swift */; };
3FA46CE5238496B7007ACC82 /* cache.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3FA46CE3238496B7007ACC82 /* cache.xcdatamodeld */; };
3FE23B1F2340B96200916DA6 /* UploadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE23B1E2340B96200916DA6 /* UploadManager.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -66,6 +68,8 @@
3F3ABABC2273119D002A8D16 /* pCloudStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = pCloudStorage.swift; sourceTree = "<group>"; };
3F92E7042336BFBA00D1FF9B /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
3F987DEB2277608A0020A796 /* Cryptomator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptomator.swift; sourceTree = "<group>"; };
3FA46CE123849615007ACC82 /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = "<group>"; };
3FA46CE4238496B7007ACC82 /* cache.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = cache.xcdatamodel; sourceTree = "<group>"; };
3FE23B1E2340B96200916DA6 /* UploadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -131,6 +135,8 @@
3F3ABABA2272C707002A8D16 /* Secret.swift */,
3F92E7042336BFBA00D1FF9B /* Media.xcassets */,
3FE23B1E2340B96200916DA6 /* UploadManager.swift */,
3FA46CE123849615007ACC82 /* FileCache.swift */,
3FA46CE3238496B7007ACC82 /* cache.xcdatamodeld */,
);
path = RemoteCloud;
sourceTree = "<group>";
Expand Down Expand Up @@ -275,6 +281,7 @@
3CA2C95E2235569800481DB5 /* cloud.xcdatamodeld in Sources */,
3F3ABABB2272C707002A8D16 /* Secret.swift in Sources */,
3CF14614223B29360025081A /* CryptRclone.swift in Sources */,
3FA46CE223849615007ACC82 /* FileCache.swift in Sources */,
3CA2C951223495E700481DB5 /* RemoteStorage.swift in Sources */,
3C14C452225CE19E0044E4A7 /* CueSheet.swift in Sources */,
3C0654A122384BED003BD932 /* CryptCarotDAV.swift in Sources */,
Expand All @@ -287,6 +294,7 @@
3FE23B1F2340B96200916DA6 /* UploadManager.swift in Sources */,
3C14C454225DBB1A0044E4A7 /* LocalStorage.swift in Sources */,
3CA2C952223495E700481DB5 /* GoogleDriveStorage.swift in Sources */,
3FA46CE5238496B7007ACC82 /* cache.xcdatamodeld in Sources */,
3C400922224190920028A45A /* OneDriveStorage.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -569,6 +577,16 @@
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
3FA46CE3238496B7007ACC82 /* cache.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
3FA46CE4238496B7007ACC82 /* cache.xcdatamodel */,
);
currentVersion = 3FA46CE4238496B7007ACC82 /* cache.xcdatamodel */;
path = cache.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = 3CA2C9252234952800481DB5 /* Project object */;
Expand Down
254 changes: 254 additions & 0 deletions RemoteCloud/RemoteCloud/FileCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
//
// FileCache.swift
// RemoteCloud
//
// Created by rei8 on 2019/11/20.
// Copyright © 2019 lithium03. All rights reserved.
//

import Foundation
import CoreData

public class FileCache {
public var cacheMaxSize: Int {
get {
return UserDefaults.standard.integer(forKey: "networkCacheSize")
}
set {
UserDefaults.standard.set(newValue, forKey: "networkCacheSize")
}
}

public func getCache(storage: String, id: String, offset: Int64, size: Int64) -> URL? {
var ret: URL? = nil
guard cacheMaxSize > 0 else {
return nil
}
if Thread.isMainThread {
guard let orgItem = CloudFactory.shared.data.getData(storage: storage, fileId: id) else {
return nil
}

let viewContext = self.persistentContainer.viewContext

let fetchrequest = NSFetchRequest<NSFetchRequestResult>(entityName: "FileCacheItem")
fetchrequest.predicate = NSPredicate(format: "storage == %@ && id == %@ && chunkOffset == %lld", storage, id, offset)
do{
guard let item = try viewContext.fetch(fetchrequest).first as? FileCacheItem else {
return nil
}
if orgItem.mdate != item.mdate || orgItem.size != item.orgSize {
if let target = item.filename?.uuidString {
let base = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("NetCache", isDirectory: true)
let target_path = base.appendingPathComponent(String(target.prefix(2)), isDirectory: true).appendingPathComponent(String(target.prefix(4).suffix(2)), isDirectory: true).appendingPathComponent(target)
try FileManager.default.removeItem(at: target_path)
}
viewContext.delete(item)
try viewContext.save()
return nil
}
if let target = item.filename?.uuidString, (size == item.chunkSize || size < 0) {
let base = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("NetCache", isDirectory: true)
let target_path = base.appendingPathComponent(String(target.prefix(2)), isDirectory: true).appendingPathComponent(String(target.prefix(4).suffix(2)), isDirectory: true).appendingPathComponent(target)
if FileManager.default.fileExists(atPath: target_path.path) {
ret = target_path
item.rdate = Date()
try? viewContext.save()
}
else {
viewContext.delete(item)
try viewContext.save()
}
}
}
catch{
return nil
}
return ret
}
else {
DispatchQueue.main.sync {
ret = getCache(storage: storage, id: id, offset: offset, size: size)
}
return ret
}
}

public func saveCache(storage: String, id: String, offset: Int64, data: Data) {
guard cacheMaxSize > 0 else {
increseFreeSpace()
return
}
do {
let size = Int64(data.count)
let base = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("NetCache", isDirectory: true)
let newId = UUID()
let target = newId.uuidString
let target_path = base.appendingPathComponent(String(target.prefix(2)), isDirectory: true).appendingPathComponent(String(target.prefix(4).suffix(2)), isDirectory: true)
try FileManager.default.createDirectory(at: target_path, withIntermediateDirectories: true, attributes: nil)
try data.write(to: target_path.appendingPathComponent(target))

if getCacheSize() > cacheMaxSize {
increseFreeSpace()
}

DispatchQueue.main.async {
guard let orgItem = CloudFactory.shared.data.getData(storage: storage, fileId: id) else {
try? FileManager.default.removeItem(at: target_path.appendingPathComponent(target))
return
}

let viewContext = self.persistentContainer.viewContext

let fetchrequest = NSFetchRequest<NSFetchRequestResult>(entityName: "FileCacheItem")
fetchrequest.predicate = NSPredicate(format: "storage == %@ && id == %@ && chunkOffset == %lld && chunkSize == %lld", storage, id, offset, size)
do {
if let items = try viewContext.fetch(fetchrequest) as? [FileCacheItem], items.count > 0 {
try FileManager.default.removeItem(at: target_path.appendingPathComponent(target))
return
}
}
catch {
print(error)
}

let newitem = FileCacheItem(context: viewContext)
newitem.filename = newId
newitem.storage = storage
newitem.id = id
newitem.chunkSize = size
newitem.chunkOffset = offset
newitem.rdate = Date()
newitem.mdate = orgItem.mdate
newitem.orgSize = orgItem.size

try? viewContext.save()
}
}
catch {
print(error)
}
}

public func increseFreeSpace() {
guard let attributes = try? FileManager.default.attributesOfFileSystem(forPath: NSTemporaryDirectory()) else {
return
}
let freesize = (attributes[.systemFreeSize] as? NSNumber)?.int64Value ?? 0
var incSize = getCacheSize() - cacheMaxSize
if freesize < 512*1024*1024 {
let incSize2 = 512*1024*1024 - Int(freesize)
if incSize < 0 {
incSize = incSize2
}
else {
incSize += incSize2
}
}
var delSize = 0
guard let base = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("NetCache", isDirectory: true) else {
return
}
DispatchQueue.main.async {
let viewContext = self.persistentContainer.viewContext

let fetchrequest = NSFetchRequest<NSFetchRequestResult>(entityName: "FileCacheItem")
fetchrequest.predicate = NSPredicate(value: true)
fetchrequest.sortDescriptors = [NSSortDescriptor(key: "rdate", ascending: true)]
do {
if let items = try viewContext.fetch(fetchrequest) as? [FileCacheItem], items.count > 0 {
for item in items {
if delSize > incSize {
break
}
guard let target = item.filename?.uuidString else {
continue
}
let target_path = base.appendingPathComponent(String(target.prefix(2)), isDirectory: true).appendingPathComponent(String(target.prefix(4).suffix(2)), isDirectory: true).appendingPathComponent(target)
try FileManager.default.removeItem(at: target_path)
delSize += Int(item.chunkSize)
viewContext.delete(item)
}
try viewContext.save()
}
}
catch {
print(error)
}
}
}

public func getCacheSize() -> Int {
guard let base = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("NetCache", isDirectory: true) else {
return 0
}

let resourceKeys = Set<URLResourceKey>([.fileAllocatedSizeKey])
let directoryEnumerator = FileManager.default.enumerator(at: base, includingPropertiesForKeys: Array(resourceKeys), options: .skipsHiddenFiles)!

var allocSize = 0
for case let fileURL as URL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
let size = resourceValues.fileAllocatedSize
else {
continue
}
allocSize += size
}
return allocSize
}

// MARK: - Core Data stack
public lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let modelURL = Bundle(for: CloudFactory.self).url(forResource: "cache", withExtension: "momd")!
let mom = NSManagedObjectModel(contentsOf: modelURL)!

let container = NSPersistentContainer(name: "cache", managedObjectModel: mom)
let location = container.persistentStoreDescriptions.first!.url!
let description = NSPersistentStoreDescription(url: location)
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
description.setOption(FileProtectionType.completeUnlessOpen as NSObject, forKey: NSPersistentStoreFileProtectionKey)
container.persistentStoreDescriptions = [description]

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
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.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()

// MARK: - Core Data Saving support

public func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
1 change: 1 addition & 0 deletions RemoteCloud/RemoteCloud/RemoteStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ public class CloudFactory {
}

public let data = dataItems()
public let cache = FileCache()

public func getIcon(service: CloudStorages) -> UIImage? {
switch service {
Expand Down
10 changes: 10 additions & 0 deletions RemoteCloud/RemoteCloud/Storages/GoogleDriveStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,13 @@ public class GoogleDriveStorage: NetworkStorage, URLSessionTaskDelegate, URLSess
}

override func readFile(fileId: String, start: Int64? = nil, length: Int64? = nil, callCount: Int = 0, onFinish: ((Data?) -> Void)?) {
if let cache = CloudFactory.shared.cache.getCache(storage: storageName!, id: fileId, offset: start ?? 0, size: length ?? -1) {
if let data = try? Data(contentsOf: cache) {
os_log("%{public}@", log: log, type: .debug, "hit cache(google:\(storageName ?? "") \(fileId) \(start ?? -1) \(length ?? -1) \((start ?? 0) + (length ?? 0))")
onFinish?(data)
return
}
}
if lastCall.timeIntervalSinceNow > -callWait || callSemaphore.wait(wallTimeout: .now()+Double.random(in: 0..<callWait)) == .timedOut {
if cancelTime.timeIntervalSinceNow > 0 {
cancelTime = Date(timeIntervalSinceNow: 0.5)
Expand Down Expand Up @@ -748,6 +755,9 @@ public class GoogleDriveStorage: NetworkStorage, URLSessionTaskDelegate, URLSess
return
}
}
if let d = data {
CloudFactory.shared.cache.saveCache(storage: self.storageName!, id: fileId, offset: start ?? 0, data: d)
}
onFinish?(data)
}
task.resume()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="15508" systemVersion="19B88" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="FileCacheItem" representedClassName="FileCacheItem" syncable="YES" codeGenerationType="class">
<attribute name="chunkOffset" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="chunkSize" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="filename" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="id" optional="YES" attributeType="String"/>
<attribute name="mdate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="orgSize" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rdate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="storage" optional="YES" attributeType="String"/>
</entity>
<elements>
<element name="FileCacheItem" positionX="-63" positionY="-18" width="128" height="163"/>
</elements>
</model>
4 changes: 2 additions & 2 deletions ccViewer/CryptCloudViewer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = ccViewer/ccViewer.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
DEVELOPMENT_TEAM = 7A9X38B4YU;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
Expand Down Expand Up @@ -848,7 +848,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = ccViewer/ccViewer.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
DEVELOPMENT_TEAM = 7A9X38B4YU;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
Expand Down
1 change: 0 additions & 1 deletion ccViewer/ccViewer/TableViewControllerItems.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,6 @@ class CustomPresentationController: UIPresentationController {
}

overlayView.frame = containerView.bounds
//overlayView.gestureRecognizers = [UITapGestureRecognizer(target: self, action: #selector(CustomPresentationController.overlayViewDidTouch(_:)))]
overlayView.backgroundColor = .black
overlayView.alpha = 0.0
containerView.insertSubview(overlayView, at: 0)
Expand Down
Loading

0 comments on commit 8de1db1

Please sign in to comment.