#include "youku.h" #include "../logger.h" #include "../process_url.h" #include "../utils/ffmpeg.h" #include "../utils/utils.h" #include #include #include #include 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(youku_options->vid); FREE_AND_NULLIFY(youku_options->utid); FREE_AND_NULLIFY(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(resp); goto end; } // DEBUG_PRINT("resp: %s\n", resp); youku_options.json = cJSON_Parse(resp); FREE_AND_NULLIFY(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(m3u8fn); FREE_AND_NULLIFY(filename); end: free_str_array(&results); youku_options_cleanup(&youku_options); }