sparkle-updater.mm 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #import <Cocoa/Cocoa.h>
  2. #import <Sparkle/Sparkle.h>
  3. static inline bool equali(NSString *a, NSString *b)
  4. {
  5. return a && b && [a caseInsensitiveCompare:b] == NSOrderedSame;
  6. }
  7. @interface OBSSparkleUpdateDelegate
  8. : NSObject <SUUpdaterDelegate, SUVersionComparison> {
  9. }
  10. @property (nonatomic) bool updateToUndeployed;
  11. @end
  12. @implementation OBSSparkleUpdateDelegate {
  13. }
  14. @synthesize updateToUndeployed;
  15. - (SUAppcastItem *)bestValidUpdateWithDeltasInAppcast:(SUAppcast *)appcast
  16. forUpdater:(SUUpdater *)updater
  17. {
  18. SUAppcastItem *item = appcast.items.firstObject;
  19. if (!appcast.items.firstObject)
  20. return nil;
  21. SUAppcastItem *app = nil, *mpkg = nil;
  22. for (SUAppcastItem *item in appcast.items) {
  23. NSString *deployed = item.propertiesDictionary[@"ce:deployed"];
  24. if (deployed && !(deployed.boolValue || updateToUndeployed))
  25. continue;
  26. NSString *type = item.propertiesDictionary[@"ce:packageType"];
  27. if (!mpkg && (!type || equali(type, @"mpkg")))
  28. mpkg = item;
  29. else if (!app && type && equali(type, @"app"))
  30. app = item;
  31. if (app && mpkg)
  32. break;
  33. }
  34. if (app)
  35. item = app;
  36. NSBundle *host = updater.hostBundle;
  37. if (mpkg && (!app || equali(host.bundlePath, @"/Applications/OBS.app")))
  38. item = mpkg;
  39. NSMutableDictionary *dict = [NSMutableDictionary
  40. dictionaryWithDictionary:item.propertiesDictionary];
  41. NSString *build = [host objectForInfoDictionaryKey:@"CFBundleVersion"];
  42. NSString *url = dict[@"sparkle:releaseNotesLink"];
  43. dict[@"sparkle:releaseNotesLink"] =
  44. [url stringByAppendingFormat:@"#%@", build];
  45. return [[SUAppcastItem alloc] initWithDictionary:dict];
  46. }
  47. - (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast
  48. forUpdater:(SUUpdater *)updater
  49. {
  50. SUAppcastItem *selected = [self
  51. bestValidUpdateWithDeltasInAppcast:appcast
  52. forUpdater:updater];
  53. NSBundle *host = updater.hostBundle;
  54. NSString *build = [host objectForInfoDictionaryKey:@"CFBundleVersion"];
  55. SUAppcastItem *deltaUpdate = [selected deltaUpdates][build];
  56. if (deltaUpdate)
  57. return deltaUpdate;
  58. return selected;
  59. }
  60. - (NSString *)feedURLStringForUpdater:(SUUpdater *)updater
  61. {
  62. //URL from Info.plist takes precedence because there may be bundles with
  63. //differing feed URLs on the system
  64. NSBundle *bundle = updater.hostBundle;
  65. return [bundle objectForInfoDictionaryKey:@"SUFeedURL"];
  66. }
  67. - (NSComparisonResult)compareVersion:(NSString *)versionA
  68. toVersion:(NSString *)versionB
  69. {
  70. if (![versionA isEqual:versionB])
  71. return NSOrderedAscending;
  72. return NSOrderedSame;
  73. }
  74. - (id<SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)__unused
  75. updater
  76. {
  77. return self;
  78. }
  79. @end
  80. static inline bool bundle_matches(NSBundle *bundle)
  81. {
  82. if (!bundle.executablePath)
  83. return false;
  84. NSRange r = [bundle.executablePath rangeOfString:@"Contents/MacOS/"];
  85. return [bundle.bundleIdentifier isEqual:@"com.obsproject.obs-studio"] &&
  86. r.location != NSNotFound;
  87. }
  88. static inline NSBundle *find_bundle()
  89. {
  90. NSFileManager *fm = [NSFileManager defaultManager];
  91. NSString *path = [fm currentDirectoryPath];
  92. NSString *prev = path;
  93. do {
  94. NSBundle *bundle = [NSBundle bundleWithPath:path];
  95. if (bundle_matches(bundle))
  96. return bundle;
  97. prev = path;
  98. path = [path stringByDeletingLastPathComponent];
  99. } while (![prev isEqual:path]);
  100. return nil;
  101. }
  102. static SUUpdater *updater;
  103. static OBSSparkleUpdateDelegate *delegate;
  104. void init_sparkle_updater(bool update_to_undeployed)
  105. {
  106. updater = [SUUpdater updaterForBundle:find_bundle()];
  107. delegate = [[OBSSparkleUpdateDelegate alloc] init];
  108. delegate.updateToUndeployed = update_to_undeployed;
  109. updater.delegate = delegate;
  110. }
  111. void trigger_sparkle_update()
  112. {
  113. [updater checkForUpdates:nil];
  114. }