A PeerTube client in C++
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

343 lines
10 KiB

#include "libtorrent/session.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/magnet_uri.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <thread>
#include <chrono>
#include <memory>
#include <ncurses.h>
void canvas() {
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
int ch;
while (ch != 27 && ch != 'q') { // 27 = ESC
printw("Type any character to see it in bold\n");
ch = getch();
if(ch == 22) { // ctrl+v
printw("copy pasta pressed\n");
} else {
printw("The pressed key is ");
attron(A_BOLD);
printw("%i\n", ch);
attroff(A_BOLD);
}
refresh();
}
endwin();
}
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 && last_state > 3) {
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
break;
case 2: // downloading meta data
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;
}
// 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;
std::vector<std::string> torrents = readfile(torrentfiles.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;
}
handles.push_back(s.add_torrent(p));
}
// std::vector<std::string> maglinks = readfile(magnetlinks.c_str());
char c;
const int TORRENT_COUNT = handles.size();
unsigned int not_dl_count = 0;
unsigned int tid = 0;
unsigned int elapsed = 0;
while(c != 'q') {
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
// watch a video given a URL
void 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);
std::string got = request(api);
std::cout << api << std::endl;
rapidjson::Document root;
root.Parse(got.c_str());
// figure something out with playlists.
if (is_playlist) {
std::cout << "playlist support not implemented" << std::endl;
return;
}
// video downloads can either be in "files"
// or under "streamingPlaylists" if HLS is enabled on the server
const rapidjson::Value& vf = get_video_files(root);
std::string magnet_link = get_magnet_link(vf);
lt::session s;
lt::add_torrent_params params = lt::parse_magnet_uri(magnet_link);
params.save_path = "./downloaded";
lt::torrent_handle h = s.add_torrent(params);
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);
std::cout << std::endl;
// prompt user to save torrent file?
// reference:
// https://github.com/steeve/libtorrent/blob/master/examples/client_test.cpp#L880
// char response;
// std::cout << "Save file? [Y/n]" << std::endl;
// std::cin >> response;
// if (response == 'y' || response == 'Y') {
// save_torrent(h);
// }
return;
}
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);
}
}
}
// 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) {
ss << title <<"-"<< video_file -> resolution << "p.torrent";
} else { // 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());
}
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;
}
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);
// get torrent download rul
int highest_res = 0;
struct file_t *best_file = video_pick_file(video);
std::string saved = dl_torrentfile(best_file, video -> name);
if (!saved.empty()) {
std::cout << "acquired .torrent file! " << std::endl;
}
std::cout << "\n\n\n" << std::endl;
video_print(video);
video_free(video);
}
int main(int argc, char const* argv[]) {
std::cout << "This is cppia (unreleased)." << std::endl;
if (argc != 3) {
fprintf(stderr, "usage: %s <.txt list of torrents>\n", argv[0]);
return 1;
}
// std::cout << "Press 'q' to quit and 'h' for more options." << std::endl;
std::string s(argv[2]);
// add real argument parsing later.
int option = atoi(argv[1]);
switch (option) {
case 0:
seedbox(s);
break;
case 1:
watch(s);
break;
case 2:
test2(s);
break;
}
// API generated by openapi doesn't compile.'
// apiClient_t* peertube = apiClient_create();
// apiClient_free(peertube);
return 0;
}