Merge pull request #279 from palana/sparkle-update

Add option to use Sparkle for updates
master
Jim 2014-10-11 18:50:22 -07:00
commit daf2b02c73
3 changed files with 156 additions and 0 deletions

View File

@ -50,6 +50,20 @@ elseif(APPLE)
set(obs_PLATFORM_LIBRARIES ${APPKIT_LIBRARIES})
add_definitions(-fobjc-arc)
option(ENABLE_SPARKLE_UPDATER "Enables updates via the Sparkle framework (don't forget to update the Info.plist for your .app)" OFF)
if(ENABLE_SPARKLE_UPDATER)
find_library(SPARKLE Sparkle)
include_directories(${SPARKLE})
set(obs_PLATFORM_SOURCES
${obs_PLATFORM_SOURCES}
sparkle-updater.mm)
set(obs_PLATFORM_LIBRARIES
${obs_PLATFORM_LIBRARIES}
${SPARKLE})
add_definitions(-DUPDATE_SPARKLE=1)
endif()
elseif(UNIX)
find_package(Qt5X11Extras REQUIRED)

128
obs/sparkle-updater.mm Normal file
View File

@ -0,0 +1,128 @@
#import <Cocoa/Cocoa.h>
#import <Sparkle/Sparkle.h>
static inline bool equali(NSString *a, NSString *b)
{
return a && b && [a caseInsensitiveCompare:b] == NSOrderedSame;
}
@interface OBSSparkleUpdateDelegate :
NSObject<SUUpdaterDelegate, SUVersionComparison>
{
}
@property (nonatomic) bool updateToUndeployed;
@end
@implementation OBSSparkleUpdateDelegate
{
}
@synthesize updateToUndeployed;
- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast
forUpdater:(SUUpdater *)updater
{
static SUAppcastItem *selected;
SUAppcastItem *item = appcast.items.firstObject;
if (!appcast.items.firstObject)
return nil;
SUAppcastItem *app = nil, *mpkg = nil;
for (SUAppcastItem *item in appcast.items) {
NSString *deployed = item.propertiesDictionary[@"ce:deployed"];
if (deployed && !(deployed.boolValue || updateToUndeployed))
continue;
NSString *type = item.propertiesDictionary[@"ce:packageType"];
if (!mpkg && (!type || equali(type, @"mpkg")))
mpkg = item;
else if (!app && type && equali(type, @"app"))
app = item;
if (app && mpkg)
break;
}
if (app)
item = app;
NSBundle *host = updater.hostBundle;
if (mpkg && (!app || equali(host.bundlePath, @"/Applications/OBS.app")))
item = mpkg;
NSMutableDictionary *dict = [NSMutableDictionary
dictionaryWithDictionary:item.propertiesDictionary];
NSString *build = [host objectForInfoDictionaryKey:@"CFBundleVersion"];
NSString *url = dict[@"sparkle:releaseNotesLink"];
dict[@"sparkle:releaseNotesLink"] = [url stringByAppendingFormat:@"#%@",
build];
return selected = [[SUAppcastItem alloc] initWithDictionary:dict];
}
- (NSString *)feedURLStringForUpdater:(SUUpdater *)updater
{
//URL from Info.plist takes precedence because there may be bundles with
//differing feed URLs on the system
NSBundle *bundle = updater.hostBundle;
return [bundle objectForInfoDictionaryKey:@"SUFeedURL"];
}
- (NSComparisonResult)compareVersion:(NSString *)versionA
toVersion:(NSString *)versionB
{
if (![versionA isEqual:versionB])
return NSOrderedAscending;
return NSOrderedSame;
}
- (id <SUVersionComparison>)
versionComparatorForUpdater:(SUUpdater *)__unused updater
{
return self;
}
@end
static inline bool bundle_matches(NSBundle *bundle)
{
if (!bundle.executablePath)
return false;
NSRange r = [bundle.executablePath rangeOfString:@"Contents/MacOS/"];
return [bundle.bundleIdentifier isEqual:@"com.obsproject.obs-studio"] &&
r.location != NSNotFound;
}
static inline NSBundle *find_bundle()
{
NSFileManager *fm = [NSFileManager defaultManager];
NSString *path = [fm currentDirectoryPath];
NSString *prev = path;
do {
NSBundle *bundle = [NSBundle bundleWithPath:path];
if (bundle_matches(bundle))
return bundle;
prev = path;
path = [path stringByDeletingLastPathComponent];
} while (![prev isEqual:path]);
return nil;
}
static SUUpdater *updater;
static OBSSparkleUpdateDelegate *delegate;
void init_sparkle_updater(bool update_to_undeployed)
{
updater = [SUUpdater updaterForBundle:find_bundle()];
delegate = [[OBSSparkleUpdateDelegate alloc] init];
delegate.updateToUndeployed = update_to_undeployed;
updater.delegate = delegate;
}
void trigger_sparkle_update()
{
[updater checkForUpdates:nil];
}

View File

@ -890,8 +890,17 @@ bool OBSBasic::QueryRemoveSource(obs_source_t *source)
#define UPDATE_CHECK_INTERVAL (60*60*24*4) /* 4 days */
#ifdef UPDATE_SPARKLE
void init_sparkle_updater(bool update_to_undeployed);
void trigger_sparkle_update();
#endif
void OBSBasic::TimedCheckForUpdates()
{
#ifdef UPDATE_SPARKLE
init_sparkle_updater(config_get_bool(App()->GlobalConfig(), "General",
"UpdateToUndeployed"));
#else
long long lastUpdate = config_get_int(App()->GlobalConfig(), "General",
"LastUpdateCheck");
uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General",
@ -908,10 +917,14 @@ void OBSBasic::TimedCheckForUpdates()
if (secs > UPDATE_CHECK_INTERVAL)
CheckForUpdates();
#endif
}
void OBSBasic::CheckForUpdates()
{
#ifdef UPDATE_SPARKLE
trigger_sparkle_update();
#else
ui->actionCheckForUpdates->setEnabled(false);
string versionString("obs-basic ");
@ -926,6 +939,7 @@ void OBSBasic::CheckForUpdates()
this, SLOT(updateFileFinished()));
connect(updateReply, SIGNAL(readyRead()),
this, SLOT(updateFileRead()));
#endif
}
void OBSBasic::updateFileRead()