diff --git a/inc/mthread/mthread.h b/inc/mthread/mthread.h
index cef1aa21e938592721d142f15b4807049d2dc529..869e0ddd4c3b0ecfc0b1e4eaca697a5a5761046a 100644
--- a/inc/mthread/mthread.h
+++ b/inc/mthread/mthread.h
@@ -48,35 +48,35 @@ typedef struct mthread_sem_s mthread_sem_t;
 
 /* Function for handling threads.  */
 
-int mthread_create(mthread_t *__threadp, const mthread_attr_t __attr, void *(*__start_routine) (void *), void *__arg);
+extern int mthread_create(mthread_t *__threadp, const mthread_attr_t __attr, void *(*__start_routine) (void *), void *__arg);
 
-mthread_t mthread_self(void);
-int  mthread_equal (mthread_t __thread1, mthread_t __thread2);
-void mthread_exit  (void *__retval);
-int  mthread_join  (mthread_t __th, void **__thread_return);
+extern mthread_t mthread_self(void);
+extern int  mthread_equal (mthread_t __thread1, mthread_t __thread2);
+extern void mthread_exit  (void *__retval);
+extern int  mthread_join  (mthread_t __th, void **__thread_return);
 
 /* Functions for mutex handling.  */
 
-int mthread_mutex_init   (mthread_mutex_t *__mutex, const mthread_mutexattr_t *__mutex_attr);
-int mthread_mutex_destroy(mthread_mutex_t *__mutex);
-int mthread_mutex_trylock(mthread_mutex_t *__mutex);
-int mthread_mutex_lock   (mthread_mutex_t *__mutex);
-int mthread_mutex_unlock (mthread_mutex_t *__mutex);
+extern int mthread_mutex_init   (mthread_mutex_t *__mutex, const mthread_mutexattr_t *__mutex_attr);
+extern int mthread_mutex_destroy(mthread_mutex_t *__mutex);
+extern int mthread_mutex_trylock(mthread_mutex_t *__mutex);
+extern int mthread_mutex_lock   (mthread_mutex_t *__mutex);
+extern int mthread_mutex_unlock (mthread_mutex_t *__mutex);
 
 /* Functions for handling conditional variables.  */
 
-int mthread_cond_init     (mthread_cond_t *__cond, const mthread_condattr_t *__cond_attr);
-int mthread_cond_destroy  (mthread_cond_t *__cond);
-int mthread_cond_signal   (mthread_cond_t *__cond);
-int mthread_cond_broadcast(mthread_cond_t *__cond);
-int mthread_cond_wait     (mthread_cond_t *__cond, mthread_mutex_t *__mutex);
+extern int mthread_cond_init     (mthread_cond_t *__cond, const mthread_condattr_t *__cond_attr);
+extern int mthread_cond_destroy  (mthread_cond_t *__cond);
+extern int mthread_cond_signal   (mthread_cond_t *__cond);
+extern int mthread_cond_broadcast(mthread_cond_t *__cond);
+extern int mthread_cond_wait     (mthread_cond_t *__cond, mthread_mutex_t *__mutex);
 
 /* Functions for handling thread-specific data.  */
 
-int mthread_key_create   (mthread_key_t *__key, void (*__destr_function) (void *));
-int mthread_key_delete   (mthread_key_t __key);
-int mthread_setspecific  (mthread_key_t __key, const void *__pointer);
-void *mthread_getspecific(mthread_key_t __key);
+extern int  mthread_key_create  (mthread_key_t *__key, void (*__destr_function) (void *));
+extern int  mthread_key_delete  (mthread_key_t __key);
+extern int  mthread_setspecific (mthread_key_t __key, const void *__pointer);
+extern void *mthread_getspecific(mthread_key_t __key);
 
 
 /* Functions for handling initialization.  */
@@ -88,21 +88,21 @@ void *mthread_getspecific(mthread_key_t __key);
 
    The initialization functions might throw exception which is why
    this function is not marked with .  */
-int mthread_once(mthread_once_t *__once_control, void (*__init_routine) (void));
+extern int mthread_once(mthread_once_t *__once_control, void (*__init_routine) (void));
 
 /* Functions for handling semaphore.  */
 
-int mthread_sem_init(mthread_sem_t *sem, unsigned int value);
-int mthread_sem_wait(mthread_sem_t *sem);     /* P(sem), wait(sem) */
-int mthread_sem_post(mthread_sem_t *sem);     /* V(sem), signal(sem) */
+extern int mthread_sem_init(mthread_sem_t *sem, unsigned int value);
+extern int mthread_sem_wait(mthread_sem_t *sem);     /* P(sem), wait(sem) */
+extern int mthread_sem_post(mthread_sem_t *sem);     /* V(sem), signal(sem) */
 
-int mthread_sem_getvalue(mthread_sem_t *sem, int *sval);
-int mthread_sem_trywait (mthread_sem_t *sem);
-int mthread_sem_destroy (mthread_sem_t *sem);  /* undo sem_init() */
+extern int mthread_sem_getvalue(mthread_sem_t *sem, int *sval);
+extern int mthread_sem_trywait (mthread_sem_t *sem);
+extern int mthread_sem_destroy (mthread_sem_t *sem);  /* undo sem_init() */
 
-void mthread_yield();
+extern void mthread_yield();
 
 /* Initialize mthread. */
 
-void mthread_init(void);
-void *mthread_main(void *arg);
+extern void mthread_init(void);
+extern void *mthread_main(void *arg);
diff --git a/meson.build b/meson.build
index f92d8edd08fb11ebfbc60f6398a0825b809623ee..091dd75832c62eabeef329eeab8df89277a56bcd 100644
--- a/meson.build
+++ b/meson.build
@@ -60,7 +60,6 @@ core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/utils.c'
                 , 'src/uri.c'
                 , 'src/ini/ini.c'
-                , 'src/repo/curl.c'
                 , 'src/repo/downloader.c'
                 , 'src/repo/sync.c'
                 , 'src/thread.c'
@@ -106,7 +105,7 @@ srv = executable( meson.project_name() + 'd'
 metadata = executable( 'lktadm'
                      , files('src/main/lktadm.c', 'src/cmd.c')
                      , include_directories : includes
-                     , dependencies : bin_deps
+                     , dependencies : [ bin_deps, mthread_deps ]
                      , install : true
                      )
 
diff --git a/src/repo/curl.c b/src/repo/curl.c
deleted file mode 100644
index 727070127ee09396e13cf8bd1abd834f14507aa1..0000000000000000000000000000000000000000
--- a/src/repo/curl.c
+++ /dev/null
@@ -1,301 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include <common/common.h>
-#include <lektor/repo.h>
-#include <lektor/macro.h>
-#include <lektor/database.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-static volatile unsigned int curl_init = false;
-
-#define URL_MAX_LEN 1024
-#define DEFAULT_URL "https://kurisu.iiens.net"
-#define GET_ID_JSON DEFAULT_URL "/api_karas.php?id=%ld"
-#define GET_ID_FILE DEFAULT_URL "/download.php?id=%ld"
-
-struct memory {
-    void *mem;
-    size_t size;
-};
-
-struct file {
-    const char *path;
-    int fd;
-};
-
-static size_t
-write_mem__(char *data, size_t size, size_t nmem, void *user)
-{
-    size_t realsize = size * nmem;
-    struct memory *mem = (struct memory *) user;
-
-    void *ptr = realloc(mem->mem, mem->size + realsize);
-    RETURN_UNLESS(ptr, "Out of memory", 0);
-
-    mem->mem = ptr;
-    memcpy(((uint8_t *) mem->mem) + mem->size, data, realsize);
-    mem->size += realsize;
-    return realsize;
-}
-
-static size_t
-write_disk__(char *data, size_t size, size_t nmem, void *user)
-{
-    ssize_t realsize = size * nmem;
-    struct file *file = (struct file *) user;
-    RETURN_IF(write(file->fd, data, realsize) != realsize, "Failed to write", 0);
-    return realsize;
-}
-
-int
-repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_, volatile sqlite3 *db)
-{
-    if (!curl_init) {
-        curl_global_init(CURL_GLOBAL_ALL);
-        curl_init = 1;
-    } else
-        ++curl_init;
-
-    struct lkt_repo repo = {
-        /* Just the repo */
-        .name = name_,
-        .base_url = url_,
-        .get_id_json = GET_ID_JSON,
-        .get_id_file = GET_ID_FILE,
-        .get_all_json = DEFAULT_URL "/api_karas.php", // TODO
-        .kara_dir = "/home/kara/", // TODO
-        .version = 1,
-
-        /* The db */
-        .db = db,
-    };
-
-    memcpy(repo_, &repo, sizeof(struct lkt_repo));
-    return 0;
-}
-
-void
-repo_free(struct lkt_repo *const repo)
-{
-    UNUSED(repo);
-    --curl_init;
-    if (!curl_init)
-        curl_global_cleanup();
-}
-
-int
-safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len)
-{
-    const char *got;
-    struct json_object *field;
-    RETURN_UNLESS(json_object_object_get_ex(jobj, key, &field), "Key not found in json", 1);
-    got = json_object_get_string(field);
-    RETURN_UNLESS(got, "Got a NULL for the key, may be an error", 1);
-    strncpy(content, got, len - 1);
-    content[len - 1] = 0;
-    return 0;
-}
-
-int
-safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret)
-{
-    struct json_object *field;
-    RETURN_UNLESS(json_object_object_get_ex(json, key, &field), "Key not found", 1);
-    errno = 0;
-    *ret = json_object_get_int(field);
-    RETURN_IF(errno = EINVAL, "Invalid 32bit integer", 1);
-    RETURN_IF(*ret == INT32_MAX || *ret == INT32_MIN, "Out of bound 32bit integer", 1);
-    return 0;
-}
-
-int
-repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json)
-{
-    RETURN_UNLESS(json, "Invalid argument", 1);
-
-    CURL *curl_handle;
-    CURLcode res;
-    int ret = 1;
-
-    struct memory file = {
-        .mem = NULL,
-        .size = 0.
-    };
-
-    curl_handle = curl_easy_init();
-    curl_easy_setopt(curl_handle, CURLOPT_URL, repo->get_all_json);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
-    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
-    res = curl_easy_perform(curl_handle);
-
-    if (res != CURLE_OK) {
-        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
-        free(file.mem);
-        goto err;
-    }
-
-    *json = json_tokener_parse(file.mem);
-    ret = 0;
-err:
-    curl_easy_cleanup(curl_handle);
-    return ret;
-}
-
-int
-repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata *mdt)
-{
-    CURL *curl_handle;
-    CURLcode res;
-    struct json_object *jobj, *field;
-    int ret = 1, err = 0;
-
-    RETURN_UNLESS(mdt, "Invalid argument", 1);
-    char *url = calloc(URL_MAX_LEN, sizeof(char));
-    RETURN_UNLESS(url, "Out of memory", errno = ENOMEM);
-
-    struct memory file = {
-        .mem = NULL,
-        .size = 0,
-    };
-
-    memset(url, 0, URL_MAX_LEN * sizeof(char));
-    snprintf(url, URL_MAX_LEN - 1, repo->get_id_json, id);
-    url[URL_MAX_LEN - 1] = 0;
-    curl_handle = curl_easy_init();
-    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
-    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
-    res = curl_easy_perform(curl_handle);
-
-    if (res != CURLE_OK) {
-        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
-        free(url);
-        free(file.mem);
-        curl_easy_cleanup(curl_handle);
-        return 1;
-    }
-
-    curl_easy_cleanup(curl_handle);
-
-    /* Read the json. */
-
-    jobj = json_tokener_parse(file.mem);
-
-    if (json_object_object_get_ex(jobj, "message", &field)) {
-        LOG_ERROR("Kara with id %lu not found, message is: %s", id, json_object_get_string(field));
-        goto err;
-    }
-
-    LOG_INFO("Got kara with id %lu", id);
-
-    err |= safe_json_get_string(jobj, "song_name", mdt->song_name, LEKTOR_TAG_MAX);
-    err |= safe_json_get_string(jobj, "source_name", mdt->source_name, LEKTOR_TAG_MAX);
-    err |= safe_json_get_string(jobj, "category", mdt->category, LEKTOR_TAG_MAX);
-    err |= safe_json_get_string(jobj, "language", mdt->language, LEKTOR_TAG_MAX);
-    err |= safe_json_get_string(jobj, "author_name", mdt->author_name, LEKTOR_TAG_MAX);
-    err |= safe_json_get_string(jobj, "song_type", mdt->song_type, LEKTOR_TAG_MAX);
-
-    errno = 0;
-
-    if (!json_object_object_get_ex(jobj, "song_number", &field)) {
-        LOG_ERROR("%s", "No object with key 'song_number' in json, error");
-        goto err;
-    }
-
-    mdt->song_number = json_object_get_int(field);
-
-    if (errno == EINVAL) {
-        LOG_ERROR("%s", "Invalid integer for field with key 'song_number'");
-        goto err;
-    }
-
-    if (mdt->song_number == INT32_MAX || mdt->song_number == INT32_MIN) {
-        LOG_ERROR("Out of bound int32_t '%d' for field with key 'song_number'", mdt->song_number);
-        goto err;
-    }
-
-
-    ret = 0;
-err:
-    free(url);
-    json_object_put(jobj); /* Delete object. */
-    return ret;
-}
-
-int
-repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt_ret)
-{
-    RETURN_UNLESS(kara_path, "Invalid argument", 1);
-    struct kara_metadata mdt;
-    CURL *curl_handle;
-    int ret = 1, fd;
-    char *url = calloc(URL_MAX_LEN, sizeof(char));
-    char *ct;
-    RETURN_UNLESS(url, "Out of memory", errno = ENOMEM);
-
-    if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) {
-        LOG_ERROR("%s", "Failed to get kara metadata from kurisu");
-        goto err_no_curl;
-    }
-
-    errno = 0;
-    fd = open(kara_path,
-              O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW,
-              S_IRUSR | S_IWUSR);
-
-    if (fd < 0) {
-        if (errno == EEXIST)
-            LOG_ERROR("File '%s' already exists", kara_path);
-
-        else
-            LOG_ERROR("Could not open file '%s'", kara_path);
-
-        goto err_no_curl;
-    }
-
-    struct file file = {
-        .path = kara_path,
-        .fd = fd,
-    };
-
-    memset(url, 0, URL_MAX_LEN * sizeof(char));
-    snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id);
-    url[URL_MAX_LEN - 1] = 0;
-    curl_handle = curl_easy_init();
-    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_disk__);
-    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
-    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
-    ret = curl_easy_perform(curl_handle);
-
-    if (ret != CURLE_OK) {
-        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(ret));
-        goto err;
-    }
-
-    if (CURLE_OK == (ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct)))
-        LOG_INFO_SCT("CURL", "Content-Type is '%s'", ct);
-    else {
-        LOG_ERROR_SCT("CURL", "Failed to get Content-Type: %s", curl_easy_strerror(ret));
-        goto err;
-    }
-
-    if (repo->db && !database_update_add((sqlite3 *) repo->db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) {
-        LOG_ERROR("%s", "Failed to add kara to database");
-        goto err;
-    }
-
-    ret = 0;
-err:
-    curl_easy_cleanup(curl_handle);
-err_no_curl:
-    free(url);
-    return ret;
-}
diff --git a/src/repo/downloader.c b/src/repo/downloader.c
index d6ad28e13f245418d9870ca5123554719770b0f6..10f27fd5e4fb20b126e4ef08b39b7d07121c92c5 100644
--- a/src/repo/downloader.c
+++ b/src/repo/downloader.c
@@ -5,17 +5,305 @@
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
+#include <common/common.h>
 #include <mthread/mthread.h>
 #include <lektor/macro.h>
 #include <lektor/repo.h>
 #include <lektor/database.h>
 
-/* Find it in the repo/curl.c file. FIXME */
-extern int
-safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len);
-extern int
-safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret);
+static volatile unsigned int curl_init = false;
+
+#define URL_MAX_LEN 1024
+#define DEFAULT_URL "https://kurisu.iiens.net"
+#define GET_ID_JSON DEFAULT_URL "/api_karas.php?id=%ld"
+#define GET_ID_FILE DEFAULT_URL "/download.php?id=%ld"
+
+struct memory {
+    void *mem;
+    size_t size;
+};
+
+struct file {
+    const char *path;
+    int fd;
+};
+
+static size_t
+write_mem__(char *data, size_t size, size_t nmem, void *user)
+{
+    size_t realsize = size * nmem;
+    struct memory *mem = (struct memory *) user;
+
+    void *ptr = realloc(mem->mem, mem->size + realsize);
+    RETURN_UNLESS(ptr, "Out of memory", 0);
+
+    mem->mem = ptr;
+    memcpy(((uint8_t *) mem->mem) + mem->size, data, realsize);
+    mem->size += realsize;
+    return realsize;
+}
+
+static size_t
+write_disk__(char *data, size_t size, size_t nmem, void *user)
+{
+    ssize_t realsize = size * nmem;
+    struct file *file = (struct file *) user;
+    RETURN_IF(write(file->fd, data, realsize) != realsize, "Failed to write", 0);
+    return realsize;
+}
+
+int
+repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_, volatile sqlite3 *db)
+{
+    if (!curl_init) {
+        curl_global_init(CURL_GLOBAL_ALL);
+        curl_init = 1;
+    } else
+        ++curl_init;
+
+    struct lkt_repo repo = {
+        /* Just the repo */
+        .name = name_,
+        .base_url = url_,
+        .get_id_json = GET_ID_JSON,
+        .get_id_file = GET_ID_FILE,
+        .get_all_json = DEFAULT_URL "/api_karas.php", // TODO
+        .kara_dir = "/home/kara/", // TODO
+        .version = 1,
+
+        /* The db */
+        .db = db,
+    };
+
+    memcpy(repo_, &repo, sizeof(struct lkt_repo));
+    return 0;
+}
+
+void
+repo_free(struct lkt_repo *const repo)
+{
+    UNUSED(repo);
+    --curl_init;
+    if (!curl_init)
+        curl_global_cleanup();
+}
+
+int
+safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len)
+{
+    const char *got;
+    struct json_object *field;
+    RETURN_UNLESS(json_object_object_get_ex(jobj, key, &field), "Key not found in json", 1);
+    got = json_object_get_string(field);
+    RETURN_UNLESS(got, "Got a NULL for the key, may be an error", 1);
+    strncpy(content, got, len - 1);
+    content[len - 1] = 0;
+    return 0;
+}
+
+int
+safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret)
+{
+    struct json_object *field;
+    RETURN_UNLESS(json_object_object_get_ex(json, key, &field), "Key not found", 1);
+    errno = 0;
+    *ret = json_object_get_int(field);
+    RETURN_IF(errno = EINVAL, "Invalid 32bit integer", 1);
+    RETURN_IF(*ret == INT32_MAX || *ret == INT32_MIN, "Out of bound 32bit integer", 1);
+    return 0;
+}
+
+int
+repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json)
+{
+    RETURN_UNLESS(json, "Invalid argument", 1);
+
+    CURL *curl_handle;
+    CURLcode res;
+    int ret = 1;
+
+    struct memory file = {
+        .mem = NULL,
+        .size = 0.
+    };
+
+    curl_handle = curl_easy_init();
+    curl_easy_setopt(curl_handle, CURLOPT_URL, repo->get_all_json);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
+    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+    res = curl_easy_perform(curl_handle);
+
+    if (res != CURLE_OK) {
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
+        free(file.mem);
+        goto err;
+    }
+
+    *json = json_tokener_parse(file.mem);
+    ret = 0;
+err:
+    curl_easy_cleanup(curl_handle);
+    return ret;
+}
+
+int
+repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata *mdt)
+{
+    CURL *curl_handle;
+    CURLcode res;
+    struct json_object *jobj, *field;
+    int ret = 1, err = 0;
+
+    RETURN_UNLESS(mdt, "Invalid argument", 1);
+    char *url = calloc(URL_MAX_LEN, sizeof(char));
+    RETURN_UNLESS(url, "Out of memory", errno = ENOMEM);
+
+    struct memory file = {
+        .mem = NULL,
+        .size = 0,
+    };
+
+    memset(url, 0, URL_MAX_LEN * sizeof(char));
+    snprintf(url, URL_MAX_LEN - 1, repo->get_id_json, id);
+    url[URL_MAX_LEN - 1] = 0;
+    curl_handle = curl_easy_init();
+    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
+    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+    res = curl_easy_perform(curl_handle);
+
+    if (res != CURLE_OK) {
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
+        free(url);
+        free(file.mem);
+        curl_easy_cleanup(curl_handle);
+        return 1;
+    }
+
+    curl_easy_cleanup(curl_handle);
+
+    /* Read the json. */
+
+    jobj = json_tokener_parse(file.mem);
+
+    if (json_object_object_get_ex(jobj, "message", &field)) {
+        LOG_ERROR("Kara with id %lu not found, message is: %s", id, json_object_get_string(field));
+        goto err;
+    }
+
+    LOG_INFO("Got kara with id %lu", id);
+
+    err |= safe_json_get_string(jobj, "song_name", mdt->song_name, LEKTOR_TAG_MAX);
+    err |= safe_json_get_string(jobj, "source_name", mdt->source_name, LEKTOR_TAG_MAX);
+    err |= safe_json_get_string(jobj, "category", mdt->category, LEKTOR_TAG_MAX);
+    err |= safe_json_get_string(jobj, "language", mdt->language, LEKTOR_TAG_MAX);
+    err |= safe_json_get_string(jobj, "author_name", mdt->author_name, LEKTOR_TAG_MAX);
+    err |= safe_json_get_string(jobj, "song_type", mdt->song_type, LEKTOR_TAG_MAX);
+
+    errno = 0;
+
+    if (!json_object_object_get_ex(jobj, "song_number", &field)) {
+        LOG_ERROR("%s", "No object with key 'song_number' in json, error");
+        goto err;
+    }
+
+    mdt->song_number = json_object_get_int(field);
+
+    if (errno == EINVAL) {
+        LOG_ERROR("%s", "Invalid integer for field with key 'song_number'");
+        goto err;
+    }
+
+    if (mdt->song_number == INT32_MAX || mdt->song_number == INT32_MIN) {
+        LOG_ERROR("Out of bound int32_t '%d' for field with key 'song_number'", mdt->song_number);
+        goto err;
+    }
+
+
+    ret = 0;
+err:
+    free(url);
+    json_object_put(jobj); /* Delete object. */
+    return ret;
+}
+
+int
+repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt_ret)
+{
+    RETURN_UNLESS(kara_path, "Invalid argument", 1);
+    struct kara_metadata mdt;
+    CURL *curl_handle;
+    int ret = 1, fd;
+    char *url = calloc(URL_MAX_LEN, sizeof(char));
+    char *ct;
+    RETURN_UNLESS(url, "Out of memory", errno = ENOMEM);
+
+    if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) {
+        LOG_ERROR("%s", "Failed to get kara metadata from kurisu");
+        goto err_no_curl;
+    }
+
+    errno = 0;
+    fd = open(kara_path,
+              O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW,
+              S_IRUSR | S_IWUSR);
+
+    if (fd < 0) {
+        if (errno == EEXIST)
+            LOG_ERROR("File '%s' already exists", kara_path);
+
+        else
+            LOG_ERROR("Could not open file '%s'", kara_path);
+
+        goto err_no_curl;
+    }
+
+    struct file file = {
+        .path = kara_path,
+        .fd = fd,
+    };
+
+    memset(url, 0, URL_MAX_LEN * sizeof(char));
+    snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id);
+    url[URL_MAX_LEN - 1] = 0;
+    curl_handle = curl_easy_init();
+    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_disk__);
+    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
+    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+    ret = curl_easy_perform(curl_handle);
+
+    if (ret != CURLE_OK) {
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(ret));
+        goto err;
+    }
+
+    if (CURLE_OK == (ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct)))
+        LOG_INFO_SCT("CURL", "Content-Type is '%s'", ct);
+    else {
+        LOG_ERROR_SCT("CURL", "Failed to get Content-Type: %s", curl_easy_strerror(ret));
+        goto err;
+    }
+
+    if (repo->db && !database_update_add((sqlite3 *) repo->db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) {
+        LOG_ERROR("%s", "Failed to add kara to database");
+        goto err;
+    }
+
+    ret = 0;
+err:
+    curl_easy_cleanup(curl_handle);
+err_no_curl:
+    free(url);
+    return ret;
+}
 
 /* Download a kara */