rtmp-services: Add Twitch ingest update API

Updates ingests via Twitch's ingest API.  The servers are ordered by
closest to farthest from the user via the API.  It's probably still good
to keep the services.json Twitch ingests updated in case there are
issues with the Twitch ingest API, but otherwise the ingests will update
from Twitch itself every time the program is opened automatically.
master
jp9000 2017-08-01 02:41:00 -07:00
parent 3786a203c7
commit 435e251809
5 changed files with 227 additions and 0 deletions

View File

@ -3,11 +3,13 @@ project(rtmp-services)
include_directories(${OBS_JANSSON_INCLUDE_DIRS})
set(rtmp-services_SOURCES
twitch.c
rtmp-common.c
rtmp-custom.c
rtmp-services-main.c)
set(rtmp-services_HEADERS
twitch.h
rtmp-format-ver.h)
set(RTMP_SERVICES_URL

View File

@ -4,6 +4,7 @@
#include <jansson.h>
#include "rtmp-format-ver.h"
#include "twitch.h"
struct rtmp_common {
char *service;
@ -242,6 +243,32 @@ static void properties_data_destroy(void *data)
json_decref(root);
}
static bool fill_twitch_servers_locked(obs_property_t *servers_prop)
{
size_t count = twitch_ingest_count();
if (count <= 1)
return false;
for (size_t i = 0; i < count; i++) {
struct twitch_ingest ing = twitch_ingest(i);
obs_property_list_add_string(servers_prop, ing.name, ing.url);
}
return true;
}
static inline bool fill_twitch_servers(obs_property_t *servers_prop)
{
bool success;
twitch_ingests_lock();
success = fill_twitch_servers_locked(servers_prop);
twitch_ingests_unlock();
return success;
}
static void fill_servers(obs_property_t *servers_prop, json_t *service,
const char *name)
{
@ -263,6 +290,10 @@ static void fill_servers(obs_property_t *servers_prop, json_t *service,
obs_property_list_add_string(servers_prop,
obs_module_text("Server.Auto"), "auto");
}
if (name && strcmp(name, "Twitch") == 0) {
if (fill_twitch_servers(servers_prop))
return;
}
json_array_foreach (servers, index, server) {
const char *server_name = get_string_val(server, "name");

View File

@ -40,6 +40,9 @@ static bool confirm_service_file(void *param, struct file_download_data *file)
return true;
}
extern void load_twitch_data(const char *module_str);
extern void unload_twitch_data(void);
bool obs_module_load(void)
{
char *local_dir = obs_module_file("");
@ -60,6 +63,8 @@ bool obs_module_load(void)
confirm_service_file, NULL);
}
load_twitch_data(module_name.array);
bfree(local_dir);
bfree(cache_dir);
dstr_free(&module_name);
@ -72,4 +77,5 @@ bool obs_module_load(void)
void obs_module_unload(void)
{
update_info_destroy(update_info);
unload_twitch_data();
}

View File

@ -0,0 +1,177 @@
#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;
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 inline void init_defaults(void)
{
struct ingest ingest = {0};
ingest.name = bstrdup("Default");
ingest.url = bstrdup("rtmp://live.twitch.tv/app");
da_push_back(cur_ingests, &ingest);
}
static void 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 (!write_file || !cur_ingests.num)
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 (!cur_ingests.num)
init_defaults();
if (root)
json_decref(root);
}
static bool twitch_ingest_update(void *param, struct file_download_data *data)
{
pthread_mutex_lock(&mutex);
load_ingests(data->buffer.array, true);
pthread_mutex_unlock(&mutex);
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 load_twitch_data(const char *module_str)
{
char *twitch_cache = obs_module_config_path("twitch_ingests.json");
if (os_file_exists(twitch_cache)) {
char *data = os_quick_read_utf8_file(twitch_cache);
pthread_mutex_lock(&mutex);
load_ingests(data, false);
pthread_mutex_unlock(&mutex);
bfree(data);
} else {
init_defaults();
}
twitch_update_info = update_info_create_single(
"[twitch ingest update] ",
module_str,
"https://ingest.twitch.tv/api/v2/ingests",
twitch_ingest_update, NULL);
bfree(twitch_cache);
}
void unload_twitch_data(void)
{
update_info_destroy(twitch_update_info);
free_ingests();
}

View File

@ -0,0 +1,11 @@
#pragma once
struct twitch_ingest {
const char *name;
const char *url;
};
extern void twitch_ingests_lock(void);
extern void twitch_ingests_unlock(void);
extern size_t twitch_ingest_count(void);
extern struct twitch_ingest twitch_ingest(size_t idx);