diff options
author | Mole Shang <[email protected]> | 2023-08-09 22:39:30 +0800 |
---|---|---|
committer | Mole Shang <[email protected]> | 2023-08-10 12:09:42 +0800 |
commit | 06feb4557af89dfa8e695a5e89f77d38e6342fb4 (patch) | |
tree | 92790010a675f6e9408a82fb5c45cf9935d701dc /src/extractors | |
parent | f7ccf916d9755700c655499afb0c8c636be07242 (diff) | |
download | hinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.tar.gz hinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.tar.bz2 hinata-06feb4557af89dfa8e695a5e89f77d38e6342fb4.zip |
extractors: initial youku support
Diffstat (limited to 'src/extractors')
-rw-r--r-- | src/extractors/extractor.c | 10 | ||||
-rw-r--r-- | src/extractors/extractor.h | 6 | ||||
-rw-r--r-- | src/extractors/youku.c | 179 | ||||
-rw-r--r-- | src/extractors/youku.h | 29 |
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 |