From 43a35f141b9b5a8d3db3ae35114db0ac1f6dafc9 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Sat, 30 Jan 2021 16:55:17 +0100
Subject: [PATCH] MPD: Add the seek, seekid, seekcur commands in lektord

NOTE: Still need to add the module function "set_position(int)" in the
sdl2 player module.
---
 README.md                |  6 ++---
 inc/lektor/commands.h    |  4 +++
 inc/lektor/database.h    |  3 ++-
 src/base/commands.c      | 53 ++++++++++++++++++++++++++++++++++++++++
 src/database/queue.c     | 24 ++++++++++++++++--
 src/module/module_sdl2.c |  1 +
 src/net/listen.c         |  7 ++++++
 7 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 71c5cb9a..0e19ed3a 100644
--- a/README.md
+++ b/README.md
@@ -204,9 +204,9 @@ For the compatibility column, the possible values are the following:
 | `play [songpos]`                  | `play [songpos]`                  | +     | if no `songpos` passed, defaults to `0`         |
 | `playid [songid]`                 | `playid {songid}`                 | +     | get the first found `songid` in the queue       |
 | `previous`                        | `previous`                        | +     |                                                 |
-| `seek {songpos} {time}`           |                                   |       | not implemented                                 |
-| `seekid {songid} {time}`          |                                   |       | not implemented                                 |
-| `seekcur {time}`                  | `seekcur {time}`                  |       | not implemented                                 |
+| `seek {songpos} {time}`           | `seek {songpos} {time}`           | +     |                                                 |
+| `seekid {songid} {time}`          | `seekid {songid} {time}`          | +     | seek to the first found id, sqlite dependent    |
+| `seekcur {time}`                  | `seekcur {time}`                  | +     |                                                 |
 | `stop`                            | `stop`                            | +     |                                                 |
 | `add {uri}`                       | `add {uri}`                       | +     |                                                 |
 | `addid {id} [pos]`                | `addid {id...}`                   | ~     | can add multiple songs, but no position support |
diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h
index bae6beeb..0eb603cb 100644
--- a/inc/lektor/commands.h
+++ b/inc/lektor/commands.h
@@ -27,6 +27,10 @@ bool command_play    (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
 bool command_playid  (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
 bool command_stop    (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
 
+bool command_seek    (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
+bool command_seekid  (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
+bool command_seekcur (struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX]);
+
 bool command_set_pos(struct lkt_state *srv, int index); /* FIXME: Not used in net/listen.c */
 
 /* The queue */
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index e57b4aba..61bea9e2 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -46,7 +46,8 @@ bool database_queue_set_paused       (volatile sqlite3 *db, bool paused);
 bool database_queue_set_current_index(volatile sqlite3 *db, int idx);
 bool database_queue_get_current_file (volatile sqlite3 *db, char filepath[PATH_MAX]);
 
-bool database_get_kara_path(volatile sqlite3 *db, int id, char filepath[PATH_MAX]);
+bool database_get_kara_path    (volatile sqlite3 *db, int id, char filepath[PATH_MAX]);
+bool database_get_kara_position(volatile sqlite3 *db, int id, int *pos);
 
 /* Update the database. */
 bool database_update              (volatile sqlite3 *db, const char *kara_dir, int check_timestamp);
diff --git a/src/base/commands.c b/src/base/commands.c
index ca8b9208..049afeb3 100644
--- a/src/base/commands.c
+++ b/src/base/commands.c
@@ -694,6 +694,59 @@ command_set_pos(struct lkt_state *srv, int index)
     return ! MOD_CALL(srv->window_mod, "load", filepath);
 }
 
+bool
+command_seek(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
+{
+    int pos, seconds, err_flag;
+    char *endptr;
+    RETURN_UNLESS(args && args[0] && args[1], "Invalid arguments", false);
+
+    STRTOL(pos, args[0], endptr, err_flag);
+    RETURN_IF(err_flag, "Failed to get a number!", false);
+
+    STRTOL(seconds, args[1], endptr, err_flag);
+    RETURN_IF(err_flag, "Failed to get a number!", false);
+
+    RETURN_UNLESS(command_set_pos(srv, pos), "Failed to get to the right file", false);
+    srv->mpd_idle_events |= MPD_IDLE_PLAYER;
+    return ! MOD_CALL(srv->window_mod, "set_position", seconds);
+}
+
+bool
+command_seekid(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
+{
+    int id, seconds, err_flag, position;
+    char *endptr;
+    RETURN_UNLESS(args && args[0] && args[1], "Invalid arguments", false);
+
+    STRTOL(id, args[0], endptr, err_flag);
+    RETURN_IF(err_flag, "Failed to get a number!", false);
+
+    STRTOL(seconds, args[1], endptr, err_flag);
+    RETURN_IF(err_flag, "Failed to get a number!", false);
+
+    RETURN_UNLESS(database_get_kara_position(srv->db, id, &position),
+                  "Can't find kara in queue", false);
+
+    srv->mpd_idle_events |= MPD_IDLE_PLAYER;
+    RETURN_UNLESS(command_set_pos(srv, position), "Failed to set position to right kara", false);
+    return ! MOD_CALL(srv->window_mod, "set_position", seconds);
+}
+
+bool
+command_seekcur(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
+{
+    int seconds, err_flag;
+    char *endptr;
+    RETURN_UNLESS(args && args[0], "Invalid arguments", false);
+
+    STRTOL(seconds, args[0], endptr, err_flag);
+    RETURN_IF(err_flag, "Failed to get a number!", false);
+
+    srv->mpd_idle_events |= MPD_IDLE_PLAYER;
+    return ! MOD_CALL(srv->window_mod, "set_position", seconds);
+}
+
 bool
 command_plt_add(struct lkt_state *srv, char *args[LKT_MESSAGE_ARGS_MAX])
 {
diff --git a/src/database/queue.c b/src/database/queue.c
index 6ef69d50..635c3644 100644
--- a/src/database/queue.c
+++ b/src/database/queue.c
@@ -85,8 +85,7 @@ error:
 }
 
 bool
-database_queue_current_kara(volatile sqlite3 *db, struct kara_metadata *res,
-                            int *id)
+database_queue_current_kara(volatile sqlite3 *db, struct kara_metadata *res, int *id)
 {
     static const char *SQL_STMT =
         "SELECT song_name, source_name, category, language, author_name, "
@@ -668,6 +667,27 @@ error:
     return false;
 }
 
+bool
+database_get_kara_position(volatile sqlite3 *db, int id, int *pos)
+{
+    sqlite3_stmt *stmt = NULL;
+    static const char *SQL =
+        "WITH content AS ("
+        " SELECT kara.id AS id, position"
+        "  FROM queue"
+        "  JOIN kara ON kara_id = kara.id"
+        "  GROUP BY position ORDER BY position ASC, priority DESC)"
+        "SELECT id, position FROM content WHERE id = ?;";
+    RETURN_UNLESS(db && pos, "Invalid argument", false);
+    SQLITE_PREPARE(db, stmt, SQL, error);
+    SQLITE_BIND_INT(db, stmt, 1, id, error);
+    SQLITE_STEP_ROW(db, stmt, error);
+    *pos = sqlite3_column_int(stmt, 0);
+    return true;
+error:
+    return false;
+}
+
 bool
 database_queue_shuffle(volatile sqlite3 *db)
 {
diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c
index f1169076..67418b9a 100644
--- a/src/module/module_sdl2.c
+++ b/src/module/module_sdl2.c
@@ -235,6 +235,7 @@ REG_ADD_NAMED("load",         mod_load_file)
 REG_ADD_NAMED("set_volume",   mod_set_volume)
 REG_ADD_NAMED("get_duration", mod_get_duration)
 REG_ADD_NAMED("get_elapsed",  mod_get_elapsed)
+/* Add "set_position" */
 REG_END()
 #if ! defined (LKT_STATIC_MODULE)
     REG_EXPORT(sdl2_reg)
diff --git a/src/net/listen.c b/src/net/listen.c
index cffd0fe0..bd2b9811 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -204,6 +204,13 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd)
         else if (STR_MATCH(cmd.name, "shuffle"))
             err = ! command_shuffle(srv, NULL);
 
+        else if (STR_MATCH(cmd.name, "seek"))
+            err = ! command_seek(srv, cmd.args);
+        else if (STR_MATCH(cmd.name, "seekid"))
+            err = ! command_seekid(srv, cmd.args);
+        else if (STR_MATCH(cmd.name, "seekcur"))
+            err = ! command_seekcur(srv, cmd.args);
+
         else if (STR_MATCH(cmd.name, "playlist") || STR_MATCH(cmd.name, "playlistinfo"))
             err = ! command_queue_list(srv, c, cmd.args);
         else if (STR_MATCH(cmd.name, "playlistfind") || STR_MATCH(cmd.name, "playlistsearch"))
-- 
GitLab