mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-04-20 23:40:17 +00:00
Avoid storing images in the database
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -21,5 +21,6 @@
|
||||
- (id) initWithCallback:(id<AppAssetCallback>)callback;
|
||||
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
|
||||
- (void) stopRetrieving;
|
||||
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user