From cd72083b6994982f1158caa3417da5c030bebed6 Mon Sep 17 00:00:00 2001 From: Moss Date: Thu, 29 Sep 2022 14:33:56 -0400 Subject: [PATCH] Options: Added Season Option This allows an entire season to be downloaded at once. --- readme.md | 3 +- src/episode.h | 1 - src/main.cpp | 55 +++++++------------ src/season.cpp | 11 +++- src/season.h | 5 +- src/series.cpp | 139 +++++++++++++++++++++++++++++++++---------------- src/series.h | 8 +-- 7 files changed, 132 insertions(+), 90 deletions(-) diff --git a/readme.md b/readme.md index e1af34b..f5c68d0 100644 --- a/readme.md +++ b/readme.md @@ -64,7 +64,6 @@ This needs to be redone every time the cookies expire (~30 minutes) --verbose Display debug information while running --force-cookies Interpret the next to arguments as authentication cookie and session cookie --series Interpret the url as a link to a series and download all episodes from all seasons ---episode Select an episode from the series to download ---season Select a season from the series to download +--season Interpret the url as a link to a season and download all episodes from all seasons ``` By default, dropout-dl will download the episode in the format `/SE.mp4` \ No newline at end of file diff --git a/src/episode.h b/src/episode.h index ce3eb3b..6ef7f8b 100644 --- a/src/episode.h +++ b/src/episode.h @@ -77,7 +77,6 @@ namespace dropout_dl { void download(const std::string& quality, const std::string& series_directory, std::string filename = ""); episode(const std::string& episode_url, std::vector cookies, bool verbose = false) { - std::cout << episode_url << std::endl; this->episode_url = episode_url; this->verbose = verbose; diff --git a/src/main.cpp b/src/main.cpp index 88282c5..57778d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -292,26 +292,6 @@ std::vector get_cookies(bool verbose = false) { } } -/* - * - */ - class options { public: @@ -319,11 +299,11 @@ public: bool verbose = false; bool cookies_forced = false; bool series = false; + bool season = false; std::string quality; std::string filename; std::string series_dir; std::string episode; - std::string season; std::vector cookies; static std::vector convert_program_args(int argc, char** argv) { @@ -380,19 +360,8 @@ public: else if (arg == "series") { series = true; } - else if (arg == "episode") { - if (i + 1 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --episode used with too few following arguments\n"; - exit(8); - } - episode = args[++i]; - } else if (arg == "season") { - if (i + 1 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --season used with too few following arguments\n"; - exit(8); - } - season = args[++i]; + season = true; } else if (arg == "help") { std::cout << "Usage: dropout-dl [OPTIONS] [OPTIONS]\n" @@ -406,14 +375,17 @@ public: "\t--verbose Display debug information while running\n" "\t--force-cookies Interpret the next to arguments as authentication cookie and session cookie\n" "\t--series Interpret the url as a link to a series and download all episodes from all seasons\n" - "\t--episode Select an episode from the series to download\n" - "\t--season Select a season from the series to download\n" + "\t--season Interpret the url as a link to a season and download all episodes from all seasons\n" << std::endl; exit(0); } } + + if (season && series) { + std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n"; + } if (quality.empty()) { quality = "1080p"; } @@ -458,6 +430,19 @@ int main(int argc, char** argv) { series.download(options.quality, options.series_dir); } + else if (options.season) { + dropout_dl::season season = dropout_dl::series::get_season(options.url, options.cookies); + + if (options.series_dir.empty()) { + options.series_dir = season.series_name; + + std::replace(options.series_dir.begin(), options.series_dir.end(), ' ', '_'); + + std::replace(options.series_dir.begin(), options.series_dir.end(), ',', '_'); + } + + season.download(options.quality, options.series_dir); + } else { dropout_dl::episode ep(options.url, options.cookies, options.verbose); diff --git a/src/season.cpp b/src/season.cpp index c0dd438..b7177b5 100644 --- a/src/season.cpp +++ b/src/season.cpp @@ -24,7 +24,7 @@ namespace dropout_dl { for (int j = 0; j + i < html_data.size(); j++) { if (html_data[i + j] == '"') { start_point += 15; - return episode(html_data.substr(i, j), cookies); + return {html_data.substr(i, j), cookies}; } } } @@ -45,6 +45,7 @@ namespace dropout_dl { if (e.episode_url.empty()) { continue; } + std::cout << e.episode_number << ": " << e.name << ": " << e.episode_url << '\n'; out.push_back(e); } } @@ -53,8 +54,14 @@ namespace dropout_dl { } void season::download(const std::string &quality, const std::string &series_directory) { + std::string dir = series_directory + "/" + this->name; + + std::replace(dir.begin(), dir.end(), ' ', '_'); + + std::replace(dir.begin(), dir.end(), ',', '_'); + for (auto& ep : episodes) { - ep.download(quality, series_directory + "/" + this->name); + ep.download(quality, dir); } } } // dropout_dl \ No newline at end of file diff --git a/src/season.h b/src/season.h index 0bf93d0..583eab9 100644 --- a/src/season.h +++ b/src/season.h @@ -13,6 +13,7 @@ namespace dropout_dl { class season { public: std::string name; + std::string series_name; std::string url; std::string page_data; std::vector episodes; @@ -21,9 +22,11 @@ namespace dropout_dl { void download(const std::string& quality, const std::string& series_directory); - season(const std::string& url, const std::string& name, const std::vector& cookies) { + season(const std::string& url, const std::string& name, const std::vector& cookies, const std::string& series_name = "") { this->url = url; this->name = name; + this->series_name = series_name; + std::cout << series_name << ": " << name << ": " << url << "\n"; this->page_data = get_generic_page(url); this->episodes = get_episodes(page_data, cookies); } diff --git a/src/series.cpp b/src/series.cpp index 7f93731..b567333 100644 --- a/src/series.cpp +++ b/src/series.cpp @@ -6,51 +6,6 @@ namespace dropout_dl { - std::string series::get_series_page(const std::string &url, bool verbose) { - CURLcode ret; - CURL *hnd; - struct curl_slist *slist1; - - std::string series_data; - - slist1 = NULL; - slist1 = curl_slist_append(slist1, "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"); - slist1 = curl_slist_append(slist1, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"); - slist1 = curl_slist_append(slist1, "Accept-Language: en-US,en;q=0.5"); - slist1 = curl_slist_append(slist1, "Accept-Encoding: utf-8"); - slist1 = curl_slist_append(slist1, "DNT: 1"); - slist1 = curl_slist_append(slist1, "Connection: keep-alive"); - slist1 = curl_slist_append(slist1, "Upgrade-Insecure-Requests: 1"); - slist1 = curl_slist_append(slist1, "Sec-Fetch-Dest: document"); - slist1 = curl_slist_append(slist1, "Sec-Fetch-Mode: navigate"); - slist1 = curl_slist_append(slist1, "Sec-Fetch-Site: cross-site"); - slist1 = curl_slist_append(slist1, "Sec-GPC: 1"); - - hnd = curl_easy_init(); - curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); - curl_easy_setopt(hnd, CURLOPT_URL, url.c_str()); - curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1); - curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.84.0"); - curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); - curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L); - curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L); - curl_easy_setopt(hnd, CURLOPT_VERBOSE, verbose); - - curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &series_data); - - ret = curl_easy_perform(hnd); - - curl_easy_cleanup(hnd); - hnd = NULL; - curl_slist_free_all(slist1); - slist1 = NULL; - - return series_data; - } - std::string series::get_series_name(const std::string& html_data) { std::string collection_title("collection-title"); std::string open_a_tag("& cookies) { + std::string html_data = get_generic_page(url); + + std::string search_class("js-switch-season"); + std::string open_select(""); + std::string close_select(""); + + std::string open_option(""); + std::string value("value="); + + bool seasons_dropdown = false; + bool selected = false; + std::string season_url; + std::string season_name; + for (int i = 0; i < html_data.size(); i++) { + if (substr_is(html_data, i, open_select)) { + for (int j = i; j < html_data.size(); j++) { + if (substr_is(html_data, j, search_class)) { + i = j; + seasons_dropdown = true; + break; + } + else if (substr_is(html_data, j, close_tag)) { + break; + } + } + } + if (seasons_dropdown) { + if (substr_is(html_data, i, value)) { + i += value.size() + 1; + for (int j = 0; j + i < html_data.size(); j++) { + if (html_data[i + j] == '"') { + season_url = html_data.substr(i, j); + i += j; + break; + } + } + } + else if (!season_url.empty() && substr_is(html_data, i, "selected")) { + selected = true; + } + else if (!season_url.empty() && substr_is(html_data, i, close_tag)) { + i += close_tag.size() + 1; + for (int j = 0; i + j < html_data.size(); j++) { + if (html_data[i + j] == '\n') { + if (selected) { + season_name = html_data.substr(i, j); + + // Remove leading and trailing whitespace + bool leading_whitespace = true; + int name_start; + int name_end; + for (int k = 0; k < season_name.size(); k++) { + if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') { + name_start = k; + break; + } + } + for (int k = season_name.size() - 1; k > 0; k--) { + if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') { + name_end = k; + break; + } + } + season_name = season_name.substr(name_start, + season_name.size() - name_start - name_end); + + return {season_url, season_name, cookies, get_series_name(html_data)}; + } + + season_url.clear(); + season_name.clear(); + + i = i + j; + + break; + } + } + } + + if (substr_is(html_data, i, close_select)) { + break; + } + } + } + + std::cerr << "SEASON PARSE ERROR: No selected season found\n"; + exit(9); + } + void series::download(const std::string &quality, const std::string &series_directory) { for (auto& season : seasons) { season.download(quality, series_directory); } } + } // dropout_dl \ No newline at end of file diff --git a/src/series.h b/src/series.h index 77e45a9..c54fb07 100644 --- a/src/series.h +++ b/src/series.h @@ -17,17 +17,17 @@ namespace dropout_dl { std::string page_data; std::vector seasons; - static std::string get_series_page(const std::string& url, bool verbose = false); - static std::string get_series_name(const std::string& html_data); static std::vector get_seasons(const std::string& html_data, const std::vector& cookies); + static season get_season(const std::string& url, const std::vector& cookies); + void download(const std::string& quality, const std::string& series_directory); - explicit series(const std::string& url, std::vector cookies) { + explicit series(const std::string& url, const std::vector& cookies) { this->url = url; - this->page_data = get_series_page(url); + this->page_data = get_generic_page(url); this->name = get_series_name(page_data); this->seasons = get_seasons(page_data, cookies); }