admin 管理员组文章数量: 1086019
sbt
用于创建bt种子文件和下载的命令行bt客户端工具
简单的下载:
sbt_client -d xxx.torrent
种子制作:
sbt_client -f xxx.tgz -t http://xxx.6969/announce -o xxx.tgz.torrent
编译命令:
g++ -std=gnu++11 simple_client.cpp -o sbt_client -L/usr/lib -lssl -lcrypto -L/usr/local/lib /usr/local/lib/libtorrent-rasterbar.so.10.0.0 -lboost_system -lpthread
源码:
#include <cstdlib>
#include <iostream>
#include <functional>
#include <cstdio>
#include <sstream>
#include <fstream>#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/aux_/max_path.hpp" // for TORRENT_MAX_PATH#include <chrono>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/torrent_status.hpp>#ifdef TORRENT_WINDOWS
#include <direct.h> // for _getcwd
#endifusing namespace std::placeholders;std::vector<char> load_file(std::string const& filename)
{std::fstream in;in.exceptions(std::ifstream::failbit);in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);in.seekg(0, std::ios_base::end);size_t const size = size_t(in.tellg());in.seekg(0, std::ios_base::beg);std::vector<char> ret(size);in.read(ret.data(), ret.size());return ret;
}std::string branch_path(std::string const& f)
{if (f.empty()) return f;#ifdef TORRENT_WINDOWSif (f == "\\\\") return "";
#endifif (f == "/") return "";int len = int(f.size());// if the last character is / or \ ignore itif (f[len - 1] == '/' || f[len - 1] == '\\') --len;while (len > 0) {--len;if (f[len] == '/' || f[len] == '\\')break;}if (f[len] == '/' || f[len] == '\\') ++len;return std::string(f.c_str(), len);
}bool file_filter(std::string const& f)
{if (f.empty()) return false;char const* first = f.c_str();char const* sep = strrchr(first, '/');
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)char const* altsep = strrchr(first, '\\');if (sep == nullptr || altsep > sep) sep = altsep;
#endif// if there is no parent path, just set 'sep'// to point to the filename.// if there is a parent path, skip the '/' characterif (sep == nullptr) sep = first;else ++sep;// return false if the first character of the filename is a .if (sep[0] == '.') return false;std::cerr << f << "\n";return true;
}void print_usage()
{std::cerr << R"(usage: sbt_client [OPTIONS][*]Download files from a torrent file
[*]Generates a torrent file from the specified fileor directory and writes it to standard outOPTIONS:
-d file download files from a torrent file
-f file generate torrent from a file
-m file generate a merkle hash tree torrent.merkle torrents require client supportthe resulting full merkle tree is written tothe specified file
-w url adds a web seed to the torrent withthe specified url
-t url adds the specified tracker to thetorrent. For multiple trackers, specify more-t options
-c comment sets the comment to the specified string
-C creator sets the created-by field to the specified string
-p bytes enables padding files. Files largerthan bytes will be piece-aligned
-s bytes specifies a piece size for the torrentThis has to be a multiple of 16 kiB
-l Don't follow symlinks, instead encode them aslinks in the torrent file
-o file specifies the output filename of the torrent fileIf this is not specified, the torrent file isprinted to the standard out, except on windowswhere the filename defaults to a.torrent
-r file add root certificate to the torrent, to verifythe HTTPS tracker
-S info-hash add a similar torrent by info-hash. The similartorrent is expected to share some files with this one
-L collection add a collection name to this torrent. Other torrentsin the same collection is expected to share fileswith this one.
-M make the torrent compatible with mutable torrentsthis means aligning large files and pad them in orderfor piece hashes to uniquely indentify a file withoutoverlap
)";
}using clk = std::chrono::steady_clock;// return the name of a torrent status enum
char const* state(lt::torrent_status::state_t s)
{switch (s) {case lt::torrent_status::checking_files: return "checking";case lt::torrent_status::downloading_metadata: return "dl metadata";case lt::torrent_status::downloading: return "downloading";case lt::torrent_status::finished: return "finished";case lt::torrent_status::seeding: return "seeding";case lt::torrent_status::allocating: return "allocating";case lt::torrent_status::checking_resume_data: return "checking resume";default: return "<>";}
}int bt_client(char* torrent) {lt::settings_pack pack;pack.set_int(lt::settings_pack::alert_mask, lt::alert::error_notification| lt::alert::storage_notification| lt::alert::status_notification);lt::session ses(pack);lt::add_torrent_params atp;clk::time_point last_save_resume = clk::now();// load resume data from disk and pass it in as we add the magnet linkstd::ifstream ifs(".resume_file", std::ios_base::binary);ifs.unsetf(std::ios_base::skipws);atp.resume_data.assign(std::istream_iterator<char>(ifs), std::istream_iterator<char>());atp.save_path = "./";atp.ti = std::make_shared<lt::torrent_info>(torrent);ses.add_torrent(atp);// this is the handle we'll set once we get the notification of it being// addedlt::torrent_handle h;for (;;) {std::vector<lt::alert*> alerts;ses.pop_alerts(&alerts);for (lt::alert const* a : alerts) {if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {h = at->handle;}// if we receive the finished alert or an error, we're doneif (lt::alert_cast<lt::torrent_finished_alert>(a)) {h.save_resume_data();goto done;}if (lt::alert_cast<lt::torrent_error_alert>(a)) {std::cout << a->message() << std::endl;goto err;}// when resume data is ready, save itif (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {std::ofstream of(".resume_file", std::ios_base::binary);of.unsetf(std::ios_base::skipws);lt::bencode(std::ostream_iterator<char>(of), *rd->resume_data);}if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {if (st->status.empty()) continue;// we only have a single torrent, so we know which one// the status is forlt::torrent_status const& s = st->status[0];std::cout << "\r" << state(s.state) << " "<< (s.download_payload_rate / 1000) << " kB/s "<< (s.total_done / 1000) << " kB ("<< (s.progress_ppm / 10000) << "%) downloaded\x1b[K"; std::cout.flush();}}std::this_thread::sleep_for(std::chrono::milliseconds(100));// ask the session to post a state_update_alert, to update our// state output for the torrentses.post_torrent_updates();// save resume data once every 30 secondsif (clk::now() - last_save_resume > std::chrono::seconds(30)) {h.save_resume_data();last_save_resume = clk::now();}}// TODO: ideally we should save resume data hereerr:std::cout << "\nError happened!!!" << std::endl;return 0;done:std::cout << "\n100% downloaded!" << std::endl;std::cout << "Seeding..." << std::endl;// wait for the user to endchar a;int ret = std::scanf("%c\n", &a);(void)ret; // ignorereturn 0;}int main(int argc, char* argv[]) try
{lt::span<char *> args(argv, size_t(argc));std::string creator_str = "sbtc";std::string comment_str;if (args.size() < 2) {print_usage();return 1;}std::vector<std::string> web_seeds;std::vector<std::string> trackers;std::vector<std::string> collections;std::vector<lt::sha1_hash> similar;int pad_file_limit = -1;int piece_size = 0;lt::create_flags_t flags = {};std::string root_cert;std::string outfile;std::string merklefile;
#ifdef TORRENT_WINDOWS// don't ever write binary data to the console on windows// it will just be interpreted as text and corruptedoutfile = "new.torrent";
#endifstruct ST{std::string full_path;};ST st;//std::string full_path = args[1];args = args.subspan(1);for (; !args.empty(); args = args.subspan(1)) {//std::cout << "00" << args[0][0] << std::endl;//std::cout << "01" << args[0][1] << std::endl;if (args[0][0] != '-') {print_usage();//std::cout << "error" << std::endl;/*std::cout << args[0][0] << std::endl;*/return 1;}char const flag = args[0][1];switch (flag){case 'd':bt_client(args[1]);break;case 'M':flags |= lt::create_torrent::mutable_torrent_support;pad_file_limit = 0x4000;continue;case 'l':flags |= lt::create_torrent::symlinks;continue;}if (args.size() < 2) {print_usage();return 1;}switch (flag){case 'f':st.full_path = args[1]; break;case 'w': web_seeds.push_back(args[1]); break;case 't': trackers.push_back(args[1]); break;case 's': piece_size = atoi(args[1]); break;case 'o': outfile = args[1]; break;case 'C': creator_str = args[1]; break;case 'c': comment_str = args[1]; break;case 'r': root_cert = args[1]; break;case 'L': collections.push_back(args[1]); break;case 'p':pad_file_limit = atoi(args[1]);flags |= lt::create_torrent::optimize_alignment;break;case 'm':merklefile = args[1];flags |= lt::create_torrent::merkle;break;case 'S': {if (strlen(args[1]) != 40) {std::cerr << "invalid info-hash for -S. ""Expected 40 hex characters\n";print_usage();return 1;}std::stringstream hash(args[1]);lt::sha1_hash ih;hash >> ih;if (hash.fail()) {std::cerr << "invalid info-hash for -S\n";print_usage();return 1;}similar.push_back(ih);break;}default:print_usage();return 1;}args = args.subspan(1);}lt::file_storage fs;
#ifdef TORRENT_WINDOWSif (full_path[1] != ':')
#elseif (st.full_path[0] != '/')
#endif{char cwd[TORRENT_MAX_PATH];
#ifdef TORRENT_WINDOWS_getcwd(cwd, sizeof(cwd));full_path = cwd + ("\\" + full_path);
#elsechar const* ret = getcwd(cwd, sizeof(cwd));if (ret == nullptr) {std::cerr << "failed to get current working directory: "<< strerror(errno) << "\n";return 1;}st.full_path = cwd + ("/" + st.full_path);
#endif}lt::add_files(fs, st.full_path, file_filter, flags);if (fs.num_files() == 0) {std::cerr << "no files specified.\n";return 1;}lt::create_torrent t(fs, piece_size, pad_file_limit, flags);int tier = 0;for (std::string const& tr : trackers) {t.add_tracker(tr, tier);++tier;}for (std::string const& ws : web_seeds)t.add_url_seed(ws);for (std::string const& c : collections)t.add_collection(c);for (lt::sha1_hash const& s : similar)t.add_similar_torrent(s);auto const num = t.num_pieces();lt::set_piece_hashes(t, branch_path(st.full_path), [num](lt::piece_index_t const p) {std::cerr << "\r" << p << "/" << num;});std::cerr << "\n";t.set_creator(creator_str.c_str());if (!comment_str.empty()) {t.set_comment(comment_str.c_str());}if (!root_cert.empty()) {std::vector<char> const pem = load_file(root_cert);t.set_root_cert(std::string(&pem[0], pem.size()));}// create the torrent and print it to stdoutstd::vector<char> torrent;lt::bencode(back_inserter(torrent), t.generate());if (!outfile.empty()) {std::fstream out;out.exceptions(std::ifstream::failbit);out.open(outfile.c_str(), std::ios_base::out | std::ios_base::binary);out.write(torrent.data(), torrent.size());}else {std::cout.write(torrent.data(), torrent.size());}if (!merklefile.empty()) {std::fstream merkle;merkle.exceptions(std::ifstream::failbit);merkle.open(merklefile.c_str(), std::ios_base::out | std::ios_base::binary);auto const& tree = t.merkle_tree();merkle.write(reinterpret_cast<char const*>(tree.data()), tree.size() * 20);}return 0;
}catch (std::exception const& e) {std::cerr << "ERROR: " << e.what() << "\n";return 1;
}
本文标签: sbt
版权声明:本文标题:sbt 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1700286462a380429.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论