Skip to content
Extraits de code Groupes Projets

AMADEUS: Implementation of lkt-lib

Fusionnées Kubat a demandé de fusionner amadeus-rs vers master
Comparer et Afficher la dernière version
16 fichiers
+ 191
551
Comparer les modifications
  • Côte à côte
  • En ligne
Fichiers
16
+ 0
472
#include <lektor/common.h>
#include <lektor/cmd.h>
#include <lektor/config.h>
#include <lektor/segv.h>
#include <lektor/commands.h>
#include <lektor/database.h>
#include <lektor/uri.h>
#include <lektor/internal/dbmacro.h>
#include <signal.h>
/* Static global variables */
static struct queue __queue;
static struct lkt_module __mod_repo = MOD_NULL;
static sqlite3 *__db = NULL;
/* The register */
REG_DECLARE(repo_reg)
REG_BEGIN(luka_reg)
REG_REGISTER("repo", repo_reg)
REG_END()
/* Utility functions */
DESTRUCTOR_FUNCTION
___cleanup_db(void)
{
if (__db == NULL)
return;
static const char *SQL = "UPDATE " LKT_PROTECTED_DATABASE ".misc SET opened = 0;";
SQLITE_EXEC(__db, SQL, error);
LOG_INFO("DB", "Dec open count on db '" LKT_PROTECTED_DATABASE "'");
sqlite3_close(__db);
LOG_INFO("DB", "Database was closed successfully");
__db = NULL;
return;
error:
LOG_ERROR("DB", "Failed to close database");
sqlite3_close(__db);
__db = NULL;
}
DESTRUCTOR_FUNCTION
___cleanup_repo(void)
{
if (__mod_repo.handle != NULL && __mod_repo.data != NULL && __mod_repo.reg != NULL &&
MOD_PROC(__mod_repo, "free")) {
LOG_FATAL("Failed to free module repo");
}
LOG_INFO("REPO", "Module 'repo' was freed successfully");
}
CONSTRUCTOR_FUNCTION
___setup(void)
{
char path[PATH_MAX];
memset(path, 0, sizeof(path));
lkt_queue_new(&__queue);
reg_export(luka_reg);
FAIL_UNLESS(database_new((lkt_db **)&__db), "Can't init sqlite database");
FAIL_IF(config_open(__db, path, PATH_MAX), "Failed to read the config");
database_config_get_text(__db, "database", "db_path", path, PATH_MAX);
FAIL_UNLESS(database_open(__db, path, true), "Can't open database %s", path);
FAIL_IF(atexit(___cleanup_db), "Failed to register cleanup function");
LOG_INFO("SETUP", "Setup of database is OK");
database_config_get_text(__db, "repo", "module", path, PATH_MAX);
LOG_INFO("SETUP", "Try to import repo module from '%s'", path);
reg_import(path, &__mod_repo.reg, &__mod_repo.handle);
FAIL_IF(MOD_CALL(__mod_repo, "new", &__queue, &__db), "Failed to init repo module %s", path);
LOG_INFO("SETUP", "Setup of repo module is OK");
LOG_INFO("SETUP", "Setup phase was successfull");
}
/* Private callbacks and internal functions */
PRIVATE_FUNCTION EXIT_FUNCTION
___handle_queue_and_exit(void)
{
#define __CASE(type, func) \
case LKT_EVENT_##type: \
if (evt.attr != NULL) { \
LOG_DEBUG("EVENT", "Got event " #type " (%d) with attr (int: %d, ptr %0*p)", \
LKT_EVENT_##type, evt.attr, sizeof(size_t) * CHAR_BIT / 4, evt.attr); \
} else \
LOG_DEBUG("EVENT", "Got event " #type " (%d) with NULL attr", LKT_EVENT_##type); \
func; \
break;
lkt_event evt;
size_t update_total = 0;
size_t update_current = 0;
bool is_updating = true;
redo:
if (!is_updating) {
LKT_OUTPUT("GENERAL", "Luka is not updating the database, exiting");
LKT_OUTPUT("GENERAL", "Total update count is %ld out of %ld", update_current, update_total);
exit(EXIT_SUCCESS);
}
evt = lkt_queue_handle(&__queue);
switch (evt.type) {
// clang-format off
__CASE(DB_UPDATE_TOTAL, { update_total += (size_t)evt.attr; })
__CASE(DB_UPDATING, { is_updating = (((uint8_t)(size_t)evt.attr) > 0); })
__CASE(DB_UPDATE_TICK, {
if (update_current != ((size_t)-1))
update_current++;
if (update_current >= update_total)
(LOG_WARN("EVENT", "Force updating state because tikcs exceded the update count"),
is_updating = false, update_total = 0, update_current = 0);
})
__CASE(SKIP_CURRENT, {})
__CASE(PLAY_FILE, {})
__CASE(PLAY_POS, {})
__CASE(PLAY_NEXT, {})
__CASE(PLAY_PREV, {})
__CASE(PLAY_TOGGLE, {})
__CASE(PROP_VOL, {})
__CASE(PROP_DUR, {})
__CASE(PROP_TIME, {})
__CASE(NULL, {})
/* Something went REALLY wrong */
default:
LOG_FATAL("Got unknown type event: %ld", evt.type);
// clang-format on
}
goto redo;
#undef __ATTR_IS_STATE
#undef __CASE
}
PRIVATE_FUNCTION bool
___callback_plt_list(struct lkt_state UNUSED *srv, size_t UNUSED c, const char *plt_name)
{
LKT_OUTPUT("PLT_LIST", "name -> %s", plt_name);
return true;
}
PRIVATE_FUNCTION bool
___callback_list_row(struct lkt_state UNUSED *srv, size_t UNUSED c, int id, int id_len,
const char *sql_row)
{
LKT_OUTPUT("SEARCH", "%*d %s", id_len, id, sql_row);
return true;
}
/* Init database search function */
typedef bool (*___init_search_function)(lkt_db *, struct lkt_search *);
PRIVATE_FUNCTION void
___iter_search(___init_search_function init_function, struct lkt_search *search)
{
size_t continuation = 0, count;
struct lkt_search *new_search = database_search_new_from(search);
redo_search_for_continuation:
FAIL_UNLESS(init_function((lkt_db *)__db, search), "Failed to init search for database");
for (count = 0; database_search_iter(search); ++count)
continue;
if (count) {
continuation += count;
database_search_set_continuation(new_search, continuation);
search = database_search_new_from(new_search);
goto redo_search_for_continuation;
}
else
LOG_WARN("COMMAND", "Nothing found");
}
PRIVATE_FUNCTION EXIT_FUNCTION
___search(struct cmd_args *args, ___init_search_function init_function)
{
long count;
struct lkt_uri *search_uri = lkt_uri_new();
struct lkt_search *search =
database_search_new(NULL, 0, 0, FUNCTION_POINTER(___callback_list_row));
database_search_set_uri(search, search_uri);
database_search_set_name(search, args->argv[0]);
FAIL_UNLESS(NULL == args->argv[0], "Invalid argument");
FAIL_IF(args->argv[0] && !args->argv[1] && (count = strtol(args->argv[0], NULL, 0)),
"Just an id, use the 'search get' command for that");
if (!lkt_uri_from(search_uri, (char **)args->argv)) {
/* Try from idx 1, in case of playlust searches */
LOG_DEBUG(
"COMMAND",
"URI may not starts at idx 0, may be because of playlist search. At idx 0, value was '%s'",
args->argv[0]);
FAIL_UNLESS(lkt_uri_from(search_uri, (char **)&(args->argv[1])),
"Failed to create the uri");
}
database_search_set_uri(search, search_uri);
___iter_search(init_function, search);
lkt_uri_free(search_uri);
exit(EXIT_SUCCESS);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__sigpipe(int sig)
{
LOG_ERROR("GENERAL", "Exit because of signal sigpipe (%d)", sig);
exit(EXIT_FAILURE);
}
/* Command line functions */
PRIVATE_FUNCTION EXIT_FUNCTION __version(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __unref(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin_rescan(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin_populate(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin_update(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin_import(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __admin_config(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __search(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __search_database(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __search_plt(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __search_count(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __search_get(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __plt(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __plt_delete(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __plt_add(struct cmd_args *args);
PRIVATE_FUNCTION EXIT_FUNCTION __plt_list(struct cmd_args *args);
// clang-format off
static struct cmd_opt __options_plt[] = {
{ "delete", __plt_delete },
{ "add", __plt_add },
{ "list", __plt_list },
CMD_OPT_NULL,
};
static struct cmd_opt __options_search[] = {
{ "plt", __search_plt },
{ "count", __search_count },
{ "get", __search_get },
{ "database", __search_database },
CMD_OPT_DEFAULT(__search_database),
};
static struct cmd_opt __options_admin[] = {
{ "rescan", __admin_rescan },
{ "populate", __admin_populate },
{ "update", __admin_update },
{ "import", __admin_import },
{ "config", __admin_config },
CMD_OPT_NULL,
};
static struct cmd_opt __options[] = {
{ "version", __version },
{ "unref", __unref },
{ "search", __search },
{ "search", __plt },
{ "admin", __admin },
CMD_OPT_NULL,
};
// clang-format on
PRIVATE_FUNCTION EXIT_FUNCTION
__version(struct cmd_args UNUSED *args)
{
puts("Lektor version " LKT_VERSION);
exit(EXIT_SUCCESS);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__unref(struct cmd_args UNUSED *args)
{
/* Should be enaugh to clear the inc in the db */
exit(EXIT_SUCCESS);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__search_database(struct cmd_args *args)
{
___search(args, database_search_database_init);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__search_plt(struct cmd_args *args)
{
___search(args, database_search_playlist_init);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__search_count(struct cmd_args UNUSED *args)
{
NOT_IMPLEMENTED;
}
PRIVATE_FUNCTION EXIT_FUNCTION
__search_get(struct cmd_args *args)
{
FAIL_UNLESS(args->argc > 1, "Invalid argument");
struct kara_metadata kara;
char filepath[PATH_MAX];
long id = strtol(args->argv[0], NULL, 0);
double duration = 0.0;
memset(&kara, 0, sizeof(struct kara_metadata));
memset(filepath, 0, sizeof(filepath));
FAIL_UNLESS(database_kara_by_id(__db, (int)id, &kara, filepath), "Failed to find kara %ld", id);
LKT_OUTPUT("SEARCH", "%s - %s / %s - %s%d - %s [%s]", kara.category, kara.language,
kara.source_name, kara.song_type, kara.song_number, kara.song_name,
kara.author_name);
/* Print with the duration */
if (!kara_read_length(&duration, filepath)) {
int s = (int)(duration * 10e-10);
int h = s / 3600;
s = s % 3600;
int m = s / 60;
s = s % 60;
LKT_OUTPUT("SEARCH", "Duration: %d:%02d:%02d", h, m, s);
}
exit(EXIT_SUCCESS);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__plt_delete(struct cmd_args *args)
{
FAIL_UNLESS(args->argc < 1, "Invalid number of arguments");
char *endptr = NULL, err;
long pos;
if (args->argv[1] == NULL)
exit(!database_plt_remove(__db, args->argv[0]));
STRTOL(pos, args->argv[1], endptr, err);
FAIL_IF(err, "STRTOL failed");
exit(!database_plt_remove_pos(__db, args->argv[0], (int)pos));
}
PRIVATE_FUNCTION EXIT_FUNCTION
__plt_add(struct cmd_args *args)
{
struct lkt_uri *uri = lkt_uri_new();
int ret_code = EXIT_FAILURE;
if (args->argc == 1)
exit(!database_plt_create(__db, args->argv[0]));
if (!lkt_uri_from(uri, (char *)args->argv[1])) {
LOG_ERROR("COMMAND", "Failed to get uri");
goto end_plt_add_uri;
}
else if (!database_plt_add_uri(__db, args->argv[0], uri)) {
LOG_ERROR("COMMAND", "Failed to add uri '%s' to playlist", args->argv[1]);
goto end_plt_add_uri;
}
ret_code = EXIT_SUCCESS;
end_plt_add_uri:
lkt_uri_free(uri);
exit(ret_code);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__plt_list(struct cmd_args *args)
{
function_ptr callback = args->argc ? FUNCTION_POINTER(___callback_list_row)
: FUNCTION_POINTER(___callback_plt_list);
struct lkt_uri *null_uri = lkt_uri_new();
struct lkt_search *search = database_search_new(NULL, 0, 0, callback);
database_search_set_name(search, args->argc ? args->argv[0] : NULL);
database_search_set_uri(search, null_uri);
___iter_search(database_search_listplaylist_init, search);
lkt_uri_free(null_uri);
exit(EXIT_SUCCESS);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__admin_rescan(struct cmd_args UNUSED *args)
{
FAIL_IF(MOD_PROC(__mod_repo, "rescan"),
"Failed to call the 'rescan' function from the repo module");
___handle_queue_and_exit();
}
PRIVATE_FUNCTION EXIT_FUNCTION
__admin_populate(struct cmd_args UNUSED *args)
{
__admin_rescan(args);
}
PRIVATE_FUNCTION EXIT_FUNCTION
__admin_update(struct cmd_args UNUSED *args)
{
/* Will need to be copied by the repo module before calling the thread thing */
struct lkt_uri *null_uri = lkt_uri_new();
FAIL_IF(MOD_CALL(__mod_repo, "update", null_uri),
"Failed to call the 'update' function from the repo module");
lkt_uri_free(null_uri);
___handle_queue_and_exit();
}
PRIVATE_FUNCTION EXIT_FUNCTION
__admin_import(struct cmd_args UNUSED *args)
{
FAIL_IF(MOD_PROC(__mod_repo, "import"),
"Failed to call the 'import' function from the repo module");
___handle_queue_and_exit();
}
PRIVATE_FUNCTION EXIT_FUNCTION
__admin_config(struct cmd_args UNUSED *args)
{
fwrite(lkt_default_config_file, sizeof(char), strlen(lkt_default_config_file), stdout);
exit(EXIT_SUCCESS);
}
/* Sub commands functions */
#define __SUB_COMMAND(name) /* Create sub-commands here */ \
PRIVATE_FUNCTION EXIT_FUNCTION __##name(struct cmd_args *args) \
{ \
FAIL_IF(args->argc == 0, "Invalid command, specify a sub command for " #name); \
cmd_parse(__options_##name, args->argc, args->argv); \
}
__SUB_COMMAND(plt)
__SUB_COMMAND(search)
__SUB_COMMAND(admin)
#undef __SUB_COMMAND
/* The main function */
int
main(const int argc, const char **argv)
{
cmd_set_executable_name("luka");
lkt_set_log_level(LOG_LEVEL_ERROR);
lkt_segv_quiet();
lkt_install_segv_handler();
/* Enforce some locals */
RETURN_UNLESS(setlocale(LC_ALL, ""), "Failed to set LC_ALL to UTF-8", 1);
RETURN_UNLESS(setlocale(LC_CTYPE, ""), "Failed to set LC_CTYPE", 1);
RETURN_UNLESS(setlocale(LC_NUMERIC, ""), "Failed to set LC_NUMERIC for mpv", 1);
RETURN_UNLESS(STR_MATCH(nl_langinfo(CODESET), "UTF-8"),
"Your locale is not set to an UTF-8 one. "
"Consider using en_US.UTF-8 or fr_FR.UTF-8!",
1);
if (signal(SIGPIPE, __sigpipe))
LOG_ERROR("SYS", "Failed to install handler for SIGPIPE signal (you may be using php...)");
cmd_parse(__options, argc - 1, argv + 1);
}
Chargement en cours