220 lines
5.2 KiB
C
220 lines
5.2 KiB
C
|
|
#include <getopt.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#include <curl/curl.h>
|
||
|
|
|
||
|
|
#include "cJSON.h"
|
||
|
|
#include "fetch.h"
|
||
|
|
#include "image.h"
|
||
|
|
|
||
|
|
#define UNUSED(x) (void)x
|
||
|
|
|
||
|
|
#define OMDB_URL "http://www.omdbapi.com"
|
||
|
|
#define PROJECT_NAME "omdb"
|
||
|
|
|
||
|
|
/* Default to plain output. */
|
||
|
|
static enum _output_format {
|
||
|
|
OUTPUT_JSON,
|
||
|
|
OUTPUT_PLAIN,
|
||
|
|
OUTPUT_FANCY
|
||
|
|
} output_format = OUTPUT_PLAIN;
|
||
|
|
|
||
|
|
static struct option opts[] = {{"id", no_argument, NULL, 'i'},
|
||
|
|
{"json", no_argument, NULL, 'j'},
|
||
|
|
{"api-key", required_argument, NULL, 'k'},
|
||
|
|
{"search-type", required_argument, NULL, 't'},
|
||
|
|
{NULL, 0, NULL, 0}};
|
||
|
|
|
||
|
|
void print_usage(void) {
|
||
|
|
printf(
|
||
|
|
PROJECT_NAME
|
||
|
|
" [ikt] <TITLE-OR-ID>\n"
|
||
|
|
"\t-i/--id: Search by ID rather than title. (optional)\n"
|
||
|
|
"\t-j/--json: Output full JSON response.\n"
|
||
|
|
"\t-k/--api-key: OMDB API Key. (required)\n"
|
||
|
|
"\t-t/--search-type: Search type. (optional) [movie, series, episode]\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set a URL query parameter. */
|
||
|
|
void set_param(CURLU *restrict url, const char *restrict key,
|
||
|
|
char *restrict value) {
|
||
|
|
CURLUcode err;
|
||
|
|
char param[2048];
|
||
|
|
|
||
|
|
snprintf(param, sizeof param, "%s=%s", key, value);
|
||
|
|
|
||
|
|
if ((err = curl_url_set(url, CURLUPART_QUERY, param,
|
||
|
|
CURLU_APPENDQUERY | CURLU_URLENCODE)) != CURLUE_OK) {
|
||
|
|
fprintf(stderr, "Error setting url param '%s': %s\n", key,
|
||
|
|
curl_url_strerror(err));
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Just print the url if needed for debugging. */
|
||
|
|
void print_url(CURLU *restrict url) {
|
||
|
|
char *url_string = NULL;
|
||
|
|
|
||
|
|
curl_url_get(url, CURLUPART_URL, &url_string, 0);
|
||
|
|
printf("%s\n", url_string);
|
||
|
|
curl_free(url_string);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Print entire JSON response as is. */
|
||
|
|
int print_title_json(const cJSON *const json) {
|
||
|
|
char *json_str = cJSON_Print(json);
|
||
|
|
|
||
|
|
if (!json_str) {
|
||
|
|
fputs("Couldn't output formatted JSON.\n", stderr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
fputs(json_str, stdout);
|
||
|
|
putchar('\n');
|
||
|
|
free(json_str);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Print some title information in plain text. */
|
||
|
|
int print_title_plain(const cJSON *json) {
|
||
|
|
cJSON *title = cJSON_GetObjectItem(json, "title");
|
||
|
|
cJSON *imdb_rating = cJSON_GetObjectItem(json, "imdbRating");
|
||
|
|
|
||
|
|
if (title == NULL || imdb_rating == NULL) {
|
||
|
|
printf("Could not find title or rating in response.\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("Found: '%s' with rating of %s\n", title->valuestring,
|
||
|
|
imdb_rating->valuestring);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int handle_response(struct response *restrict resp) {
|
||
|
|
cJSON *json = NULL;
|
||
|
|
|
||
|
|
if (strstr(resp->content_type, "application/json") == NULL) {
|
||
|
|
fprintf(stderr, "Unexpected response: %s\n", resp->content_type);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
json = cJSON_ParseWithLength(resp->data, resp->size);
|
||
|
|
|
||
|
|
if (json == NULL) {
|
||
|
|
const char *error_ptr = cJSON_GetErrorPtr();
|
||
|
|
if (error_ptr != NULL) {
|
||
|
|
fprintf(stderr, "Error before: %s\n", error_ptr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (output_format) {
|
||
|
|
case OUTPUT_PLAIN:
|
||
|
|
print_title_plain(json);
|
||
|
|
break;
|
||
|
|
case OUTPUT_JSON:
|
||
|
|
print_title_json(json);
|
||
|
|
break;
|
||
|
|
case OUTPUT_FANCY:
|
||
|
|
/* Just print_title + image planned here for now */
|
||
|
|
print_title_plain(json);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
cJSON_free(json);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char **argv) {
|
||
|
|
int ch;
|
||
|
|
bool k_flag = false;
|
||
|
|
char search_mode[8] = "movie";
|
||
|
|
|
||
|
|
if (argc == 0) {
|
||
|
|
fputs("Argument required.\n", stderr);
|
||
|
|
print_usage();
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Just validate and add options directly to the request now. */
|
||
|
|
CURLU *url = curl_url();
|
||
|
|
CURLUcode ures;
|
||
|
|
|
||
|
|
if (url == NULL) {
|
||
|
|
fputs("Out of memory.\n", stderr);
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((ures = curl_url_set(url, CURLUPART_URL, OMDB_URL, 0)) != CURLUE_OK) {
|
||
|
|
fprintf(stderr, "Couldn't set url: %s\n", curl_url_strerror(ures));
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
|
||
|
|
while ((ch = getopt_long(argc, argv, "hijk:t:", opts, NULL)) != -1) {
|
||
|
|
switch (ch) {
|
||
|
|
case 'i':
|
||
|
|
/* Lookup by ID */
|
||
|
|
set_param(url, "id", optarg);
|
||
|
|
break;
|
||
|
|
case 'j':
|
||
|
|
output_format = OUTPUT_JSON;
|
||
|
|
break;
|
||
|
|
case 'k':
|
||
|
|
/* API Key */
|
||
|
|
k_flag = true;
|
||
|
|
set_param(url, "apikey", optarg);
|
||
|
|
break;
|
||
|
|
case 't':
|
||
|
|
/* Type: movie, series, episode */
|
||
|
|
strlcpy(search_mode, optarg, sizeof search_mode);
|
||
|
|
break;
|
||
|
|
case '?':
|
||
|
|
fprintf(stderr, "Invalid option'%c'.\n", optopt);
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
case 'h':
|
||
|
|
default:
|
||
|
|
print_usage();
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
argc -= optind;
|
||
|
|
|
||
|
|
if (!k_flag) {
|
||
|
|
fputs("Missing required arguments.\n", stderr);
|
||
|
|
exit(EXIT_FAILURE);
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("Looking for: %s\n", argv[optind]);
|
||
|
|
|
||
|
|
/* Set remaining params if any... */
|
||
|
|
set_param(url, "type", search_mode);
|
||
|
|
set_param(url, "r", "json");
|
||
|
|
/* Just testing... */
|
||
|
|
set_param(url, "t", argv[optind]);
|
||
|
|
|
||
|
|
/* Print it... */
|
||
|
|
print_url(url);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* All of the call setup should be done via the URL so all that's left is to
|
||
|
|
* send the request and conditionally handle any response.
|
||
|
|
*/
|
||
|
|
struct fetch *f = fetch_init(); /* Check this */
|
||
|
|
struct response *resp = fetch(f, url);
|
||
|
|
handle_response(resp);
|
||
|
|
process_image("./test.jpg");
|
||
|
|
free(resp->data);
|
||
|
|
free(resp);
|
||
|
|
curl_url_cleanup(url);
|
||
|
|
fetch_cleanup(f);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|