123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- #include "libtorrent/session.hpp"
- #include "libtorrent/torrent_handle.hpp"
- #include "libtorrent/magnet_uri.hpp"
- #include <string>
- #include <vector>
- #include <iostream>
- #include <sstream>
- #include <vector>
- #include <thread>
- #include <chrono>
- #include <memory>
- void read_alerts(std::vector<lt::alert*> &alerts) {
- for (lt::alert const* a : alerts) {
- // alert examples on libtorrent do not work.
- // https://www.libtorrent.org/tutorial-ref.html
- // results in: compilation error - incomplete type
- // type code referenced from:
- // ./deps/libtorrent/include/libtorrent/alert_types.hpp
- switch (a -> type()) {
- default:
- std::cout << a -> message() << ", " << a -> type() << std::endl;
- // case 11: // tracker unreachable
- // case 67: // torrent added
- // case 23:
- // case 26: // torrent finished alert
- // case 64: // torrent error alert
- }
- }
- return;
- }
- std::vector<std::string> readfile(std::string manifest) {
- std::vector<std::string> out;
- std::fstream fin;
- fin.open(manifest);
- std::string line;
- if (!fin.is_open()) {
- fprintf(stderr, "Error: could not open file.\n");
- }
- while (fin) {
- std::getline(fin, line);
- out.push_back(line);
- }
- return out;
- }
- void display(lt::torrent_status &ts) {
- // <download_rate> downloading <name> from <num_peers> (<num seeds> seeds)
- // <upload rate> uploading <name> to <num_peers> (<num seeds> seeds)
- // fuck cout
- static char last_state = 0;
- // don't bother line breaking for checks and meta data'
- if (last_state != ts.state) {
- std::cout << std::endl;
- }
- // long names will spam console
- #define MAX_LENGTH 40
- std::string shortname;
- if (ts.name.length() > MAX_LENGTH) {
- shortname = ts.name.substr(0, MAX_LENGTH);
- } else {
- shortname = ts.name;
- }
- const char *tname = shortname.c_str();
- float perc = ts.progress * 100;
- float down = ts.download_rate/1000000;
- float up = ts.upload_rate/1000000;
- std::int64_t total_upload = ts.total_upload/1000000;
- switch(ts.state) {
- case 1: // checking files
- fprintf(stdout, "checking files\r");
- break;
- case 2: // downloading meta data
- fprintf(stdout, "downloading meta data\r");
- break;
- case 3: // downloading
- fprintf(stdout, "[%2.2f%] Downloading %s from %i peers (%i seeds) @ %2.2f MB/s\r",
- perc, tname, ts.num_peers, ts.num_seeds, down);
- break;
- case 4: // finished
- break;
- case 5: // seeding
- fprintf(stdout, "Uploading %s to %i/%i peers (%i seeds) @ %2.2f MB/s [%i MB]\r",
- tname, ts.num_peers, ts.list_peers, ts.list_seeds, up, total_upload);
- break;
- }
- last_state = ts.state;
- }
- void get_handles_from_list(lt::session &s,
- std::vector<lt::torrent_handle> &h,
- std::string manifest) {
- std::vector<std::string> torrents = readfile(manifest.c_str());
- if (torrents.empty()) {
- return;
- }
- std::string torrent;
- for (unsigned int i = 0; i < torrents.size(); ++i) {
- torrent = torrents[i];
- if (torrent.length() < 2) { // less than 1 char and newline
- continue;
- }
- lt::add_torrent_params p;
- p.save_path = "./downloaded";
- try {
- p.ti = std::make_shared<lt::torrent_info>(torrent);
- } catch (std::exception const&e) {
- std::cerr << "Encountered error while reading torrent files ("
- << torrent
- << ").\n"
- << e.what() << std::endl;
- return;
- }
- h.push_back(s.add_torrent(p));
- }
- }
- // run torrents in the background
- void seedbox (std::string &torrentfiles) {
- #define MINUTE 60
- lt::session s;
- // magnet link version
- // lt::add_torrent_params params = lt::parse_magnet_uri(magnet_link);
- // params.save_path = "."; // save in current dir
- // lt::torrent_handle h = s.add_torrent(params);
- std::vector<lt::torrent_handle> handles;
- get_handles_from_list(s, handles, torrentfiles);
- // std::vector<std::string> maglinks = readfile(magnetlinks.c_str());
- const int TORRENT_COUNT = handles.size();
- unsigned int not_dl_count = 0;
- unsigned int tid = 0;
- unsigned int elapsed = 0;
- while(true) {
- std::vector<lt::alert*> alerts;
- s.pop_alerts(&alerts);
- read_alerts(alerts);
- for (unsigned int i = 0; i < TORRENT_COUNT; ++i) {
- lt::torrent_status ts = handles[i].status();
- if (!ts.is_seeding && !ts.is_finished) {
- not_dl_count++;
- }
- }
- // if no torrents are downloading
- if (!(TORRENT_COUNT > not_dl_count)) {
- lt::torrent_status spotlight = handles[tid].status();
- display(spotlight);
- if (elapsed >= MINUTE) {
- std::cout << std::endl;
- tid = (tid + 1) % TORRENT_COUNT;
- }
- }
- elapsed = (elapsed + 1) % (MINUTE + 1);
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
- }
- #include <curlpp/cURLpp.hpp>
- #include <curlpp/Easy.hpp>
- #include <curlpp/Options.hpp>
- std::string request(const std::string& url) {
- curlpp::options::Url source (url);
- curlpp::Easy request;
- request.setOpt(source);
- std::ostringstream response;
- try {
- response << request;
- } catch (curlpp::LibcurlRuntimeError e) {
- std::cout << e.what() << std::endl;
- } catch (std::exception const& e) {
- std::cout << e.what() << std::endl;
- }
- return response.str();
- }
- #include "libtorrent/create_torrent.hpp"
- #include "libtorrent/entry.hpp"
- #include "libtorrent/bencode.hpp"
- #include "peertube.hpp" // also includes rapidjson
- void torrent_basic_loop(std::string torrent_file) {
- lt::session s;
- lt::add_torrent_params p;
- p.save_path = "./downloaded";
- p.ti = std::make_shared<lt::torrent_info>(torrent_file);
- lt::torrent_handle h = s.add_torrent(p);
- h.set_flags(lt::torrent_flags::sequential_download);
- lt::torrent_status ts;
- do {
- std::vector<lt::alert*> alerts;
- s.pop_alerts(&alerts);
- read_alerts(alerts);
- ts = h.status();
- display(ts);
- std::this_thread::sleep_for(std::chrono::seconds(1));
- } while (!ts.is_seeding && !ts.is_finished);
- }
- // download torrent file from peertube server
- std::string dl_torrentfile(struct file_t *video_file, const char * title = NULL) {
- if (video_file == NULL) {
- return std::string();
- }
- if (video_file -> attribs == NULL) {
- return std::string();
- }
- std::string torrent_url (video_file -> attribs[TORRENTDOWNLOAD_URL]);
- std::string buffer = request(torrent_url);
- std::string filename;
- {
- std::stringstream ss;
- if (title == NULL) { // use the file name from the url
- unsigned int pos = torrent_url.rfind("/");
- pos++; // +1 to omit '/'
- ss << torrent_url.substr(pos, torrent_url.size());
- } else {
- // does not remove forbidden characters in file name yet
- ss << title <<"-"<< video_file -> resolution << "p.torrent";
- }
- filename = ss.str();
- std::cout << filename << std::endl;
- }
- std::fstream fout;
- fout.open (filename, std::fstream::out | std::fstream::binary);
- if (fout.good()) {
- fout << buffer;
- }
- fout.close();
- return filename;
- }
- // watch a video given a URL
- bool watch(std::string video_url) {
- //turn peertube video url into api request
- bool is_playlist = false;
- std::string api = get_endpoint(video_url, is_playlist);
- if (api.length() == 0) {
- std::cout << "Error: can't watch video." << std::endl;
- return false;
- }
- // figure something out with playlists.
- if (is_playlist) {
- std::cout << "playlist support not implemented" << std::endl;
- return false;
- }
- std::string got = request(api);
- std::cout << api << std::endl;
- rapidjson::Document root;
- root.Parse(got.c_str());
- // video downloads can either be in "files"
- // or under "streamingPlaylists" if HLS is enabled on the server
- struct video_t *video = init_from_json(root);
- video_print(video);
- // get torrent download url
- struct file_t *best_file = video_pick_file(video);
- // torrent file > magnet link if peertube instance's tracker is unreliable
- std::string saved = dl_torrentfile(best_file);
- if (!saved.empty()) {
- std::cout << "acquired .torrent file! " << saved << std::endl;
- torrent_basic_loop(saved);
- }
- std::cout << "\n\n\n" << std::endl;
- video_free(video);
- return true;
- }
- void test (std::string video_url) {
- bool is_playlist;
- std::string api = get_endpoint(video_url, is_playlist);
- std::string got = request(api);
- std::cout << got.length() << std::endl;
- rapidjson::Document root;
- root.Parse(got.c_str());
- const rapidjson::Value& vf = get_video_files(root);
- for (rapidjson::SizeType i = 0; i < vf.Size(); ++i) {
- struct file_t *test = init_from_json(vf[i]);
- if (test != NULL) {
- file_print(test);
- file_free(test);
- }
- }
- }
- void test2 (std::string video_url) {
- bool is_playlist;
- std::string api = get_endpoint(video_url, is_playlist);
- std::string got = request(api);
- // std::cout << api << std::endl;
- rapidjson::Document root;
- root.Parse(got.c_str());
- struct video_t *video = init_from_json(root);
- video_print(video);
- // get torrent download url
- struct file_t *best_file = video_pick_file(video);
- std::string saved = dl_torrentfile(best_file);
- if (!saved.empty()) {
- std::cout << "acquired .torrent file! " << saved << std::endl;
- torrent_basic_loop(saved);
- }
- std::cout << "\n\n\n" << std::endl;
- video_free(video);
- }
- int main(int argc, char const* argv[]) {
- std::cout << "This is cppia (unreleased)." << std::endl;
- bool success;
- switch (argc) {
- case 1:
- std::cout << "Usages: \n"
- << "\t cppia <video url>\n"
- << "\t cppia seed <list of torrent files>" << std::endl;
- break;
- case 2:
- // if there is only one argument, assume it is a video url
- success = watch(std::string(argv[1]));
- if (!success) {
- std::cout << "Usage: cppia <video url>" << std::endl;
- }
- break;
- case 3:
- std::string option (argv[1]);
- std::string s(argv[2]);
- if (option.compare("seed") == 0) {
- seedbox(s);
- } else if (option.compare("test") == 0) {
- test2(s);
- }
- break;
- }
- return 0;
- }
|