sparkle-updater.mm 3.6 KB

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