jp9000 f53df7da64 clang-format: Apply formatting
Code submissions have continually suffered from formatting
inconsistencies that constantly have to be addressed.  Using
clang-format simplifies this by making code formatting more consistent,
and allows automation of the code formatting so that maintainers can
focus more on the code itself instead of code formatting.
2019-06-23 23:49:10 -07:00

222 lines
4.6 KiB
C

#include <file-updater/file-updater.h>
#include <util/threading.h>
#include <util/platform.h>
#include <obs-module.h>
#include <util/dstr.h>
#include <jansson.h>
#include "twitch.h"
static update_info_t *twitch_update_info = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool ingests_refreshed = false;
static bool ingests_refreshing = false;
static bool ingests_loaded = false;
struct ingest {
char *name;
char *url;
};
static DARRAY(struct ingest) cur_ingests;
static void free_ingests(void)
{
for (size_t i = 0; i < cur_ingests.num; i++) {
struct ingest *ingest = cur_ingests.array + i;
bfree(ingest->name);
bfree(ingest->url);
}
da_free(cur_ingests);
}
static bool load_ingests(const char *json, bool write_file)
{
json_t *root;
json_t *ingests;
bool success = false;
char *cache_old;
char *cache_new;
size_t count;
root = json_loads(json, 0, NULL);
if (!root)
goto finish;
ingests = json_object_get(root, "ingests");
if (!ingests)
goto finish;
count = json_array_size(ingests);
if (count <= 1 && cur_ingests.num)
goto finish;
free_ingests();
for (size_t i = 0; i < count; i++) {
json_t *item = json_array_get(ingests, i);
json_t *item_name = json_object_get(item, "name");
json_t *item_url = json_object_get(item, "url_template");
struct ingest ingest = {0};
struct dstr url = {0};
if (!item_name || !item_url)
continue;
const char *url_str = json_string_value(item_url);
const char *name_str = json_string_value(item_name);
/* At the moment they currently mis-spell "deprecated",
* but that may change in the future, so blacklist both */
if (strstr(name_str, "deprecated") != NULL ||
strstr(name_str, "depracated") != NULL)
continue;
dstr_copy(&url, url_str);
dstr_replace(&url, "/{stream_key}", "");
ingest.name = bstrdup(name_str);
ingest.url = url.array;
da_push_back(cur_ingests, &ingest);
}
if (!cur_ingests.num)
goto finish;
success = true;
if (!write_file)
goto finish;
cache_old = obs_module_config_path("twitch_ingests.json");
cache_new = obs_module_config_path("twitch_ingests.new.json");
os_quick_write_utf8_file(cache_new, json, strlen(json), false);
os_safe_replace(cache_old, cache_new, NULL);
bfree(cache_old);
bfree(cache_new);
finish:
if (root)
json_decref(root);
return success;
}
static bool twitch_ingest_update(void *param, struct file_download_data *data)
{
bool success;
pthread_mutex_lock(&mutex);
success = load_ingests((const char *)data->buffer.array, true);
pthread_mutex_unlock(&mutex);
if (success) {
os_atomic_set_bool(&ingests_refreshed, true);
os_atomic_set_bool(&ingests_loaded, true);
}
UNUSED_PARAMETER(param);
return true;
}
void twitch_ingests_lock(void)
{
pthread_mutex_lock(&mutex);
}
void twitch_ingests_unlock(void)
{
pthread_mutex_unlock(&mutex);
}
size_t twitch_ingest_count(void)
{
return cur_ingests.num;
}
struct twitch_ingest twitch_ingest(size_t idx)
{
struct twitch_ingest ingest;
if (cur_ingests.num <= idx) {
ingest.name = NULL;
ingest.url = NULL;
} else {
ingest = *(struct twitch_ingest *)(cur_ingests.array + idx);
}
return ingest;
}
void init_twitch_data(void)
{
da_init(cur_ingests);
pthread_mutex_init(&mutex, NULL);
}
extern const char *get_module_name(void);
void twitch_ingests_refresh(int seconds)
{
if (os_atomic_load_bool(&ingests_refreshed))
return;
if (!os_atomic_load_bool(&ingests_refreshing)) {
os_atomic_set_bool(&ingests_refreshing, true);
twitch_update_info = update_info_create_single(
"[twitch ingest update] ", get_module_name(),
"https://ingest.twitch.tv/api/v2/ingests",
twitch_ingest_update, NULL);
}
/* wait five seconds max when loading ingests for the first time */
if (!os_atomic_load_bool(&ingests_loaded)) {
for (int i = 0; i < seconds * 100; i++) {
if (os_atomic_load_bool(&ingests_refreshed)) {
break;
}
os_sleep_ms(10);
}
}
}
void load_twitch_data(void)
{
char *twitch_cache = obs_module_config_path("twitch_ingests.json");
struct ingest def = {.name = bstrdup("Default"),
.url = bstrdup("rtmp://live.twitch.tv/app")};
pthread_mutex_lock(&mutex);
da_push_back(cur_ingests, &def);
pthread_mutex_unlock(&mutex);
if (os_file_exists(twitch_cache)) {
char *data = os_quick_read_utf8_file(twitch_cache);
bool success;
pthread_mutex_lock(&mutex);
success = load_ingests(data, false);
pthread_mutex_unlock(&mutex);
if (success) {
os_atomic_set_bool(&ingests_loaded, true);
}
bfree(data);
}
bfree(twitch_cache);
}
void unload_twitch_data(void)
{
update_info_destroy(twitch_update_info);
free_ingests();
pthread_mutex_destroy(&mutex);
}