diff --git a/README.md b/README.md
index 6e55ce985d0209de9df78ce5fd09d9c48aea3647..8008095b418f4c4d55f58b325bd62ff0a0deba27 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,10 @@ Prerequisites:
 - the [json-c](https://github.com/json-c/json-c) development library
 - a POSIX.1-2008 compatible system (i.e. not MS Windows)
 
-For the module x11, you will need the folowing prerequisites:
+For the module x11 and sdl2, you will need the folowing prerequisites:
 
 - the [mpv](https://mpv.io/) development library
+- the [sdl2](https://www.libsdl.org/) development library
 - you will need `x11` for the default window module
 
 You will also need the following executables, with their path setted in the
@@ -43,6 +44,12 @@ You may need to put the `lib_window_x11.so` in the right directory to have the
 lektor player actually playing something (see the ini file). This path can be
 setted manually in the INI file.
 
+On archlinux, you need only to install the package:
+
+```sh
+makepkg -si
+```
+
 ## Preparing a kara for lektor
 
 A karamaker needs at least python3, with `mkvpropedit` (often installed with the
diff --git a/doc/lkt.1 b/doc/lkt.1
index 73c36811edd4d8ab56b7b51d306c3c4f0fd5fdb1..069f193d18d2bfe32808fe3d6f7ab3e240c0ec79 100644
--- a/doc/lkt.1
+++ b/doc/lkt.1
@@ -134,13 +134,17 @@ Search in the queue and prints the karas that match the query
 .PD 0
 .TP
 .PD
+\fBsticker create\fP <name>
+Create a sticker that can be used to tag \fIkara\fP and \fIplt\fP objects
+.TP
 \fBsticker get\fP <type> <uri> [ <name> [ <op> <value> ] ]
 List the stickers of an object \fIuri\fP. The object \fItype\fP can be
 \fIkara\fP or \fIplt\fP.
 
 A condition can be defined on the value of the sticker with an operator
-\fIop\fP and an integer value \fIvalue\fP. Supported operators are \fI<\fP,
-\fI=\fP and \fI>\fP. Operations are not strict.
+\fIop\fP and an integer value \fIvalue\fP. Supported operators are \fIl\fP
+for 'less than', \fIe\fP for 'equal to' and \fIg\fP for 'greater than'.
+Operations are not strict.
 .TP
 \fBsticker set\fP <type> <uri> <name> <value>
 Set the value of a sticker \fIname\fP to \fIvalue\fP for the object with the
@@ -149,12 +153,6 @@ id \fIuri\fP
 \fBsticker delete\fP <type> <uri> [name]
 Delete all the stickers or only one (specified by \fIname\fP) of the object with
 the id \fIuri\fP
-.TP
-\fBksticker\fP ...
-An alias for the sticker commands with the type \fIkara\fP
-.TP
-\fBpsticker\fP ...
-An alias for the sticker commands with the type \fIplt\fP
 .PP
 
 \fIADMIN-COMMANDS\fP
diff --git a/inc/common/macro.h b/inc/common/macro.h
index 3e220015527cf73428f3a5799b45d417e6c0b9e5..8b1b527e5c3c1f4460b5f478af963b791b52f3e4 100644
--- a/inc/common/macro.h
+++ b/inc/common/macro.h
@@ -161,7 +161,6 @@ typedef volatile enum {
 
 #define SQLITE_STEP_ROW(db, stmt, error)    SQLITE_STEP(db, stmt, SQLITE_ROW, error)
 #define SQLITE_STEP_DONE(db, stmt, error)   SQLITE_STEP(db, stmt, SQLITE_DONE, error)
-#define SQLITE_STEP_OK(db, stmt, error)     SQLITE_STEP(db, stmt, SQLITE_OK, error)
 
 #define SQLITE_DO_ROLLBACK(db)                                          \
     sqlite3_exec((sqlite3 *) db, "ROLLBACK TRANSACTION;\n", NULL, NULL, NULL);
diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h
index 53067fe5f6c6752625b7d1d1e0bc08dae8444828..9b18e0e9ca38c0781a55a07d16dae456cde2f4d7 100644
--- a/inc/lektor/commands.h
+++ b/inc/lektor/commands.h
@@ -86,7 +86,7 @@ bool command_update (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG
 bool command_rescan (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
 
 /* Sticker management */
+bool command_sticker_create(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
 bool command_sticker_get   (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
 bool command_sticker_set   (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
 bool command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
-bool command_sticker_list  (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index c2d090799c38f9184c682abf0a28af80f9d4ec4a..b0383152b10acc95f9d82f029f2a74fafa7e6b47 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -50,6 +50,7 @@ bool database_update              (volatile sqlite3 *db, const char *kara_dir, i
 bool database_update_add          (volatile sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail);
 bool database_update_set_available(volatile sqlite3 *db, int id);
 void database_update_del          (volatile sqlite3 *db, int id);
+void database_update_touch        (volatile sqlite3 *db, int id);
 
 /* Control the content of the queue. */
 bool database_queue_add_uri(volatile sqlite3 *db, struct lkt_uri *uri, int priority);
diff --git a/meson.build b/meson.build
index 70e40f579baf6f0bedd218cbe01c12329e5c36e3..caaaf1bb6d5337f0cfe0596cca81bd061280b7d2 100644
--- a/meson.build
+++ b/meson.build
@@ -82,20 +82,21 @@ mthread_deps = [ declare_dependency( link_with: static_library( 'mthread'
                                    , include_directories: includes
                                    , dependencies: [ common_deps ] ) ]
 
-generated_deps = [ declare_dependency( link_with: static_library( 'generated'
-                                                                , [ custom_target( 'sqlinit'
-                                                                                 , output: 'sqlinit.c'
-                                                                                 , input: 'src/database/disk.sql'
-                                                                                 , command: [ find_program('xxd'), '-i', '@INPUT@', '@OUTPUT@' ] ) ]
-                                                                , [ custom_target( 'sqlmemory'
-                                                                                 , output: 'sqlmemory.c'
-                                                                                 , input: 'src/database/memory.sql'
-                                                                                 , command: [ find_program('xxd'), '-i', '@INPUT@', '@OUTPUT@' ] ) ]
-                                                                , [ custom_target( 'manpath'
-                                                                                 , output: 'manpath.c'
-                                                                                 , input: 'meson.build'
-                                                                                 , command: [ find_program('sh'), '-c', 'echo \'const char *man_executable_path = "\'"$(which man)"\'";\' > @OUTPUT@' ] ) ] )
-                                     ) ]
+generated_deps = [ declare_dependency(
+  link_with: static_library( 'generated'
+                           , [ custom_target( 'sqlinit'
+                                            , output: 'sqlinit.c'
+                                            , input: 'src/database/disk.sql'
+                                            , command: [ find_program('xxd'), '-i', '@INPUT@', '@OUTPUT@' ] ) ]
+                           , [ custom_target( 'sqlmemory'
+                                            , output: 'sqlmemory.c'
+                                            , input: 'src/database/memory.sql'
+                                            , command: [ find_program('xxd'), '-i', '@INPUT@', '@OUTPUT@' ] ) ]
+                           , [ custom_target( 'manpath'
+                                            , output: 'manpath.c'
+                                            , input: 'meson.build'
+                                            , command: [ find_program('sh'), '-c', 'echo \'const char *man_executable_path = "\'"$(which man)"\'";\' > @OUTPUT@' ] ) ] )
+                           ) ]
 
 lib = static_library( 'lektor'
                     , files(core_sources)
diff --git a/src/commands.c b/src/commands.c
index f3337062feb3b3712edbe6f2a553996ab9c548c5..66a78d43c4d83d72de65062e0762c315dc8a9d7e 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -728,55 +728,47 @@ command_user_add(struct lkt_state *srv, size_t c, volatile sqlite3 *db, char *ar
 /* Stickers */
 
 static bool
-sticker_send(struct lkt_state *srv, size_t c, char *name, int id, int value)
+sticker_send(struct lkt_state *srv, size_t c, char *name, char *type, int id, int value)
 {
+    UNUSED(type);
     struct lkt_message *msg = lkt_message_new();
-    msg->data_len = safe_snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s . %d", id, name, value);
+    msg->data_len = safe_snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s -> %d\n", id, name, value);
     lkt_state_send(srv, c, msg);
     return true;
 }
 
 bool
-command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+command_sticker_create(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
 {
-    RETURN_UNLESS(argv[0] && argv[1] && argv[2] && !argv[3], "Invalid argument", false);
-    long uri;
-    char *endptr, err;
-    STRTOL(uri, argv[1], endptr, err);
-    RETURN_IF(err, "STRTOL failed", false);
-    struct lkt_search cb = {
-        .call = (void(*)(void)) sticker_send,
-        .srv = srv,
-        .c = c,
-        .name = argv[2],
-        .st_type = argv[0],
-        .st_uri = (int) uri,
-    };
-    if (!database_search_sticker_init(srv->db, &cb))
+    LOG_INFO_SCT("COMMAND", "Client %ld is using the sticker create command", c);
+    RETURN_UNLESS(argv[0], "Invalid argument", false);
+    if (!database_sticker_create(srv->db, argv[0])) {
+        LOG_ERROR_SCT("COMMAND", "Failed to create sticker '%s'", argv[0]);
         return false;
-    while (database_search_iter(&cb))
-        continue;
+    }
+    LOG_INFO_SCT("COMMAND", "Created sticker '%s'", argv[0]);
     return true;
 }
 
 bool
 command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
 {
-    UNUSED(c);
     RETURN_UNLESS(argv[0] && argv[1] && argv[2] && argv[3] && !argv[4], "Invalid argument", false);
-    int uri, value, err1, err2;
-    char *endptr;
+    long uri, value;
+    char *endptr, err1, err2;
     STRTOL(uri, argv[1], endptr, err1);
-    STRTOL(value, argv[4], endptr, err2);
+    STRTOL(value, argv[3], endptr, err2);
     RETURN_IF(err1 || err2, "STRTOL failed", false);
+    LOG_INFO_SCT("COMMAND", "Client %ld is using the sticker set command", c);
     RETURN_UNLESS(database_sticker_set(srv->db, argv[0], argv[2], uri, value), "Failed to get sticker", false);
     srv->mpd_idle_events |= MPD_IDLE_STICKER;
     return true;
 }
 
 bool
-command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
 {
+    LOG_INFO_SCT("COMMAND", "Client %ld is using the sticker get command", c);
     struct lkt_search callback = {
         .srv = srv,
         .c = c,
@@ -787,8 +779,8 @@ command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG
     /* Simple list {type} {uri} command */
     if (argv[0] != NULL && argv[1] != NULL && argv[2] == NULL) {
         callback.st_uri = atoi(argv[1]);
-        if (database_search_sticker_init(srv->db, &callback))
-            goto iter;
+        if (!database_search_sticker_init(srv->db, &callback))
+            return false;
     }
 
     /* list {type} {uri} {name} command */
@@ -796,8 +788,8 @@ command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG
              argv[2] != NULL && argv[3] == NULL) {
         callback.name = argv[2];
         callback.st_uri = atoi(argv[1]);
-        if (database_search_sticker_init(srv->db, &callback))
-            goto iter;
+        if (!database_search_sticker_init(srv->db, &callback))
+            return false;
     }
 
     /* list {type} {uri} {name} `op` {value} command */
@@ -808,20 +800,20 @@ command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG
         callback.st_uri = atoi(argv[1]);
         callback.st_op = argv[3][0];
         callback.st_value = atoi(argv[4]);
-        if (database_search_sticker_init(srv->db, &callback))
-            goto iter;
+        if (!database_search_sticker_init(srv->db, &callback))
+            return false;
     }
 
     /* Just list all stickers */
     else if ( (argv[0] != NULL && argv[1] == NULL) || argv[0] == NULL ) {
-        if (database_search_sticker_init(srv->db, &callback))
-            goto iter;
+        if (!database_search_sticker_init(srv->db, &callback))
+            return false;
     }
 
     else
         goto unknown;
 
-iter:
+    /* Send results */
     while (database_search_iter(&callback))
         continue;
     return true;
@@ -834,9 +826,17 @@ unknown:
 bool
 command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
 {
-    UNUSED(c);
-    RETURN_UNLESS(argv[0] && argv[1], "Invalid argument", false);
+    LOG_INFO_SCT("COMMAND", "Client %ld is using the sticker delete command", c);
+    RETURN_UNLESS(argv[0] && argv[1] && argv[2], "Invalid argument", false);
     int uri = atoi(argv[1]);
     srv->mpd_idle_events |= MPD_IDLE_STICKER;
     return database_sticker_delete_specify(srv->db, argv[0], uri, argv[2]);
 }
+
+bool
+command_sticker_destroy(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+{
+    LOG_INFO_SCT("COMMAND", "Client %ld is using the sticker destroy command", c);
+    RETURN_UNLESS(argv[0], "Invalid argument", false);
+    return database_sticker_delete(srv->db, argv[0]);
+}
diff --git a/src/database/disk.sql b/src/database/disk.sql
index 285457514f1585114e3412b91a7b73c46e9147fa..5ebe5772cd1e061d34a1e58bdad9f94b445d42ef 100644
--- a/src/database/disk.sql
+++ b/src/database/disk.sql
@@ -104,7 +104,7 @@ CREATE TABLE IF NOT EXISTS 'stickers'
   , name    TEXT    NOT NULL UNIQUE
   );
 
-CREATE TABLE IF NOT EXISTS 'stickers.song'
+CREATE TABLE IF NOT EXISTS 'stickers.kara'
   ( id      INTEGER REFERENCES kara     ON DELETE CASCADE
   , sticker INTEGER REFERENCES stickers ON DELETE CASCADE
   , value   INTEGER NOT NULL
diff --git a/src/database/find.c b/src/database/find.c
index 8cab8488045c254566baf24ab05c845ff6fff6b3..5fbc5c468dbafa856317a81d3ea71a4b0c26f522 100644
--- a/src/database/find.c
+++ b/src/database/find.c
@@ -37,35 +37,36 @@ database_search_sticker_init(volatile sqlite3 *db, struct lkt_search *ret)
 {
     /* No bound checks in strcats, should be fine. Possible SQL injection, depend on the `type`. */
     static const char *SQL_all_types =
-        "SELECT name, sts.id, value FROM 'stickers' LEFT OUTER JOIN "
-        "( SELECT id, sticker, value FROM 'stickers.song'"
+        "SELECT name, sts.id, value FROM 'stickers' JOIN "
+        "( SELECT id, sticker, value, 'kara' FROM 'stickers.kara'"
         "  UNION"
-        "  SELECT id, sticker, value FROM 'stickers.plt'"
+        "  SELECT id, sticker, value , 'plt' FROM 'stickers.plt'"
         ") AS sts"
         "ON sts.sticker = 'stickers'.id";
     static const char *SQL_one_type =
-        "SELECT name, 'stickers.%s'.id, value "
-        "FROM 'stickers.%s' AS sts"
-        "LEFT OUTER JOIN 'stickers' "
+        "SELECT name, sts.id, value, '%s' "
+        "FROM 'stickers.%s' AS sts "
+        "JOIN 'stickers' "
         "ON 'stickers'.id = sts.sticker";
     char SQL[LKT_MAX_SQLITE_STATEMENT];
+    ret->type = lkt_search_sticker;
 
     if (ret->st_type == NULL)
         memcpy(SQL, SQL_all_types, strlen(SQL_all_types) + 1);
     else
         sprintf(SQL, SQL_one_type, ret->st_type, ret->st_type);
     if (ret->st_uri != 0)
-        sprintf(SQL, " AND sts.id = %d", ret->st_uri);
+        sprintf(SQL + strlen(SQL), " AND sts.id = %d", ret->st_uri);
     if (ret->st_op != 0)
-        sprintf(SQL, " AND sts.value %s %d",
+        sprintf(SQL + strlen(SQL), " AND sts.value %s %d",
                 ret->st_op == '>' ? ">=" : ret->st_op == '<' ? "<=" : "=",
                 ret->st_value);
-    strcat(SQL, ret->name ? " AND name = ?;" : ";");
+    strcat(SQL, !ret->name ? ";" : " AND name = ?;");
 
     SQLITE_PREPARE(db, ret->stmt, SQL, error);
     if (ret->name)
-        SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->name, error)
-        return true;
+        SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->name, error);
+    return true;
 error:
     sqlite3_finalize(ret->stmt);
     return false;
@@ -100,7 +101,7 @@ error:
 bool
 database_search_iter(struct lkt_search *item)
 {
-    const char *sql_row;
+    const char *sql_row, *type;
     int id, code, id_len;
     RETURN_UNLESS(item, "Exit because item is NULL, end iterations", false);
     GOTO_UNLESS(item->call, "Call function is NULL, terminate the search", end);
@@ -116,15 +117,17 @@ database_search_iter(struct lkt_search *item)
 
     switch (item->type) {
     case lkt_search_database:
-        id = sqlite3_column_int(item->stmt, 0);
-        sql_row = (const char *) sqlite3_column_text(item->stmt, 1);
-        id_len = sqlite3_column_int(item->stmt, 2);
-        return ((lkt_search_database_func) item->call)(item->srv, item->c, id, id_len, sql_row);
     case lkt_search_queue:
-        id = sqlite3_column_int(item->stmt, 0);
+        id      = sqlite3_column_int(item->stmt, 0);
         sql_row = (const char *) sqlite3_column_text(item->stmt, 1);
-        id_len = sqlite3_column_int(item->stmt, 2);
-        return ((lkt_search_queue_func) item->call)(item->srv, item->c, id, id_len, sql_row);
+        id_len  = sqlite3_column_int(item->stmt, 2);
+        return ((lkt_search_database_func) item->call)(item->srv, item->c, id, id_len, sql_row);
+    case lkt_search_sticker:
+        sql_row = (const char *) sqlite3_column_text(item->stmt, 0);    /* Name  */
+        id      = sqlite3_column_int(item->stmt, 1);                    /* Id    */
+        code    = sqlite3_column_int(item->stmt, 2);                    /* Value */
+        type    = (const char *) sqlite3_column_text(item->stmt, 3);    /* Type  */
+        return ((lkt_search_sticker_func) item->call)(item->srv, item->c, sql_row, type, id, code);
     case lkt_search_playlist:
     default:
         LOG_WARN_SCT("DB", "Search operation %d is not implemented", item->type);
diff --git a/src/database/playlist.c b/src/database/playlist.c
index a805e47b214f86c4d35dbb9eadbd3430f6c3ac36..b3d259ab8066d934b47fe7c4be47940baf215330 100644
--- a/src/database/playlist.c
+++ b/src/database/playlist.c
@@ -51,7 +51,7 @@ database_plt_remove_pos(volatile sqlite3 *db, const char *name, int pos)
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
     SQLITE_BIND_INT(db, stmt, 2, pos, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     sta = true;
 error:
     sqlite3_finalize(stmt);
@@ -67,7 +67,7 @@ database_plt_clear(volatile sqlite3 *db, const char *name)
     bool sta = false;
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     sta = true;
 error:
     sqlite3_finalize(stmt);
@@ -83,7 +83,7 @@ database_plt_rename(volatile sqlite3 *db, const char *old_name, const char *new_
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, new_name, error);
     SQLITE_BIND_TEXT(db, stmt, 2, old_name, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     sta = true;
 error:
     sqlite3_finalize(stmt);
diff --git a/src/database/queue.c b/src/database/queue.c
index abce4426ed9a77a7d5833570b8a04f71a51a907a..fbdf30dc7ac4d51a84792699819b39fc0c10f8a8 100644
--- a/src/database/queue.c
+++ b/src/database/queue.c
@@ -9,7 +9,7 @@
 
 #define sqlite_just_exec(func, query)                                   \
     bool func (volatile sqlite3 *db) {                                  \
-        SQLITE_EXEC(db, "BEGIN TRANSACTION;" #query "COMMIT;", error);  \
+        SQLITE_EXEC(db, "BEGIN TRANSACTION;" query "COMMIT;", error);   \
         return true;                                                    \
         error:                                                          \
         SQLITE_DO_ROLLBACK(db);                                         \
@@ -18,8 +18,10 @@
 sqlite_just_exec(database_queue_toggle_pause, "UPDATE queue_state SET paused = 1 - paused;")
 sqlite_just_exec(database_queue_crop, "DELETE FROM queue WHERE queue.kara_id <> (SELECT current FROM queue_state LIMIT 1);")
 sqlite_just_exec(database_queue_stop, "UPDATE queue_state SET current = NULL;")
-sqlite_just_exec(database_queue_clear, "DELETE FROM queue;DELETE FROM sqlite_sequence WHERE name = 'queue';UPDATE queue_state SET current = NULL;")
-sqlite_just_exec(database_config_queue_default, "UPDATE queue_state SET volume = 100, paused = 1, random = 0, repeat = 0, single = 0, consume = 0, current = NULL, duration = 0;")
+sqlite_just_exec(database_queue_clear,
+                 "DELETE FROM queue;DELETE FROM sqlite_sequence WHERE name = 'queue';UPDATE queue_state SET current = NULL;")
+sqlite_just_exec(database_config_queue_default,
+                 "UPDATE queue_state SET volume = 100, paused = 1, random = 0, repeat = 0, single = 0, consume = 0, current = NULL, duration = 0;")
 #undef sqlite_just_exec
 
 bool
diff --git a/src/database/stickers.c b/src/database/stickers.c
index 8863b827b0d3ac61f982c31ff1d42f0180c3d811..223a9ac99232b8d4883767d6adaa89115b6b95ac 100644
--- a/src/database/stickers.c
+++ b/src/database/stickers.c
@@ -7,22 +7,27 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+static inline int
+__check_type(const char *type)
+{
+    return ! ( STR_MATCH(type, "kara") || STR_MATCH(type, "plt") );
+}
+
 bool
 database_sticker_create(volatile sqlite3 *db, const char *name)
 {
     static const char *INSERT =
-        "WITH id_max (id) AS (SELECT MAX(id) FROM 'stickers')"
         "INSERT INTO 'stickers' (id, name)"
-        " SELECT CASE WHEN id_max.id IS NULL THEN 1 ELSE id_max.id + 1 END, ?"
-        " FROM id_max"
-        "WHERE NOT EXISTS (SELECT 1 FROM 'stickers' WHERE name = ?);";
+        " SELECT CASE WHEN MAX(id) IS NULL THEN 1 ELSE MAX(id) + 1 END, ?"
+        " FROM 'stickers'"
+        "WHERE (SELECT COUNT(*) FROM 'stickers' WHERE name = ?) = 0;";
     sqlite3_stmt *stmt;
     int ret = false;
     RETURN_IF(strlen(name) == 0, "A sticker name must be at least one character long", ret);
     SQLITE_PREPARE(db, stmt, INSERT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
     SQLITE_BIND_TEXT(db, stmt, 2, name, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     ret = true;
 error:
     sqlite3_finalize(stmt);
@@ -38,7 +43,7 @@ database_sticker_delete(volatile sqlite3 *db, const char *name)
     RETURN_IF(strlen(name) == 0, "A sticker name must be at least one character long", ret);
     SQLITE_PREPARE(db, stmt, INSERT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     ret = true;
 error:
     sqlite3_finalize(stmt);
@@ -48,38 +53,33 @@ error:
 bool
 database_sticker_set(volatile sqlite3 *db, const char *type, const char *name, int uri, int value)
 {
-    const char *SQL = NULL;
+    static const char *SQL_TEMPLATE =
+        "INSERT OR REPLACE INTO 'stickers.%s' (id, sticker, value) "
+        "SELECT ?, 'stickers'.id, ? "
+        "FROM 'stickers' "
+        "WHERE 'stickers'.name = ?;";
+    char SQL[LKT_MAX_SQLITE_STATEMENT];
     sqlite3_stmt *stmt;
     int ret = false;
 
+    if (__check_type(type)) {
+        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
+        return false;
+    }
+
+    safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, type);
+
     /* Bindings:
      * 1 -> the uri of the plalist or kara
      * 2 -> the value of the sticker
      * 3 -> the name of the sticker
      */
 
-    if (!strcasecmp(type, "song"))
-        SQL =
-            "INSERT OR REPLACE INTO 'stickers.song' (id, sticker, value) "
-            "SELECT ?, 'stickers'.id, ? "
-            "FROM 'stickers'"
-            "WHERE 'stickers'.id = ?;\n";
-    else if (!strcasecmp(type, "plt"))
-        SQL =
-            "INSERT OR REPLACE INTO 'stickers.plt' (id, sticker, value) "
-            "SELECT ?, 'stickers'.id, ? "
-            "FROM 'stickers'"
-            "WHERE 'stickers'.id = ?;\n";
-    else {
-        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
-        return false;
-    }
-
     SQLITE_PREPARE(db, stmt, SQL, error);
     SQLITE_BIND_INT(db, stmt, 1, uri, error);
     SQLITE_BIND_INT(db, stmt, 2, value, error);
     SQLITE_BIND_TEXT(db, stmt, 3, name, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     ret = true;
 error:
     sqlite3_finalize(stmt);
@@ -92,9 +92,7 @@ database_sticker_delete_specify(volatile sqlite3 *db, const char *type, int uri,
     char SQL[LKT_MAX_SQLITE_STATEMENT];
     sqlite3_stmt *stmt;
     int ret = false;
-
-    /* Base query. */
-    if (strcasecmp("plt", type) && strcasecmp("song", type)) {
+    if (__check_type(type)) {
         LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
         return false;
     }
@@ -118,7 +116,7 @@ database_sticker_delete_specify(volatile sqlite3 *db, const char *type, int uri,
     if (name)
         SQLITE_BIND_TEXT(db, stmt, 2, name, error);
 
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     ret = true;
 error:
     sqlite3_finalize(stmt);
diff --git a/src/database/update.c b/src/database/update.c
index 7ff464aa795b96b77fc3bd8d34e054de40049a3c..4966e6302c20e2120315289506f051d600934bde 100644
--- a/src/database/update.c
+++ b/src/database/update.c
@@ -24,7 +24,7 @@ __add_kara_to_update_job(volatile sqlite3 *db, size_t id)
     sqlite3_stmt *stmt;
     SQLITE_PREPARE(db, stmt, SQL, error);
     SQLITE_BIND_INT(db, stmt, 1, (int) id, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     return true;
 error:
     return false;
@@ -115,7 +115,6 @@ database_update_set_available(volatile sqlite3 *db, int id)
     if (sqlite3_step(stmt) != SQLITE_DONE)
         goto error;
     sqlite3_finalize(stmt);
-    LOG_INFO_SCT("DB", "Kara %d is now available", id);
     return true;
 error:
     sqlite3_finalize(stmt);
@@ -224,7 +223,7 @@ database_deleted_kara(volatile sqlite3 *db, int **kara_id, size_t *len)
 
     while (sqlite3_step(stmt) == SQLITE_ROW) {
         if (size == *len) {
-            new = realloc((void *) kara_id, (*len) * 2 * sizeof(void *));
+            new = realloc(*kara_id, size * 2 * sizeof(int));
             GOTO_UNLESS(new, "Out of memory", out);
             size *= 2;
             *kara_id = new;
@@ -254,6 +253,12 @@ error:
     LOG_WARN_SCT("DB", "Failed to get informations about the last update: %s", sqlite3_errmsg((sqlite3 *) db));
 }
 
+void
+database_update_touch(volatile sqlite3 *db, int id)
+{
+    __add_kara_to_update_job(db, id);
+}
+
 void
 database_update_del(volatile sqlite3 *db, int id)
 {
@@ -261,7 +266,7 @@ database_update_del(volatile sqlite3 *db, int id)
     sqlite3_stmt *stmt = NULL;
     SQLITE_PREPARE(db, stmt, SQL, error);
     SQLITE_BIND_INT(db, stmt, 1, id, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     LOG_WARN_SCT("DB", "Deleted kara with id '%d' from database", id);
 error:
     return;
@@ -269,7 +274,7 @@ error:
 
 #define sqlite_just_exec(func, query)   \
     void func (volatile sqlite3 *db) {  \
-        SQLITE_EXEC(db, #query, error); \
+        SQLITE_EXEC(db, query, error);  \
         error: return;                  \
     }
 sqlite_just_exec(database_stamp, "UPDATE misc SET last_update = strftime('%s','now');")
diff --git a/src/database/user.c b/src/database/user.c
index cad67826f5fec56ab1e9b58dea3121d729de8a28..8460216eb5cb5f174506a908b92f19bd586bc3b0 100644
--- a/src/database/user.c
+++ b/src/database/user.c
@@ -29,7 +29,7 @@ database_user_add(volatile sqlite3 *db, const char *username, const char *passwo
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, username, error);
     SQLITE_BIND_TEXT(db, stmt, 2, password, error);
-    SQLITE_STEP_OK(db, stmt, error);
+    SQLITE_STEP_DONE(db, stmt, error);
     LOG_INFO_SCT("DB", "User '%s' successfully added", username);
     ret = true;
 error:
diff --git a/src/main/lkt.c b/src/main/lkt.c
index 8ab454adb96e83f47c107f01ffd2efb5396a9049..06050b755d6e095a7a3e9d796fc721b789886be3 100644
--- a/src/main/lkt.c
+++ b/src/main/lkt.c
@@ -28,11 +28,9 @@
 #define LKT_KEY_VALUE_SEP               ": \n\t\0"
 #define fail_if(cond, msg)              { if (cond) { LOG_ERROR("%s", msg); exit(EXIT_FAILURE); } }
 #define fail(msg)                       { LOG_ERROR("%s", msg); exit(EXIT_FAILURE); }
-#define lkt_send_and_exit(buffer, len)  exit(write_socket(lkt_connect(), buffer, len))
-#define write_socket(sock, buff, len)   (fwrite(buff, sizeof(char), len, sock) != len)
 
 #define exit_with_status(sock, buff) {                \
-    fail_if(read_socket(sock, buff, 2) == 0, "ACK");  \
+    read_socket(sock, buff, 2);                       \
     fail_if(buff[0] != 'O' && buff[1] != 'K', "ACK"); \
     exit(EXIT_SUCCESS);                               \
 }
@@ -76,34 +74,12 @@ read_socket(FILE *sock, char *buff, size_t max_len)
     for (i = 0; i < max_len; ++i) {
         len = fread(buff + i, sizeof(char), 1, sock);
         if (buff[i] == '\n' || len != 1)
-            return i;
+            break;
     }
+    fail_if(len == 0, "Connexion error");
     return i;
 }
 
-static int
-write_socket_format(FILE *sock, const char *format, ...)
-{
-    int ret = 1, size;
-    char buff[LKT_MESSAGE_MAX];
-    va_list ap;
-
-    va_start(ap, format);
-    size = vsnprintf(buff, LKT_MESSAGE_MAX - 1, format, ap);
-    buff[LKT_MESSAGE_MAX - 1] = 0;
-
-    if (size < 0)
-        goto err;
-
-    if (fwrite(buff, sizeof(char), size, sock) < (unsigned int) size)
-        goto err;
-
-    ret = 0;
-err:
-    va_end(ap);
-    return ret;
-}
-
 static FILE *
 create_socket(const char *host, const char *port)
 {
@@ -137,15 +113,38 @@ static FILE *
 lkt_connect(void)
 {
     char buff[LKT_MESSAGE_MAX];
-    size_t recv_len;
-
     FILE *sock = create_socket(host, port);
-    recv_len = read_socket(sock, buff, LKT_MESSAGE_MAX);
-
-    fail_if(recv_len == 0, "Failed to connect to lektord");
+    read_socket(sock, buff, LKT_MESSAGE_MAX);
     return sock;
 }
 
+static void
+write_socket(FILE *sock, const char *format, ...)
+{
+    int size;
+    char buff[LKT_MESSAGE_MAX];
+    va_list ap;
+
+    va_start(ap, format);
+    size = vsnprintf(buff, LKT_MESSAGE_MAX - 1, format, ap);
+    buff[LKT_MESSAGE_MAX - 1] = 0;
+
+    if (size < 0)
+        fail("Connexion error")
+
+        if (fwrite(buff, sizeof(char), size, sock) < (unsigned int) size)
+            fail("Connexion error")
+
+            va_end(ap);
+}
+
+noreturn static inline void
+lkt_send_and_exit(const char *buffer, size_t len)
+{
+    write_socket(lkt_connect(), buffer, len);
+    exit(EXIT_SUCCESS);
+}
+
 static char *
 lkt_skip_key(char *buffer)
 {
@@ -166,22 +165,36 @@ send_cmd_with_uri(FILE *sock, char *cmd, int argc, const char **argv)
     }
     strncat(buf, argv[i], LKT_MESSAGE_MAX - 1);
     strncat(buf, "\n", LKT_MESSAGE_MAX - 1);
-    (void) write_socket_format(sock, "%s %s://%s", cmd, argv[0], buf);
+    write_socket(sock, "%s %s://%s", cmd, argv[0], buf);
 }
 
 /* Functions implementing options. */
 
+#define just_send_one_arg(func, cmd)                \
+noreturn void func (struct lkt_cmd_args *args) {    \
+    fail_if(args->argc != 1, "Invalid argument");   \
+    FILE *sock = lkt_connect();                     \
+    char buff[2];                                   \
+    write_socket(sock, cmd " %s\n", args->argv[0]); \
+    exit_with_status(sock, buff);                   \
+}
+just_send_one_arg(stickers_create__, "sticker __create")
+just_send_one_arg(stickers_destroy__, "sticker __destroy")
+just_send_one_arg(plt_destroy__, "rm")
+just_send_one_arg(plt_create__, "playlistadd")
+#undef just_send_one_arg
+
 #define just_send(func, msg) /* Just send a simple string functions */  \
     noreturn void func (struct lkt_cmd_args *args) {                    \
         fail_if(args->argc, "This command takes no arguments");         \
         lkt_send_and_exit(msg, sizeof(msg));                            \
     }
-just_send(queue_clear__, "clear\nclose\n")
-just_send(queue_crop__,  "crop\nclose\n")
-just_send(next__,        "next\nclose\n")
-just_send(prev__,        "previous\nclose\n")
-just_send(stop__,        "stop\nclose\n")
-just_send(shuffle__,     "shuffle\nplay\nclose\n")
+just_send(queue_clear__, "clear\n")
+just_send(queue_crop__,  "crop\n")
+just_send(next__,        "next\n")
+just_send(prev__,        "previous\n")
+just_send(stop__,        "stop\n")
+just_send(shuffle__,     "shuffle\nplay\n")
 #undef just_send
 
 noreturn void
@@ -189,9 +202,12 @@ simple_send_with_password__(const char *cmd)
 {
     if (!password)
         fail("Password not provided");
-    static const char cmd__[] = "password %s\n%s\nclose\n";
     FILE *sock = lkt_connect();
-    exit(write_socket_format(sock, cmd__, password, cmd));
+    write_socket(sock, "command_list_begin\n");
+    write_socket(sock, "password %s\n", password);
+    write_socket(sock, "%s\n", cmd);
+    write_socket(sock, "command_list_end\n");
+    exit(EXIT_SUCCESS);
 }
 
 noreturn void
@@ -220,18 +236,25 @@ rescan_or_update__(struct lkt_cmd_args *args, const char *cmd)
     int i;
 
     /* All the db */
-    if (args->argc == 0)
-        exit(write_socket_format(sock, "password %s\n%s\n", password, cmd));
+    if (args->argc == 0) {
+        write_socket(sock, "command_list_begin\n");
+        write_socket(sock, "password %s\n", password);
+        write_socket(sock, "%s\n", cmd);
+        write_socket(sock, "command_list_end\n");
+    }
 
     /* With a query */
     else {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
         for (i = 0; i < args->argc; ++i)
             strncat(buff, args->argv[i], LKT_MESSAGE_MAX - 1);
-        exit(write_socket_format(sock, "password %s\n%s %s\n", password, cmd, buff));
+        write_socket(sock, "command_list_begin\n");
+        write_socket(sock, "password %s\n", password);
+        write_socket(sock, "%s %s\n", cmd, buff);
+        write_socket(sock, "command_list_end\n");
     }
 
-    abort();
+    exit(EXIT_SUCCESS);
 }
 
 noreturn void
@@ -257,22 +280,17 @@ play__(struct lkt_cmd_args *args)
         pos = atoi(args->argv[0]);
 
     static const char status__[]        = "status\n";
-    static const char cmd_play__[]      = "play\nclose\n";
-    static const char cmd_play_from__[] = "play %d\nclose\n";
-    static const char cmd_pause__[]     = "pause\nclose\n";
+    static const char cmd_play__[]      = "play\n";
+    static const char cmd_play_from__[] = "play %d\n";
+    static const char cmd_pause__[]     = "pause\n";
 
     char buff[LKT_MESSAGE_MAX];
-
     FILE *sock = lkt_connect();
-
-    if (write_socket(sock, status__, sizeof(status__))) /* In bytes. */
-        exit(EXIT_FAILURE);
+    write_socket(sock, status__);
 
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        if (read_socket(sock, buff, LKT_MESSAGE_MAX - 1) <= 0)
-            exit(EXIT_FAILURE);
-
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
         size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
 
         if (STR_NMATCH(buff, "state", len)) {
@@ -281,8 +299,10 @@ play__(struct lkt_cmd_args *args)
             if (STR_NMATCH(lkt_skip_key(buff), "stop", 4)) {
                 if (!pos)
                     lkt_send_and_exit(cmd_play__, sizeof(cmd_play__));  /* In bytes. */
-                else
-                    exit(write_socket_format(lkt_connect(), cmd_play_from__, pos));
+                else {
+                    write_socket(lkt_connect(), cmd_play_from__, pos);
+                    exit(EXIT_SUCCESS);
+                }
             }
 
             else
@@ -300,13 +320,11 @@ ping__(struct lkt_cmd_args *args)
         fail("Invalid argument, the ping command takes no arguments");
     char buff[6] = {0};
     FILE *sock = lkt_connect();
-    if (write_socket(sock, "ping\nclose\n", sizeof("ping\n")))
-        fail("Failed to send the ping to lektord");
-    if (read_socket(sock, buff, 6 * sizeof(char)) <= 0)
-        fail("Failed to recieve the response of lektord");
+    write_socket(sock, "ping\n");
+    read_socket(sock, buff, 6 * sizeof(char));
     if (!STR_NMATCH(buff, "OK", 2))
         fail("ACK");
-    exit(write(1, "OK\n", sizeof("OK\n")) > 0);
+    exit(write(1, "OK\n", sizeof("OK\n")) == 0);
 }
 
 noreturn void
@@ -321,9 +339,7 @@ current__(struct lkt_cmd_args *args)
     char *mem = NULL;
     FILE *sock = lkt_connect();
 
-    if (write_socket(sock, current_song__, sizeof(current_song__))) /* In bytes. */
-        exit(EXIT_FAILURE);
-
+    write_socket(sock, current_song__);
     assert(mem = calloc(6 * LKT_MESSAGE_MAX, sizeof(char)));
     assert(memset(mem, 0, 6 * LKT_MESSAGE_MAX * sizeof(char)));
 
@@ -336,8 +352,7 @@ current__(struct lkt_cmd_args *args)
 
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        if (read_socket(sock, buff, LKT_MESSAGE_MAX - 1) <= 0)
-            exit(EXIT_FAILURE);
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
 
         const size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
         char *value = lkt_skip_key(buff);
@@ -388,16 +403,12 @@ queue_pop__(struct lkt_cmd_args *args)
     FILE *sock = lkt_connect();
 
     /* Get lektor's status. */
-
-    if (write_socket(sock, "status\n", sizeof("status\n")))
-        goto error;
+    write_socket(sock, "status\n");
 
 #define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (atoi(lkt_skip_key(buff))); continue; }
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        if (read_socket(sock, buff, LKT_MESSAGE_MAX - 1) <= 0)
-            goto error;
-
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
         size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
         assign_int("songid", songid)
 
@@ -405,18 +416,16 @@ queue_pop__(struct lkt_cmd_args *args)
         if (STR_NMATCH(buff, "OK", 2))
             break;
         else if (STR_NMATCH(buff, "ACK", 3))
-            goto error;
+            exit(EXIT_FAILURE);
     }
 #undef assign_int
 
     fclose(sock);
     sock = lkt_connect();
     if (!songid)
-        goto error;
-    write_socket_format(sock, "next\ndeleteid %d\n", songid);
+        exit(EXIT_FAILURE);
+    write_socket(sock, "next\ndeleteid %d\n", songid);
     exit_with_status(sock, buff);
-error:
-    exit(EXIT_FAILURE);
 }
 
 noreturn void
@@ -439,16 +448,13 @@ status__(struct lkt_cmd_args *args)
 
     /* Get lektor's status. */
 
-    if (write_socket(sock, status_str__, strlen(status_str__)))
-        goto error;
+    write_socket(sock, status_str__);
 
 #define assign_flag(str, f)  if (STR_NMATCH(buff, str, len) && (atoi(lkt_skip_key(buff)) == 1)) { flags[it++] = f; continue; }
 #define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (atoi(lkt_skip_key(buff))); continue; }
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        if (read_socket(sock, buff, LKT_MESSAGE_MAX - 1) <= 0)
-            goto error;
-
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
         size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
 
         if (STR_NMATCH(buff, "state", len)) {
@@ -502,14 +508,14 @@ queue_delete__(struct lkt_cmd_args *args)
     if (args->argc != 1)
         fail("Invalid argument, need onlt one argument: queue delete <id>");
 
-    static const char *cmd_id__ = "deleteid %d\nclose\n";
+    static const char *cmd_id__ = "deleteid %d\n";
     int dumy = 0;
     FILE *sock = lkt_connect();
     char buff[3];
 
     sscanf(args->argv[0], "%d", &dumy);
     if (dumy != 0) {
-        fail_if(write_socket_format(sock, cmd_id__, dumy), "ACK");
+        write_socket(sock, cmd_id__, dumy);
         exit_with_status(sock, buff);
     }
 
@@ -555,7 +561,7 @@ queue_seek__(struct lkt_cmd_args *args)
         fail("Invalid argument, must be only one integer");
 
     FILE *sock = lkt_connect();
-    fail_if(write_socket_format(sock, "playid %ld\n", id), "ACK");
+    write_socket(sock, "playid %ld\n", id);
     exit_with_status(sock, buf);
 }
 
@@ -585,12 +591,14 @@ queue_pos__(struct lkt_cmd_args *args)
     FILE *sock = NULL;
 redo:
     sock = lkt_connect();
-    fail_if(up != 0 && write_socket_format(sock, "playlist %d:%d\n", continuation, up), "ACK");
-    fail_if(up == 0 && write_socket_format(sock, "playlist %d\n", continuation),        "ACK");
+    if (up != 0)
+        write_socket(sock, "playlist %d:%d\n", continuation, up);
+    else
+        write_socket(sock, "playlist %d\n", continuation);
 
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        assert(read_socket(sock, buff, LKT_MESSAGE_MAX - 1) > 0);
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
 
         if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
             continuation = atoi(lkt_skip_key(buff));
@@ -634,15 +642,12 @@ queue_list__(struct lkt_cmd_args *args)
     /* Get the current pos to get limits for the playlist command. */
 
     sock = lkt_connect();
-    if (write_socket(sock, "status\n", sizeof("status\n")))
-        fail("Communication error");
+    write_socket(sock, "status\n");
 
 #define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (atoi(lkt_skip_key(buff))); continue; }
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        if (read_socket(sock, buff, LKT_MESSAGE_MAX - 1) <= 0)
-            fail("Connextion error");
-
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
         size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
         assign_int("song", song_index);
 
@@ -688,32 +693,35 @@ plt_delete__(struct lkt_cmd_args *args)
     exit_with_status(sock, buff);
 }
 
-noreturn void
-plt_destroy__(struct lkt_cmd_args *args)
-{
-    fail_if(args->argc != 1, "Invalid argument");
-    FILE *sock = lkt_connect();
-    char buff[2];
-    write_socket_format(sock, "rm %s\n", args->argv[0]);
-    exit_with_status(sock, buff);
-}
-
-noreturn void
-plt_create__(struct lkt_cmd_args *args)
-{
-    fail_if(args->argc != 1, "Invalid argument");
-
-    FILE *sock = lkt_connect();
-    char buff[2];
-    write_socket_format(sock, "playlistadd %s\n", args->argv[0]);
-    exit_with_status(sock, buff);
-}
-
 noreturn void
 stickers_get__(struct lkt_cmd_args *args)
 {
-    UNUSED(args);
-    exit(EXIT_FAILURE);
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock;
+    if (args->argc == 2)
+        write_socket(sock = lkt_connect(), "sticker get %s %s\n", args->argv[0], args->argv[1]);
+    else if (args->argc == 3)
+        write_socket(sock = lkt_connect(), "sticker get %s %s %s\n", args->argv[0], args->argv[1],
+                     args->argv[2]);
+    else if (args->argc == 5) {
+        const char *op = args->argv[3];
+        op = op[0] == 'e' ? "=" : op[0] == 'l' ? "<" : op[0] == 'g' ? ">" : NULL;
+        fail_if(!op, "Invalid argument");
+        write_socket(sock = lkt_connect(), "sticker get %s %s %s %s %s\n", args->argv[0],
+                     args->argv[1], args->argv[2], op, args->argv[4]);
+    }
+    else
+        fail("Invalid argument");
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+        fprintf(stdout, "%s", buff);
+    }
 }
 
 noreturn void
@@ -722,8 +730,8 @@ stickers_set__(struct lkt_cmd_args *args)
     fail_if(args->argc != 4, "Invalid argument");
     FILE *sock = lkt_connect();
     char buff[2];
-    write_socket_format(sock, "sticker set %s %s %s %s\n", args->argv[0],
-                        args->argv[1], args->argv[2], args->argv[3]);
+    write_socket(sock, "sticker set %s %s %s %s\n", args->argv[0],
+                 args->argv[1], args->argv[2], args->argv[3]);
     exit_with_status(sock, buff);
 }
 
@@ -733,11 +741,11 @@ stickers_delete__(struct lkt_cmd_args *args)
     FILE *sock;
     char buff[2];
     if (args->argc == 2)
-        write_socket_format(sock = lkt_connect(), "sticker delete %s %s",
-                            args->argv[0], args->argv[1]);
+        write_socket(sock = lkt_connect(), "sticker delete %s %s",
+                     args->argv[0], args->argv[1]);
     else if (args->argc == 3)
-        write_socket_format(sock = lkt_connect(), "sticker delete %s %s %s",
-                            args->argv[0], args->argv[1], args->argv[2]);
+        write_socket(sock = lkt_connect(), "sticker delete %s %s %s",
+                     args->argv[0], args->argv[1], args->argv[2]);
     else
         fail("Invalid argument");
     exit_with_status(sock, buff);
@@ -757,14 +765,14 @@ search_with_cmd__(struct lkt_cmd_args *args, const char *cmd)
 redo:
     sock = lkt_connect();
 
-    write_socket_format(sock, "%d %s", continuation, cmd);
+    write_socket(sock, "%d %s", continuation, cmd);
     for (i = 0; i < args->argc; ++i)
-        write_socket_format(sock, " %s", args->argv[i]);
-    (void) write_socket(sock, "\n", sizeof("\n") / sizeof(char));
+        write_socket(sock, " %s", args->argv[i]);
+    write_socket(sock, "\n");
 
     for (;;) {
         memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
-        assert(read_socket(sock, buff, LKT_MESSAGE_MAX - 1) > 0);
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
 
         if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
             continuation = atoi(lkt_skip_key(buff));
@@ -832,6 +840,8 @@ static struct lkt_cmd_opt options_stickers[] = {
     { .name = "get",        .call = stickers_get__      },
     { .name = "set",        .call = stickers_set__      },
     { .name = "delete",     .call = stickers_delete__   },
+    { .name = "create",     .call = stickers_create__   },
+    { .name = "destroy",    .call = stickers_destroy__  },
     LKT_OPT_NULL,
 };
 
diff --git a/src/main/lktadm.c b/src/main/lktadm.c
index 009d93d6114d08fdbc84ff663dcc28edbd12bafe..1a5e561d1e0c905750b8570d20e5a24751e0b11c 100644
--- a/src/main/lktadm.c
+++ b/src/main/lktadm.c
@@ -25,7 +25,7 @@ fail(const char *format, ...)
     va_start(ap, format);
     vfprintf(stderr, format, ap);
     va_end(ap);
-    exit(write(2, "\n", 1) > 0);
+    exit(write(2, "\n", 1) == 0);
 }
 
 /* ----------------- *
diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c
index bce1e3b6477a10186a90fa27d08bbe84a9bcdb8c..8421533b22ad4f8bf115a43e60954c9fcb304c33 100644
--- a/src/module/module_sdl2.c
+++ b/src/module/module_sdl2.c
@@ -277,6 +277,7 @@ module_sdl2_close(struct lkt_win *const win)
 {
     RETURN_UNLESS(win && win->window, "Invalid arguments", NOTHING);
     struct module_sdl2_window *sdl2 = win->window;
+    sdl2->mpv_time_pos = (sdl2->mpv_duration = 0);
     RETURN_UNLESS(sdl2->mpv, "Missing mpv ctx", NOTHING);
     static const char *cmd[] = { "stop", NULL };
     mpv_command_async((mpv_handle *) sdl2->mpv, 0, cmd);
diff --git a/src/module/module_x11.c b/src/module/module_x11.c
index 5852a9c4cf6cd70f0cf56bc65eb59be70694b8b1..521acfdad421a92e82e09050ac7d0403b2ed8757 100644
--- a/src/module/module_x11.c
+++ b/src/module/module_x11.c
@@ -200,7 +200,7 @@ module_x11_close(struct lkt_win *const win)
     struct module_x11_window *const x11_win = win->window;
     lmpv_free(&x11_win->mpv);
     x11_win->child_display = NULL;
-    x11_win->child_win = 0;
+    x11_win->child_win = (x11_win->mpv_time_pos = (x11_win->mpv_duration = 0));
 }
 
 void
diff --git a/src/module/mpv.c b/src/module/mpv.c
index 492ac110f59a2a7f779bf8bcba5fd7225447707e..f1b891cbc1f33643eb90034e55e4b986b9f003d5 100644
--- a/src/module/mpv.c
+++ b/src/module/mpv.c
@@ -75,7 +75,7 @@ lmpv_new(unsigned long int wid)
         goto error;
     }
 
-    LOG_ERROR_SCT("WINDOW", "%s", "Create mpv context");
+    LOG_INFO_SCT("WINDOW", "%s", "Create mpv context");
     return ctx;
 error:
     lmpv_free(&ctx);
@@ -151,6 +151,7 @@ loop:
         break;
 
     case MPV_EVENT_SHUTDOWN:
+        *time_pos = (*time_duration = 0);
         database_queue_stop(db);
         win->close(win);
         return 1;
diff --git a/src/net/downloader.c b/src/net/downloader.c
index 9606701e0e7a89b1bd3f3e15430df3f142864528..d0d58690ed052d6cf32c3b70c1f31ade34f2468f 100644
--- a/src/net/downloader.c
+++ b/src/net/downloader.c
@@ -369,6 +369,8 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec
         database_get_update(db, &db_timestamp, NULL, NULL);
         if (db_timestamp >= filestamp && filestamp > timestamp &&
             ! kara_metadata_equals(&kara.mdt, kara.filename)) {
+            database_update_touch(db, kara.id);
+            database_update_set_available(db, kara.id);
             LOG_INFO_SCT("REPO", "Ignore kara '%ld' with path '%s'", kara.id, kara.filename);
             continue;
         }
@@ -390,7 +392,7 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec
             continue;
         }
 
-        if (!database_update_set_available((sqlite3 *) db, kara.id)) {
+        if (!database_update_set_available(db, kara.id)) {
             LOG_WARN_SCT("REPO", "Could not set kara %ld available", kara.id);
             continue;
         }
@@ -407,12 +409,12 @@ __handle_deleted_kara(volatile sqlite3 *db)
     int *kara_ids;
     char filepath[PATH_MAX];
     database_deleted_kara(db, &kara_ids, &len);
-
     for (i = 0; i < len; ++i) {
         if (!database_get_kara_path(db, kara_ids[i], filepath))
             continue;
         database_update_del(db, kara_ids[i]);
     }
+    free(kara_ids);
 }
 
 void *
diff --git a/src/net/listen.c b/src/net/listen.c
index 01389c300c467881a113920f753388ba11d44bd0..5f50167b3cd08a4b377bda6667477fcc66bd7a9d 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -200,14 +200,14 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd)
             err = ! command_find(srv, c, cmd.args, cmd.cont, database_search_queue_init);
 
         else if (STR_MATCH(cmd.name, "sticker") && cmd.args[0]) {
-            if (STR_MATCH(cmd.args[0], "get"))
+            if (STR_MATCH(cmd.args[0], "get") || STR_MATCH(cmd.args[0], "list") || STR_MATCH(cmd.args[0], "find"))
                 err = ! command_sticker_get(srv, c, &cmd.args[1]);
             else if (STR_MATCH(cmd.args[0], "set"))
                 err = ! command_sticker_set(srv, c, &cmd.args[1]);
             else if (STR_MATCH(cmd.args[0], "delete"))
                 err = ! command_sticker_delete(srv, c, &cmd.args[1]);
-            else if (STR_MATCH(cmd.args[0], "list") || STR_MATCH(cmd.args[0], "find"))
-                err = ! command_sticker_delete(srv, c, &cmd.args[1]);
+            else if (STR_MATCH(cmd.args[0], "__create"))
+                err = ! command_sticker_create(srv, c, &cmd.args[1]);
         }
 
         else if (STR_MATCH(cmd.name, "help"))
@@ -523,7 +523,7 @@ accept_all(int listen_fd, struct pollfd *fds, size_t fds_max, size_t *fds_len)
 
         char *host = inet_ntoa(peer_addr.sin_addr);
         uint16_t port = htons(peer_addr.sin_port);
-        printf(" * New connection from %s:%d.\n", host, port);
+        LOG_WARN_SCT("NETWORK", "New connection from %s:%d", host, port);
 
         fds->fd = fd;
         fds->events = POLLIN;
@@ -597,7 +597,7 @@ handle_network_events(struct lkt_state *srv)
         if (srv->fds[i].fd > 0)
             continue;
 
-        printf(" * Connection closed.\n");
+        LOG_WARN_SCT("NETWORK", "Connection closed by client %ld", i);
         srv->fds[i] = srv->fds[srv->fds_len - 1];
         srv->clients[i - 1] = srv->clients[srv->fds_len - 2];
         srv->fds_len--;