aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-07-15 20:36:51 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-15 20:36:51 +0200
commitb2e4168ece2e6f378e06d3833c179299e82254fc (patch)
tree2fd21aa2c61bab5f17c4cb99999ad22fb8201647
parent21208ecc1c6223cdfd2dbfeaff3bcfad8d0b8937 (diff)
Add the ability to add rss from episode name
-rwxr-xr-xbuild.sh2
-rw-r--r--src/download.c12
-rw-r--r--src/download.h1
-rw-r--r--src/episode.c133
-rw-r--r--src/episode.h19
-rw-r--r--src/rss.c160
6 files changed, 322 insertions, 5 deletions
diff --git a/build.sh b/build.sh
index 9ecbe97..3acb657 100755
--- a/build.sh
+++ b/build.sh
@@ -2,4 +2,4 @@
CFLAGS="-O3 -s -flto"
[ -z "$RELEASE" ] && CFLAGS="-O0 -g3 -Wall -Wextra -Werror";
-musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/html.c src/rss_html_common.c src/download.c src/stringutils.c -o automedia $CFLAGS
+musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/html.c src/rss_html_common.c src/download.c src/stringutils.c src/episode.c -o automedia $CFLAGS
diff --git a/src/download.c b/src/download.c
index 74348e5..4154dcb 100644
--- a/src/download.c
+++ b/src/download.c
@@ -18,3 +18,15 @@ int download_to_buffer(const char *url, Buffer *buffer) {
buffer_append(buffer, "\0", 1);
return result;
}
+
+int is_header_response_ok(const char *url) {
+ const char *args[] = {
+ "curl", "-s", "-L", "-f", "-I",
+ "-H", "user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
+ "-H", "Accept-Language: en-US,en;q=0.5",
+ "--",
+ url,
+ NULL
+ };
+ return program_exec(args, NULL, NULL);
+}
diff --git a/src/download.h b/src/download.h
index 0682ba9..3c7f0aa 100644
--- a/src/download.h
+++ b/src/download.h
@@ -3,5 +3,6 @@
struct Buffer;
int download_to_buffer(const char *url, struct Buffer *buffer);
+int is_header_response_ok(const char *url);
#endif
diff --git a/src/episode.c b/src/episode.c
new file mode 100644
index 0000000..e89195c
--- /dev/null
+++ b/src/episode.c
@@ -0,0 +1,133 @@
+#include "episode.h"
+#include "stringutils.h"
+#include <stdio.h>
+#include <string.h>
+
+static int string_find(const char *str, int len, int offset, char char_to_find) {
+ for(int i = offset; i < len; ++i) {
+ if(str[i] == char_to_find)
+ return i;
+ }
+ return -1;
+}
+
+static int string_rfind(const char *str, int len, int offset, char char_to_find) {
+ for(int i = offset - 1; i >= 0; --i) {
+ if(str[i] == char_to_find)
+ return i + (len - offset);
+ }
+ return -1;
+}
+
+int episode_info_create_from_episode_name(EpisodeInfo *self, const char *episode_name) {
+ self->episode_name[0] = '\0';
+ self->group_name = NULL;
+ self->anime_name = NULL;
+ self->episode = NULL;
+ self->resolution = NULL;
+ self->extension = NULL;
+
+ int episode_name_len = strlen(episode_name);
+ if(episode_name_len > 500) {
+ fprintf(stderr, "Episode name is too long! it can't be more than 500 characters\n");
+ return -1;
+ }
+
+ char episode_name_s[512];
+ strcpy(episode_name_s, episode_name);
+ char *episode_name_stripped = strip(episode_name_s);
+ int episode_name_stripped_len = strlen(episode_name_stripped);
+ int episode_name_offset = 0;
+
+ if(episode_name_stripped_len == 0)
+ return -1;
+
+ int extension_index = string_rfind(episode_name_stripped, episode_name_stripped_len, episode_name_stripped_len, '.');
+ if(extension_index != -1) {
+ int extension_len = episode_name_stripped_len - extension_index;
+ self->extension = self->episode_name + episode_name_offset;
+ memcpy(self->extension, episode_name_stripped + extension_index, extension_len);
+ self->extension[extension_len] = '\0';
+ episode_name_offset += extension_len + 1;
+ episode_name_stripped_len = extension_index;
+ }
+
+ if(episode_name_stripped[0] != '[')
+ return -1;
+
+ int group_name_end = string_find(episode_name_stripped, episode_name_stripped_len, 0, ']');
+ if(group_name_end == -1)
+ return -1;
+
+ int group_name_len = group_name_end - 1;
+ self->group_name = self->episode_name + episode_name_offset;
+ memcpy(self->group_name, episode_name_stripped + 1, group_name_len);
+ self->group_name[group_name_len] = '\0';
+ self->group_name = strip(self->group_name);
+ episode_name_offset += group_name_len + 1;
+
+ int last_dash = string_rfind(episode_name_stripped, episode_name_stripped_len, episode_name_stripped_len, '-');
+ if(last_dash == -1)
+ return -1;
+
+ int anime_name_len = last_dash - 1 - group_name_end;
+ self->anime_name = self->episode_name + episode_name_offset;
+ memcpy(self->anime_name, episode_name_stripped + group_name_end + 1, anime_name_len);
+ self->anime_name[anime_name_len] = '\0';
+ self->anime_name = strip(self->anime_name);
+ episode_name_offset += anime_name_len + 1;
+
+ int resolution_index = string_find(episode_name_stripped, episode_name_stripped_len, last_dash + 1, '[');
+ if(resolution_index == -1) {
+ /* TODO: Test if this works */
+ int episode_len = episode_name_stripped_len - (last_dash + 1);
+ self->episode = self->episode_name + episode_name_offset;
+ memcpy(self->episode, episode_name_stripped + last_dash + 1, episode_len);
+ self->episode[episode_len] = '\0';
+ self->episode = strip(self->episode);
+ episode_name_offset += episode_len + 1;
+ } else {
+ int episode_len = resolution_index - (last_dash + 1);
+ self->episode = self->episode_name + episode_name_offset;
+ memcpy(self->episode, episode_name_stripped + last_dash + 1, episode_len);
+ self->episode[episode_len] = '\0';
+ self->episode = strip(self->episode);
+ episode_name_offset += episode_len + 1;
+
+ int resolution_end = string_find(episode_name_stripped, episode_name_stripped_len, resolution_index + 1, ']');
+ if(resolution_end != -1) {
+ int resolution_len = resolution_end - (resolution_index + 1);
+ self->resolution = self->episode_name + episode_name_offset;
+ memcpy(self->resolution, episode_name_stripped + resolution_index + 1, resolution_len);
+ self->resolution[resolution_len] = '\0';
+ self->resolution = strip(self->resolution);
+ }
+ }
+
+ return 0;
+}
+
+int episode_info_get_generic_name(EpisodeInfo *self, char *output_buffer, int output_buffer_size) {
+ if(!self->group_name || !self->anime_name)
+ return -1;
+
+ int bytes_written = 0;
+ if(self->resolution) {
+ if(self->extension)
+ bytes_written = snprintf(output_buffer, output_buffer_size, "[%s] %s [%s]%s", self->group_name, self->anime_name, self->resolution, self->extension);
+ else
+ bytes_written = snprintf(output_buffer, output_buffer_size, "[%s] %s [%s]", self->group_name, self->anime_name, self->resolution);
+ } else {
+ if(self->extension)
+ bytes_written = snprintf(output_buffer, output_buffer_size, "[%s] %s%s", self->group_name, self->anime_name, self->extension);
+ else
+ bytes_written = snprintf(output_buffer, output_buffer_size, "[%s] %s", self->group_name, self->anime_name);
+ }
+
+ if(bytes_written >= output_buffer_size) {
+ fprintf(stderr, "Failed to write generic name to buffer\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/episode.h b/src/episode.h
new file mode 100644
index 0000000..66c4e8a
--- /dev/null
+++ b/src/episode.h
@@ -0,0 +1,19 @@
+#ifndef EPISODE_H
+#define EPISODE_H
+
+typedef struct {
+ char episode_name[512];
+
+ /* These are pointers inside @episode_name */
+ char *group_name;
+ char *anime_name;
+ char *episode;
+ char *resolution;
+ char *extension;
+} EpisodeInfo;
+
+/* Fails if @episode_name is more than 500 characters long or if group or anime name is missing/cant be parsed */
+int episode_info_create_from_episode_name(EpisodeInfo *self, const char *episode_name);
+int episode_info_get_generic_name(EpisodeInfo *self, char *output_buffer, int output_buffer_size);
+
+#endif
diff --git a/src/rss.c b/src/rss.c
index 442fb58..9486e6e 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -4,6 +4,7 @@
#include "stringutils.h"
#include "fileutils.h"
#include "buffer.h"
+#include "episode.h"
#include "rss_html_common.h"
#include "json.h"
#include "alloc.h"
@@ -165,6 +166,139 @@ static int rss_parse_add_callback(char *title, char *link, void *userdata) {
return 0;
}
+static const char hex_characters[] = "0123456789ABCDEF";
+/* TODO: Also support unicode escape characters */
+static void url_escape(const char *str, char *output) {
+ int index = 0;
+ for(;;) {
+ char c = *str;
+ if(c == '\0') {
+ output[index] = '\0';
+ break;
+ } else if(c < 32) {
+ output[index++] = '%';
+ output[index++] = hex_characters[(c>>4) & 0x0F];
+ output[index++] = hex_characters[c&0x0F];
+ } else {
+ switch(c) {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '/':
+ case ':':
+ case ';':
+ case '=':
+ case '?':
+ case '@':
+ case '[':
+ case ']':
+ case '"':
+ case '-':
+ case '.':
+ case '<':
+ case '>':
+ case '\\':
+ case '^':
+ case '_':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ case ' ':
+ output[index++] = '%';
+ output[index++] = hex_characters[(c>>4) & 0x0F];
+ output[index++] = hex_characters[c&0x0F];
+ break;
+ default:
+ output[index++] = c;
+ break;
+ }
+ }
+ ++str;
+ }
+}
+
+static int get_rss_url_from_episode_info(const char *episode_name, EpisodeInfo *episode_info, char *rss_url) {
+ char *selected_submitter = NULL;
+ char response[512];
+ char group_name_escaped[1536];
+
+ for(;;) {
+ printf("Enter the name of the submitter (leave empty to choose \"%s\" or type \"anon\" to choose all submitters: ", episode_info->group_name);
+ if(!fgets(response, sizeof(response), stdin)) {
+ fprintf(stderr, "Failed to read response from stdin\n");
+ return -1;
+ }
+ fprintf(stderr, "response: %s\n", response);
+ char *response_str = strip(response);
+
+ if(strcmp(response, "anon") == 0) {
+ break;
+ } else {
+ if(strlen(response_str) == 0)
+ response_str = episode_info->group_name;
+
+ url_escape(response, group_name_escaped);
+ char url[4096];
+ if(snprintf(url, sizeof(url), "https://nyaa.si/user/%s", group_name_escaped) >= (int)sizeof(url)) {
+ fprintf(stderr, "Error: url is too long!\n");
+ return -1;
+ }
+
+ if(is_header_response_ok(url) == 0) {
+ selected_submitter = response_str;
+ break;
+ } else {
+ printf("The submitter \"%s\" doesn't exist on nyaa.si, please choose another submitter.\n", response_str);
+ }
+ }
+ }
+
+ char generic_name[2048];
+ if(episode_info_get_generic_name(episode_info, generic_name, sizeof(generic_name)) != 0) {
+ fprintf(stderr, "Failed to get name for episode!\n");
+ return -1;
+ }
+
+ 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);
+ else
+ printf("Are you sure you want to track \"%s\" by all submitters after \"%s\" ? (Y)es/No: ", generic_name, episode_name);
+
+ char sresp[128];
+ if(!fgets(sresp, sizeof(sresp), stdin)) {
+ fprintf(stderr, "Failed to read response from stdin\n");
+ return -1;
+ }
+ char *response_str = strip(response);
+ int response_len = strlen(response_str);
+
+ if(response_len > 0 && (response_str[0] == 'n' || response_str[0] == 'N')) {
+ rss_url[0] = '\0';
+ return 0;
+ } else if(response_len == 0 || response_str[0] == 'y' || response_str[0] == 'Y') {
+ break;
+ }
+ }
+
+ if(selected_submitter)
+ sprintf(rss_url, "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0&u=%s", group_name_escaped, selected_submitter);
+ else
+ sprintf(rss_url, "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", group_name_escaped);
+
+ return 0;
+}
+
int add_rss(const char *name, const char *url, char *rss_config_dir, const char *start_after) {
int result = 0;
@@ -172,8 +306,28 @@ int add_rss(const char *name, const char *url, char *rss_config_dir, const char
buffer_init(&buffer);
result = download_to_buffer(url, &buffer);
if(result != 0) {
- fprintf(stderr, "Failed to download rss: %s\n", url);
- goto cleanup;
+ EpisodeInfo episode_info;
+ if(episode_info_create_from_episode_name(&episode_info, url) != 0) {
+ fprintf(stderr, "Failed to download rss: %s\n", url);
+ goto cleanup;
+ }
+
+ char rss_url[4096];
+ if(get_rss_url_from_episode_info(url, &episode_info, rss_url) != 0)
+ goto cleanup;
+
+ /* User didn't want to track rss */
+ if(rss_url[0] == '\0') {
+ result = 0;
+ goto cleanup;
+ }
+
+ buffer_clear(&buffer);
+ result = download_to_buffer(url, &buffer);
+ if(result != 0) {
+ fprintf(stderr, "Failed to download rss: %s\n", url);
+ goto cleanup;
+ }
}
RssParseUserdata rss_parse_userdata;
@@ -194,8 +348,6 @@ int add_rss(const char *name, const char *url, char *rss_config_dir, const char
goto cleanup;
}
- /* TODO: Add (add rss <episode name>) here */
-
if(!name) {
if(!rss_title) {
fprintf(stderr, "Failed to find rss title and --name was not provided\n");