main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include "libtorrent/session.hpp"
  2. #include "libtorrent/torrent_handle.hpp"
  3. #include "libtorrent/magnet_uri.hpp"
  4. #include <string>
  5. #include <vector>
  6. #include <iostream>
  7. #include <sstream>
  8. #include <vector>
  9. #include <thread>
  10. #include <chrono>
  11. #include <memory>
  12. void read_alerts(std::vector<lt::alert*> &alerts) {
  13. for (lt::alert const* a : alerts) {
  14. // alert examples on libtorrent do not work.
  15. // https://www.libtorrent.org/tutorial-ref.html
  16. // results in: compilation error - incomplete type
  17. // type code referenced from:
  18. // ./deps/libtorrent/include/libtorrent/alert_types.hpp
  19. switch (a -> type()) {
  20. default:
  21. std::cout << a -> message() << ", " << a -> type() << std::endl;
  22. // case 11: // tracker unreachable
  23. // case 67: // torrent added
  24. // case 23:
  25. // case 26: // torrent finished alert
  26. // case 64: // torrent error alert
  27. }
  28. }
  29. return;
  30. }
  31. std::vector<std::string> readfile(std::string manifest) {
  32. std::vector<std::string> out;
  33. std::fstream fin;
  34. fin.open(manifest);
  35. std::string line;
  36. if (!fin.is_open()) {
  37. fprintf(stderr, "Error: could not open file.\n");
  38. }
  39. while (fin) {
  40. std::getline(fin, line);
  41. out.push_back(line);
  42. }
  43. return out;
  44. }
  45. void display(lt::torrent_status &ts) {
  46. // <download_rate> downloading <name> from <num_peers> (<num seeds> seeds)
  47. // <upload rate> uploading <name> to <num_peers> (<num seeds> seeds)
  48. // fuck cout
  49. static char last_state = 0;
  50. // don't bother line breaking for checks and meta data'
  51. if (last_state != ts.state) {
  52. std::cout << std::endl;
  53. }
  54. // long names will spam console
  55. #define MAX_LENGTH 40
  56. std::string shortname;
  57. if (ts.name.length() > MAX_LENGTH) {
  58. shortname = ts.name.substr(0, MAX_LENGTH);
  59. } else {
  60. shortname = ts.name;
  61. }
  62. const char *tname = shortname.c_str();
  63. float perc = ts.progress * 100;
  64. float down = ts.download_rate/1000000;
  65. float up = ts.upload_rate/1000000;
  66. std::int64_t total_upload = ts.total_upload/1000000;
  67. switch(ts.state) {
  68. case 1: // checking files
  69. fprintf(stdout, "checking files\r");
  70. break;
  71. case 2: // downloading meta data
  72. fprintf(stdout, "downloading meta data\r");
  73. break;
  74. case 3: // downloading
  75. fprintf(stdout, "[%2.2f%] Downloading %s from %i peers (%i seeds) @ %2.2f MB/s\r",
  76. perc, tname, ts.num_peers, ts.num_seeds, down);
  77. break;
  78. case 4: // finished
  79. break;
  80. case 5: // seeding
  81. fprintf(stdout, "Uploading %s to %i/%i peers (%i seeds) @ %2.2f MB/s [%i MB]\r",
  82. tname, ts.num_peers, ts.list_peers, ts.list_seeds, up, total_upload);
  83. break;
  84. }
  85. last_state = ts.state;
  86. }
  87. void get_handles_from_list(lt::session &s,
  88. std::vector<lt::torrent_handle> &h,
  89. std::string manifest) {
  90. std::vector<std::string> torrents = readfile(manifest.c_str());
  91. if (torrents.empty()) {
  92. return;
  93. }
  94. std::string torrent;
  95. for (unsigned int i = 0; i < torrents.size(); ++i) {
  96. torrent = torrents[i];
  97. if (torrent.length() < 2) { // less than 1 char and newline
  98. continue;
  99. }
  100. lt::add_torrent_params p;
  101. p.save_path = "./downloaded";
  102. try {
  103. p.ti = std::make_shared<lt::torrent_info>(torrent);
  104. } catch (std::exception const&e) {
  105. std::cerr << "Encountered error while reading torrent files ("
  106. << torrent
  107. << ").\n"
  108. << e.what() << std::endl;
  109. return;
  110. }
  111. h.push_back(s.add_torrent(p));
  112. }
  113. }
  114. // run torrents in the background
  115. void seedbox (std::string &torrentfiles) {
  116. #define MINUTE 60
  117. lt::session s;
  118. // magnet link version
  119. // lt::add_torrent_params params = lt::parse_magnet_uri(magnet_link);
  120. // params.save_path = "."; // save in current dir
  121. // lt::torrent_handle h = s.add_torrent(params);
  122. std::vector<lt::torrent_handle> handles;
  123. get_handles_from_list(s, handles, torrentfiles);
  124. // std::vector<std::string> maglinks = readfile(magnetlinks.c_str());
  125. const int TORRENT_COUNT = handles.size();
  126. unsigned int not_dl_count = 0;
  127. unsigned int tid = 0;
  128. unsigned int elapsed = 0;
  129. while(true) {
  130. std::vector<lt::alert*> alerts;
  131. s.pop_alerts(&alerts);
  132. read_alerts(alerts);
  133. for (unsigned int i = 0; i < TORRENT_COUNT; ++i) {
  134. lt::torrent_status ts = handles[i].status();
  135. if (!ts.is_seeding && !ts.is_finished) {
  136. not_dl_count++;
  137. }
  138. }
  139. // if no torrents are downloading
  140. if (!(TORRENT_COUNT > not_dl_count)) {
  141. lt::torrent_status spotlight = handles[tid].status();
  142. display(spotlight);
  143. if (elapsed >= MINUTE) {
  144. std::cout << std::endl;
  145. tid = (tid + 1) % TORRENT_COUNT;
  146. }
  147. }
  148. elapsed = (elapsed + 1) % (MINUTE + 1);
  149. std::this_thread::sleep_for(std::chrono::seconds(1));
  150. }
  151. }
  152. #include <curlpp/cURLpp.hpp>
  153. #include <curlpp/Easy.hpp>
  154. #include <curlpp/Options.hpp>
  155. std::string request(const std::string& url) {
  156. curlpp::options::Url source (url);
  157. curlpp::Easy request;
  158. request.setOpt(source);
  159. std::ostringstream response;
  160. try {
  161. response << request;
  162. } catch (curlpp::LibcurlRuntimeError e) {
  163. std::cout << e.what() << std::endl;
  164. } catch (std::exception const& e) {
  165. std::cout << e.what() << std::endl;
  166. }
  167. return response.str();
  168. }
  169. #include "libtorrent/create_torrent.hpp"
  170. #include "libtorrent/entry.hpp"
  171. #include "libtorrent/bencode.hpp"
  172. #include "peertube.hpp" // also includes rapidjson
  173. void torrent_basic_loop(std::string torrent_file) {
  174. lt::session s;
  175. lt::add_torrent_params p;
  176. p.save_path = "./downloaded";
  177. p.ti = std::make_shared<lt::torrent_info>(torrent_file);
  178. lt::torrent_handle h = s.add_torrent(p);
  179. h.set_flags(lt::torrent_flags::sequential_download);
  180. lt::torrent_status ts;
  181. do {
  182. std::vector<lt::alert*> alerts;
  183. s.pop_alerts(&alerts);
  184. read_alerts(alerts);
  185. ts = h.status();
  186. display(ts);
  187. std::this_thread::sleep_for(std::chrono::seconds(1));
  188. } while (!ts.is_seeding && !ts.is_finished);
  189. }
  190. // download torrent file from peertube server
  191. std::string dl_torrentfile(struct file_t *video_file, const char * title = NULL) {
  192. if (video_file == NULL) {
  193. return std::string();
  194. }
  195. if (video_file -> attribs == NULL) {
  196. return std::string();
  197. }
  198. std::string torrent_url (video_file -> attribs[TORRENTDOWNLOAD_URL]);
  199. std::string buffer = request(torrent_url);
  200. std::string filename;
  201. {
  202. std::stringstream ss;
  203. if (title == NULL) { // use the file name from the url
  204. unsigned int pos = torrent_url.rfind("/");
  205. pos++; // +1 to omit '/'
  206. ss << torrent_url.substr(pos, torrent_url.size());
  207. } else {
  208. // does not remove forbidden characters in file name yet
  209. ss << title <<"-"<< video_file -> resolution << "p.torrent";
  210. }
  211. filename = ss.str();
  212. std::cout << filename << std::endl;
  213. }
  214. std::fstream fout;
  215. fout.open (filename, std::fstream::out | std::fstream::binary);
  216. if (fout.good()) {
  217. fout << buffer;
  218. }
  219. fout.close();
  220. return filename;
  221. }
  222. // watch a video given a URL
  223. bool watch(std::string video_url) {
  224. //turn peertube video url into api request
  225. bool is_playlist = false;
  226. std::string api = get_endpoint(video_url, is_playlist);
  227. if (api.length() == 0) {
  228. std::cout << "Error: can't watch video." << std::endl;
  229. return false;
  230. }
  231. // figure something out with playlists.
  232. if (is_playlist) {
  233. std::cout << "playlist support not implemented" << std::endl;
  234. return false;
  235. }
  236. std::string got = request(api);
  237. std::cout << api << std::endl;
  238. rapidjson::Document root;
  239. root.Parse(got.c_str());
  240. // video downloads can either be in "files"
  241. // or under "streamingPlaylists" if HLS is enabled on the server
  242. struct video_t *video = init_from_json(root);
  243. video_print(video);
  244. // get torrent download url
  245. struct file_t *best_file = video_pick_file(video);
  246. // torrent file > magnet link if peertube instance's tracker is unreliable
  247. std::string saved = dl_torrentfile(best_file);
  248. if (!saved.empty()) {
  249. std::cout << "acquired .torrent file! " << saved << std::endl;
  250. torrent_basic_loop(saved);
  251. }
  252. std::cout << "\n\n\n" << std::endl;
  253. video_free(video);
  254. return true;
  255. }
  256. void test (std::string video_url) {
  257. bool is_playlist;
  258. std::string api = get_endpoint(video_url, is_playlist);
  259. std::string got = request(api);
  260. std::cout << got.length() << std::endl;
  261. rapidjson::Document root;
  262. root.Parse(got.c_str());
  263. const rapidjson::Value& vf = get_video_files(root);
  264. for (rapidjson::SizeType i = 0; i < vf.Size(); ++i) {
  265. struct file_t *test = init_from_json(vf[i]);
  266. if (test != NULL) {
  267. file_print(test);
  268. file_free(test);
  269. }
  270. }
  271. }
  272. void test2 (std::string video_url) {
  273. bool is_playlist;
  274. std::string api = get_endpoint(video_url, is_playlist);
  275. std::string got = request(api);
  276. // std::cout << api << std::endl;
  277. rapidjson::Document root;
  278. root.Parse(got.c_str());
  279. struct video_t *video = init_from_json(root);
  280. video_print(video);
  281. // get torrent download url
  282. struct file_t *best_file = video_pick_file(video);
  283. std::string saved = dl_torrentfile(best_file);
  284. if (!saved.empty()) {
  285. std::cout << "acquired .torrent file! " << saved << std::endl;
  286. torrent_basic_loop(saved);
  287. }
  288. std::cout << "\n\n\n" << std::endl;
  289. video_free(video);
  290. }
  291. int main(int argc, char const* argv[]) {
  292. std::cout << "This is cppia (unreleased)." << std::endl;
  293. bool success;
  294. switch (argc) {
  295. case 1:
  296. std::cout << "Usages: \n"
  297. << "\t cppia <video url>\n"
  298. << "\t cppia seed <list of torrent files>" << std::endl;
  299. break;
  300. case 2:
  301. // if there is only one argument, assume it is a video url
  302. success = watch(std::string(argv[1]));
  303. if (!success) {
  304. std::cout << "Usage: cppia <video url>" << std::endl;
  305. }
  306. break;
  307. case 3:
  308. std::string option (argv[1]);
  309. std::string s(argv[2]);
  310. if (option.compare("seed") == 0) {
  311. seedbox(s);
  312. } else if (option.compare("test") == 0) {
  313. test2(s);
  314. }
  315. break;
  316. }
  317. return 0;
  318. }