From eaa0851500c111a0fe5a50bc3f8b5aa4354774db Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 11 Apr 2021 09:27:13 +0200 Subject: Add cleanup option to remove old tracked items --- src/main.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) (limited to 'src/main.c') diff --git a/src/main.c b/src/main.c index 85a331b..b210b71 100644 --- a/src/main.c +++ b/src/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ static void usage(void) { fprintf(stderr, " add Add media to track\n"); fprintf(stderr, " sync Start syncing tracked media\n"); fprintf(stderr, " downloaded List downloaded media\n"); + fprintf(stderr, " cleanup Stop tracking media that has stopped getting updates (either finished or dropped media)\n"); exit(1); } @@ -60,6 +62,15 @@ static void usage_sync(void) { exit(1); } +static void usage_cleanup(void) { + fprintf(stderr, "usage: automedia cleanup \n"); + fprintf(stderr, "OPTIONS\n"); + fprintf(stderr, " days Media that haven't received any updates in the specified amount of days are removed\n"); + fprintf(stderr, "EXAMPLES\n"); + fprintf(stderr, " automedia cleanup 100\n"); + exit(1); +} + typedef void (*DownloadedListCallback)(const char *title, double timestamp, void *userdata); static void data_file_get_downloaded(const char *dir_name, const char *data_filepath, int is_html, DownloadedListCallback callback, void *userdata) { @@ -178,6 +189,73 @@ static int compare_downloaded_item(const void *a, const void *b) { return list_data_a->timestamp - list_data_b->timestamp; } +typedef struct { + char *item_tracked_path; + time_t updated_time; +} TrackedItemData; + +static void get_tracked_items(const char *tracked_dir, time_t age_sec, Buffer /*TrackedItemData*/ *tracked_items_buffer) { + struct dirent *dir; + DIR *d = opendir(tracked_dir); + if(!d) { + fprintf(stderr, "Failed to open directory: %s\n", tracked_dir); + return; + } + + char data_filepath[PATH_MAX]; + strcpy(data_filepath, tracked_dir); + strcat(data_filepath, "/"); + int data_filepath_length = strlen(data_filepath); + + time_t time_now = time(NULL); + char *file_data; + long file_size; + + while((dir = readdir(d)) != NULL) { + int filename_len = strlen(dir->d_name); + if((filename_len == 1 && dir->d_name[0] == '.') || (filename_len == 2 && dir->d_name[0] == '.' && dir->d_name[1] == '.')) + continue; + + strcpy(data_filepath + data_filepath_length, dir->d_name); + + strcpy(data_filepath + data_filepath_length + filename_len, "/synced"); + if(file_get_content(data_filepath, &file_data, &file_size) != 0) { + fprintf(stderr, "Failed to read the content of file %s\n", data_filepath); + continue; + } + + time_t synced_time = 0; + sscanf(file_data, "%ld", &synced_time); + free(file_data); + if(time_now - synced_time > age_sec) + continue; + + strcpy(data_filepath + data_filepath_length + filename_len, "/updated"); + if(file_get_content(data_filepath, &file_data, &file_size) != 0) { + fprintf(stderr, "Failed to read the content of file %s\n", data_filepath); + continue; + } + + time_t updated_time = 0; + sscanf(file_data, "%ld", &updated_time); + free(file_data); + if(time_now - updated_time <= age_sec) + continue; + + char *p = alloc_or_crash(data_filepath_length + filename_len + 1); + memcpy(p, data_filepath, data_filepath_length); + memcpy(p + data_filepath_length, dir->d_name, filename_len); + p[data_filepath_length + filename_len] = '\0'; + + TrackedItemData tracked_item_data; + tracked_item_data.item_tracked_path = p; + tracked_item_data.updated_time = updated_time; + buffer_append(tracked_items_buffer, &tracked_item_data, sizeof(tracked_item_data)); + } + + closedir(d); +} + static void command_add(int argc, char **argv, char *rss_config_dir, char *html_config_dir, char *program_dir) { if(argc < 2) usage_add(); @@ -589,6 +667,182 @@ static void command_downloaded(const char *rss_config_dir, const char *html_conf buffer_deinit(&downloaded_items); } +const char* path_get_filename(const char *filepath) { + int len = strlen(filepath); + for(int i = len - 1; i >= 0; --i) { + if(filepath[i] == '/') + return filepath + i + 1; + } + return filepath; +} + +static void command_cleanup(int argc, char **argv, const char *rss_config_dir, const char *html_config_dir) { + if(argc < 1) + usage_cleanup(); + + time_t days = strtol(argv[0], NULL, 0); + if(errno || days < 0) { + fprintf(stderr, "Invalid value for days argument %s, expected a positive number\n", argv[0]); + return; + } + + char rss_tracked_dir[PATH_MAX]; + strcpy(rss_tracked_dir, rss_config_dir); + strcat(rss_tracked_dir, "/tracked"); + + char html_tracked_dir[PATH_MAX]; + strcpy(html_tracked_dir, html_config_dir); + strcat(html_tracked_dir, "/tracked"); + + Buffer tracked_items; + buffer_init(&tracked_items); + + int num_rss_items = 0; + time_t age_sec = 60 * 60 * 24 * days; + get_tracked_items(rss_tracked_dir, age_sec, &tracked_items); + num_rss_items = buffer_get_size(&tracked_items, sizeof(TrackedItemData)); + get_tracked_items(html_tracked_dir, age_sec, &tracked_items); + int num_tracked_items = buffer_get_size(&tracked_items, sizeof(TrackedItemData)); + + TrackedItemData *list_begin = buffer_begin(&tracked_items); + TrackedItemData *list_it = list_begin; + TrackedItemData *list_end = buffer_end(&tracked_items); + int index = 0; + for(; list_it != list_end; ++list_it) { + const char *name = path_get_filename(list_it->item_tracked_path); + printf("%d %s/%s\n", 1 + index, index < num_rss_items ? "rss" : "html", name); + ++index; + } + + if(tracked_items.size == 0) { + fprintf(stderr, "There is no media that hasn't been updated in the last %ld %s\n", days, days == 1 ? "day" : "days"); + goto cleanup; + } + + for(;;) { + fprintf(stderr, "==> Media to stop tracking: (in number range, eg: 10, \"1-3\")\n"); + fprintf(stderr, "==> "); + fflush(stderr); + + char resp[128]; + if(!fgets(resp, sizeof(resp), stdin)) { + fprintf(stderr, "Failed to read response from stdin\n"); + goto cleanup; + } + char *response_str = strip(resp); + int response_len = strlen(response_str); + if(response_len == 0) { + fprintf(stderr, "Invalid input, expected number range\n"); + continue; + } + + int start_range = 0; + int end_range = 0; + int num_variables_read = sscanf(response_str, "%d-%d", &start_range, &end_range); + + if(num_variables_read == 0) { + fprintf(stderr, "Invalid input, expected number range\n"); + } else if(num_variables_read == 1) { + int index = start_range - 1; + if(index < 0 || index >= num_tracked_items) { + fprintf(stderr, "Invalid item selected: %d, expected a value between 1 and %d\n", start_range, num_tracked_items); + continue; + } + + const char *name = path_get_filename((list_begin + index)->item_tracked_path); + for(;;) { + fprintf(stderr, "==> Are you sure you want to stop tracking %s/%s? Yes/No: \n", index < num_rss_items ? "rss" : "html", name); + fprintf(stderr, "==> "); + fflush(stderr); + + char resp[128]; + if(!fgets(resp, sizeof(resp), stdin)) { + fprintf(stderr, "Failed to read response from stdin\n"); + goto cleanup; + } + char *response_str = strip(resp); + int response_len = strlen(response_str); + if(response_len == 0) + continue; + + if(response_str[0] == 'n' || response_str[0] == 'N') { + goto cleanup; + } else if(response_str[0] == 'y' || response_str[0] == 'Y') { + if(remove_recursive((list_begin + index)->item_tracked_path) != 0) { + fprintf(stderr, "Failed to remove %s/%s\n", index < num_rss_items ? "rss" : "html", name); + goto cleanup; + } + + fprintf(stderr, "Removed %s/%s\n", index < num_rss_items ? "rss" : "html", name); + goto cleanup; + } + } + } else if(num_variables_read == 2) { + int start_index_f = start_range - 1; + if(start_index_f < 0 || start_index_f >= num_tracked_items) { + fprintf(stderr, "Invalid item start range: %d, expected a value between 1 and %d\n", start_range, num_tracked_items); + continue; + } + + int end_index_f = end_range - 1; + if(end_index_f < start_index_f || end_index_f >= num_tracked_items) { + fprintf(stderr, "Invalid item end range: %d, expected a value between %d and %d\n", end_index_f, start_range, num_tracked_items); + continue; + } + + for(;;) { + fprintf(stderr, "==> Are you sure you want to stop tracking:\n"); + TrackedItemData *list_it = list_begin + start_index_f; + TrackedItemData *list_end = list_begin + end_range; + int index = 0; + for(; list_it != list_end; ++list_it) { + const char *name = path_get_filename(list_it->item_tracked_path); + printf(" %s/%s\n", index < num_rss_items ? "rss" : "html", name); + ++index; + } + + fprintf(stderr, "==> Yes/No: "); + fflush(stderr); + + char resp[128]; + if(!fgets(resp, sizeof(resp), stdin)) { + fprintf(stderr, "Failed to read response from stdin\n"); + goto cleanup; + } + char *response_str = strip(resp); + int response_len = strlen(response_str); + fprintf(stderr, "response len: %d\n", response_len); + if(response_len == 0) + continue; + + if(response_str[0] == 'n' || response_str[0] == 'N') { + goto cleanup; + } else if(response_str[0] == 'y' || response_str[0] == 'Y') { + TrackedItemData *list_it = list_begin + start_index_f; + TrackedItemData *list_end = list_begin + end_range; + for(; list_it != list_end; ++list_it) { + const char *name = path_get_filename(list_it->item_tracked_path); + if(remove_recursive(list_it->item_tracked_path) != 0) { + fprintf(stderr, "Failed to remove %s/%s\n", index < num_rss_items ? "rss" : "html", name); + goto cleanup; + } + fprintf(stderr, "Removed %s/%s\n", index < num_rss_items ? "rss" : "html", name); + } + goto cleanup; + } + } + } + } + + cleanup: + list_it = buffer_begin(&tracked_items); + list_end = buffer_end(&tracked_items); + for(; list_it != list_end; ++list_it) { + free(list_it->item_tracked_path); + } + buffer_deinit(&tracked_items); +} + int main(int argc, char **argv) { if(argc < 2) usage(); @@ -612,6 +866,8 @@ int main(int argc, char **argv) { 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 if(strcmp(command, "cleanup") == 0) { + command_cleanup(argc - 2, argv + 2, rss_config_dir, html_config_dir); } else { fprintf(stderr, "Error: Invalid command %s\n", command); usage(); -- cgit v1.2.3