Просмотр исходного кода

Merge pull request #9302 from derrod/ui-update-nlohmann

UI: Migrate WhatsNew, update branches, and Windows update check to nlohmann JSON
Lain 2 лет назад
Родитель
Сommit
106e28d60f

+ 14 - 3
UI/cmake/feature-macos-update.cmake

@@ -1,10 +1,21 @@
 include_guard(DIRECTORY)
 
+find_package(nlohmann_json REQUIRED)
+
 if(NOT TARGET OBS::blake2)
   add_subdirectory("${CMAKE_SOURCE_DIR}/deps/blake2" "${CMAKE_BINARY_DIR}/deps/blake2")
 endif()
 
-target_sources(obs-studio PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mac.mm update/shared-update.cpp
-                                  update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp)
+target_sources(
+  obs-studio
+  PRIVATE update/crypto-helpers.hpp
+          update/crypto-helpers-mac.mm
+          update/shared-update.cpp
+          update/shared-update.hpp
+          update/update-helpers.cpp
+          update/update-helpers.hpp
+          update/models/branches.hpp
+          update/models/whatsnew.hpp)
 
-target_link_libraries(obs-studio PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Security.framework>" OBS::blake2)
+target_link_libraries(obs-studio PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Security.framework>" nlohmann_json::nlohmann_json
+                                         OBS::blake2)

+ 10 - 3
UI/cmake/feature-whatsnew.cmake

@@ -5,11 +5,18 @@ if(ENABLE_WHATSNEW AND TARGET OBS::browser-panels)
     include(cmake/feature-macos-update.cmake)
   elseif(OS_LINUX)
     find_package(MbedTLS REQUIRED)
-    target_link_libraries(obs-studio PRIVATE MbedTLS::MbedTLS OBS::blake2)
+    find_package(nlohmann_json REQUIRED)
+    target_link_libraries(obs-studio PRIVATE MbedTLS::MbedTLS nlohmann_json::nlohmann_json OBS::blake2)
 
     target_sources(
-      obs-studio PRIVATE update/crypto-helpers-mbedtls.cpp update/crypto-helpers.hpp update/shared-update.cpp
-                         update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp)
+      obs-studio
+      PRIVATE update/crypto-helpers-mbedtls.cpp
+              update/crypto-helpers.hpp
+              update/shared-update.cpp
+              update/shared-update.hpp
+              update/update-helpers.cpp
+              update/update-helpers.hpp
+              update/models/whatsnew.hpp)
   endif()
 
   target_enable_feature(obs-studio "What's New panel" WHATSNEW_ENABLED)

+ 21 - 6
UI/cmake/legacy.cmake

@@ -340,6 +340,7 @@ if(OS_WINDOWS)
   configure_file(${CMAKE_CURRENT_SOURCE_DIR}/obs.rc.in ${CMAKE_BINARY_DIR}/obs.rc)
 
   find_package(Detours REQUIRED)
+  find_package(nlohmann_json REQUIRED)
 
   target_sources(
     obs
@@ -356,10 +357,13 @@ if(OS_WINDOWS)
             update/update-helpers.hpp
             update/crypto-helpers-mbedtls.cpp
             update/crypto-helpers.hpp
+            update/models/branches.hpp
+            update/models/whatsnew.hpp
+            win-update/updater/manifest.hpp
             ${CMAKE_BINARY_DIR}/obs.rc)
 
   find_package(MbedTLS)
-  target_link_libraries(obs PRIVATE Mbedtls::Mbedtls OBS::blake2 Detours::Detours)
+  target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2 Detours::Detours)
 
   target_compile_features(obs PRIVATE cxx_std_17)
 
@@ -423,17 +427,27 @@ elseif(OS_MACOS)
 
   if(ENABLE_WHATSNEW)
     find_library(SECURITY Security)
+    find_package(nlohmann_json REQUIRED)
     mark_as_advanced(SECURITY)
-    target_link_libraries(obs PRIVATE ${SECURITY} OBS::blake2)
 
-    target_sources(obs PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mac.mm update/shared-update.cpp
-                               update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp)
+    target_link_libraries(obs PRIVATE ${SECURITY} OBS::blake2 nlohmann_json::nlohmann_json)
+
+    target_sources(
+      obs
+      PRIVATE update/crypto-helpers.hpp
+              update/crypto-helpers-mac.mm
+              update/shared-update.cpp
+              update/shared-update.hpp
+              update/update-helpers.cpp
+              update/update-helpers.hpp
+              update/models/whatsnew.hpp)
 
     if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY)
       find_library(SPARKLE Sparkle)
       mark_as_advanced(SPARKLE)
 
-      target_sources(obs PRIVATE update/mac-update.cpp update/mac-update.hpp update/sparkle-updater.mm)
+      target_sources(obs PRIVATE update/mac-update.cpp update/mac-update.hpp update/sparkle-updater.mm
+                                 update/models/branches.hpp)
       target_compile_definitions(obs PRIVATE ENABLE_SPARKLE_UPDATER)
       target_link_libraries(obs PRIVATE ${SPARKLE})
       # Enable Automatic Reference Counting for Sparkle wrapper
@@ -465,13 +479,14 @@ elseif(OS_POSIX)
 
   if(OS_LINUX AND ENABLE_WHATSNEW)
     find_package(MbedTLS)
+    find_package(nlohmann_json REQUIRED)
     if(NOT MBEDTLS_FOUND)
       obs_status(FATAL_ERROR "mbedTLS not found, but required for WhatsNew support on Linux")
     endif()
 
     target_sources(obs PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mbedtls.cpp update/shared-update.cpp
                                update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp)
-    target_link_libraries(obs PRIVATE Mbedtls::Mbedtls OBS::blake2)
+    target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2)
   endif()
 endif()
 

+ 7 - 2
UI/cmake/os-windows.cmake

@@ -8,6 +8,7 @@ endif()
 
 find_package(MbedTLS)
 find_package(Detours REQUIRED)
+find_package(nlohmann_json REQUIRED)
 
 configure_file(cmake/windows/obs.rc.in obs.rc)
 
@@ -26,9 +27,13 @@ target_sources(
           update/update-window.cpp
           update/update-window.hpp
           update/win-update.cpp
-          update/win-update.hpp)
+          update/win-update.hpp
+          update/models/branches.hpp
+          update/models/whatsnew.hpp
+          win-update/updater/manifest.hpp)
 
-target_link_libraries(obs-studio PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS Detours::Detours)
+target_link_libraries(obs-studio PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS
+                                         nlohmann_json::nlohmann_json Detours::Detours)
 target_compile_definitions(obs-studio PRIVATE PSAPI_VERSION=2)
 target_link_options(obs-studio PRIVATE /IGNORE:4098 /IGNORE:4099)
 

+ 17 - 14
UI/obs-app.cpp

@@ -65,7 +65,7 @@
 #endif
 
 #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
-#include <json11.hpp>
+#include "update/models/branches.hpp"
 #endif
 
 #if !defined(_WIN32) && !defined(__APPLE__)
@@ -1289,28 +1289,31 @@ bool OBSApp::InitTheme()
 void ParseBranchesJson(const std::string &jsonString, vector<UpdateBranch> &out,
 		       std::string &error)
 {
-	json11::Json root;
-	root = json11::Json::parse(jsonString, error);
-	if (!error.empty() || !root.is_array())
+	JsonBranches branches;
+
+	try {
+		nlohmann::json json = nlohmann::json::parse(jsonString);
+		branches = json.get<JsonBranches>();
+	} catch (nlohmann::json::exception &e) {
+		error = e.what();
 		return;
+	}
 
-	for (const json11::Json &item : root.array_items()) {
+	for (const JsonBranch &json_branch : branches) {
 #ifdef _WIN32
-		if (!item["windows"].bool_value())
+		if (!json_branch.windows)
 			continue;
 #elif defined(__APPLE__)
-		if (!item["macos"].bool_value())
+		if (!json_branch.macos)
 			continue;
 #endif
 
 		UpdateBranch branch = {
-			QString::fromStdString(item["name"].string_value()),
-			QString::fromStdString(
-				item["display_name"].string_value()),
-			QString::fromStdString(
-				item["description"].string_value()),
-			item["enabled"].bool_value(),
-			item["visible"].bool_value(),
+			QString::fromStdString(json_branch.name),
+			QString::fromStdString(json_branch.display_name),
+			QString::fromStdString(json_branch.description),
+			json_branch.enabled,
+			json_branch.visible,
 		};
 		out.push_back(branch);
 	}

+ 43 - 0
UI/update/models/branches.hpp

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 Dennis Sädtler <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <string>
+#include <nlohmann/json.hpp>
+
+struct JsonBranch {
+	/* Internal name / ID of the branch (used in updater) */
+	std::string name;
+	/* Human readable name */
+	std::string display_name;
+	/* Description */
+	std::string description;
+	/* Whether updating should use the branch if selected or fall back to stable */
+	bool enabled = false;
+	/* Whether the branch should be displayed in the UI */
+	bool visible = false;
+	/* OS compatibility */
+	bool windows = false;
+	bool macos = false;
+
+	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonBranch, name,
+						    display_name, description,
+						    enabled, visible, windows,
+						    macos)
+};
+
+using JsonBranches = std::vector<JsonBranch>;

+ 93 - 0
UI/update/models/whatsnew.hpp

@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 Dennis Sädtler <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <string>
+#include <optional>
+
+#include <nlohmann/json.hpp>
+
+/* Ubuntu 22.04 be damned. */
+#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \
+	nlohmann_json_t.v1 =                \
+		nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)              \
+	friend void to_json(nlohmann::json &nlohmann_json_j,                \
+			    const Type &nlohmann_json_t)                    \
+	{                                                                   \
+		NLOHMANN_JSON_EXPAND(                                       \
+			NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \
+	}                                                                   \
+	friend void from_json(const nlohmann::json &nlohmann_json_j,        \
+			      Type &nlohmann_json_t)                        \
+	{                                                                   \
+		Type nlohmann_json_default_obj;                             \
+		NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(                   \
+			NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__))      \
+	}
+
+#endif
+
+/*
+ * Support for (de-)serialising std::optional
+ * Adapted from https://github.com/nlohmann/json/issues/1749#issuecomment-1555093802
+ */
+template<typename T> struct nlohmann::adl_serializer<std::optional<T>> {
+	static std::optional<T> from_json(const json &json)
+	{
+		return json.is_null() ? std::nullopt
+				      : std::optional{json.get<T>()};
+	}
+
+	static void to_json(json &json, std::optional<T> t)
+	{
+		if (t)
+			json = *t;
+		else
+			json = nullptr;
+	}
+};
+
+struct WhatsNewPlatforms {
+	bool windows = false;
+	bool macos = false;
+	bool linux = false;
+
+	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewPlatforms, windows,
+						    macos, linux)
+};
+
+struct WhatsNewItem {
+	/* Target OBS version (patch is ignored) */
+	std::string version;
+	/* Beta/RC release to target */
+	int Beta = 0;
+	int RC = 0;
+	/* URL of webpage to be displayed */
+	std::string url;
+	/* Increment for this version's item */
+	int increment = 0;
+	/* Optional OS filter */
+	std::optional<WhatsNewPlatforms> os = std::nullopt;
+
+	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewItem, version, Beta,
+						    RC, url, increment, os)
+};
+
+using WhatsNewList = std::vector<WhatsNewItem>;

+ 22 - 41
UI/update/win-update.cpp

@@ -1,3 +1,4 @@
+#include "../win-update/updater/manifest.hpp"
 #include "update-helpers.hpp"
 #include "shared-update.hpp"
 #include "update-window.hpp"
@@ -17,14 +18,13 @@
 
 #include <util/windows/WinHandle.hpp>
 #include <util/util.hpp>
-#include <json11.hpp>
 
 #ifdef BROWSER_AVAILABLE
 #include <browser-panel.hpp>
 #endif
 
 using namespace std;
-using namespace json11;
+using namespace updater;
 
 /* ------------------------------------------------------------------------ */
 
@@ -71,60 +71,41 @@ using namespace json11;
 #define CUR_COMMIT "00000000"
 #endif
 
-static bool ParseUpdateManifest(const char *manifest, bool *updatesAvailable,
-				string &notes_str, uint64_t &updateVer,
-				string &branch)
+static bool ParseUpdateManifest(const char *manifest_data,
+				bool *updatesAvailable, string &notes,
+				uint64_t &updateVer, const string &branch)
 try {
+	json manifestContents = json::parse(manifest_data);
+	Manifest manifest = manifestContents.get<Manifest>();
 
-	string error;
-	Json root = Json::parse(manifest, error);
-	if (!error.empty())
-		throw strprintf("Failed reading json string: %s",
-				error.c_str());
+	if (manifest.version_major == 0 && manifest.commit.empty())
+		throw strprintf("Invalid version number: %d.%d.%d",
+				manifest.version_major, manifest.version_minor,
+				manifest.version_patch);
 
-	if (!root.is_object())
-		throw string("Root of manifest is not an object");
-
-	int major = root["version_major"].int_value();
-	int minor = root["version_minor"].int_value();
-	int patch = root["version_patch"].int_value();
-	int rc = root["rc"].int_value();
-	int beta = root["beta"].int_value();
-	string commit_hash = root["commit"].string_value();
-
-	if (major == 0 && commit_hash.empty())
-		throw strprintf("Invalid version number: %d.%d.%d", major,
-				minor, patch);
-
-	const Json &notes = root["notes"];
-	if (!notes.is_string())
-		throw string("'notes' value invalid");
-
-	notes_str = notes.string_value();
-
-	const Json &packages = root["packages"];
-	if (!packages.is_array())
-		throw string("'packages' value invalid");
+	notes = manifest.notes;
 
 	uint64_t cur_ver;
 	uint64_t new_ver;
 
-	if (commit_hash.empty()) {
+	if (manifest.commit.empty()) {
 		cur_ver = CUR_VER;
-		new_ver = MAKE_SEMANTIC_VERSION(
-			(uint64_t)major, (uint64_t)minor, (uint64_t)patch);
+		new_ver =
+			MAKE_SEMANTIC_VERSION((uint64_t)manifest.version_major,
+					      (uint64_t)manifest.version_minor,
+					      (uint64_t)manifest.version_patch);
 		new_ver <<= 16;
 		/* RC builds are shifted so that rc1 and beta1 versions do not result
 		 * in the same new_ver. */
-		if (rc > 0)
-			new_ver |= (uint64_t)rc << 8;
-		else if (beta > 0)
-			new_ver |= (uint64_t)beta;
+		if (manifest.rc > 0)
+			new_ver |= (uint64_t)manifest.rc << 8;
+		else if (manifest.beta > 0)
+			new_ver |= (uint64_t)manifest.beta;
 	} else {
 		/* Test or nightly builds may not have a (valid) version number,
 		 * so compare commit hashes instead. */
 		cur_ver = stoul(CUR_COMMIT, nullptr, 16);
-		new_ver = stoul(commit_hash.substr(0, 8), nullptr, 16);
+		new_ver = stoul(manifest.commit.substr(0, 8), nullptr, 16);
 	}
 
 	updateVer = new_ver;

+ 4 - 4
UI/win-update/updater/manifest.hpp

@@ -53,17 +53,17 @@ struct Manifest {
 	uint8_t version_patch = 0;
 	uint8_t beta = 0;
 	uint8_t rc = 0;
+	std::string commit;
 
 	/* Hash of VC redist file */
 	std::string vc2019_redist_x64;
 
-	/* Unused until UI is migrated to nlohmann_json */
-	// std::string commit;
-	// std::string notes;
+	/* Release notes in HTML format */
+	std::string notes;
 
 	NLOHMANN_DEFINE_TYPE_INTRUSIVE(Manifest, packages, version_major,
 				       version_minor, version_patch, beta, rc,
-				       vc2019_redist_x64)
+				       commit, vc2019_redist_x64, notes)
 };
 
 struct PatchRequest {

+ 21 - 24
UI/window-basic-main.cpp

@@ -97,13 +97,12 @@
 
 #include <QWindow>
 
-#include <json11.hpp>
+#include "update/models/whatsnew.hpp"
 
 #ifdef ENABLE_WAYLAND
 #include <obs-nix-platform.h>
 #endif
 
-using namespace json11;
 using namespace std;
 
 #ifdef BROWSER_AVAILABLE
@@ -2399,54 +2398,52 @@ void OBSBasic::ReceivedIntroJson(const QString &text)
 	if (closing)
 		return;
 
-	std::string err;
-	Json json = Json::parse(QT_TO_UTF8(text), err);
-	if (!err.empty())
+	WhatsNewList items;
+	try {
+		nlohmann::json json = nlohmann::json::parse(text.toStdString());
+		items = json.get<WhatsNewList>();
+	} catch (nlohmann::json::exception &e) {
+		blog(LOG_WARNING, "Parsing whatsnew data failed: %s", e.what());
 		return;
+	}
 
 	std::string info_url;
 	int info_increment = -1;
 
 	/* check to see if there's an info page for this version */
-	const Json::array &items = json.array_items();
-	for (const Json &item : items) {
-		if (item["os"].is_object()) {
-			Json::object platforms = item["os"].object_items();
+	for (const WhatsNewItem &item : items) {
+		if (item.os) {
+			WhatsNewPlatforms platforms = *item.os;
 #ifdef _WIN32
-			if (!platforms["windows"].bool_value())
+			if (!platforms.windows)
 				continue;
 #elif defined(__APPLE__)
-			if (!platforms["macos"].bool_value())
+			if (!platforms.macos)
 				continue;
 #else
-			if (!platforms["linux"].bool_value())
+			if (!platforms.linux)
 				continue;
 #endif
 		}
 
-		const std::string &version = item["version"].string_value();
-		const std::string &url = item["url"].string_value();
-		int increment = item["increment"].int_value();
-		int beta = item["Beta"].int_value();
-		int rc = item["RC"].int_value();
-
 		int major = 0;
 		int minor = 0;
 
-		sscanf(version.c_str(), "%d.%d", &major, &minor);
+		sscanf(item.version.c_str(), "%d.%d", &major, &minor);
 #if defined(OBS_RELEASE_CANDIDATE) && OBS_RELEASE_CANDIDATE > 0
 		if (major == OBS_RELEASE_CANDIDATE_MAJOR &&
 		    minor == OBS_RELEASE_CANDIDATE_MINOR &&
-		    rc == OBS_RELEASE_CANDIDATE) {
+		    item.RC == OBS_RELEASE_CANDIDATE) {
 #elif OBS_BETA > 0
 		if (major == OBS_BETA_MAJOR && minor == OBS_BETA_MINOR &&
-		    beta == OBS_BETA) {
+		    item.Beta == OBS_BETA) {
 #else
 		if (major == LIBOBS_API_MAJOR_VER &&
-		    minor == LIBOBS_API_MINOR_VER && rc == 0 && beta == 0) {
+		    minor == LIBOBS_API_MINOR_VER && item.RC == 0 &&
+		    item.Beta == 0) {
 #endif
-			info_url = url;
-			info_increment = increment;
+			info_url = item.url;
+			info_increment = item.increment;
 		}
 	}