parse.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #include "parse.h"
  2. #include "menu.h"
  3. #include "common.h"
  4. #include <stdarg.h>
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <ctype.h>
  9. static char* json_get_value(const char* start, const char* end, const char* key);
  10. static char* get_obj_end(const char* ptr, const char* end);
  11. static char* get_obj_end(const char* ptr, const char* end)
  12. {
  13. const char* prev = ptr;
  14. char* found = NULL;
  15. int skip = 0;
  16. if(*prev == '{') prev++;
  17. while((found = memchr(prev, '}', end-prev)) != NULL)
  18. {
  19. while((prev = memchr(prev, '{', found-prev)) != NULL)
  20. {
  21. skip++;
  22. prev++;
  23. }
  24. skip--;
  25. if(skip < 0) break;
  26. prev = found+1;
  27. }
  28. return found;
  29. }
  30. int parse_url(const char* url, char** base, char** rq)
  31. {
  32. const char* base_start;
  33. const char* rq_start;
  34. int base_size, rq_size, url_size;
  35. url_size = strlen(url);
  36. // Skip http(s):// if there is
  37. if((base_start = strstr(url, "://")) != NULL)
  38. base_start += 3;
  39. else
  40. base_start = url;
  41. if(!(rq_start = strstr(base_start, "/")))
  42. return -1;
  43. if(base != NULL)
  44. {
  45. base_size = rq_start - base_start + 1;
  46. *base = malloc(base_size);
  47. memcpy(*base, base_start, base_size);
  48. *(*base+base_size-1) = '\0';
  49. }
  50. if(rq != NULL)
  51. {
  52. rq_size = url + url_size - rq_start + 1;
  53. *rq = malloc(rq_size);
  54. memcpy(*rq, rq_start, rq_size);
  55. *(*rq+rq_size-1) = '\0';
  56. }
  57. return 0;
  58. }
  59. static size_t strcnt(char const *str, char const *substr)
  60. {
  61. size_t count = 0;
  62. size_t len = strlen(substr);
  63. while((str = strstr(str, substr)))
  64. {
  65. str += len;
  66. count++;
  67. }
  68. return count;
  69. }
  70. static char* json_get_value(const char* start, const char* end, const char* key)
  71. {
  72. char* ptr = NULL;
  73. size_t keylen = strlen(key);
  74. if(!(ptr = memmem(start, end - start, key, keylen)))
  75. return NULL;
  76. ptr += keylen;
  77. ptr++; // Skip delimiter
  78. size_t len;
  79. switch(*ptr)
  80. {
  81. case '\"': // String
  82. ptr++;
  83. for(len = 0; *(ptr+len) != '\"'; len++)
  84. if(*(ptr+len) == '\\') len++;
  85. break;
  86. case '-': // Number
  87. case '0' ... '9':
  88. for(len = 0; isdigit(*(ptr+len)) || *(ptr+len) == '-'; len++);
  89. break;
  90. default:
  91. return NULL;
  92. break;
  93. }
  94. char* string = malloc(len+1);
  95. memcpy(string, ptr, len);
  96. string[len] = '\0';
  97. return string;
  98. }
  99. video_list* parse_playlist(const char* info)
  100. {
  101. #define title_str "data-title"
  102. #define id_str "data-video-id"
  103. const char* ptr = info;
  104. unsigned count = strcnt(ptr, title_str);
  105. video_list* vl = malloc(sizeof(*vl));
  106. vl->vi = malloc(count*sizeof(*vl->vi));
  107. vl->count = count;
  108. for(unsigned i = 0; i < count; i++)
  109. {
  110. video_info* vi = calloc(1, sizeof(*vi));
  111. vl->vi[i] = vi;
  112. ptr = strstr(ptr, title_str);
  113. vi->title = json_get_value(ptr, ptr+strlen(ptr), title_str);
  114. vi->id = json_get_value(ptr, ptr+strlen(ptr), id_str);
  115. ptr += sizeof(title_str);
  116. }
  117. return vl;
  118. #undef title_str
  119. #undef id_str
  120. }
  121. static int populate_media_info(media_info* mi)
  122. {
  123. if(!mi || !mi->url)
  124. return -1;
  125. // Get itag from url
  126. const char* find = "itag=";
  127. char* ptr = strstr(mi->url, find);
  128. if(!ptr)
  129. return -1;
  130. ptr += strlen(find);
  131. sscanf(ptr, "%3hu", &mi->itag);
  132. for(unsigned i = 0; i < itag_table_count; i++)
  133. {
  134. if(mi->itag == itag_table[i].itag)
  135. {
  136. char* url = mi->url;
  137. memcpy(mi, &itag_table[i], sizeof(*mi));
  138. mi->url = url;
  139. return 0;
  140. }
  141. }
  142. return -1;
  143. }
  144. media_list* parse_media_info(const char* info)
  145. {
  146. char* start = NULL;
  147. char* end = NULL;
  148. char const *find = "adaptive_fmts=";
  149. if(!(start = strstr(info, find))) return NULL;
  150. start += strlen(find);
  151. if(!(end = strchr(start, '&'))) return NULL;
  152. // Do not modify original buffer
  153. // Create new one
  154. char* buf = strndup(start, end-start);
  155. percent_decode(buf);
  156. start = buf;
  157. find = "url=";
  158. unsigned count = strcnt(start, find);
  159. media_list* ml = malloc(sizeof(*ml));
  160. ml->mi = malloc(count*sizeof(*ml->mi));
  161. ml->count = count;
  162. for(unsigned i = 0; i < count; i++)
  163. ml->mi[i] = calloc(1, sizeof(*ml->mi[i]));
  164. for(char* str = strtok(start, "&,"); str; str = strtok(NULL, "&,"))
  165. {
  166. if(!strncmp(str, "url=", 4))
  167. {
  168. ml->mi[--count]->url = strdup(str+4);
  169. percent_decode(ml->mi[count]->url);
  170. populate_media_info(ml->mi[count]);
  171. }
  172. }
  173. free(buf);
  174. return ml;
  175. }
  176. video_info* parse_video_info(const char* info)
  177. {
  178. char* start = NULL;
  179. char* end = NULL;
  180. // Find video information block
  181. char const *find = "player_response=";
  182. if(!(start = strstr(info, find))) return NULL;
  183. start += strlen(find);
  184. // Do not modify original buffer
  185. // Create new one
  186. char* buf = strndup(start, end-start);
  187. percent_decode(buf);
  188. start = replace_sub_str(buf, "\\n", "\n");
  189. delete_sub_str(start, "\\");
  190. end = start + strlen(start);
  191. video_info* vi = calloc(1, sizeof(*vi));
  192. vi->id = json_get_value(start, end, "\"videoId\"");
  193. vi->title = json_get_value(start, end, "\"title\"");
  194. vi->length = json_get_value(start, end, "\"lengthSeconds\"");
  195. vi->channel_id = json_get_value(start, end, "\"channelId\"");
  196. vi->views = json_get_value(start, end, "\"viewCount\"");
  197. vi->author = json_get_value(start, end, "\"author\"");
  198. vi->description = json_get_value(start, end, "\"shortDescription\"");
  199. free(start);
  200. free(buf);
  201. return vi;
  202. }
  203. video_list* parse_search_json(const char* info)
  204. {
  205. char* const find = "\"video\":[";
  206. char* ptr = NULL;
  207. if(!(ptr = strstr(info, find))) return NULL;
  208. ptr += strlen(find);
  209. unsigned count = strcnt(ptr, "{");
  210. video_list* vl = malloc(sizeof(*vl));
  211. vl->vi = malloc(count*sizeof(*vl->vi));
  212. vl->count = count;
  213. for(unsigned i = 0; i < count; i++)
  214. {
  215. video_info* vi = calloc(1, sizeof(*vi));
  216. vl->vi[i] = vi;
  217. ptr = strchr(ptr, '{')+1;
  218. char* end = get_obj_end(ptr, ptr + strlen(ptr));
  219. vi->title = json_get_value(ptr, end, "\"title\"");
  220. vi->author = json_get_value(ptr, end, "\"author\"");
  221. vi->channel_id = json_get_value(ptr, end, "\"user_id\"");
  222. vi->id = json_get_value(ptr, end, "\"encrypted_id\"");
  223. vi->length = json_get_value(ptr, end, "\"length_seconds\"");
  224. vi->views = json_get_value(ptr, end, "\"views\"");
  225. }
  226. return vl;
  227. }
  228. char* replace_sub_str(const char* start, const char* sub, const char* new)
  229. {
  230. size_t copied = 0;
  231. size_t sublen = strlen(sub);
  232. size_t newlen = strlen(new);
  233. size_t subcnt = strcnt(start, sub);
  234. const char* str = NULL;
  235. const char* cur = start;
  236. const char* end = start+strlen(start);
  237. char* buf = malloc(end-start + newlen*subcnt - sublen*subcnt + 1);
  238. if(!buf) return NULL;
  239. while((str = memmem(cur, end-cur, sub, sublen)))
  240. {
  241. memcpy(buf+copied, cur, str-cur);
  242. copied += str-cur;
  243. cur = str+sublen;
  244. memcpy(buf+copied, new, newlen);
  245. copied += newlen;
  246. }
  247. memcpy(buf+copied, cur, end-cur);
  248. copied += end-cur;
  249. buf[copied] = '\0';
  250. return buf;
  251. }
  252. int delete_sub_str(char* buf, char* sub)
  253. {
  254. size_t copied = 0;
  255. size_t sublen = sub == NULL ? 0 : strlen(sub);
  256. if(!sublen) return -1;
  257. char* str = NULL;
  258. char* cur = buf;
  259. char* end = buf+strlen(buf);
  260. while((str = memmem(cur, end-cur, sub, sublen)) != NULL)
  261. {
  262. memmove(buf+copied, cur, str-cur);
  263. copied += str-cur;
  264. cur = str+sublen;
  265. }
  266. memmove(buf+copied, cur, end-cur);
  267. copied += end-cur;
  268. buf[copied] = '\0';
  269. return copied;
  270. }
  271. int percent_decode(char* buf)
  272. {
  273. size_t copied = 0;
  274. char* str = NULL;
  275. char* cur = buf;
  276. char* end = cur + strlen(buf);
  277. while((str = memchr(cur, '%', end-cur)) != NULL)
  278. {
  279. unsigned chr;
  280. sscanf(str, "%%%02X", &chr);
  281. memmove(buf+copied, cur, str-cur);
  282. copied += str-cur;
  283. buf[copied++] = chr;
  284. cur = str+3;
  285. }
  286. memmove(buf+copied, cur, end-cur);
  287. copied += end-cur;
  288. buf[copied] = '\0';
  289. return 0;
  290. }
  291. char* percent_encode(const char* s)
  292. {
  293. static char table[256] = {0};
  294. static _Bool init = 0;
  295. char* buf = malloc(strlen(s)*3 + 1);
  296. char* enc = buf;
  297. if(!buf)
  298. return NULL;
  299. if(!init)
  300. {
  301. for(unsigned i = 0; i < 256; i++)
  302. table[i] = isalnum(i) || i == '*' || i == '-' || i == '.' || i == '_' ? i : (i == ' ') ? '+' : 0;
  303. init = 1;
  304. }
  305. for(; *s; s++)
  306. {
  307. if(table[(unsigned char)*s])
  308. *(enc++) = table[(unsigned char)*s];
  309. else
  310. {
  311. sprintf(enc, "%%%02X", *s);
  312. enc += 3;
  313. }
  314. }
  315. *enc = '\0';
  316. return buf;
  317. }