Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • d1bb1251272500934416ddb744c5ef9df616c594
  • master par défaut
  • cinch
  • ruby
  • gh-pages
5 résultats

playbot.rb

  • Bifurcation depuis Alexandre MORIGNOT / PlayBot
    Le projet source a une visibilité limitée.
    luka.c 14,87 Kio
    #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);
    }