#include #include #include #include "../../server/common.h" #include "../../liana/list.h" #include "../../codec/packet_ext.h" #include "cmc.h" #include "ui/ui.h" enum { CLI_OPEN_UI = 0, CLI_ADD, CLI_SEARCH }; /* static struct cmc_search *get_search_by_id(struct cmc *c, s32 id) { struct cmc_search *search; al_array_foreach(c->searches, i, search) { if (search->id == id) return search; } return NULL; } */ static void read_list_entry(struct nn_packet *packet, struct cmc_list_entry *entry, u32 id) { entry->id = id; entry->node_id = nn_packet_read_u32(packet); entry->duration = nn_packet_read_u64(packet); entry->start = nn_packet_read_u64(packet); entry->paused_at = nn_packet_read_u64(packet); entry->offset = nn_packet_read_u64(packet); str brief; nn_packet_read_str(packet, &brief); if (!al_str_eq(&entry->brief, &brief)) { al_str_free(&entry->brief); al_str_clone(&entry->brief, &brief); } } static void parse_initial_user_state(struct cmc *c, struct nn_packet *packet) { u32 count = nn_packet_read_u32(packet); for (u32 i = 0; i < count; i++) { struct cmc_search *search = al_alloc_object(struct cmc_search); search->id = nn_packet_read_s32(packet); str s; nn_packet_read_str(packet, &s); al_str_clone(&search->module, &s); nn_packet_read_str(packet, &s); al_str_clone(&search->query, &s); al_array_init(search->pages); al_array_push(c->searches, search); } count = nn_packet_read_u32(packet); for (u32 i = 0; i < count; i++) { struct cmc_list *list = al_alloc_object(struct cmc_list); str s; nn_packet_read_str(packet, &s); al_str_clone(&list->name, &s); list->current = nn_packet_read_s32(packet); list->meta_changed = false; al_array_init(list->entries); u32 inner_count = nn_packet_read_s32(packet); for (u32 j = 0; j < inner_count; j++) { u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry = al_alloc_object(struct cmc_list_entry); read_list_entry(packet, entry, id); if (list->current >= 0 && j == (u32)list->current) { camu_client_request_visual_data(&c->client, entry->node_id); } al_array_push(list->entries, entry); } al_array_push(c->lists, list); } } static struct cmc_list *get_list_by_name(struct cmc *c, str *name) { struct cmc_list *list; al_array_foreach(c->lists, i, list) { if (al_str_eq(&list->name, name)) return list; } return NULL; } static void client_callback(void *userdata, u8 op, void *opaque) { struct cmc *c = (struct cmc *)userdata; // @TODO: String macro. switch (op) { case CAMU_CLIENT_LOGGED_IN: { struct nn_packet *packet = (struct nn_packet *)opaque; switch (c->cli.command) { case CLI_OPEN_UI: parse_initial_user_state(c, packet); if (!cmc_ui_init(&c->ui, &c->loop, c)) { camu_client_disconnect(&c->client); break; } struct cmc_list *list; al_array_foreach(c->lists, i, list) { cmc_list_pane_add_list(&c->ui.lists, list); } //struct cmc_search *search; //al_array_foreach(c->searches, i, search) { // cmc_sp_add_search(&c->ui, search); //} break; case CLI_ADD: { str *arg; al_array_foreach_ptr(c->cli.args, i, arg) { al_str_free(arg); } camu_client_disconnect(&c->client); break; } case CLI_SEARCH: { str *arg; al_array_foreach_ptr(c->cli.args, i, arg) { camu_client_create_search(&c->client, &al_str_c("youtube"), arg); al_str_free(arg); } camu_client_disconnect(&c->client); break; } } break; } case CAMU_CLIENT_SEARCH_CREATED: { struct cmc_search *search = al_alloc_object(struct cmc_search); search->id = *(s32 *)opaque; al_array_init(search->pages); al_array_push(c->searches, search); //cmc_sp_add_search(&c->ui, search); break; } case CAMU_CLIENT_GOT_RESULTS: { break; } case CAMU_CLIENT_GOT_META: { struct nn_packet *packet = (struct nn_packet *)opaque; u8 meta_op = nn_packet_read_u8(packet); str name; nn_packet_read_str(packet, &name); struct cmc_list *list = get_list_by_name(c, &name); list->meta_changed = true; if (!list) break; switch (meta_op) { case LIANA_META_ADDED_ENTRY: { u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry = al_alloc_object(struct cmc_list_entry); read_list_entry(packet, entry, id); al_array_push(list->entries, entry); cmc_ui_queue_render(&c->ui); break; } case LIANA_META_REMOVED_ENTRY: { break; } case LIANA_META_CURRENT_CHANGED: { list->current = nn_packet_read_s32(packet); u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry = al_array_at(list->entries, list->current); read_list_entry(packet, entry, id); camu_client_request_visual_data(&c->client, entry->node_id); cmc_ui_queue_render(&c->ui); break; } case LIANA_META_ORDER_PROBABLY_CHANGED: { struct cmc_list_entry *entry; u32 count = nn_packet_read_u32(packet); for (u32 i = 0; i < count; i++) { u32 id = nn_packet_read_u32(packet); al_array_foreach(list->entries, j, entry) { if (entry->id == id) { if (i != j) SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j)); break; } } } break; } case LIANA_META_ENTRY_PAUSED: case LIANA_META_ENTRY_SEEKED: { u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry; al_array_foreach(list->entries, i, entry) { if (entry->id == id) { read_list_entry(packet, entry, id); break; } } break; } } break; } case CAMU_CLIENT_GOT_VISUAL_DATA: { struct nn_packet *packet = (struct nn_packet *)opaque; u32 node_id = nn_packet_read_u32(packet); struct cmc_list *list = al_array_at(c->lists, 0); struct cmc_list_entry *entry; al_array_foreach(list->entries, i, entry) { if (entry->node_id == node_id) { if (entry->raw.data != NULL) break; nn_packet_read_audio_format(packet, &entry->raw.fmt); u32 size = nn_packet_read_u32(packet); u8 *ptr; NNWT_PACKET_READ_DATA(packet, size, ptr); entry->raw.data = (u8 *)al_malloc(size); al_memcpy(entry->raw.data, ptr, size); entry->raw.sample_count = nn_packet_read_u32(packet); cmc_ui_queue_render(&c->ui); break; } } break; } } } static struct cmc c = { 0 }; static bool parse_command_line(s32 argc, char *argv[]) { if (argc < 2) { c.cli.command = CLI_OPEN_UI; return true; } if (al_str_eq(&al_str_cr(argv[1]), &al_str_c("add"))) { if (argc < 3) return false; c.cli.command = CLI_ADD; char path[PATH_MAX]; str arg; for (s32 i = 2; i < argc; i++) { if (realpath(argv[i], path)) { al_str_clone(&arg, &al_str_cs(path)); al_array_push(c.cli.args, arg); } } } else if (al_str_eq(&al_str_cr(argv[1]), &al_str_c("search"))) { if (argc < 3) return false; c.cli.command = CLI_SEARCH; str query; al_str_from(&query, ""); for (s32 i = 2; i < argc; i++) { al_str_cat(&query, &al_str_cr(argv[i])); if (i != argc - 1) { al_str_cat(&query, &al_str_c(" ")); } } al_array_push(c.cli.args, query); } return true; } s32 main(s32 argc, char *argv[]) { if (!nn_common_init("cmc_main")) { return EXIT_FAILURE; } al_array_init(c.cli.args); if (!parse_command_line(argc, argv)) { goto err; } nn_event_loop_init(&c.loop); al_array_init(c.lists); camu_post_cache_init(&c.cache); al_array_init(c.searches); str user = al_str_c("guest"); char *CMC_USER = getenv("CMC_USER"); if (CMC_USER) user = al_str_cr(CMC_USER); c.client.callback = client_callback; c.client.userdata = &c; if (!camu_client_login(&c.client, &user, &c.loop, NNWT_SOCKET_TCP, &al_str_c("127.0.0.1"), CAMU_PORT)) { goto err; } nn_event_loop_run(&c.loop); cmc_ui_close(&c.ui); nn_common_close(); return EXIT_SUCCESS; err: nn_common_close(); return EXIT_FAILURE; }