From 21208ecc1c6223cdfd2dbfeaff3bcfad8d0b8937 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 15 Jul 2020 18:13:49 +0200 Subject: Add torrent complete notification --- src/transmission.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 9 deletions(-) (limited to 'src/transmission.c') 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); -- cgit v1.2.3