Avoid storing images in the database

This commit is contained in:
Cameron Gutman
2018-08-27 01:54:58 -07:00
parent 450960eaaf
commit 90d47004e7
12 changed files with 92 additions and 46 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Moonlight v1.1.xcdatamodel</string>
<string>Moonlight v1.2.xcdatamodel</string>
</dict>
</plist>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14135" systemVersion="17G65" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="165"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
</elements>
</model>

View File

@@ -21,5 +21,6 @@
- (id) initWithCallback:(id<AppAssetCallback>)callback;
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
- (void) stopRetrieving;
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app;
@end

View File

@@ -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<AppAssetCallback>)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;

View File

@@ -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];
}
}

View File

@@ -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
// First check the memory cache
UIImage* appImage = [_artCache objectForKey:_app];
if (appImage == nil) {
// Not cached; we have to decode this now
appImage = [UIImage imageWithData:_app.image];
// 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);

View File

@@ -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];
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];
}
else if ([_boxArtCache objectForKey:app] == nil) {
[_boxArtCache setObject:[MainFrameViewController loadBoxArtForCaching:app] forKey:app];
}
}

View File

@@ -164,6 +164,7 @@
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
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 = "<group>"; };
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
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 = "<group>"; };
@@ -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 = "<group>";
versionGroupType = wrapper.xcdatamodel;