aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-07-15 18:13:49 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-15 18:13:49 +0200
commit21208ecc1c6223cdfd2dbfeaff3bcfad8d0b8937 (patch)
tree67f151d022fdcd71bf2ddc5fff8046a4d3a8bfa1 /src
parent9946c0363648b44d396b07d8a1a4557c568edc88 (diff)
Add torrent complete notification
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c1
-rw-r--r--src/alloc.h1
-rw-r--r--src/buffer.c1
-rw-r--r--src/html.c8
-rw-r--r--src/main.c51
-rw-r--r--src/rss.c4
-rw-r--r--src/transmission.c141
-rw-r--r--src/transmission.h11
8 files changed, 198 insertions, 20 deletions
diff --git a/src/alloc.c b/src/alloc.c
index dca69b5..282119f 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -1,5 +1,6 @@
#include "alloc.h"
#include <stdio.h>
+#include <stdlib.h>
void* alloc_or_crash(size_t size) {
void *mem = malloc(size);
diff --git a/src/alloc.h b/src/alloc.h
index e8c6c85..9670ee7 100644
--- a/src/alloc.h
+++ b/src/alloc.h
@@ -2,7 +2,6 @@
#define ALLOC_H
#include <stddef.h>
-#include <stdlib.h>
void* alloc_or_crash(size_t size);
void* realloc_or_crash(void *mem, size_t new_size);
diff --git a/src/buffer.c b/src/buffer.c
index f5e1d65..05f99f2 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2,6 +2,7 @@
#include "alloc.h"
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <assert.h>
void buffer_init(Buffer *self) {
diff --git a/src/html.c b/src/html.c
index 3159500..c8aa902 100644
--- a/src/html.c
+++ b/src/html.c
@@ -349,8 +349,13 @@ static int download_html_items_in_reverse(const char *plugin_filepath, Buffer *d
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) {
+ char notify_msg[PATH_MAX];
+ const char *path_components[] = { tracked_html->title, download_items_it->title };
+ path_join(notify_msg, path_components, 2);
+
item_dir[item_dir_len] = '/';
strcpy(item_dir + item_dir_len + 1, download_items_it->title);
+ fprintf(stderr, "Starting download of html item: %s (title: %s)\n", download_items_it->link, notify_msg);
if(create_directory_recursive(item_dir) != 0) {
fprintf(stderr, "Failed to create directory for html item: %s\n", download_items_it->title);
result = -1;
@@ -363,9 +368,6 @@ static int download_html_items_in_reverse(const char *plugin_filepath, Buffer *d
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);
diff --git a/src/main.c b/src/main.c
index e8ff068..10ba396 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,10 +3,12 @@
#include "transmission.h"
#include "fileutils.h"
#include "stringutils.h"
+#include "program.h"
#include "rss.h"
#include "rss_html_common.h"
#include "html.h"
#include "json.h"
+#include "alloc.h"
#include <stdio.h>
#include <stdlib.h>
@@ -383,6 +385,37 @@ static void sync_tracked_html(char *html_config_dir, char *program_dir, const ch
iterate_tracked_items(html_config_dir, iterate_tracked_item_html_callback, &iterate_html_item_userdata);
}
+typedef struct {
+ char *items;
+ int size;
+} UnfinishedTorrents;
+
+static void torrent_list_check_new_downloads_callback(int id, const char *name, double percentage_done, void *userdata) {
+ /* Sanity check, random high number */
+ if(id <= 0 || id >= 650000) {
+ fprintf(stderr, "Invalid torrent id: %d\n", id);
+ return;
+ }
+
+ id--;
+ UnfinishedTorrents *unfinished_torrents = userdata;
+
+ int is_finished = (percentage_done >= 0.9999);
+ if(is_finished) {
+ if(id < unfinished_torrents->size && unfinished_torrents->items[id] == 1) {
+ unfinished_torrents->items[id] = 0;
+ const char *notify_args[] = { "notify-send", "-u", "normal", "--", "Download finished", name, NULL };
+ program_exec(notify_args, NULL, NULL);
+ }
+ } else {
+ if(id >= unfinished_torrents->size) {
+ unfinished_torrents->size = id + 128;
+ unfinished_torrents->items = realloc_or_crash(unfinished_torrents->items, unfinished_torrents->size);
+ }
+ unfinished_torrents->items[id] = 1;
+ }
+}
+
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) {
@@ -397,16 +430,28 @@ static void sync_rss_html(char *rss_config_dir, char *html_config_dir, char *pro
exit(2);
}
+ /* Check for torrent progress every 15 seconds */
+ int check_torrent_status_rate_sec = 15;
+
+ UnfinishedTorrents unfinished_torrents;
+ unfinished_torrents.items = alloc_or_crash(1024);
+ unfinished_torrents.size = 1024;
+
automedia_running = 1;
/* running is set to 0 in SIGINT signal handler (ctrl+c) */
while(automedia_running) {
sync_tracked_rss(&transmission_session, rss_config_dir);
sync_tracked_html(html_config_dir, program_dir, download_dir);
- /* TODO: Show finished html/rss items */
- if(automedia_running)
- sleep(sync_rate_sec);
+ int check_count = 0;
+ while(automedia_running && check_count < sync_rate_sec/check_torrent_status_rate_sec) {
+ transmission_list_torrents(&transmission_session, torrent_list_check_new_downloads_callback, &unfinished_torrents);
+ sleep(check_torrent_status_rate_sec);
+ ++check_count;
+ }
}
+
+ free(unfinished_torrents.items);
}
static int cmdline_contains_str(const char *cmdline, const char *str) {
diff --git a/src/rss.c b/src/rss.c
index 90b3ed6..442fb58 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -328,14 +328,14 @@ static int add_torrents_in_reverse(TransmissionSession *transmission_session, Bu
download_items_end--;
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, "Starting download of torrent: %s (title: %s)\n", download_items_it->link, download_items_it->title);
+ if(transmission_add_torrent(transmission_session, download_items_it->link, NULL, &torrent_names[torrent_name_index]) != 0) {
fprintf(stderr, "Failed to add torrent: %s\n", download_items_it->link);
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? */
}
diff --git a/src/transmission.c b/src/transmission.c
index d789053..39b355f 100644
--- a/src/transmission.c
+++ b/src/transmission.c
@@ -90,6 +90,13 @@ static struct json_string_s* json_object_get_child_string_by_name(struct json_ob
return (struct json_string_s*)json_child_str->payload;
}
+static struct json_number_s* json_object_get_child_number_by_name(struct json_object_s *json_obj, const char *field_name) {
+ struct json_value_s *json_number_str = json_object_get_field_by_name(json_obj, field_name);
+ if(!json_number_str || json_number_str->type != json_type_number)
+ return NULL;
+ return (struct json_number_s*)json_number_str->payload;
+}
+
static int transmission_response_is_success(struct json_object_s *json_root) {
struct json_string_s *result_field = json_object_get_child_string_by_name(json_root, "result");
if(!result_field) {
@@ -100,7 +107,7 @@ static int transmission_response_is_success(struct json_object_s *json_root) {
return strcmp(result_field->string, "success");
}
-static int transmission_add_torrent_response_get_torrent_name(struct json_object_s *json_root, const char **torrent_name) {
+static int transmission_add_torrent_response_get_torrent_name(struct json_object_s *json_root, int *torrent_id, const char **torrent_name) {
struct json_object_s *arguments_obj = json_object_get_child_object_by_name(json_root, "arguments");
if(!arguments_obj) {
fprintf(stderr, "Error: transmission add torrent response is missing arguments field or its not an object\n");
@@ -116,17 +123,33 @@ static int transmission_add_torrent_response_get_torrent_name(struct json_object
return -1;
}
- struct json_string_s *torrent_name_field = json_object_get_child_string_by_name(torrent_added_obj, "name");
- if(!torrent_name_field) {
- fprintf(stderr, "Error: transmission add torrent response is missing torrent name\n");
- return -1;
+ if(torrent_id) {
+ struct json_number_s *torrent_id_field = json_object_get_child_number_by_name(torrent_added_obj, "id");
+ if(!torrent_id_field) {
+ fprintf(stderr, "Error: transmission add torrent response is missing torrent id\n");
+ return -1;
+ }
+
+ *torrent_id = atoi(torrent_id_field->number);
+ if(*torrent_id == 0) {
+ fprintf(stderr, "Error: transmission add torrent response has invalid torrent id\n");
+ return -1;
+ }
+ }
+
+ if(torrent_name) {
+ struct json_string_s *torrent_name_field = json_object_get_child_string_by_name(torrent_added_obj, "name");
+ if(!torrent_name_field) {
+ fprintf(stderr, "Error: transmission add torrent response is missing torrent name\n");
+ return -1;
+ }
+ *torrent_name = torrent_name_field->string;
}
- *torrent_name = torrent_name_field->string;
return 0;
}
-int transmission_add_torrent(TransmissionSession *session, const char *url, char **torrent_name) {
+int transmission_add_torrent(TransmissionSession *session, const char *url, int *torrent_id, char **torrent_name) {
int result = 0;
Buffer buffer;
buffer_init(&buffer);
@@ -174,12 +197,112 @@ int transmission_add_torrent(TransmissionSession *session, const char *url, char
goto cleanup;
}
+ int response_torrent_id;
const char *response_torrent_name;
- result = transmission_add_torrent_response_get_torrent_name(json_response_obj, &response_torrent_name);
+ result = transmission_add_torrent_response_get_torrent_name(json_response_obj,
+ torrent_id ? &response_torrent_id : NULL,
+ torrent_name ? &response_torrent_name : NULL);
+
if(result != 0)
goto cleanup;
- *torrent_name = strdup(response_torrent_name);
+ if(torrent_id)
+ *torrent_id = response_torrent_id;
+
+ if(torrent_name)
+ *torrent_name = strdup(response_torrent_name);
+
+ cleanup:
+ free(json_response_val);
+ buffer_deinit(&buffer);
+ return result;
+}
+
+int transmission_list_torrents(TransmissionSession *session, TorrentListCallback callback, void *userdata) {
+ int result = 0;
+ Buffer buffer;
+ buffer_init(&buffer);
+ struct json_value_s *json_response_val = NULL;
+
+ char request[100];
+ int written_bytes = snprintf(request, sizeof(request), "{ \"arguments\": { \"fields\": [ \"id\", \"name\", \"percentDone\" ] }, \"method\": \"torrent-get\" }");
+ if(written_bytes >= (int)sizeof(request)) {
+ fprintf(stderr, "Failed to write all bytes to request (this is a bug)\n");
+ result = -1;
+ goto cleanup;
+ }
+
+ const char *args[] = { "curl", "-s", "-f", "-d", request, "-H", session->session_header, "--", "http://127.0.0.1:9091/transmission/rpc", NULL };
+ result = program_exec(args, program_buffer_write_callback, &buffer);
+ if(result != 0) {
+ /* Maybe the request failed because the session is no longer valid? retry once with new session id */
+ if(transmission_connect(session) != 0) {
+ fprintf(stderr, "Failed to list torrents\n");
+ goto cleanup;
+ }
+
+ buffer_clear(&buffer);
+ const char *new_args[] = { "curl", "-s", "-f", "-d", request, "-H", session->session_header, "--", "http://127.0.0.1:9091/transmission/rpc", NULL };
+ result = program_exec(new_args, program_buffer_write_callback, &buffer);
+ if(result != 0) {
+ fprintf(stderr, "Failed to list torrents\n");
+ goto cleanup;
+ }
+ }
+
+ json_response_val = json_parse(buffer.data, buffer.size);
+ if(!json_response_val || json_response_val->type != json_type_object) {
+ fprintf(stderr, "Failed to parse torrent list response: %.*s as json\n", (int)buffer.size, (char*)buffer.data);
+ result = -1;
+ goto cleanup;
+ }
+
+ struct json_object_s *json_response_obj = json_value_as_object(json_response_val);
+
+ if(transmission_response_is_success(json_response_obj) != 0) {
+ fprintf(stderr, "Error: transmission torrent list request failed, response: %.*s\n", (int)buffer.size, (char*)buffer.data);
+ result = -1;
+ goto cleanup;
+ }
+
+ struct json_value_s *arguments_field = json_object_get_field_by_name(json_response_obj, "arguments");
+ if(!arguments_field || arguments_field->type != json_type_object) {
+ fprintf(stderr, "Error: transmission torrent list response is missing arguments or its not an object\n");
+ result = -1;
+ goto cleanup;
+ }
+
+ struct json_object_s *arguments_field_obj = json_value_as_object(arguments_field);
+ struct json_value_s *torrents_field = json_object_get_field_by_name(arguments_field_obj, "torrents");
+ if(!torrents_field || torrents_field->type != json_type_array) {
+ fprintf(stderr, "Error: transmission torrent list response is missing torrents or its not an array\n");
+ result = -1;
+ goto cleanup;
+ }
+
+ struct json_array_element_s *torrent_item = json_value_as_array(torrents_field)->start;
+ while(torrent_item) {
+ struct json_object_s *torrent_item_obj = json_value_as_object(torrent_item->value);
+ if(!torrent_item_obj)
+ continue;
+
+ struct json_value_s *id_field = json_object_get_field_by_name(torrent_item_obj, "id");
+ struct json_value_s *name_field = json_object_get_field_by_name(torrent_item_obj, "name");
+ struct json_value_s *percent_done_field = json_object_get_field_by_name(torrent_item_obj, "percentDone");
+ if(!id_field || id_field->type != json_type_number
+ || !name_field || name_field->type != json_type_string
+ || !percent_done_field || percent_done_field->type != json_type_number)
+ {
+ continue;
+ }
+
+ callback(atoi(json_value_as_number(id_field)->number),
+ json_value_as_string(name_field)->string,
+ atof(json_value_as_number(percent_done_field)->number),
+ userdata);
+
+ torrent_item = torrent_item->next;
+ }
cleanup:
free(json_response_val);
diff --git a/src/transmission.h b/src/transmission.h
index cbb6c7c..c15d42b 100644
--- a/src/transmission.h
+++ b/src/transmission.h
@@ -6,13 +6,20 @@ struct TransmissionSession {
char session_header[128];
};
+typedef void (*TorrentListCallback)(int id, const char *name, double percentage_done, void *userdata);
+
int transmission_connect(TransmissionSession *session);
/* Returns 0 if the daemon is running, otherwise returns an error value */
int transmission_is_daemon_running();
int transmission_start_daemon(const char *download_dir);
-/* The torrent name will be stored in @torrent_name, malloc'ed */
-int transmission_add_torrent(TransmissionSession *session, const char *url, char **torrent_name);
+/*
+ The torrent id will be stored in @torrent_id, malloc'ed and
+ the torrent name will be stored in @torrent_name, malloc'ed.
+*/
+int transmission_add_torrent(TransmissionSession *session, const char *url, int *torrent_id, char **torrent_name);
+
+int transmission_list_torrents(TransmissionSession *session, TorrentListCallback callback, void *userdata);
#endif