summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMole Shang <[email protected]>2023-08-09 22:39:30 +0800
committerMole Shang <[email protected]>2023-08-10 12:09:42 +0800
commit06feb4557af89dfa8e695a5e89f77d38e6342fb4 (patch)
tree92790010a675f6e9408a82fb5c45cf9935d701dc
parentf7ccf916d9755700c655499afb0c8c636be07242 (diff)
downloadhinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.tar.gz
hinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.tar.bz2
hinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.zip
extractors: initial youku support
-rw-r--r--src/extractors/extractor.c10
-rw-r--r--src/extractors/extractor.h6
-rw-r--r--src/extractors/youku.c179
-rw-r--r--src/extractors/youku.h29
4 files changed, 218 insertions, 6 deletions
diff --git a/src/extractors/extractor.c b/src/extractors/extractor.c
index 4bfc7cb..833686e 100644
--- a/src/extractors/extractor.c
+++ b/src/extractors/extractor.c
@@ -6,9 +6,10 @@
#include "haokan.h"
#include "youku.h"
-Site_map site_map = {
- {{"www.bilibili.com", SITE_BILIBILI}, {"haokan.baidu.com", SITE_HAOKAN}},
- 2};
+Site_map site_map = {{{"www.bilibili.com", SITE_BILIBILI},
+ {"haokan.baidu.com", SITE_HAOKAN},
+ {"v.youku.com", SITE_YOUKU}},
+ 3};
void options_cleanup(Options *options) {
free_and_nullify((void **)&options->URL);
@@ -26,6 +27,9 @@ int extract(void *v) {
case SITE_HAOKAN:
haokan_extract(options);
break;
+ case SITE_YOUKU:
+ youku_extract(options);
+ break;
}
options_cleanup(options);
return 0;
diff --git a/src/extractors/extractor.h b/src/extractors/extractor.h
index 347662b..9daf0ff 100644
--- a/src/extractors/extractor.h
+++ b/src/extractors/extractor.h
@@ -6,14 +6,14 @@
#include <stdbool.h>
#include <stddef.h>
-enum site { SITE_BILIBILI, SITE_HAOKAN };
+enum site { SITE_BILIBILI, SITE_HAOKAN, SITE_YOUKU };
typedef enum site site_t;
typedef struct site_map {
struct {
char domain[SHRT_MAX];
site_t site;
- } pairs[2];
+ } pairs[3];
unsigned char size;
} Site_map;
@@ -25,7 +25,7 @@ typedef struct options {
char *pagedata;
} Options;
-void options_cleanup(Options*);
+void options_cleanup(Options *);
int extract(void *);
diff --git a/src/extractors/youku.c b/src/extractors/youku.c
new file mode 100644
index 0000000..4e5bc47
--- /dev/null
+++ b/src/extractors/youku.c
@@ -0,0 +1,179 @@
+#include "youku.h"
+#include "../logger.h"
+#include "../process_url.h"
+#include "../utils/ffmpeg.h"
+#include "../utils/utils.h"
+#include <cjson/cJSON.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static callback_struct_t callback_struct;
+
+static int generate_api(Youku_options *youku_options) {
+ char params[UCHAR_MAX];
+ snprintf(params, sizeof(params),
+ "vid=%s&ccode=%s&client_ip=192.168.1.1&client_ts=%d&utid=%s",
+ youku_options->vid, youku_options->ccode,
+ (int)(youku_options->client_ts / 1000), youku_options->utid);
+ youku_options->api = malloc(strlen(YOUKU_API) + strlen(params) + 1);
+ strcpy(youku_options->api, YOUKU_API);
+ strcat(youku_options->api, params);
+ return 0;
+}
+
+static int parse_jsondata(cJSON *json, Youku_data *youku_data) {
+ cJSON *data_obj = cJSON_GetObjectItem(json, "data");
+ if (!data_obj) {
+ LOG("cJSON", "Parse API data failed.\n");
+ return 1;
+ }
+
+ cJSON *video_obj = cJSON_GetObjectItem(data_obj, "video");
+ cJSON *streams_obj = cJSON_GetObjectItem(data_obj, "stream");
+ if (!video_obj || !streams_obj) {
+ LOG("cJSON", "Parse API data child object failed.\n");
+ return 1;
+ }
+ cJSON *title_obj = cJSON_GetObjectItem(video_obj, "title");
+ if (!title_obj) {
+ LOG("cJSON", "Parse API data.video.title failed.\n");
+ return 1;
+ }
+ youku_data->title = title_obj->valuestring;
+ DEBUG_PRINT("data.video.title: %s\n", youku_data->title);
+
+ cJSON *e;
+ int max_size = 0;
+ cJSON_ArrayForEach(e, streams_obj) {
+ cJSON *size_obj = cJSON_GetObjectItem(e, "size");
+ cJSON *width_obj = cJSON_GetObjectItem(e, "width");
+ cJSON *height_obj = cJSON_GetObjectItem(e, "height");
+ cJSON *m3u8_url_obj = cJSON_GetObjectItem(e, "m3u8_url");
+ if (!size_obj || !width_obj || !height_obj || !m3u8_url_obj) {
+ LOG("cJSON", "Parse API data.stream child object failed.\n");
+ return 1;
+ }
+ if (size_obj->valueint > max_size) {
+ max_size = size_obj->valueint;
+ youku_data->width = width_obj->valueint;
+ youku_data->height = height_obj->valueint;
+ youku_data->m3u8_url = m3u8_url_obj->valuestring;
+ }
+ }
+ DEBUG_PRINT("width: %d, height: %d\n", youku_data->width, youku_data->height);
+
+ return 0;
+}
+
+static void youku_options_cleanup(Youku_options *youku_options) {
+ free_and_nullify((void **)&youku_options->vid);
+ free_and_nullify((void **)&youku_options->utid);
+ free_and_nullify((void **)&youku_options->api);
+ cJSON_Delete(youku_options->json);
+}
+
+static int youku_convert(void *v) {
+ callback_struct_t *callback_struct = (callback_struct_t *)v;
+ const char *m3u8fn = get_str_element(callback_struct, 0);
+ const char *filename = get_str_element(callback_struct, 1);
+ DEBUG_PRINT("m3u8fn: %s\n", m3u8fn);
+ DEBUG_PRINT("filename: %s\n", filename);
+
+ int r = remux(m3u8fn, filename);
+ LOG("Youku", "All done!\n");
+ free_str_array(callback_struct);
+ return r;
+}
+
+void youku_extract(Options *options) {
+ Youku_options youku_options = {0};
+ str_array_t results = create_str_array(0);
+ {
+ const char *patterns_str[2] = {"id_(.+?)\\.html", "id_(.+)"};
+ const str_array_t patterns = {(char **)patterns_str, 2};
+ int r = regex_match(options->URL, patterns, &results);
+ if (r) {
+ LOG("Youku", "Parse URL failed.\n");
+ goto end;
+ }
+ // for (unsigned short i = 0; i < results.n; i++) {
+ // DEBUG_PRINT("%s\n", results.str[i]);
+ // }
+ if (!results.str[0]) {
+ LOG("Youku", "Get vid failed.\n");
+ goto end;
+ }
+ youku_options.vid = malloc(strlen(results.str[0]) + 1);
+ strcpy(youku_options.vid, results.str[0]);
+ DEBUG_PRINT("vid: %s\n", youku_options.vid);
+ }
+
+ {
+ resize_str_array(&results, 0);
+ char *cookie = NULL;
+ set_referer("https://v.youku.com");
+ get_info("http://log.mmstat.com/eg.js", NULL, NULL, &cookie);
+ if (!cookie) {
+ goto end;
+ }
+ const char *patterns_str[1] = {"cna=(.+?);"};
+ const str_array_t patterns = {(char **)patterns_str, 1};
+ int r = regex_match(cookie, patterns, &results);
+ if (r) {
+ LOG("Youku", "Parse cookie failed.\n");
+ goto end;
+ }
+ // for (unsigned short i = 0; i < results.n; i++) {
+ // DEBUG_PRINT("%s\n", results.str[i]);
+ // }
+ if (!results.str[0]) {
+ LOG("Youku", "Get utid failed.\n");
+ goto end;
+ }
+ youku_options.utid = malloc(strlen(results.str[0]) + 1);
+ strcpy(youku_options.utid, results.str[0]);
+ DEBUG_PRINT("utid: %s\n", youku_options.utid);
+ }
+
+ youku_options.ccode = "0524";
+ youku_options.client_ip = "192.168.1.1";
+ youku_options.client_ts = time(NULL);
+ if (generate_api(&youku_options)) {
+ LOG("Youku", "Generate API failed.\n");
+ goto end;
+ }
+ DEBUG_PRINT("Generated API: %s\n", youku_options.api);
+ char *resp = NULL;
+ if (get(youku_options.api, &resp)) {
+ LOG("Youku", "Get API data failed.\n");
+ free_and_nullify((void **)&resp);
+ goto end;
+ }
+ // DEBUG_PRINT("resp: %s\n", resp);
+
+ youku_options.json = cJSON_Parse(resp);
+ free_and_nullify((void **)&resp);
+ Youku_data youku_data = {0};
+ if (parse_jsondata(youku_options.json, &youku_data)) {
+ LOG("Youku", "Parse jsondata failed.\n");
+ goto end;
+ }
+
+ callback_struct = create_str_array(2);
+ char *m3u8fn = malloc(strlen(youku_data.title) + 18);
+ sprintf(m3u8fn, "%s-[%dx%d].m3u8", youku_data.title, youku_data.width,
+ youku_data.height);
+ set_str_element(&callback_struct, 0, m3u8fn);
+ char *filename = malloc(strlen(youku_data.title) + 17);
+ sprintf(filename, "%s-[%dx%d].mp4", youku_data.title, youku_data.width,
+ youku_data.height);
+ set_str_element(&callback_struct, 1, filename);
+ add_url(youku_data.m3u8_url, NULL, m3u8fn, youku_convert, &callback_struct);
+ free_and_nullify((void **)&m3u8fn);
+ free_and_nullify((void **)&filename);
+
+end:
+ free_str_array(&results);
+ youku_options_cleanup(&youku_options);
+}
diff --git a/src/extractors/youku.h b/src/extractors/youku.h
new file mode 100644
index 0000000..721ff75
--- /dev/null
+++ b/src/extractors/youku.h
@@ -0,0 +1,29 @@
+#ifndef YOUKU_H_
+#define YOUKU_H_
+
+#include <time.h>
+
+#include "extractor.h"
+
+#define YOUKU_API "https://ups.youku.com/ups/get.json?"
+
+typedef struct youku_options {
+ char *api;
+ char *vid;
+ char *utid;
+ char *ccode;
+ const char *client_ip;
+ time_t client_ts;
+ cJSON *json;
+} Youku_options;
+
+typedef struct youku_data {
+ char *title;
+ int width;
+ int height;
+ char *m3u8_url;
+} Youku_data;
+
+void youku_extract(Options *options);
+
+#endif