aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xrelease.sh3
-rw-r--r--src/fileutils.c9
-rw-r--r--src/fileutils.h3
-rw-r--r--src/html.c234
-rw-r--r--src/html.h8
-rw-r--r--src/main.c119
-rw-r--r--src/main.h6
-rw-r--r--src/rss.c198
-rw-r--r--src/rss.h2
-rw-r--r--src/rss_html_common.c196
-rw-r--r--src/rss_html_common.h23
11 files changed, 567 insertions, 234 deletions
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..4e817fd
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+RELEASE=1 ./build.sh
diff --git a/src/fileutils.c b/src/fileutils.c
index 4fba0d0..524a603 100644
--- a/src/fileutils.c
+++ b/src/fileutils.c
@@ -25,7 +25,7 @@ int file_get_content(const char *filepath, char **data, long *size) {
FILE *file = fopen(filepath, "rb");
if(!file) {
int err = -errno;
- perror(filepath);
+ /*perror(filepath);*/
return err;
}
@@ -116,11 +116,11 @@ int file_overwrite(const char *filepath, const char *data, size_t size) {
int file_overwrite_in_dir(const char *dir, const char *filename, const char *data, size_t size) {
char filepath[PATH_MAX];
const char *filepath_components[] = { dir, filename };
- path_join(filepath, filepath_components, 2);
+ int filepath_len = path_join(filepath, filepath_components, 2);
char tmp_filepath[PATH_MAX];
strcpy(tmp_filepath, filepath);
- strcat(tmp_filepath, ".tmp");
+ strcpy(tmp_filepath + filepath_len, ".tmp");
int result = file_overwrite(tmp_filepath, data, size);
if(result != 0)
@@ -130,7 +130,7 @@ int file_overwrite_in_dir(const char *dir, const char *filename, const char *dat
return rename(tmp_filepath, filepath);
}
-void path_join(char *output, const char **components, int num_components) {
+int path_join(char *output, const char **components, int num_components) {
int offset = 0;
for(int i = 0; i < num_components; ++i) {
if(i > 0) {
@@ -143,4 +143,5 @@ void path_join(char *output, const char **components, int num_components) {
offset += component_len;
}
output[offset] = '\0';
+ return offset;
}
diff --git a/src/fileutils.h b/src/fileutils.h
index 2b9906c..9f8f4fe 100644
--- a/src/fileutils.h
+++ b/src/fileutils.h
@@ -15,6 +15,7 @@ int create_lock_file(const char *path);
int file_overwrite(const char *filepath, const char *data, size_t size);
int file_overwrite_in_dir(const char *dir, const char *filename, const char *data, size_t size);
-void path_join(char *output, const char **components, int num_components);
+/* Returns the length of the new path (the length of @output) */
+int path_join(char *output, const char **components, int num_components);
#endif
diff --git a/src/html.c b/src/html.c
index 02982d6..3159500 100644
--- a/src/html.c
+++ b/src/html.c
@@ -4,6 +4,7 @@
#include "buffer.h"
#include "stringutils.h"
#include "rss_html_common.h"
+#include "main.h"
#include "json.h"
#include <limits.h>
#include <string.h>
@@ -11,6 +12,8 @@
#include <libgen.h>
#include <signal.h>
#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
#include <assert.h>
static int str_starts_with(const char *str, int len, const char *substr, int substr_len) {
@@ -48,9 +51,38 @@ static int url_extract_domain(const char *url, char *domain, int domain_len) {
return 0;
}
+/*
+ The plugin should print the names and urls of each item (chapter for manga) and the output list should stop when an
+ item matches any item in the input. The output should be sorted from newest to oldest.
+
+ The input should be in this format:
+ [
+ {
+ "title": "Example name",
+ "url": "https://example.com"
+ },
+ {
+ "title": "Another item",
+ "url": "https://another.url.com"
+ }
+ ]
+
+ And the output should be in this format:
+ [
+ {
+ "name": "Example name",
+ "url": "https://example.com"
+ },
+ {
+ "name": "Another item",
+ "url": "https://another.url.com"
+ }
+ ]
+
+ TODO: Rename input "title" to "url", to make input and output match (easier to test with).
+*/
typedef int (*PluginListCallback)(const char *name, const char *url, void *userdata);
-static struct json_value_s* plugin_list(char *plugin_filepath, const char *url, const char *latest, PluginListCallback callback, void *userdata) {
- (void)latest;
+static struct json_value_s* plugin_list(char *plugin_filepath, const char *url, struct json_array_s *downloaded_items, PluginListCallback callback, void *userdata) {
int result;
Buffer buffer;
buffer_init(&buffer);
@@ -65,6 +97,34 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
goto err_cleanup;
}
+ if(downloaded_items) {
+ struct json_value_s downloaded_items_value;
+ downloaded_items_value.payload = downloaded_items;
+ downloaded_items_value.type = json_type_array;
+ size_t json_output_len = 0;
+ void *json_body_str = json_write_minified(&downloaded_items_value, &json_output_len);
+ if(!json_body_str) {
+ fprintf(stderr, "Failed to convert downloaded items to json\n");
+ if(process_id != -1)
+ kill(process_id, SIGKILL);
+ close(stdin_file);
+ close(stdout_file);
+ goto err_cleanup;
+ }
+
+ /* This is a bug in the json library */
+ json_output_len = strlen(json_body_str);
+ if(write(stdin_file, json_body_str, json_output_len) != (ssize_t)json_output_len) {
+ fprintf(stderr, "Failed to write all bytes to plugin list\n");
+ if(process_id != -1)
+ kill(process_id, SIGKILL);
+ close(stdin_file);
+ close(stdout_file);
+ goto err_cleanup;
+ }
+ free(json_body_str);
+ }
+
result = program_wait_until_exit(process_id, stdin_file, stdout_file, program_buffer_write_callback, &buffer);
if(result != 0) {
fprintf(stderr, "Failed to launch plugin list for plugin %s\n", basename(plugin_filepath));
@@ -76,6 +136,7 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
fprintf(stderr, "Failed to load plugin %s list output as json\n", basename(plugin_filepath));
goto err_cleanup;
}
+ buffer_deinit(&buffer);
struct json_array_s *json_root_array = json_value_as_array(json_root);
if(!json_root_array) {
@@ -108,7 +169,6 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
break;
}
- buffer_deinit(&buffer);
return json_root;
err_cleanup:
@@ -123,7 +183,6 @@ typedef struct {
} PluginListUserdata;
static int plugin_list_callback(const char *name, const char *url, void *userdata) {
- /*fprintf(stderr, "name: |%s|, url: |%s|\n", name, url);*/
PluginListUserdata *plugin_list_userdata = userdata;
if(plugin_list_userdata->start_after && strcmp(plugin_list_userdata->start_after, name) == 0) {
plugin_list_userdata->found_start_after = 1;
@@ -133,6 +192,31 @@ static int plugin_list_callback(const char *name, const char *url, void *userdat
return 0;
}
+/* Store result in @plugin_filepath */
+static int get_plugin_filepath(const char *program_dir, const char *plugin_name, char *plugin_filepath) {
+ const char *path_components[] = { program_dir, "plugins" };
+ path_join(plugin_filepath, path_components, 2);
+ if(file_exists(plugin_filepath) != 0) {
+ strcpy(plugin_filepath, "/usr/share/automedia/plugins");
+ if(file_exists(plugin_filepath) != 0) {
+ fprintf(stderr, "Failed to find plugins directory\n");
+ return -1;
+ }
+ }
+
+ strcat(plugin_filepath, "/");
+ strcat(plugin_filepath, plugin_name);
+ if(file_exists(plugin_filepath) != 0) {
+ strcat(plugin_filepath, ".py");
+ if(file_exists(plugin_filepath) != 0) {
+ fprintf(stderr, "Plugin doesn't exist: %s\n", plugin_name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
int add_html(const char *name, const char *url, char *html_config_dir, char *program_dir, const char *start_after) {
int result = 0;
@@ -152,26 +236,9 @@ int add_html(const char *name, const char *url, char *html_config_dir, char *pro
return -1;
}
- const char *path_components[] = { program_dir, "plugins" };
char domain_plugin_path[PATH_MAX];
- path_join(domain_plugin_path, path_components, 2);
- if(file_exists(domain_plugin_path) != 0) {
- strcpy(domain_plugin_path, "/usr/share/automedia/plugins");
- if(file_exists(domain_plugin_path) != 0) {
- fprintf(stderr, "Failed to find plugins directory\n");
- return -1;
- }
- }
-
- strcat(domain_plugin_path, "/");
- strcat(domain_plugin_path, domain);
- if(file_exists(domain_plugin_path) != 0) {
- strcat(domain_plugin_path, ".py");
- if(file_exists(domain_plugin_path) != 0) {
- fprintf(stderr, "Plugin doesn't exist: %s\n", domain);
- return -1;
- }
- }
+ if(get_plugin_filepath(program_dir, domain, domain_plugin_path) != 0)
+ return -1;
PluginListUserdata plugin_list_userdata;
plugin_list_userdata.start_after = start_after;
@@ -255,3 +322,124 @@ int add_html(const char *name, const char *url, char *html_config_dir, char *pro
free(json_root);
return result;
}
+
+static int plugin_list_sync_callback(const char *name, const char *url, void *userdata) {
+ Buffer *download_items_buffer = userdata;
+ DownloadItemsData download_items_data;
+ download_items_data.title = name;
+ download_items_data.link = url;
+ buffer_append(download_items_buffer, &download_items_data, sizeof(download_items_data));
+ return 0;
+}
+
+static int download_html_items_in_reverse(const char *plugin_filepath, Buffer *download_items_buffer, TrackedHtml *tracked_html, char *html_tracked_dir, const char *download_dir) {
+ int result = 0;
+ DownloadItemsData *added_download_items[MAX_UPDATE_ITEMS];
+
+ char item_dir[PATH_MAX];
+ const char *path_components[] = { download_dir, tracked_html->title };
+ int item_dir_len = path_join(item_dir, path_components, 2);
+
+ Buffer json_element_buffer;
+ buffer_init(&json_element_buffer);
+
+ DownloadItemsData *download_items_it = buffer_end(download_items_buffer);
+ DownloadItemsData *download_items_end = buffer_begin(download_items_buffer);
+ download_items_it--;
+ download_items_end--;
+ int download_item_index = 0;
+ for(; download_items_it != download_items_end && download_item_index < MAX_UPDATE_ITEMS && is_program_running(); --download_items_it) {
+ item_dir[item_dir_len] = '/';
+ strcpy(item_dir + item_dir_len + 1, download_items_it->title);
+ if(create_directory_recursive(item_dir) != 0) {
+ fprintf(stderr, "Failed to create directory for html item: %s\n", download_items_it->title);
+ result = -1;
+ break;
+ }
+
+ /* TODO: Make asynchronous */
+ const char *args[] = { plugin_filepath, "download", download_items_it->link, NULL };
+ result = program_exec(args, NULL, NULL);
+ if(result != 0)
+ fprintf(stderr, "Failed while downloading html, url: %s\n", download_items_it->link);
+
+ char notify_msg[PATH_MAX];
+ const char *path_components[] = { tracked_html->title, download_items_it->title };
+ path_join(notify_msg, path_components, 2);
+ const char *notify_args[] = { "notify-send", "-u", result == 0 ? "normal" : "critical", "--", result == 0 ? "Download finished" : "Download failed", notify_msg, NULL };
+ program_exec(notify_args, NULL, NULL);
+
+ if(result != 0)
+ break;
+
+ added_download_items[download_item_index] = download_items_it;
+ ++download_item_index;
+ }
+
+ TrackedItem tracked_item;
+ tracked_item.title = tracked_html->title;
+ tracked_item.link = tracked_html->link;
+ tracked_item.json_data = tracked_html->json_data;
+ result = tracked_item_update_latest(&tracked_item, html_tracked_dir, added_download_items, NULL, download_item_index);
+
+ buffer_deinit(&json_element_buffer);
+ return result;
+}
+
+/* TODO: Make asynchronous. Right now this will only complete when the whole chapter download completes */
+int sync_html(TrackedHtml *tracked_html, char *program_dir, const char *download_dir, char *html_config_dir) {
+ /* TODO: This can be cached */
+ int html_config_dir_len = strlen(html_config_dir);
+
+ fprintf(stderr, "Syncing %s\n", tracked_html->title);
+
+ char plugin_filepath[PATH_MAX];
+ /* This will check with ${tracked_html->plugin}.py as well, but that is fine */
+ if(get_plugin_filepath(program_dir, tracked_html->plugin, plugin_filepath) != 0)
+ return -1;
+
+ struct json_value_s *downloaded_items = json_object_get_field_by_name(tracked_html->json_data, "downloaded");
+ struct json_array_s *downloaded_items_array = NULL;
+ if(downloaded_items) {
+ downloaded_items_array = json_value_as_array(downloaded_items);
+ if(!downloaded_items_array) {
+ fprintf(stderr, "Corrupt json for html item: %s\n", tracked_html->title);
+ return -1;
+ }
+ }
+
+ Buffer download_items_buffer;
+ buffer_init(&download_items_buffer);
+
+ int result = 0;
+
+ struct json_value_s *json_root = plugin_list(plugin_filepath, tracked_html->link, downloaded_items_array, plugin_list_sync_callback, &download_items_buffer);
+ if(!json_root) {
+ result = -1;
+ goto cleanup;
+ }
+
+ char *html_tracked_dir = html_config_dir;
+ strcat(html_tracked_dir, "/tracked/");
+
+ result = download_html_items_in_reverse(plugin_filepath, &download_items_buffer, tracked_html, html_tracked_dir, download_dir);
+ if(result != 0) {
+ fprintf(stderr, "Failed while download html item for url: %s\n", tracked_html->link);
+ goto cleanup;
+ }
+
+ char updated[32];
+ sprintf(updated, "%ld", time(NULL));
+ strcat(html_tracked_dir, tracked_html->title);
+ result = file_overwrite_in_dir(html_tracked_dir, "synced", updated, strlen(updated));
+ if(result != 0) {
+ fprintf(stderr, "Failed to update %s/synced\n", html_tracked_dir);
+ goto cleanup;
+ }
+
+ cleanup:
+ free(json_root);
+ buffer_deinit(&download_items_buffer);
+ html_config_dir[html_config_dir_len] = '\0';
+ return result;
+}
diff --git a/src/html.h b/src/html.h
index 02c0f7b..987f281 100644
--- a/src/html.h
+++ b/src/html.h
@@ -1,6 +1,14 @@
#ifndef HTML_H
#define HTML_H
+typedef struct {
+ char *plugin;
+ char *title;
+ char *link;
+ struct json_object_s *json_data;
+} TrackedHtml;
+
int add_html(const char *name, const char *url, char *html_config_dir, char *program_dir, const char *start_after);
+int sync_html(TrackedHtml *tracked_html, char *program_dir, const char *download_dir, char *html_config_dir);
#endif
diff --git a/src/main.c b/src/main.c
index bb8114a..e8ff068 100644
--- a/src/main.c
+++ b/src/main.c
@@ -258,29 +258,35 @@ static void command_add(int argc, char **argv, char *rss_config_dir, char *html_
}
}
-sig_atomic_t running = 0;
+sig_atomic_t automedia_running = 0;
static void automedia_pid_signal_handler(int signum) {
(void)signum;
- running = 0;
+ automedia_running = 0;
}
-static void sync_tracked_rss(TransmissionSession *transmission_session, char *rss_config_dir) {
- char rss_tracked_dir[PATH_MAX];
- strcpy(rss_tracked_dir, rss_config_dir);
- strcat(rss_tracked_dir, "/tracked");
+int is_program_running() {
+ return automedia_running;
+}
+
+/* plugin is NULL for rss */
+typedef int (*IterateTrackedItemCallback)(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata);
+static void iterate_tracked_items(char *config_dir, IterateTrackedItemCallback iterate_callback, void *userdata) {
+ char tracked_dir[PATH_MAX];
+ strcpy(tracked_dir, config_dir);
+ strcat(tracked_dir, "/tracked");
struct dirent *dir;
- DIR *d = opendir(rss_tracked_dir);
+ DIR *d = opendir(tracked_dir);
if(!d) {
- fprintf(stderr, "Failed to open directory: %s\n", rss_tracked_dir);
+ fprintf(stderr, "Failed to open directory: %s\n", tracked_dir);
return;
}
- char *item_filepath = rss_tracked_dir;
+ char *item_filepath = tracked_dir;
strcat(item_filepath, "/");
int item_filepath_len = strlen(item_filepath);
- while((dir = readdir(d)) != NULL && running) {
+ while((dir = readdir(d)) != NULL && automedia_running) {
int title_len = strlen(dir->d_name);
if((title_len == 1 && dir->d_name[0] == '.') || (title_len == 2 && dir->d_name[0] == '.' && dir->d_name[1] == '.'))
continue;
@@ -293,49 +299,91 @@ static void sync_tracked_rss(TransmissionSession *transmission_session, char *rs
continue;
}
- strcpy(item_filepath + item_filepath_len + title_len, "/link");
char *link_file_content = NULL;
+ char *data_file_content = NULL;
+ char *plugin_file_content = NULL;
+ struct json_value_s *json_data = NULL;
+
+ strcpy(item_filepath + item_filepath_len + title_len, "/link");
long link_file_size = 0;
int has_link = file_get_content(item_filepath, &link_file_content, &link_file_size);
strcpy(item_filepath + item_filepath_len + title_len, "/data");
- char *data_file_content = NULL;
long data_file_size = 0;
int has_data = file_get_content(item_filepath, &data_file_content, &data_file_size);
+ strcpy(item_filepath + item_filepath_len + title_len, "/plugin");
+ long plugin_file_size = 0;
+ file_get_content(item_filepath, &plugin_file_content, &plugin_file_size);
+
if(has_link != 0 || has_data != 0) {
- free(link_file_content);
- free(data_file_content);
fprintf(stderr, "Rss corrupt, link or data missing for rss %s\n", dir->d_name);
- continue;
+ goto cleanup_item;
}
- struct json_value_s *json_data = json_parse(data_file_content, data_file_size);
- free(data_file_content);
+ json_data = json_parse(data_file_content, data_file_size);
if(!json_data || json_data->type != json_type_object) {
- free(link_file_content);
- free(json_data);
fprintf(stderr, "Rss corrupt for %s\n", dir->d_name);
- continue;
+ goto cleanup_item;
}
+ free(data_file_content);
+ data_file_content = NULL;
- TrackedRss tracked_rss;
- tracked_rss.title = dir->d_name;
- tracked_rss.link = link_file_content;
- tracked_rss.json_data = json_value_as_object(json_data);
- if(sync_rss(transmission_session, &tracked_rss, rss_config_dir) != 0)
+ if(iterate_callback(dir->d_name, link_file_content, plugin_file_content, config_dir, json_value_as_object(json_data), userdata) != 0)
fprintf(stderr, "Failed to sync %s\n", dir->d_name);
- free(link_file_content);
+ cleanup_item:
free(json_data);
+ free(plugin_file_content);
+ free(data_file_content);
+ free(link_file_content);
}
closedir(d);
}
-static void sync_rss_html(char *rss_config_dir, char *html_config_dir, const char *download_dir, int sync_rate_sec) {
- (void)html_config_dir;
+static int iterate_tracked_item_rss_callback(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata) {
+ (void)plugin;
+ TransmissionSession *transmission_session = userdata;
+ TrackedRss tracked_rss;
+ tracked_rss.title = title;
+ tracked_rss.link = link;
+ tracked_rss.json_data = json_data;
+ return sync_rss(&tracked_rss, transmission_session, config_dir);
+}
+
+typedef struct {
+ char *program_dir;
+ const char *download_dir;
+} IterateHtmlItemUserdata;
+
+static int iterate_tracked_item_html_callback(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata) {
+ if(!plugin) {
+ fprintf(stderr, "Missing plugin name for html item: %s\n", title);
+ return -1;
+ }
+
+ IterateHtmlItemUserdata *iterate_html_item_userdata = userdata;
+ TrackedHtml tracked_html;
+ tracked_html.plugin = plugin;
+ tracked_html.title = title;
+ tracked_html.link = link;
+ tracked_html.json_data = json_data;
+ return sync_html(&tracked_html, iterate_html_item_userdata->program_dir, iterate_html_item_userdata->download_dir, config_dir);
+}
+
+static void sync_tracked_rss(TransmissionSession *transmission_session, char *rss_config_dir) {
+ iterate_tracked_items(rss_config_dir, iterate_tracked_item_rss_callback, transmission_session);
+}
+
+static void sync_tracked_html(char *html_config_dir, char *program_dir, const char *download_dir) {
+ IterateHtmlItemUserdata iterate_html_item_userdata;
+ iterate_html_item_userdata.program_dir = program_dir;
+ iterate_html_item_userdata.download_dir = download_dir;
+ iterate_tracked_items(html_config_dir, iterate_tracked_item_html_callback, &iterate_html_item_userdata);
+}
+static void sync_rss_html(char *rss_config_dir, char *html_config_dir, char *program_dir, const char *download_dir, int sync_rate_sec) {
if(transmission_is_daemon_running() != 0) {
if(transmission_start_daemon(download_dir) != 0) {
fprintf(stderr, "Failed to start torrent daemon\n");
@@ -349,11 +397,14 @@ static void sync_rss_html(char *rss_config_dir, char *html_config_dir, const cha
exit(2);
}
- running = 1;
+ automedia_running = 1;
/* running is set to 0 in SIGINT signal handler (ctrl+c) */
- while(running) {
+ while(automedia_running) {
sync_tracked_rss(&transmission_session, rss_config_dir);
- if(running)
+ sync_tracked_html(html_config_dir, program_dir, download_dir);
+
+ /* TODO: Show finished html/rss items */
+ if(automedia_running)
sleep(sync_rate_sec);
}
}
@@ -371,7 +422,7 @@ static int cmdline_contains_str(const char *cmdline, const char *str) {
}
}
-static void command_sync(int argc, char **argv, char *rss_config_dir, char *html_config_dir) {
+static void command_sync(int argc, char **argv, char *rss_config_dir, char *html_config_dir, char *program_dir) {
if(argc < 1)
usage_sync();
@@ -435,7 +486,7 @@ static void command_sync(int argc, char **argv, char *rss_config_dir, char *html
close(pid_file);
const int sync_rate_sec = 15 * 60; /* every 15 min */
- sync_rss_html(rss_config_dir, html_config_dir, download_dir, sync_rate_sec);
+ sync_rss_html(rss_config_dir, html_config_dir, program_dir, download_dir, sync_rate_sec);
remove(automedia_pid_path);
}
@@ -483,7 +534,7 @@ int main(int argc, char **argv) {
if(strcmp(command, "add") == 0) {
command_add(argc - 2, argv + 2, rss_config_dir, html_config_dir, dirname(argv[0]));
} else if(strcmp(command, "sync") == 0) {
- command_sync(argc - 2, argv + 2, rss_config_dir, html_config_dir);
+ command_sync(argc - 2, argv + 2, rss_config_dir, html_config_dir, dirname(argv[0]));
} else if(strcmp(command, "downloaded") == 0) {
command_downloaded(rss_config_dir, html_config_dir);
} else {
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..5312063
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,6 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+int is_program_running();
+
+#endif
diff --git a/src/rss.c b/src/rss.c
index 005bd03..90b3ed6 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -6,6 +6,7 @@
#include "buffer.h"
#include "rss_html_common.h"
#include "json.h"
+#include "alloc.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -297,11 +298,6 @@ static int is_item_already_downloaded(const char *title, const char *link, Track
}
typedef struct {
- const char *title;
- const char *link;
-} DownloadItemsData;
-
-typedef struct {
TrackedRss *tracked_rss;
Buffer *download_items_buffer;
} RssParseSyncData;
@@ -318,186 +314,46 @@ static int rss_parse_sync_callback(char *title, char *link, void *userdata) {
return 0;
}
-static struct json_array_element_s* get_last_element_in_json_array(struct json_array_s *json_array) {
- struct json_array_element_s *json_element = json_array->start;
- while(json_element) {
- struct json_array_element_s *next_json_element = json_element->next;
- if(next_json_element)
- json_element = next_json_element;
- else
- return json_element;
- }
- return NULL;
-}
-
-/* TODO: If this fails in the middle, recover and update this next time somehow */
-static int rss_update_latest(char *rss_tracked_dir, TrackedRss *tracked_rss, const char *latest_title, const char *url, const char *filename) {
- int rss_tracked_dir_len = strlen(rss_tracked_dir);
+static int add_torrents_in_reverse(TransmissionSession *transmission_session, Buffer *download_items_buffer, TrackedRss *tracked_rss, char *rss_tracked_dir) {
int result = 0;
+ char *torrent_names[MAX_UPDATE_ITEMS];
+ DownloadItemsData *added_download_items[MAX_UPDATE_ITEMS];
- char *item_filepath = rss_tracked_dir;
- strcat(item_filepath, tracked_rss->title);
-
- char updated[32];
- assert(sizeof(time_t) == sizeof(long));
- sprintf(updated, "%ld", time(NULL));
- int updated_len = strlen(updated);
- result = file_overwrite_in_dir(item_filepath, "updated", updated, updated_len);
- if(result != 0) {
- fprintf(stderr, "Failed to update %s/updated\n", item_filepath);
- goto cleanup;
- }
-
- struct json_string_s *updated_json = json_value_as_string(json_object_get_field_by_name(tracked_rss->json_data, "updated"));
- updated_json->string = updated;
- updated_json->string_size = updated_len;
-
- struct json_value_s *downloaded_json = json_object_get_field_by_name(tracked_rss->json_data, "downloaded");
- /* TODO:; WHAT IF DJSONWLOADING JSON DOENS*T SHIT */
-
- struct json_string_s title_json_key;
- create_json_string(&title_json_key, "title", 5);
-
- struct json_string_s title_json_value_str;
- create_json_string(&title_json_value_str, latest_title, strlen(latest_title));
- struct json_value_s title_json_value;
- init_json_value_str(&title_json_value, &title_json_value_str);
-
- struct json_string_s filename_json_key;
- create_json_string(&filename_json_key, "filename", 8);
-
- struct json_string_s filename_json_value_str;
- create_json_string(&filename_json_value_str, filename, strlen(filename));
- struct json_value_s filename_json_value;
- init_json_value_str(&filename_json_value, &filename_json_value_str);
-
- struct json_string_s time_json_key;
- create_json_string(&time_json_key, "time", 4);
-
- struct json_string_s time_value_str;
- create_json_string(&time_value_str, updated, updated_len);
- struct json_value_s time_json_value;
- init_json_value_str(&time_json_value, &time_value_str);
-
- struct json_string_s url_json_key;
- create_json_string(&url_json_key, "url", 3);
-
- struct json_string_s url_value_str;
- create_json_string(&url_value_str, url, strlen(url));
- struct json_value_s url_json_value;
- init_json_value_str(&url_json_value, &url_value_str);
-
- struct json_object_element_s downloaded_title_element;
- downloaded_title_element.name = &title_json_key;
- downloaded_title_element.value = &title_json_value;
-
- struct json_object_element_s downloaded_filename_element;
- downloaded_filename_element.name = &filename_json_key;
- downloaded_filename_element.value = &filename_json_value;
-
- struct json_object_element_s downloaded_time_element;
- downloaded_time_element.name = &time_json_key;
- downloaded_time_element.value = &time_json_value;
-
- struct json_object_element_s downloaded_url_element;
- downloaded_url_element.name = &url_json_key;
- downloaded_url_element.value = &url_json_value;
-
- downloaded_title_element.next = &downloaded_filename_element;
- downloaded_filename_element.next = &downloaded_time_element;
- downloaded_time_element.next = &downloaded_url_element;
- downloaded_url_element.next = NULL;
-
- struct json_object_s new_downloaded_json_obj;
- new_downloaded_json_obj.length = 4;
- new_downloaded_json_obj.start = &downloaded_title_element;
-
- struct json_value_s new_downloaded_json_val;
- new_downloaded_json_val.payload = &new_downloaded_json_obj;
- new_downloaded_json_val.type = json_type_object;
-
- struct json_array_element_s new_downloaded_item_element;
- new_downloaded_item_element.value = &new_downloaded_json_val;
- new_downloaded_item_element.next = NULL;
+ Buffer json_element_buffer;
+ buffer_init(&json_element_buffer);
- struct json_array_s new_downloaded_array;
- struct json_value_s new_downloaded_array_val;
- new_downloaded_array_val.payload = &new_downloaded_array;
- new_downloaded_array_val.type = json_type_array;
-
- struct json_string_s downloaded_json_key;
- create_json_string(&downloaded_json_key, "downloaded", 10);
-
- struct json_object_element_s new_downloaded_array_obj_el;
- new_downloaded_array_obj_el.name = &downloaded_json_key;
- new_downloaded_array_obj_el.value = &new_downloaded_array_val;
-
- if(downloaded_json && downloaded_json->type == json_type_array) {
- struct json_array_s *downloaded_json_array = json_value_as_array(downloaded_json);
- struct json_array_element_s *last_downloaded_element = get_last_element_in_json_array(downloaded_json_array);
- if(last_downloaded_element)
- last_downloaded_element->next = &new_downloaded_item_element;
- else
- downloaded_json_array->start = &new_downloaded_item_element;
- downloaded_json_array->length++;
- } else {
- new_downloaded_array.start = &new_downloaded_item_element;
- new_downloaded_array.length = 1;
-
- struct json_object_element_s *prev_start = tracked_rss->json_data->start;
- tracked_rss->json_data->start = &new_downloaded_array_obj_el;
- new_downloaded_array_obj_el.next = prev_start;
- tracked_rss->json_data->length++;
- }
-
- struct json_value_s json_root_value;
- json_root_value.payload = tracked_rss->json_data;
- json_root_value.type = json_type_object;
-
- size_t json_body_size = 0;
- char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size);
- if(!json_body_str) {
- fprintf(stderr, "Failed to write json data to file %s/data\n", item_filepath);
- result = -1;
- goto cleanup;
- }
-
- /* Workaround json bug (?) */
- json_body_size = strlen(json_body_str);
-
- result = file_overwrite_in_dir(item_filepath, "data", json_body_str, json_body_size);
- free(json_body_str);
-
- cleanup:
- rss_tracked_dir[rss_tracked_dir_len] = '\0';
- return result;
-}
-
-static int add_torrents_in_reverse(TransmissionSession *transmission_session, Buffer *download_items_buffer, TrackedRss *tracked_rss, char *rss_tracked_dir) {
DownloadItemsData *download_items_it = buffer_end(download_items_buffer);
DownloadItemsData *download_items_end = buffer_begin(download_items_buffer);
download_items_it--;
download_items_end--;
- for(; download_items_it != download_items_end; --download_items_it) {
- char *torrent_name;
- if(transmission_add_torrent(transmission_session, download_items_it->link, &torrent_name) != 0) {
+ int torrent_name_index = 0;
+ for(; download_items_it != download_items_end && torrent_name_index < MAX_UPDATE_ITEMS; --download_items_it) {
+ if(transmission_add_torrent(transmission_session, download_items_it->link, &torrent_names[torrent_name_index]) != 0) {
fprintf(stderr, "Failed to add torrent: %s\n", download_items_it->link);
- return 1;
+ result = -1;
+ break;
}
+ added_download_items[torrent_name_index] = download_items_it;
+ ++torrent_name_index;
+ fprintf(stderr, "Starting download of torrent: %s (title: %s)\n", download_items_it->link, download_items_it->title);
+ /* Show notification that download has started? */
+ }
- if(rss_update_latest(rss_tracked_dir, tracked_rss, download_items_it->title, download_items_it->link, torrent_name) != 0) {
- free(torrent_name);
- fprintf(stderr, "Failed to update rss tracked data for %s\n", download_items_it->title);
- return 1;
- }
+ TrackedItem tracked_item;
+ tracked_item.title = tracked_rss->title;
+ tracked_item.link = tracked_rss->link;
+ tracked_item.json_data = tracked_rss->json_data;
+ result = tracked_item_update_latest(&tracked_item, rss_tracked_dir, added_download_items, torrent_names, torrent_name_index);
- free(torrent_name);
- /* Show notification that download has started? */
+ for(int i = 0; i < torrent_name_index; ++i) {
+ free(torrent_names[torrent_name_index]);
}
- return 0;
+
+ buffer_deinit(&json_element_buffer);
+ return result;
}
-int sync_rss(TransmissionSession *transmission_session, TrackedRss *tracked_rss, char *rss_config_dir) {
+int sync_rss(TrackedRss *tracked_rss, TransmissionSession *transmission_session, char *rss_config_dir) {
/* TODO: This can be cached */
int rss_config_dir_len = strlen(rss_config_dir);
diff --git a/src/rss.h b/src/rss.h
index d5fa548..1233ba4 100644
--- a/src/rss.h
+++ b/src/rss.h
@@ -11,6 +11,6 @@ typedef struct {
} TrackedRss;
int add_rss(const char *name, const char *url, char *rss_config_dir, const char *start_after);
-int sync_rss(struct TransmissionSession *transmission_session, TrackedRss *tracked_rss, char *rss_config_dir);
+int sync_rss(TrackedRss *tracked_rss, struct TransmissionSession *transmission_session, char *rss_config_dir);
#endif
diff --git a/src/rss_html_common.c b/src/rss_html_common.c
index f1aa7ad..ba4682b 100644
--- a/src/rss_html_common.c
+++ b/src/rss_html_common.c
@@ -3,6 +3,8 @@
#include "fileutils.h"
#include <string.h>
#include <stdio.h>
+#include <time.h>
+#include <assert.h>
int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name) {
struct json_string_s title_json_key;
@@ -147,6 +149,200 @@ int write_plugin_json_to_file(const char *dir, const char *filename, const char
return result;
}
+static struct json_array_element_s* get_last_element_in_json_array(struct json_array_s *json_array) {
+ struct json_array_element_s *json_element = json_array->start;
+ while(json_element) {
+ struct json_array_element_s *next_json_element = json_element->next;
+ if(next_json_element)
+ json_element = next_json_element;
+ else
+ return json_element;
+ }
+ return NULL;
+}
+
+/* TODO: If this fails in the middle, recover and update the next time somehow */
+int tracked_item_update_latest(TrackedItem *tracked_item, char *tracked_dir, DownloadItemsData **download_items, char **filenames, int num_download_items) {
+ assert(num_download_items <= MAX_UPDATE_ITEMS);
+ int tracked_dir_len = strlen(tracked_dir);
+ int result = 0;
+
+ char *item_filepath = tracked_dir;
+ strcat(item_filepath, tracked_item->title);
+
+ char updated[32];
+ assert(sizeof(time_t) == sizeof(long));
+ sprintf(updated, "%ld", time(NULL));
+ int updated_len = strlen(updated);
+ result = file_overwrite_in_dir(item_filepath, "updated", updated, updated_len);
+ if(result != 0) {
+ fprintf(stderr, "Failed to update %s/updated\n", item_filepath);
+ goto cleanup;
+ }
+
+ struct json_value_s *updated_field = json_object_get_field_by_name(tracked_item->json_data, "updated");
+ if(updated_field) {
+ struct json_string_s *updated_json = json_value_as_string(updated_field);
+ updated_json->string = updated;
+ updated_json->string_size = updated_len;
+ } else {
+ fprintf(stderr, "Corrupt json for rss item: %s\n", item_filepath);
+ }
+
+ struct json_value_s *downloaded_json = json_object_get_field_by_name(tracked_item->json_data, "downloaded");
+ struct json_array_s *downloaded_json_array = NULL;
+
+ struct json_array_s new_downloaded_array;
+ new_downloaded_array.length = 1;
+ new_downloaded_array.start = NULL;
+
+ struct json_value_s new_downloaded_array_val;
+ new_downloaded_array_val.payload = &new_downloaded_array;
+ new_downloaded_array_val.type = json_type_array;
+
+ struct json_string_s downloaded_json_key;
+ create_json_string(&downloaded_json_key, "downloaded", 10);
+
+ struct json_object_element_s new_downloaded_array_obj_el;
+ new_downloaded_array_obj_el.name = &downloaded_json_key;
+ new_downloaded_array_obj_el.value = &new_downloaded_array_val;
+
+ if(downloaded_json) {
+ downloaded_json_array = json_value_as_array(downloaded_json);
+ if(!downloaded_json_array) {
+ fprintf(stderr, "Corrupt json for rss item: %s\n", item_filepath);
+ result = -1;
+ goto cleanup;
+ }
+ } else {
+ downloaded_json_array = &new_downloaded_array;
+ }
+
+ struct json_string_s title_json_key;
+ create_json_string(&title_json_key, "title", 5);
+
+ struct json_string_s filename_json_key;
+ create_json_string(&filename_json_key, "filename", 8);
+
+ struct json_string_s time_json_key;
+ create_json_string(&time_json_key, "time", 4);
+
+ struct json_string_s url_json_key;
+ create_json_string(&url_json_key, "url", 3);
+
+ struct json_string_s title_json_value_str[MAX_UPDATE_ITEMS];
+ struct json_value_s title_json_value[MAX_UPDATE_ITEMS];
+ struct json_string_s filename_json_value_str[MAX_UPDATE_ITEMS];
+ struct json_value_s filename_json_value[MAX_UPDATE_ITEMS];
+ struct json_string_s time_value_str[MAX_UPDATE_ITEMS];
+ struct json_value_s time_json_value[MAX_UPDATE_ITEMS];
+ struct json_string_s url_value_str[MAX_UPDATE_ITEMS];
+ struct json_value_s url_json_value[MAX_UPDATE_ITEMS];
+
+ struct json_object_element_s downloaded_title_element[MAX_UPDATE_ITEMS];
+ struct json_object_element_s downloaded_filename_element[MAX_UPDATE_ITEMS];
+ struct json_object_element_s downloaded_time_element[MAX_UPDATE_ITEMS];
+ struct json_object_element_s downloaded_url_element[MAX_UPDATE_ITEMS];
+
+ struct json_object_s new_downloaded_json_obj[MAX_UPDATE_ITEMS];
+ struct json_value_s new_downloaded_json_val[MAX_UPDATE_ITEMS];
+ struct json_array_element_s new_downloaded_item_element[MAX_UPDATE_ITEMS];
+
+ struct json_array_element_s *last_downloaded_element = NULL;
+ for(int i = 0; i < num_download_items; ++i) {
+ create_json_string(&title_json_value_str[i], download_items[i]->title, strlen(download_items[i]->title));
+ init_json_value_str(&title_json_value[i], &title_json_value_str[i]);
+
+ if(filenames) {
+ create_json_string(&filename_json_value_str[i], filenames[i], strlen(filenames[i]));
+ init_json_value_str(&filename_json_value[i], &filename_json_value_str[i]);
+ }
+
+ create_json_string(&time_value_str[i], updated, updated_len);
+ init_json_value_str(&time_json_value[i], &time_value_str[i]);
+
+ create_json_string(&url_value_str[i], download_items[i]->link, strlen(download_items[i]->link));
+ init_json_value_str(&url_json_value[i], &url_value_str[i]);
+
+ downloaded_title_element[i].name = &title_json_key;
+ downloaded_title_element[i].value = &title_json_value[i];
+
+ if(filenames) {
+ downloaded_filename_element[i].name = &filename_json_key;
+ downloaded_filename_element[i].value = &filename_json_value[i];
+ }
+
+ downloaded_time_element[i].name = &time_json_key;
+ downloaded_time_element[i].value = &time_json_value[i];
+
+ downloaded_url_element[i].name = &url_json_key;
+ downloaded_url_element[i].value = &url_json_value[i];
+
+ downloaded_title_element[i].next = &downloaded_time_element[i];
+ downloaded_time_element[i].next = &downloaded_url_element[i];
+ downloaded_url_element[i].next = NULL;
+ if(filenames) {
+ downloaded_url_element[i].next = &downloaded_filename_element[i];
+ downloaded_filename_element[i].next = NULL;
+ new_downloaded_json_obj[i].length = 4;
+ } else {
+ new_downloaded_json_obj[i].length = 3;
+ }
+
+ new_downloaded_json_obj[i].start = &downloaded_title_element[i];
+
+ new_downloaded_json_val[i].payload = &new_downloaded_json_obj[i];
+ new_downloaded_json_val[i].type = json_type_object;
+
+ new_downloaded_item_element[i].value = &new_downloaded_json_val[i];
+ new_downloaded_item_element[i].next = NULL;
+
+ if(downloaded_json_array->length > 0) {
+ if(!last_downloaded_element)
+ last_downloaded_element = get_last_element_in_json_array(downloaded_json_array);
+
+ if(last_downloaded_element)
+ last_downloaded_element->next = &new_downloaded_item_element[i];
+ else
+ downloaded_json_array->start = &new_downloaded_item_element[i];
+
+ last_downloaded_element = &new_downloaded_item_element[i];
+ downloaded_json_array->length++;
+ } else {
+ downloaded_json_array->start = &new_downloaded_item_element[i];
+ downloaded_json_array->length = 1;
+ last_downloaded_element = &new_downloaded_item_element[i];
+
+ struct json_object_element_s *prev_start = tracked_item->json_data->start;
+ tracked_item->json_data->start = &new_downloaded_array_obj_el;
+ new_downloaded_array_obj_el.next = prev_start;
+ tracked_item->json_data->length++;
+ }
+ }
+
+ struct json_value_s json_root_value;
+ json_root_value.payload = tracked_item->json_data;
+ json_root_value.type = json_type_object;
+
+ size_t json_body_size = 0;
+ char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size);
+ if(!json_body_str) {
+ fprintf(stderr, "Failed to write json data to file %s/data\n", item_filepath);
+ result = -1;
+ goto cleanup;
+ }
+
+ /* Workaround json bug (?) */
+ json_body_size = strlen(json_body_str);
+
+ result = file_overwrite_in_dir(item_filepath, "data", json_body_str, json_body_size);
+ free(json_body_str);
+
+ cleanup:
+ tracked_dir[tracked_dir_len] = '\0';
+ return result;
+}
+
struct json_value_s* json_object_get_field_by_name(struct json_object_s *json_obj, const char *name) {
struct json_object_element_s *obj_element = json_obj->start;
while(obj_element) {
diff --git a/src/rss_html_common.h b/src/rss_html_common.h
index 472f090..3ae9d77 100644
--- a/src/rss_html_common.h
+++ b/src/rss_html_common.h
@@ -1,11 +1,34 @@
#ifndef RSS_HTML_COMMON_H
#define RSS_HTML_COMMON_H
+#define MAX_UPDATE_ITEMS 10
+
struct json_value_s;
struct json_object_s;
struct json_string_s;
+typedef struct {
+ const char *title;
+ const char *link;
+} DownloadItemsData;
+
+typedef struct {
+ char *title;
+ char *link;
+ struct json_object_s *json_data;
+} TrackedItem;
+
int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name);
+
+/*
+ @num_download_items can't be more than MAX_UPDATE_ITEMS.
+ @filenames can be NULL, in which case filenames are not stored for items. This is the case for html items.
+*/
+/*
+ Note: tracked_item.json_data becomes invalid after this call.
+ @tracked_dir is also modified and then restored at the end.
+*/
+int tracked_item_update_latest(TrackedItem *tracked_item, char *tracked_dir, DownloadItemsData **download_items, char **filenames, int num_download_items);
struct json_value_s* json_object_get_field_by_name(struct json_object_s *json_obj, const char *name);
void create_json_string(struct json_string_s *json_result, const char *str, int len);
void init_json_value_str(struct json_value_s *json_value, struct json_string_s *json_str);