aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-04-11 09:27:13 +0200
committerdec05eba <dec05eba@protonmail.com>2021-04-11 09:27:13 +0200
commiteaa0851500c111a0fe5a50bc3f8b5aa4354774db (patch)
tree8943d519265aec26e5fac2575f492293d820788c
parentcff81a0630937e0ffd59198a43a18f6c2b66b19a (diff)
Add cleanup option to remove old tracked items
-rwxr-xr-xautomediabin108448 -> 116640 bytes
-rw-r--r--src/fileutils.c58
-rw-r--r--src/fileutils.h2
-rw-r--r--src/main.c256
-rw-r--r--src/rss.c4
5 files changed, 318 insertions, 2 deletions
diff --git a/automedia b/automedia
index 3cfcabf..5f5eb0c 100755
--- a/automedia
+++ b/automedia
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 */
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 <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();
diff --git a/src/rss.c b/src/rss.c
index 29a7708..8e6cfe2 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -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];