peertube.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #ifndef PEERTUBE_HTP
  2. #define PEERTUBE_HPP
  3. #include <string>
  4. #include <cstring>
  5. #include <iostream>
  6. #include <sstream>
  7. int interpret(const std::string &user_input, std::string &host, std::string &id) {
  8. // what did the user link?
  9. char *token = new char[user_input.length() + 1];
  10. strcpy(token, user_input.c_str());
  11. token = strtok(token, "/");
  12. unsigned int state = 0;
  13. bool is_playlist = false;
  14. while (token != NULL) {
  15. switch (state) {
  16. case 0: // protocol
  17. if (strcmp(token, "https:") != 0
  18. && strcmp(token, "http:") != 0) {
  19. goto endwhile;
  20. }
  21. state++;
  22. break;
  23. case 1: // domain
  24. host = std::string(token);
  25. state++;
  26. break;
  27. case 2: // can be either api or 'w'
  28. if (strcmp(token, "w") == 0) {
  29. state++;
  30. } else if (strcmp(token, "api") == 0) {
  31. state = 'a';
  32. } break;
  33. case 3: // can either be playlist indicator or a video id
  34. if (strcmp(token, "p") == 0) {
  35. state++;
  36. } else {
  37. id = std::string(token);
  38. } break;
  39. case 4: // playlist id
  40. id = std::string(token);
  41. break;
  42. case 'a': // api version indicator
  43. state++;
  44. break;
  45. case 'b':
  46. if (strcmp(token, "videos") == 0) {
  47. state++;
  48. } else if (strcmp(token,"video-playlists") == 0) {
  49. is_playlist = true;
  50. state++;
  51. } break;
  52. case 'c':
  53. id = std::string(token);
  54. break;
  55. }
  56. token = strtok(NULL, "/");
  57. }
  58. endwhile:
  59. delete [] token;
  60. if (is_playlist) {
  61. state++; // so caller func knows api video vs api playlist
  62. }
  63. return state;
  64. }
  65. // https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo
  66. // https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideoPlaylistVideos
  67. std::string get_endpoint(const std::string &user_input, bool &is_playlist) {
  68. std::string host;
  69. std::string id;
  70. int what = 0;
  71. what = interpret(user_input, host, id);
  72. std::string url = std::string("");
  73. std::string api;
  74. std::stringstream ss;
  75. switch (what) {
  76. default:
  77. std::cout << what << std::endl;
  78. case 0:
  79. case 1:
  80. std::cout << "URL not recognized." << std::endl;
  81. break;
  82. case 3:
  83. std::cout << "video page" << std::endl;
  84. api = "/api/v1/videos/"; //(host, id)
  85. ss << "https://" << host << api << id;
  86. url = ss.str();
  87. break;
  88. case 'c': // video api
  89. std::cout << "video api endpoint" << std::endl;
  90. url = user_input;
  91. break;
  92. case 'd':
  93. std::cout << "playlist api endpoint" << std::endl;
  94. url = user_input;
  95. break;
  96. case 4:
  97. std::cout << "playlist page" << std::endl;
  98. api = "/api/v1/video-playlists/";
  99. ss << "https://" << host << api << id << "/videos";
  100. url = ss.str();
  101. break;
  102. }
  103. return url;
  104. }
  105. // #include "peertube/model/video.h" <- model does not compile
  106. // https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo
  107. // salvage the stuff that didn't compile.'
  108. #define FILE_T_STR_COUNT 6
  109. enum {
  110. MAGNET_URI,
  111. TORRENT_URL,
  112. TORRENTDOWNLOAD_URL,
  113. FILE_URL,
  114. FILEDOWNNLOAD_URL,
  115. METADATA_URL
  116. } file_t_attribs;
  117. struct file_t {
  118. // char *magnetUri;
  119. int resolution; // ignore label
  120. int size;
  121. // char *torrentUrl;
  122. // char *torrentDownloadUrl;
  123. // char *fileUrl;
  124. // char *fileDownloadUrl;
  125. int fps;
  126. // char *metadataurl;
  127. char *attribs[FILE_T_STR_COUNT]; // B)
  128. };
  129. struct file_t* file_init (int resolution,
  130. int size,
  131. int fps,
  132. unsigned int len_data,
  133. const char **data) {
  134. struct file_t *f = (file_t*)malloc(sizeof(file_t));
  135. f -> resolution = resolution;
  136. f -> size = size;
  137. f -> fps = fps;
  138. if (len_data != FILE_T_STR_COUNT) {
  139. return NULL;
  140. }
  141. // not going to take any cues
  142. // on how to initialize a struct from openapigenerator
  143. unsigned int len;
  144. unsigned int i = 0;
  145. for (i; i < FILE_T_STR_COUNT; ++i) {
  146. if (data[i] == NULL) {
  147. return NULL;
  148. }
  149. len = strlen(data[i]) * sizeof(char);
  150. f -> attribs[i] = (char*)malloc(len);
  151. strcpy(f -> attribs[i], data[i]);
  152. }
  153. return f;
  154. }
  155. void file_print(const struct file_t * const f) {
  156. unsigned int i = 0;
  157. for (i; i < FILE_T_STR_COUNT; ++i) {
  158. fprintf(stdout, "%s\n", f -> attribs[i]);
  159. }
  160. }
  161. void file_free(struct file_t* f) {
  162. unsigned int i = 0;
  163. for (i; i < FILE_T_STR_COUNT; ++i) {
  164. free(f -> attribs[i]);
  165. }
  166. free(f);
  167. }
  168. struct video_t {
  169. int id;
  170. char *uuid;
  171. char *short_uuid;
  172. // int is_live;
  173. // char *created_at; //date time
  174. // char *published_at; //date time
  175. // char *updated_at; //date time
  176. // char *originally_published_at; //date time
  177. // skip other models
  178. char *description; // string
  179. int duration; //numeric
  180. // int is_local; //boolean
  181. char *name; // string
  182. int views; //numeric
  183. int likes; //numeric
  184. int dislikes; //numeric
  185. int nsfw; //boolean
  186. // skip other models again
  187. int file_count;
  188. struct file_t** files;
  189. };
  190. struct video_t *video_init(int id,
  191. const char *uuid,
  192. const char *short_uuid,
  193. const char *description,
  194. int duration,
  195. const char *name,
  196. int views,
  197. int likes,
  198. int dislikes,
  199. int nsfw,
  200. int file_count,
  201. struct file_t** files) {
  202. struct video_t *v = (video_t*)malloc(sizeof(video_t));
  203. if (!v) {
  204. return NULL;
  205. }
  206. v -> id = id;
  207. v -> duration = duration;
  208. v -> views = views;
  209. v -> likes = likes;
  210. v -> dislikes = dislikes;
  211. v -> nsfw = nsfw;
  212. unsigned int len;
  213. if (uuid != NULL) {
  214. len = strlen(uuid) * sizeof(char);
  215. v -> uuid = (char *)malloc(len);
  216. strcpy(v -> uuid, uuid);
  217. }
  218. if (short_uuid != NULL) {
  219. len = strlen(short_uuid) * sizeof(char);
  220. v -> short_uuid = (char*)malloc(len);
  221. strcpy(v -> short_uuid, short_uuid);
  222. }
  223. if (description != NULL) {
  224. len = strlen(description) * sizeof(char);
  225. v -> description = (char*)malloc(len);
  226. strcpy(v -> description, description);
  227. }
  228. if (name != NULL) {
  229. len = strlen(name) * sizeof(char);
  230. v -> name = (char*)malloc(len);
  231. strcpy(v -> name, name);
  232. }
  233. v -> file_count = file_count;
  234. if (files != NULL) {
  235. v -> files = files;
  236. }
  237. return v;
  238. }
  239. #include <cstdio>
  240. void video_free(struct video_t *v) {
  241. if (v == NULL) {
  242. return;
  243. }
  244. if (v -> uuid != NULL) {
  245. free(v -> uuid);
  246. }
  247. if (v -> short_uuid != NULL) {
  248. free(v -> short_uuid);
  249. }
  250. if (v -> description != NULL) {
  251. free(v -> description);
  252. }
  253. if (v -> name != NULL) {
  254. free(v -> name);
  255. }
  256. unsigned int i;
  257. for (i = 0; i < v-> file_count; ++i) {
  258. file_free(v -> files[i]);
  259. }
  260. free(v -> files);
  261. free(v);
  262. }
  263. void video_print(const struct video_t * const v) {
  264. if (v == NULL) {
  265. return;
  266. }
  267. fprintf(stdout, "%s\n", v -> name);
  268. fprintf(stdout, "%s\n", v -> description);
  269. fprintf(stdout, "%i views\n%i:%i (likes:dislikes)\n",
  270. v -> views, v -> likes, v -> dislikes);
  271. }
  272. #include <cmath>
  273. #include <climits>
  274. // pick file download of a target resolution
  275. struct file_t *video_pick_file(const struct video_t* const v, int target_res=0) {
  276. if (v == NULL) {
  277. return NULL;
  278. }
  279. if (v -> files == NULL) {
  280. return NULL;
  281. }
  282. if (target_res == 0) {
  283. // default behavior => pick best quality
  284. return v-> files[0];
  285. }
  286. int lowest_delta = INT_MAX;
  287. struct file_t *best_file = NULL;
  288. unsigned int i;
  289. for (i = 0; i < v -> file_count; ++i) {
  290. int res = v -> files[i] -> resolution;
  291. int delta = std::abs(target_res - res);
  292. if (delta < lowest_delta) {
  293. lowest_delta = delta;
  294. best_file = v -> files[i];
  295. }
  296. }
  297. return best_file;
  298. }
  299. #include <rapidjson/document.h>
  300. #include <rapidjson/writer.h>
  301. #include <rapidjson/stringbuffer.h>
  302. rapidjson::Value& get_video_files(rapidjson::Document& root) {
  303. rapidjson::Document *ptr = &root;
  304. rapidjson::Value& out = root["files"];
  305. assert(out.IsArray());
  306. if (out.Size() > 0) {
  307. return out;
  308. }
  309. // if the list is empty - HLS is enabled on the server
  310. // therefore, look under streamingPlaylists
  311. rapidjson::Value &playlists = root["streamingPlaylists"];
  312. assert(playlists.IsArray());
  313. for (rapidjson::SizeType i = 0; i < playlists.Size(); ++i) {
  314. if (!playlists[i]["type"].IsInt()) {
  315. continue;
  316. }
  317. // don't know what type other than 1 is.'
  318. if (playlists[i]["type"].GetInt() == 1) {
  319. out = playlists[i]["files"];
  320. break;
  321. } else {
  322. std::cout << "Found streamingPlaylist type != 1" << std::endl;
  323. }
  324. }
  325. return out;
  326. }
  327. struct file_t *init_from_json(const rapidjson::Value& file) {
  328. const char *elem_ints[] = {
  329. // "resolution", // id is nested inside resolution
  330. "size",
  331. "fps"
  332. };
  333. int ints[2];
  334. // keep order -see enum above struct file_t definition
  335. const char *elem_strs[FILE_T_STR_COUNT] = {
  336. "magnetUri",
  337. "torrentUrl",
  338. "torrentDownloadUrl",
  339. "fileUrl",
  340. "fileDownloadUrl",
  341. "metadataUrl"
  342. };
  343. const char *strs[FILE_T_STR_COUNT];
  344. int resolution;
  345. if (!file["resolution"]["id"].IsInt()) {
  346. return NULL;
  347. }
  348. resolution = file["resolution"]["id"].GetInt();
  349. unsigned int i = 0;
  350. for (i; i < 2; ++i) {
  351. const char *element = elem_ints[i];
  352. const rapidjson::Value &v = file[element];
  353. if (!v.IsInt()) {
  354. return NULL;
  355. }
  356. ints[i] = v.GetInt();
  357. }
  358. i = 0;
  359. for (i; i < FILE_T_STR_COUNT; ++i) {
  360. const char *element = elem_strs[i];
  361. const rapidjson::Value &v = file[element];
  362. if (!v.IsString()) {
  363. return NULL;
  364. }
  365. strs[i] = v.GetString();
  366. }
  367. struct file_t *foo = file_init(
  368. resolution, ints[0], ints[1], FILE_T_STR_COUNT, strs);
  369. return foo;
  370. }
  371. struct video_t *init_from_json(rapidjson::Document& root) {
  372. #define INT_ATTRIB_COUNT 5
  373. const char *elem_int[INT_ATTRIB_COUNT] = {
  374. "id",
  375. "duration",
  376. "views",
  377. "likes",
  378. "dislikes",
  379. };
  380. int ints[INT_ATTRIB_COUNT];
  381. #define STR_ATTRIB_COUNT 4
  382. const char *elem_str[STR_ATTRIB_COUNT] = {
  383. "uuid",
  384. "shortUUID",
  385. "description",
  386. "name"
  387. };
  388. std::string strs[STR_ATTRIB_COUNT];
  389. unsigned int i;
  390. for (i = 0; i < INT_ATTRIB_COUNT; ++i) {
  391. const char *element = elem_int[i];
  392. rapidjson::Value& v = root[element];
  393. if (!v.IsInt()) {
  394. return NULL;
  395. }
  396. ints[i] = v.GetInt();
  397. }
  398. for (i = 0; i < STR_ATTRIB_COUNT; ++i) {
  399. const char *element = elem_str[i];
  400. rapidjson::Value& v = root[element];
  401. if (!v.IsString()) {
  402. return NULL;
  403. }
  404. strs[i] = v.GetString();
  405. }
  406. rapidjson::Value &v = root["nsfw"];
  407. if (!v.IsBool()) {
  408. return NULL;
  409. }
  410. bool is_nsfw = v.GetBool();
  411. int nsfw = (is_nsfw) ? 1 : 0;
  412. const rapidjson::Value &vf = get_video_files(root);
  413. unsigned int file_count = vf.Size();
  414. unsigned int size = file_count * sizeof(struct file_t**);
  415. struct file_t ** f = (struct file_t**)malloc(size);
  416. for (rapidjson::SizeType i = 0; i < vf.Size(); ++i) {
  417. f[i] = init_from_json(vf[i]);
  418. }
  419. struct video_t *bar = video_init(
  420. ints[0],
  421. strs[0].c_str(),
  422. strs[1].c_str(),
  423. strs[2].c_str(),
  424. ints[1],
  425. strs[3].c_str(),
  426. ints[2],
  427. ints[3],
  428. ints[4],
  429. nsfw,
  430. file_count,
  431. f
  432. );
  433. return bar;
  434. }
  435. std::string get_magnet_link(const rapidjson::Value& files) {
  436. unsigned int highest = 0;
  437. std::string best_video;
  438. // peertube tends to order highest to lowest, but just to make sure...
  439. for (rapidjson::SizeType i = 0; i < files.Size(); ++i ) {
  440. assert(files[i]["resolution"]["id"].IsInt());
  441. unsigned int resolution = files[i]["resolution"]["id"].GetInt();
  442. std::string magnet_link = files[i]["magnetUri"].GetString();
  443. if (resolution > highest) {
  444. highest = resolution;
  445. best_video = magnet_link;
  446. }
  447. }
  448. std::cout << "The best quality video is " << best_video << std::endl;
  449. return best_video;
  450. }
  451. #endif