peertube.hpp 13 KB

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