diff options
-rwxr-xr-x | automedia | bin | 108448 -> 116640 bytes | |||
-rw-r--r-- | src/fileutils.c | 58 | ||||
-rw-r--r-- | src/fileutils.h | 2 | ||||
-rw-r--r-- | src/main.c | 256 | ||||
-rw-r--r-- | src/rss.c | 4 |
5 files changed, 318 insertions, 2 deletions
Binary files differ diff --git a/src/fileutils.c b/src/fileutils.c index a66bba6..72d9c29 100644 --- a/src/fileutils.c +++ b/src/fileutils.c @@ -10,6 +10,7 @@ #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> +#include <dirent.h> const char* get_home_dir(void) { const char *home_dir = getenv("HOME"); @@ -83,6 +84,63 @@ int create_directory_recursive(char *path) { return 0; } +static int is_path(const char *path) { + struct stat s; + if(stat(path, &s) == 0) + return S_ISDIR(s.st_mode); + return 0; +} + +static int remove_recursive_mutable(char *path) { + int path_len = strlen(path); + int res = 0; + + struct dirent *dir; + DIR *d = opendir(path); + if(!d) { + fprintf(stderr, "Failed to open directory: %s\n", path); + return -1; + } + + 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; + + path[path_len] = '/'; + memcpy(path + path_len + 1, dir->d_name, filename_len); + path[path_len + 1 + filename_len] = '\0'; + + int is_dir = dir->d_type == DT_DIR; + if(dir->d_type == DT_UNKNOWN) + is_dir = is_path(path); + + if(is_dir) { + res = remove_recursive_mutable(path); + if(res != 0) + goto cleanup; + } + + res = remove(path); + if(res != 0) + goto cleanup; + } + + cleanup: + path[path_len] = '\0'; + res |= remove(path); + closedir(d); + return res; +} + +int remove_recursive(char *path) { + int path_len = strlen(path); + char fullpath[PATH_MAX]; + memcpy(fullpath, path, path_len); + fullpath[path_len] = '\0'; + return remove_recursive_mutable(fullpath); +} + int file_exists(const char *path) { return access(path, F_OK); } diff --git a/src/fileutils.h b/src/fileutils.h index 9f8f4fe..979f656 100644 --- a/src/fileutils.h +++ b/src/fileutils.h @@ -8,6 +8,8 @@ const char* get_home_dir(); int file_get_content(const char *filepath, char **data, long *size); /* Returns 0 on success (if the directories are created or if the directories already exists) */ int create_directory_recursive(char *path); +/* Returns 0 on success */ +int remove_recursive(char *path); /* Returns 0 if the file exists */ int file_exists(const char *path); /* Returns 0 on success */ @@ -15,6 +15,7 @@ #include <string.h> #include <limits.h> #include <locale.h> +#include <time.h> #include <assert.h> #include <dirent.h> @@ -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 <days>\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(); @@ -271,9 +271,9 @@ static int get_rss_url_from_episode_info(const char *episode_name, EpisodeInfo * for(;;) { if(selected_submitter) - printf("Are you sure you want to track \"%s\" by %s after \"%s\" ? (Y)es/No: ", generic_name, selected_submitter, episode_name); + printf("Are you sure you want to track \"%s\" by %s after \"%s\"? (Y)es/No: ", generic_name, selected_submitter, episode_name); else - printf("Are you sure you want to track \"%s\" by all submitters after \"%s\" ? (Y)es/No: ", generic_name, episode_name); + printf("Are you sure you want to track \"%s\" by all submitters after \"%s\"? (Y)es/No: ", generic_name, episode_name); fflush(stdout); char sresp[128]; |