diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h index 34201ae7f36b9bc65a53827a0e52e73b532cdb1d..9e3424a40660e2f87f57b19ea7b1c50b21d24ae9 100644 --- a/inc/lektor/commands.h +++ b/inc/lektor/commands.h @@ -47,6 +47,8 @@ bool command_plt_rename(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]) bool command_plt_export(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]); bool command_plt_import(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]); bool command_plt_add (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]); +bool command_plt_list (struct lkt_state *srv, size_t c, long cont); +bool command_plt_ctx (struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], long cont); /* The help */ bool command_help(struct lkt_state *srv, size_t c); diff --git a/inc/lektor/database.h b/inc/lektor/database.h index 3c46b47351981ce1ebb791bad495a58f1d1e9dc1..daae958341105a4897375ec7a9ba47e4cd8b809a 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -90,6 +90,7 @@ struct lkt_search { lkt_search_playlist, lkt_search_queue, lkt_search_sticker, + lkt_search_listplaylist, } type; void (*call)(void); /* Called during the iter phase, casted */ @@ -112,17 +113,19 @@ struct lkt_search { struct lkt_uri ka_uri; /* Search by uri */ }; -typedef bool (*lkt_search_init_add_func)(volatile sqlite3 *, struct lkt_uri *, int); -typedef bool (*lkt_search_database_func)(struct lkt_state *srv, size_t c, int id, int id_len, const char *row); -typedef bool (*lkt_search_queue_func) (struct lkt_state *srv, size_t c, int id, int id_len, const char *row); -typedef bool (*lkt_search_sticker_func) (struct lkt_state *srv, size_t c, const char *sticker, const char *type, int uri, int value); - -bool database_search_database_init(volatile sqlite3 *db, struct lkt_search *ret); -bool database_search_queue_init (volatile sqlite3 *db, struct lkt_search *ret); -bool database_search_sticker_init (volatile sqlite3 *db, struct lkt_search *ret); -bool database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret); +typedef bool (*lkt_search_init_add_func) (volatile sqlite3 *, struct lkt_uri *, int); +typedef bool (*lkt_search_database_func) (struct lkt_state *srv, size_t c, int id, int id_len, const char *row); +typedef bool (*lkt_search_queue_func) (struct lkt_state *srv, size_t c, int id, int id_len, const char *row); +typedef bool (*lkt_search_sticker_func) (struct lkt_state *srv, size_t c, const char *sticker, const char *type, int uri, int value); +typedef bool (*lkt_search_listplaylist_func)(struct lkt_state *srv, size_t c, const char *name); + +bool database_search_database_init (volatile sqlite3 *db, struct lkt_search *ret); +bool database_search_queue_init (volatile sqlite3 *db, struct lkt_search *ret); +bool database_search_sticker_init (volatile sqlite3 *db, struct lkt_search *ret); +bool database_search_playlist_init (volatile sqlite3 *db, struct lkt_search *ret); +bool database_search_listplaylist_init(volatile sqlite3 *db, struct lkt_search *ret); bool database_search_iter(struct lkt_search *item); -bool database_kara_by_id(volatile sqlite3 *db, int id, struct kara_metadata *kara, char filepath[PATH_MAX]); +bool database_kara_by_id (volatile sqlite3 *db, int id, struct kara_metadata *kara, char filepath[PATH_MAX]); /* Next and prev operation on the queue. */ bool database_queue_next(volatile sqlite3 *db, char filepath[PATH_MAX]); diff --git a/inc/lektor/uri.h b/inc/lektor/uri.h index f18e476272de2bdb261a678c198f09dbf0c0f3f5..ca506f69d3813fcc050a0785dc71970b94515da8 100644 --- a/inc/lektor/uri.h +++ b/inc/lektor/uri.h @@ -5,6 +5,7 @@ #include <stdbool.h> enum lkt_uri_type { + uri_null, uri_id, uri_playlist, uri_type, diff --git a/src/commands.c b/src/commands.c index 54c7e5b8f45db415cda28b1f05cbbdeb72f50eb3..383e0a98b6860870fd06692ae5884b48ec6a685c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -433,6 +433,15 @@ lkt_callback_send_row_v2(struct lkt_state *srv, size_t c, int id, int id_len, co return true; } +bool +lkt_callback_send_list_plts(struct lkt_state *srv, size_t c, const char *plt_name) +{ + struct lkt_message *out = lkt_message_new(); + out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "%s\n", plt_name); + lkt_state_send(srv, c, out); + return true; +} + static bool command_findid(struct lkt_state *srv, size_t c, char *id_str) { @@ -470,6 +479,20 @@ command_findid(struct lkt_state *srv, size_t c, char *id_str) return true; } +static inline bool +__iter_search(struct lkt_search *search) +{ + int count; + for (count = 0; database_search_iter(search); ++count) + continue; + + if (count) + lkt_set_continuation(search->srv, search->c, search->continuation + count); + else + LOG_WARN("COMMAND", "%s", "Nothing found"); + return true; +} + bool command_find(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], long continuation, bool(*init)(volatile sqlite3 *, struct lkt_search *)) @@ -502,15 +525,38 @@ command_find(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], } RETURN_UNLESS(init(srv->db, &search), "Failed to init search", false); - for (count = 0; database_search_iter(&search); ++count) - continue; + return __iter_search(&search); +} - if (count) - lkt_set_continuation(srv, c, continuation + count); - else - LOG_WARN("COMMAND", "%s", "Nothing found"); - lkt_uri_free(&search.ka_uri); - return true; +bool +command_plt_list(struct lkt_state *srv, size_t c, long cont) +{ + struct lkt_search search = { + .srv = srv, + .c = c, + .continuation = cont, + .msg_count = lkt_remaining_msg(srv, c) - 3, /* Slots for OK/ACK and continuation */ + .call = (void(*)(void)) lkt_callback_send_list_plts, + }; + + RETURN_UNLESS(database_search_listplaylist_init(srv->db, &search), "Failed to init search", false); + return __iter_search(&search); +} + +bool command_plt_ctx(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], long cont) +{ + struct lkt_search search = { + .srv = srv, + .c = c, + .continuation = cont, + .msg_count = lkt_remaining_msg(srv, c) - 3, + .call = (void(*)(void)) lkt_callback_send_row_v2, + .plt_name = args[0], + .ka_uri = { .type = uri_null }, + }; + + RETURN_UNLESS(database_search_playlist_init(srv->db, &search), "Failed to init search", false); + return __iter_search(&search); } bool diff --git a/src/database/find.c b/src/database/find.c index 4ec182a564105e5251696e5fdae7945616e1cd8d..92889c0de0be8330f54d3618b3289f6c425f6315 100644 --- a/src/database/find.c +++ b/src/database/find.c @@ -48,7 +48,8 @@ error: bool database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret) { - RETURN_UNLESS(ret, "Exit because return pointer is NULL", false); + /* Sql queries definitions */ + char SQL_STMT[LKT_MAX_SQLITE_STATEMENT]; static const char *SQL_TEMPLATE = "WITH content AS (" " SELECT kara.id AS id, string, LENGTH(CAST(kara.id AS TEXT)) AS len" @@ -59,9 +60,24 @@ database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret) " AND %s LIKE ?)" "SELECT id, string, (SELECT MAX(len) FROM content)" "FROM content LIMIT %d OFFSET %d;"; - char SQL_STMT[LKT_MAX_SQLITE_STATEMENT]; - ret->type = lkt_search_playlist; + static const char *SQL = + "WITH content AS (" + " SELECT kara.id AS id, string LENGTH(CAST(kara.id AS TEXT)) AS len" + " FROM kara_playlist" + " JOIN playlist ON playlist.id = playlist_id" + " AND playlist.name COLLATE NOCASE = ?" + " JOIN kara ON kara.id = kara_id)" + "SELECT id, string (SELECT MAX(len) FROM content)" + "FROM content LIMIT %d OFFSET %d;"; + + /* Common part */ + RETURN_UNLESS(ret, "Exit because return pointer is NULL", false); + ret->type = lkt_search_playlist; + ret->db = db; + if (ret->ka_uri.type == uri_null) + goto uri_is_null; + /* Filter with an uri */ safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, ret->ka_uri.column_name, ret->msg_count, ret->continuation); SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); @@ -73,8 +89,30 @@ database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret) SQLITE_BIND_INT(db, ret->stmt, 2, (int) ret->ka_uri.id, error); LOG_DEBUG("DB", "%s", "Uri was a size_t"); } - ret->db = db; return true; + + /* Don't filter */ +uri_is_null: + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL, ret->msg_count, ret->continuation); + SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); + SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->plt_name, error); + return true; + + /* Error */ +error: + sqlite3_finalize(ret->stmt); + return false; +} + +bool +database_search_listplaylist_init(volatile sqlite3 *db, struct lkt_search *ret) +{ + static const char *SQL = "SELECT name FROM playlist LIMIT %d OFFSET %d;"; + char SQL_STMT[LKT_MAX_SQLITE_STATEMENT]; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL, ret->msg_count, ret->continuation); + ret->type = lkt_search_listplaylist; + ret->db = db; + SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); error: sqlite3_finalize(ret->stmt); return false; @@ -194,6 +232,10 @@ database_search_iter(struct lkt_search *item) type = sqlite3_column_chars(item->stmt, 3); /* Type */ return ((lkt_search_sticker_func) item->call) (item->srv, item->c, sql_row, type, id, code); + case lkt_search_listplaylist: + sql_row = sqlite3_column_chars(item->stmt, 0); /* Name */ + return ((lkt_search_listplaylist_func) item->call) + (item->srv, item->c, sql_row); default: LOG_WARN("DB", "Search type %d is not implemented", item->type); goto end; diff --git a/src/net/listen.c b/src/net/listen.c index 49b3e9771f4cae99fd15023df65a12ed9f7cad10..3916f4ccc4210c3cb4c486a6c105f3d70f9f3ad8 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -244,8 +244,12 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd) err = ! command_plt_import(srv, cmd.args); else if (STR_MATCH(cmd.name, "__dump")) err = ! command_dump(srv, cmd.args); - else if (STR_MATCH(cmd.name, "listplaylist")) + else if (STR_MATCH(cmd.name, "listplaylistinfo")) err = ! command_find(srv, c, cmd.args, cmd.cont, database_search_playlist_init); + else if (STR_MATCH(cmd.name, "listplaylists")) + err = ! command_plt_list(srv, c, cmd.cont); + else if (STR_MATCH(cmd.name, "listplaylist")) + err = ! command_plt_ctx(srv, c, cmd.args, cmd.cont); else if (STR_MATCH(cmd.name, "random")) err = !command_set_playback_option(srv, c, lkt_playback_option_random, cmd.args);