summaryrefslogtreecommitdiff
path: root/src/fruits/cap
diff options
context:
space:
mode:
Diffstat (limited to 'src/fruits/cap')
-rw-r--r--src/fruits/cap/cap.c219
-rw-r--r--src/fruits/cap/cap.h42
-rw-r--r--src/fruits/cap/meson.build4
3 files changed, 265 insertions, 0 deletions
diff --git a/src/fruits/cap/cap.c b/src/fruits/cap/cap.c
new file mode 100644
index 0000000..2f60cb4
--- /dev/null
+++ b/src/fruits/cap/cap.c
@@ -0,0 +1,219 @@
+#include "cap.h"
+
+#if CAP_USE_PYTHON
+#include "../../shoki/src/search.h"
+#endif
+#include "../../cache/handlers/file.h"
+#ifdef AKIYO_HAS_CURL
+#include "../../cache/handlers/http.h"
+#endif
+
+void cap_init(struct cap_runner *cap, bool (*callback)(void *, u8, str *, str *, void *), void *userdata)
+{
+ cap->callback = callback;
+ cap->userdata = userdata;
+ al_array_init(cap->lists);
+}
+
+static struct cap_list *list_from_name(struct cap_runner *cap, str *name)
+{
+ struct cap_list *list;
+ al_array_foreach_ptr(cap->lists, i, list) {
+ if (al_str_eq(&list->name, name)) {
+ return list;
+ }
+ }
+ return NULL;
+}
+
+static struct cch_entry *entry_for_unique_id(str *unique_id)
+{
+ struct cch_entry *entry = NULL;
+#ifdef AKIYO_HAS_CURL
+ bool is_search = al_str_at(unique_id, 0) == ';';
+ if (is_search || al_str_cmp(unique_id, al_str_c("https://"), 0, 8) == 0 ||
+ al_str_cmp(unique_id, al_str_c("http://"), 0, 7) == 0) {
+#if CAP_USE_PYTHON
+ struct sho_search s;
+ sho_search_init(&s);
+ if (al_str_cmp(unique_id, al_str_c("https://twitter.com"), 0, 19) == 0 ||
+ al_str_cmp(unique_id, al_str_c("https://x.com"), 0, 13) == 0) {
+ str query;
+ al_str_from(&query, "tweet:");
+ al_str_cat(&query, unique_id);
+ if (!sho_search_more_results(&s, al_str_c("twitter"), &query)) {
+ }
+ al_str_free(&query);
+ } else {
+ str query;
+ if (is_search) {
+ //al_str_from(&query, "\"");
+ al_str_from(&query, "");
+ al_str_cat(&query, al_str_substr(unique_id, 1, unique_id->len));
+ //al_str_cat(&query, al_str_c("\""));
+ } else {
+ al_str_from(&query, "link:");
+ al_str_cat(&query, unique_id);
+ }
+ if (!sho_search_more_results(&s, al_str_c("youtube"), &query)) {
+ }
+ }
+ entry = NULL;
+ struct sho_result_page *page;
+ al_array_foreach_ptr(s.result.pages, k, page) {
+ struct sho_post *post;
+ al_array_foreach_ptr(page->posts, i, post) {
+ struct sho_post_media *media;
+ al_array_foreach_ptr(post->media, j, media) {
+ if (media->url.len > 0) {
+ entry = cch_handler_http_create(&media->url);
+ break;
+ }
+ }
+ if (entry) break;
+ }
+ }
+ sho_search_free(&s);
+#else
+ if (!is_search) entry = cch_handler_http_create(unique_id);
+#endif
+ entry->handler->maybe_spawn_worker(entry->handler, 0);
+ } else // {
+#endif
+ entry = cch_handler_file_create(unique_id);
+ // }
+ return entry;
+}
+
+static bool buffer_entry(struct cap_runner *cap, struct cap_list *list, struct cap_list_entry *entry)
+{
+ if (entry->buffer_requested) return true;
+ struct cch_entry *centry = entry_for_unique_id(&entry->unique_id);
+ if (!centry) return false;
+ if (!cap->callback(cap->userdata, CAP_BUFFER, &list->name, &entry->unique_id, centry)) {
+ entry->errored = true;
+ return false;
+ }
+ entry->buffer_requested = true;
+ return true;
+}
+
+static void maybe_cleanup_entries(struct cap_runner *cap, struct cap_list *list)
+{
+ struct cap_list_entry *entry;
+ al_array_foreach_ptr(list->entries, i, entry) {
+ if (entry->buffer_requested && abs((s32)i - list->current) >= 3) {
+ cap->callback(cap->userdata, CAP_UNLOAD, &list->name, &entry->unique_id, NULL);
+ entry->buffer_requested = false;
+ }
+ }
+}
+
+static bool list_skip_internal(struct cap_runner *cap, struct cap_list *list, s32 n);
+
+static void list_pump_internal(struct cap_runner *cap, struct cap_list *list)
+{
+ s32 size = (s32)list->entries.size;
+ if (size <= list->current) return;
+ if (list->set != list->current) {
+ struct cap_list_entry *entry = &al_array_at(list->entries, list->current);
+ if (!buffer_entry(cap, list, entry)) {
+ list_skip_internal(cap, list, 1);
+ return;
+ }
+ list->set = list->current;
+ if (list->swap && entry->buffer_requested) {
+ cap->callback(cap->userdata, CAP_SWAP, &list->name, &entry->unique_id, NULL);
+ } else {
+ cap->callback(cap->userdata, CAP_SET, &list->name, &entry->unique_id, NULL);
+ }
+ }
+ if (list->current + 1 < size) {
+ buffer_entry(cap, list, &al_array_at(list->entries, list->current + 1));
+ }
+ if (list->current - 1 >= 0) {
+ buffer_entry(cap, list, &al_array_at(list->entries, list->current - 1));
+ }
+ maybe_cleanup_entries(cap, list);
+}
+
+bool list_skip_internal(struct cap_runner *cap, struct cap_list *list, s32 n)
+{
+ s32 size = (s32)list->entries.size;
+ if (list->current + n < 0 || list->current + n >= size) {
+ return false;
+ }
+ list->current += n;
+ list_pump_internal(cap, list);
+ return true;
+}
+
+void cap_make_list(struct cap_runner *cap, str *name)
+{
+ struct cap_list list;
+ al_str_clone(&list.name, name);
+ list.current = 0;
+ list.set = -1;
+ list.idle = false;
+ list.swap = false;
+ al_array_init(list.entries);
+ al_array_push(cap->lists, list);
+}
+
+void cap_list_add(struct cap_runner *cap, str *name, str *unique_id)
+{
+ struct cap_list *list = list_from_name(cap, name);
+ if (!list) return;
+ struct cap_list_entry entry;
+ al_str_clone(&entry.unique_id, unique_id);
+ entry.buffer_requested = false;
+ entry.errored = false;
+ al_array_push(list->entries, entry);
+ if (list->idle && list_skip_internal(cap, list, 1)) {
+ list->idle = false;
+ return;
+ }
+ list_pump_internal(cap, list);
+}
+
+bool cap_list_skip(struct cap_runner *cap, str *name, s32 n)
+{
+ struct cap_list *list = list_from_name(cap, name);
+ if (!list) return false;
+ return list_skip_internal(cap, list, n);
+}
+
+void cap_list_pump(struct cap_runner *cap, str *name)
+{
+ struct cap_list *list = list_from_name(cap, name);
+ if (list) list_pump_internal(cap, list);
+}
+
+bool cap_list_set_completed(struct cap_runner *cap, str *name)
+{
+ struct cap_list *list = list_from_name(cap, name);
+ if (!list) return false;
+ list->swap = true;
+ list->idle = !list_skip_internal(cap, list, 1);
+ list->swap = false;
+ return !list->idle;
+}
+
+static void list_close_internal(struct cap_list *list)
+{
+ struct cap_list_entry *entry;
+ al_array_foreach_ptr(list->entries, i, entry) {
+ al_str_free(&entry->unique_id);
+ }
+ al_array_free(list->entries);
+ al_str_free(&list->name);
+}
+
+void cap_close(struct cap_runner *cap)
+{
+ struct cap_list *list;
+ al_array_foreach_ptr(cap->lists, i, list) {
+ list_close_internal(list);
+ }
+ al_array_free(cap->lists);
+}
diff --git a/src/fruits/cap/cap.h b/src/fruits/cap/cap.h
new file mode 100644
index 0000000..d522a82
--- /dev/null
+++ b/src/fruits/cap/cap.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#define CAP_USE_PYTHON 1
+
+#include <al/array.h>
+#include <al/str.h>
+
+struct cap_list_entry {
+ str unique_id;
+ bool buffer_requested;
+ bool errored;
+};
+
+struct cap_list {
+ str name;
+ s32 current;
+ s32 set;
+ bool idle;
+ bool swap;
+ array(struct cap_list_entry) entries;
+};
+
+enum {
+ CAP_BUFFER = 0,
+ CAP_SET,
+ CAP_SWAP,
+ CAP_UNLOAD
+};
+
+struct cap_runner {
+ array(struct cap_list) lists;
+ bool (*callback)(void *, u8, str *, str *, void *);
+ void *userdata;
+};
+
+void cap_init(struct cap_runner *cap, bool (*callback)(void *, u8, str *, str *, void *), void *userdata);
+void cap_make_list(struct cap_runner *cap, str *name);
+void cap_list_add(struct cap_runner *cap, str *name, str *unique_id);
+bool cap_list_skip(struct cap_runner *cap, str *name, s32 n);
+void cap_list_pump(struct cap_runner *cap, str *name);
+bool cap_list_set_completed(struct cap_runner *cap, str *name);
+void cap_close(struct cap_runner *cap);
diff --git a/src/fruits/cap/meson.build b/src/fruits/cap/meson.build
new file mode 100644
index 0000000..b5930ac
--- /dev/null
+++ b/src/fruits/cap/meson.build
@@ -0,0 +1,4 @@
+cap_src = ['cap.c']
+cap_deps = [common_deps, shoki]
+cap = declare_dependency(sources: cap_src,
+ dependencies: cap_deps)