From 90d47004e7bf722d334ec67289fedbaa9e03541e Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 27 Aug 2018 01:54:58 -0700 Subject: [PATCH] Avoid storing images in the database --- Limelight/Database/DataManager.h | 1 - Limelight/Database/DataManager.m | 14 ------- Limelight/Database/TemporaryApp.h | 1 - Limelight/Database/TemporaryApp.m | 1 - .../Limelight.xcdatamodeld/.xccurrentversion | 2 +- .../Moonlight v1.2.xcdatamodel/contents | 39 +++++++++++++++++++ Limelight/Network/AppAssetManager.h | 1 + Limelight/Network/AppAssetManager.m | 18 ++++++++- Limelight/Network/AppAssetRetriever.m | 17 ++++---- Limelight/UIAppView.m | 18 +++++---- .../ViewControllers/MainFrameViewController.m | 22 ++++++----- Moonlight.xcodeproj/project.pbxproj | 4 +- 12 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 Limelight/Limelight.xcdatamodeld/Moonlight v1.2.xcdatamodel/contents diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h index 1ff4057..611df00 100644 --- a/Limelight/Database/DataManager.h +++ b/Limelight/Database/DataManager.h @@ -28,7 +28,6 @@ - (NSArray*) getHosts; - (void) updateHost:(TemporaryHost*)host; - (void) updateAppsForExistingHost:(TemporaryHost *)host; -- (void) updateIconForExistingApp:(TemporaryApp*)app; - (void) removeHost:(TemporaryHost*)host; - (void) removeApp:(TemporaryApp*)app; diff --git a/Limelight/Database/DataManager.m b/Limelight/Database/DataManager.m index 374d673..22bb129 100644 --- a/Limelight/Database/DataManager.m +++ b/Limelight/Database/DataManager.m @@ -128,20 +128,6 @@ }]; } -- (void) updateIconForExistingApp:(TemporaryApp*)app { - [_managedObjectContext performBlockAndWait:^{ - App* parentApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]]; - if (parentApp == nil) { - // The app must exist to be updated - return; - } - - parentApp.image = app.image; - - [self saveData]; - }]; -} - - (TemporarySettings*) getSettings { __block TemporarySettings *tempSettings; diff --git a/Limelight/Database/TemporaryApp.h b/Limelight/Database/TemporaryApp.h index 9305655..b1c4d2d 100644 --- a/Limelight/Database/TemporaryApp.h +++ b/Limelight/Database/TemporaryApp.h @@ -12,7 +12,6 @@ @interface TemporaryApp : NSObject @property (nullable, nonatomic, retain) NSString *id; -@property (nullable, nonatomic, retain) NSData *image; @property (nullable, nonatomic, retain) NSString *name; @property (nonatomic) BOOL hdrSupported; @property (nullable, nonatomic, retain) TemporaryHost *host; diff --git a/Limelight/Database/TemporaryApp.m b/Limelight/Database/TemporaryApp.m index 85a4372..8d37320 100644 --- a/Limelight/Database/TemporaryApp.m +++ b/Limelight/Database/TemporaryApp.m @@ -14,7 +14,6 @@ self = [self init]; self.id = app.id; - self.image = app.image; self.name = app.name; self.hdrSupported = app.hdrSupported; self.host = tempHost; diff --git a/Limelight/Limelight.xcdatamodeld/.xccurrentversion b/Limelight/Limelight.xcdatamodeld/.xccurrentversion index d880a37..3bc8a00 100644 --- a/Limelight/Limelight.xcdatamodeld/.xccurrentversion +++ b/Limelight/Limelight.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - Moonlight v1.1.xcdatamodel + Moonlight v1.2.xcdatamodel diff --git a/Limelight/Limelight.xcdatamodeld/Moonlight v1.2.xcdatamodel/contents b/Limelight/Limelight.xcdatamodeld/Moonlight v1.2.xcdatamodel/contents new file mode 100644 index 0000000..0d3feba --- /dev/null +++ b/Limelight/Limelight.xcdatamodeld/Moonlight v1.2.xcdatamodel/contents @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Limelight/Network/AppAssetManager.h b/Limelight/Network/AppAssetManager.h index 1d05ce9..d202072 100644 --- a/Limelight/Network/AppAssetManager.h +++ b/Limelight/Network/AppAssetManager.h @@ -21,5 +21,6 @@ - (id) initWithCallback:(id)callback; - (void) retrieveAssetsFromHost:(TemporaryHost*)host; - (void) stopRetrieving; ++ (NSString*) boxArtPathForApp:(TemporaryApp*)app; @end diff --git a/Limelight/Network/AppAssetManager.m b/Limelight/Network/AppAssetManager.m index f8a8f5d..4a6480d 100644 --- a/Limelight/Network/AppAssetManager.m +++ b/Limelight/Network/AppAssetManager.m @@ -19,6 +19,22 @@ static const int MAX_REQUEST_COUNT = 4; ++ (NSString*) boxArtPathForApp:(TemporaryApp*)app { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + NSString *filePath = [paths objectAtIndex:0]; + + // Keep app assets separate by host UUID + filePath = [filePath stringByAppendingPathComponent:app.host.uuid]; + + // Use the app ID as the file name + filePath = [filePath stringByAppendingPathComponent:app.id]; + + // Add a png extension + filePath = [filePath stringByAppendingPathExtension:@"png"]; + + return filePath; +} + - (id) initWithCallback:(id)callback { self = [super init]; _callback = callback; @@ -30,7 +46,7 @@ static const int MAX_REQUEST_COUNT = 4; - (void) retrieveAssetsFromHost:(TemporaryHost*)host { for (TemporaryApp* app in host.appList) { - if (app.image == nil) { + if (![[NSFileManager defaultManager] fileExistsAtPath:[AppAssetManager boxArtPathForApp:app]]) { AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init]; retriever.app = app; retriever.host = host; diff --git a/Limelight/Network/AppAssetRetriever.m b/Limelight/Network/AppAssetRetriever.m index 43d3aeb..30dce85 100644 --- a/Limelight/Network/AppAssetRetriever.m +++ b/Limelight/Network/AppAssetRetriever.m @@ -18,25 +18,24 @@ static const double RETRY_DELAY = 2; // seconds static const int MAX_ATTEMPTS = 5; - (void) main { - - OSImage* appImage = nil; - int attempts = 0; - while (![self isCancelled] && appImage == nil && attempts++ < MAX_ATTEMPTS) { - + while (![self isCancelled] && attempts++ < MAX_ATTEMPTS) { HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]]; AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init]; [hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]]; #if TARGET_OS_IPHONE - appImage = [UIImage imageWithData:appAssetResp.data]; - self.app.image = UIImagePNGRepresentation(appImage); + if (appAssetResp.data != nil) { + NSString* boxArtPath = [AppAssetManager boxArtPathForApp:self.app]; + [[NSFileManager defaultManager] createDirectoryAtPath:[boxArtPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil]; + [appAssetResp.data writeToFile:boxArtPath atomically:NO]; + break; + } #else #endif - - if (![self isCancelled] && appImage == nil) { + if (![self isCancelled]) { [NSThread sleepForTimeInterval:RETRY_DELAY]; } } diff --git a/Limelight/UIAppView.m b/Limelight/UIAppView.m index f519959..03094eb 100644 --- a/Limelight/UIAppView.m +++ b/Limelight/UIAppView.m @@ -7,6 +7,7 @@ // #import "UIAppView.h" +#import "AppAssetManager.h" @implementation UIAppView { TemporaryApp* _app; @@ -74,19 +75,20 @@ static UIImage* noImage; [_appOverlay setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/6)]; } - // TODO: Improve no-app image detection BOOL noAppImage = false; - if (_app.image != nil) { - // Load the decoded image from the cache - UIImage* appImage = [_artCache objectForKey:_app]; - if (appImage == nil) { - // Not cached; we have to decode this now - appImage = [UIImage imageWithData:_app.image]; + // First check the memory cache + UIImage* appImage = [_artCache objectForKey:_app]; + if (appImage == nil) { + // Next try to load from the on disk cache + appImage = [UIImage imageWithContentsOfFile:[AppAssetManager boxArtPathForApp:_app]]; + if (appImage != nil) { [_artCache setObject:appImage forKey:_app]; } - + } + if (appImage != nil) { // This size of image might be blank image received from GameStream. + // TODO: Improve no-app image detection if (!(appImage.size.width == 130.f && appImage.size.height == 180.f) && // GFE 2.0 !(appImage.size.width == 628.f && appImage.size.height == 888.f)) { // GFE 3.0 _appButton.frame = CGRectMake(0, 0, appImage.size.width / 2, appImage.size.height / 2); diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 572736b..2b2ae89 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -263,9 +263,6 @@ static NSMutableSet* hostList; // on the main thread [self updateBoxArtCacheForApp:app]; - DataManager* dataManager = [[DataManager alloc] init]; - [dataManager updateIconForExistingApp: app]; - dispatch_async(dispatch_get_main_queue(), ^{ [self.collectionView reloadData]; }); @@ -926,7 +923,13 @@ static NSMutableSet* hostList; + (UIImage*) loadBoxArtForCaching:(TemporaryApp*)app { UIImage* boxArt; - CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)app.image, NULL); + NSData* imageData = [NSData dataWithContentsOfFile:[AppAssetManager boxArtPathForApp:app]]; + if (imageData == nil) { + // No box art on disk + return nil; + } + + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil); size_t width = CGImageGetWidth(cgImage); @@ -953,11 +956,12 @@ static NSMutableSet* hostList; } - (void) updateBoxArtCacheForApp:(TemporaryApp*)app { - if (app.image == nil) { - [_boxArtCache removeObjectForKey:app]; - } - else if ([_boxArtCache objectForKey:app] == nil) { - [_boxArtCache setObject:[MainFrameViewController loadBoxArtForCaching:app] forKey:app]; + if ([_boxArtCache objectForKey:app] == nil) { + UIImage* image = [MainFrameViewController loadBoxArtForCaching:app]; + if (image != nil) { + // Add the image to our cache if it was present + [_boxArtCache setObject:image forKey:app]; + } } } diff --git a/Moonlight.xcodeproj/project.pbxproj b/Moonlight.xcodeproj/project.pbxproj index a5fcedb..dc484dd 100644 --- a/Moonlight.xcodeproj/project.pbxproj +++ b/Moonlight.xcodeproj/project.pbxproj @@ -164,6 +164,7 @@ 9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = ""; }; 9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = ""; }; 9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; }; + 986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.2.xcdatamodel"; sourceTree = ""; }; 98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = ""; }; 9890CF6A203B7EE1006C4B06 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; }; 98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "moonlight-common/moonlight-common.xcodeproj"; sourceTree = ""; }; @@ -1499,6 +1500,7 @@ FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */, 98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */, FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */, FBB460391B50ACE400F3099C /* Moonlight v1.0.xcdatamodel */, @@ -1506,7 +1508,7 @@ FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */, FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */, ); - currentVersion = 98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */; + currentVersion = 986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */; path = Limelight.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel;