diff --git a/inc/lektor/common.h b/inc/lektor/common.h
index 64c434566054b89467841b8698f018b0fb5344fe..d025b87ddb83af9302cfb2b18daafccd867c6782 100644
--- a/inc/lektor/common.h
+++ b/inc/lektor/common.h
@@ -7,8 +7,13 @@
 
 /* Defines */
 
-#define LKT_MAX_SQLITE_STATEMENT    1024
-#define PROTECTED_DATABASE          "disk"
+#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"
+
+#define LKT_MAX_SQLITE_STATEMENT        1024
+#define PROTECTED_DATABASE              "disk"
 
 #define LKT_DATABASE_NAME_KID           "id"
 #define LKT_DATABASE_NAME_KNAME         "source_name"
@@ -21,10 +26,10 @@
 #define LKT_DATABASE_KARA_COLUMNT_ANY   "any_col"
 #define LKT_DATABASE_KARA_ALL           "string"
 
-#define LEKTOR_TAG_MAX          256
-#define LKT_MESSAGE_ARGS_MAX    32
-#define LKT_MESSAGE_MAX         2048
-#define LKT_DEFAULT_LIST_SIZE   10
+#define LEKTOR_TAG_MAX                  256
+#define LKT_MESSAGE_ARGS_MAX            32
+#define LKT_MESSAGE_MAX                 2048
+#define LKT_DEFAULT_LIST_SIZE           10
 
 enum mpd_idle_flag {
     MPD_IDLE_NONE               = 0,
diff --git a/inc/lektor/uri.h b/inc/lektor/uri.h
index d278c9e96521e8a462d3d11f5d4fdc4b86318aeb..c9a4d932566e8432b932bcbe676d69803d6cab41 100644
--- a/inc/lektor/uri.h
+++ b/inc/lektor/uri.h
@@ -26,3 +26,7 @@ struct lkt_uri {
 /* Create and delete URIs */
 bool lkt_uri_from(struct lkt_uri *ret, char *const str);
 void lkt_uri_free(struct lkt_uri *ret);
+
+/* Make an URL to download from kurisu.
+   base_url is the prefix for the api, e.g. https://kurisu.iiens.net/api_kara.php */
+int lkt_uri_to_url(struct lkt_uri *uri, const char *base_url, char *ret, size_t len);
diff --git a/meson.build b/meson.build
index ca35a64f34b65daac92a55f45650d7fb25f19446..45634813e7ad37bb8dea6a94deb18c0cd9f73082 100644
--- a/meson.build
+++ b/meson.build
@@ -56,11 +56,11 @@ core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/net/command.c'
                 , 'src/net/listen.c'
                 , 'src/net/message.c'
+                , 'src/net/downloader.c'
+                , 'src/net/sync.c'
                 , 'src/config.c'
                 , 'src/uri.c'
                 , 'src/ini/ini.c'
-                , 'src/repo/downloader.c'
-                , 'src/repo/sync.c'
                 , 'src/thread.c'
                 ]
 
diff --git a/src/repo/downloader.c b/src/net/downloader.c
similarity index 98%
rename from src/repo/downloader.c
rename to src/net/downloader.c
index 593af7044859bb65731bc8a160412d0ca1c0c17d..9ac71ea22e967e16cb5090009da7bfd929f0fb2e 100644
--- a/src/repo/downloader.c
+++ b/src/net/downloader.c
@@ -18,11 +18,6 @@
 
 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;
diff --git a/src/repo/sync.c b/src/net/sync.c
similarity index 100%
rename from src/repo/sync.c
rename to src/net/sync.c
diff --git a/src/uri.c b/src/uri.c
index 88048dd8e5f09679103603416e5e9ba8638c1911..ac8634be845720818b767601c8f5358d318dcd2d 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -88,3 +88,47 @@ lkt_uri_free(struct lkt_uri *ret)
     if (ret->_allocated)
         free(ret->value);
 }
+
+/* Support a subrange of URIs. */
+static inline int
+__lkt_to_str(struct lkt_uri *uri, char *ret, size_t len)
+{
+    switch (uri->type) {
+    case uri_id:
+        snprintf(ret, len - 1, "id=%s", (char *) uri->value);
+        break;
+    case uri_type:
+        snprintf(ret, len - 1, "type=%s", (char *) uri->value);
+        break;
+    case uri_author:
+        snprintf(ret, len - 1, "author=%s", (char *) uri->value);
+        break;
+    case uri_category:
+        snprintf(ret, len - 1, "cat=%s", (char *) uri->value);
+        break;
+    case uri_query:
+        snprintf(ret, len - 1, "search=%s", (char *) uri->value);
+        break;
+    default:
+        LOG_ERROR("URI type %d may not be supported by kurisu, generate an error", uri->type);
+        return 1;
+    }
+
+    ret[len - 1] = '\0';
+    return 0;
+}
+
+int
+lkt_uri_to_url(struct lkt_uri *uri, const char *base_url, char *ret, size_t len)
+{
+    RETURN_UNLESS(uri && base_url && ret && len > 0, "Invalid argument", 1);
+
+    /* Craft URL */
+    memset(ret, 0, len);
+    memcpy(ret, base_url, sizeof(char) * strlen(base_url));
+    strncat(ret, "?", len - 1);
+
+    /* The query */
+    size_t init_len = strlen(ret);
+    return __lkt_to_str(uri, ret + init_len, len - init_len);
+}