diff --git a/readme.md b/readme.md index 5054828..5c0da31 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ sudo xbps-install -S libcurl sqlite-devel sudo apt install libcurl4-gnutls-dev sqlite-devel ``` -## cookies +## Cookies ### Firefox #### Option 1 (requires sqlite-devel) Create a file named `firefox_profile` in the build directory and paste in your [firefox profile folder path](https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data) @@ -42,7 +42,7 @@ Create a file called `auth_cookie` and paste the cookie in the file \ Go back to firefox and copy the `_session` cookie into a file named `session_cookie` \ This needs to be redone everytime the cookie expires (~30 minutes) ### chrome -#### Option 1 (requires sqlite-devel and libgcrypt) NOT CURRENTLY FUNCTIONAL +#### Option 1 (requires sqlite-devel and libgcrypt) Create a file named `chrome_profile` in the build directory and paste in your chrome profile folder path (found on [chrome://version](chrome://version)) #### Option 2 Go to settings > privacy and security > cookies > see all cookies > vhx.tv > __cf_bm \ diff --git a/src/episode.cpp b/src/episode.cpp index e4e9086..d495520 100644 --- a/src/episode.cpp +++ b/src/episode.cpp @@ -399,6 +399,118 @@ namespace dropout_dl { return config_page; } + void cookie::get_value_from_db(sqlite3 *db, const std::string &sql_query_base, const std::string& value, bool verbose, int (*callback)(void*,int,char**,char**)) { + std::string sql_mod_base = sql_query_base; + + if (sql_mod_base.find("WHERE") == std::string::npos) { + sql_mod_base += " WHERE "; + } + else { + sql_mod_base += " AND "; + } + + sql_mod_base += "name='" + this->name + "';"; + + std::string sql_value_query = "SELECT " + value + ' ' + sql_mod_base; + std::string sql_length_query = "SELECT length(" + value + ") " + sql_mod_base; + std::string tmp; + char *err_code = nullptr; + int rc; + + if (verbose) { + std::cout << sql_value_query << '\n' << sql_length_query << std::endl; + } + + rc = sqlite3_exec(db, sql_length_query.c_str(), callback, &tmp, &err_code); + + if (rc != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_code); + sqlite3_free(err_code); + sqlite3_close(db); + exit(3); + } else if (verbose) { + std::cout << "Got " << this->name << " cookie length\n"; + } + + this->len = std::stoi(tmp); + + rc = sqlite3_exec(db, sql_value_query.c_str(), callback, &tmp, &err_code); + + if (rc != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_code); + sqlite3_free(err_code); + sqlite3_close(db); + exit(3); + } else if (verbose) { + std::cout << "Got " << this->name << " cookie\n"; + } + + this->str = tmp; + } + + void cookie::format_from_chrome() { + this->str = this->str.substr(3); + this->len -= 3; + } + + void cookie::chrome_decrypt(const std::string &password, int iterations, const std::string &salt, int length) { + + this->format_from_chrome(); + + uint8_t key[32]; + + char output[this->len + 2]; + + char iv[16]; + + for (char& c : iv) { + c = ' '; + } + + for (char& c : output) { + c = 0; + } + + gcry_kdf_derive(password.c_str(), password.size(), GCRY_KDF_PBKDF2, GCRY_KDF_ARGON2ID, salt.c_str(), salt.size(), iterations, length, key); + + gcry_cipher_hd_t handle; + + gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); + + gcry_cipher_setkey(handle, (const void*) &key, length); + + gcry_cipher_setiv(handle, (const void*)&iv, 16); + + unsigned long err = gcry_cipher_decrypt(handle, output, this->len, this->str.c_str(), this->len); + + if (err) { + std::cout << gcry_strerror(err) << std::endl; + exit(2); + } + + + this->str = output; + this->str = this->str.substr(0, this->len-7); + this->len -= 7; + } + + void cookie::url_decode() { + std::string out; + + for (int i = 0; i < this->str.size() - 3; i++) { + if (substr_is(this->str, i, "%3D")) { + out += "="; + i += 2; + } + else { + out += this->str[i]; + } + } + + this->str = out; + this->len = out.size(); + } + std::vector episode::get_qualities() { if (!qualities.empty()) { return qualities; diff --git a/src/episode.h b/src/episode.h index 6ef7f8b..a501cf4 100644 --- a/src/episode.h +++ b/src/episode.h @@ -9,9 +9,104 @@ #include #include #include +#include +#ifdef DROPOUT_DL_GCRYPT +#include +#endif namespace dropout_dl { + class cookie { + public: + static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName) + { + if (argc < 1) { + std::cerr << "ERROR: sqlite could not find dropout.tv cookie" << std::endl; + return -1; + } + else { + *(std::string*)data = argv[0]; + return 0; + } + } + + static int sqlite_write_callback_uchar(void* data, int argc, char** argv, char** azColName) + { + if (argc < 1) { + std::cerr << "ERROR: sqlite could not find dropout.tv cookie" << std::endl; + return -1; + } + else { + + auto* ck = (dropout_dl::cookie*)data; + + for (int i = 0; i < ck->len; i++) { + if (argv[0][i] > 32 && argv[0][i] < 126) { + std::cout << (unsigned char) argv[0][i] << ' '; + } + else { + std::cout << std::hex << ((int)argv[0][i] & 0xFF) << ' '; + } + ck->str[i] = (unsigned char)argv[0][i]; + } + return 0; + } + } + + + std::string name; + std::string str; + int len; + + + explicit cookie(const std::string& name) { + this->name = name; + this->len = 0; + } + + cookie(const std::string& name, const std::string& cookie) { + this->name = name; + this->str = cookie; + this->len = cookie.size(); + } + + cookie(const std::string& cookie, int length) { + this->str = cookie; + this->name = "?"; + this->len = length; + } + + cookie(const std::string& name, const std::string& cookie, int length) { + this->name = name; + this->str = cookie; + this->len = length; + } + + /** + * + * @param db - An sqlite3 database + * @param sql_query_base - A base without the name search e.g. "FROM cookies" this function would then append the text "SELECT " and "WHERE name=''" + * @param value - The name of the value to fill the cookie with + * + * + */ + void get_value_from_db(sqlite3* db, const std::string& sql_query_base, const std::string& value, bool verbose = false, int (*callback)(void*,int,char**,char**) = sqlite_write_callback); + + void format_from_chrome(); + + /** + * + * @param password - Default is "peanuts". This works for linux. The password should be keychain password on MacOS + * @param salt - Salt is "saltysalt" for both MacOS and Linux + * @param length - Length of 16 is standard for both MacOS and Linux + * @param iterations - 1 on linux and 1003 on MacOS + */ + void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16); + + void url_decode(); + }; + + bool substr_is(const std::string& string, int start, const std::string& test_str); void replace_all(std::string& str, const std::string& from, const std::string& to); @@ -76,12 +171,12 @@ 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) { + episode(const std::string& episode_url, std::vector cookies, bool verbose = false) { this->episode_url = episode_url; this->verbose = verbose; - episode_data = get_episode_page(episode_url, cookies[0], cookies[1]); + episode_data = get_episode_page(episode_url, cookies[0].str, cookies[1].str); name = get_episode_name(episode_data); @@ -124,7 +219,7 @@ namespace dropout_dl { std::cout << "Got embedded url: " << this->embedded_url << '\n'; } - this->embedded_page_data = get_embedded_page(this->embedded_url, cookies[0]); + this->embedded_page_data = get_embedded_page(this->embedded_url, cookies[0].str); if (this->embedded_page_data.find("you are not authorized") != std::string::npos) { std::cerr << "ERROR: Could not access video. Try refreshing cookies.\n"; diff --git a/src/main.cpp b/src/main.cpp index 30cafc2..dc56c10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,33 +4,124 @@ #ifdef DROPOUT_DL_SQLITE #include -#ifdef DROPOUT_DL_GCRYPT -#include -#endif #endif -static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName) -{ - if (argc < 1) { - std::cerr << "ERROR: sqlite could not find dropout.tv cookie" << std::endl; - return -1; - } - else { - *(std::string*)data = argv[0]; - return 0; - } +namespace dropout_dl { + + class options { + public: + + std::string url; + bool verbose = false; + bool cookies_forced = false; + bool series = false; + bool season = false; + std::string quality; + std::string filename; + std::string output_directory; + std::string episode; + std::vector cookies; + + static std::vector convert_program_args(int argc, char** argv) { + std::vector out; + for (int i = 1; i < argc; i++) { + out.emplace_back(argv[i]); + } + return out; + } + + options(int argc, char** argv) { + std::vector args = convert_program_args(argc, argv); + + for (int i = 0; i < args.size(); i++) { + std::string arg = args[i]; + + if (arg.substr(0, 2) != "--") { + url = arg; + continue; + } + arg = arg.substr(2); + if (arg == "verbose") { + verbose = true; + } else if (arg == "quality") { + if (i + 1 >= args.size()) { + std::cerr << "ARGUMENT PARSE ERROR: --quality used with too few following arguments\n"; + exit(8); + } + quality = args[++i]; + } + else if (arg == "force-cookies") { + if (i + 2 >= args.size()) { + std::cerr << "ARGUMENT PARSE ERROR: --force-cookies used with too few following arguments\n"; + exit(8); + } + cookies.emplace_back(args[++i]); + cookies.emplace_back(args[++i]); + cookies_forced = true; + } + else if (arg == "output") { + if (i + 1 >= args.size()) { + std::cerr << "ARGUMENT PARSE ERROR: --output used with too few following arguments\n"; + exit(8); + } + filename = args[++i]; + } + else if (arg == "output-directory") { + if (i + 1 >= args.size()) { + std::cerr << "ARGUMENT PARSE ERROR: --output-directory used with too few following arguments\n"; + exit(8); + } + output_directory = args[++i]; + } + else if (arg == "series") { + series = true; + } + else if (arg == "season") { + season = true; + } + else if (arg == "help") { + std::cout << "Usage: dropout-dl [OPTIONS] [OPTIONS]\n" + "\n" + "Options:\n" + "\t--help Display this message\n" + "\t--quality Set the quality of the downloaded video. Quality can be set to 'all' which\n" + "\t will download all qualities and place them into separate folders\n" + "\t--output Set the output filename. Only works for single episode downloads\n" + "\t--output-directory Set the directory where files are output\n" + "\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--season Interpret the url as a link to a season and download all episodes from all seasons\n" + << std::endl; + + exit(0); + } + } + + if (output_directory.empty()) { + output_directory = "."; + } + + if (season && series) { + std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n"; + } + if (quality.empty()) { + quality = "1080p"; + } + } + }; } #ifdef DROPOUT_DL_SQLITE -std::vector get_cookies_from_firefox(const std::filesystem::path& firefox_profile_path, bool verbose = false) { +std::vector get_cookies_from_firefox(const std::filesystem::path& firefox_profile_path, bool verbose = false) { std::fstream firefox_profile_file(firefox_profile_path); std::string firefox_profile; - std::string auth_cookie; - std::string session_cookie; + dropout_dl::cookie auth("__cf_bm"); + dropout_dl::cookie session("_session"); - std::vector out; + std::vector out; firefox_profile_file >> firefox_profile; @@ -57,55 +148,38 @@ std::vector get_cookies_from_firefox(const std::filesystem::path& f } } - char *err_code = nullptr; + std::string len; - std::string sql("SELECT value FROM moz_cookies WHERE host LIKE '%dropout.tv%' AND name='__cf_bm';"); + auth.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value"); - rc = sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &auth_cookie, &err_code); + session.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value"); - out.emplace_back(auth_cookie); - - if (rc != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", err_code); - sqlite3_free(err_code); - sqlite3_close(db); - exit(2); - } else if (verbose) { - std::cout << "Got __cf_bm cookie from firefox sqlite db\n"; - } - - sql = "SELECT value FROM moz_cookies WHERE host LIKE '%dropout.tv%' AND name='_session';"; - - rc = sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &session_cookie, &err_code); - - out.emplace_back(session_cookie); - - if (rc != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", err_code); - sqlite3_free(err_code); - sqlite3_close(db); - exit(3); - } else if (verbose) { - std::cout << "Got _session cookie from firefox sqlite db\n"; - } sqlite3_close(db); } + if (verbose) { + std::cout << auth.name << ": " << auth.len << ": " << auth.str << '\n'; + + std::cout << session.name << ": " << session.len << ": " << session.str << '\n'; + } + + out.push_back(auth); + out.push_back(session); + + return out; } #ifdef DROPOUT_DL_GCRYPT -std::vector get_cookies_from_chrome(const std::filesystem::path& chrome_profile_path, bool verbose = false) { +std::vector get_cookies_from_chrome(const std::filesystem::path& chrome_profile_path, bool verbose = false) { std::fstream chrome_profile_file(chrome_profile_path); std::string chrome_profile; - std::string auth_cookie; - int auth_cookie_length; - std::string session_cookie; - int session_cookie_length; + dropout_dl::cookie auth("__cf_bm"); + dropout_dl::cookie session("_session"); - std::vector out; + std::vector out; getline(chrome_profile_file, chrome_profile); @@ -127,122 +201,44 @@ std::vector get_cookies_from_chrome(const std::filesystem::path& ch } } - char *err_code = nullptr; - std::string len; - std::string sql = "SELECT length(encrypted_value) FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='__cf_bm';"; + auth.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value"); - sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &len, &err_code); + session.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value"); - auth_cookie_length = std::stoi(len); - - sql = "SELECT encrypted_value FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='__cf_bm';"; - - rc = sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &auth_cookie, &err_code); - - if (rc != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", err_code); - sqlite3_free(err_code); - sqlite3_close(db); - exit(2); - } else if (verbose) { - std::cout << "Got __cf_bm cookie from chrome sqlite db\n" << auth_cookie << '\n'; - } - - sql = "SELECT length(encrypted_value) FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='_session';"; - - sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &len, &err_code); - - session_cookie_length = std::stoi(len); - - sql = "SELECT encrypted_value FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='_session';"; - - rc = sqlite3_exec(db, sql.c_str(), sqlite_write_callback, &session_cookie, &err_code); - - if (rc != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", err_code); - sqlite3_free(err_code); - sqlite3_close(db); - exit(3); - } else if (verbose) { - std::cout << "Got _session cookie from chrome sqlite db\n"; - } sqlite3_close(db); - // system(("echo \"SELECT value FROM moz_cookies WHERE originAttributes LIKE '%dropout.tv%';\" | sqlite3 " + firefox_profile + "/cookies.sqlite > cookie").c_str()); } - // For mac os this is your keychain password - // For linux leave as "peanuts" - std::string password = "peanuts"; - std::string salt = "saltysalt"; - int length = 16; - int iterations = 1; + auth.chrome_decrypt(); - uint8_t key[32]; + session.chrome_decrypt(); - char output[2048]; + session.url_decode(); - char iv[16]; + if (verbose) { + std::cout << auth.name << ": " << auth.len << ": " << auth.str << '\n'; - for (char& c : iv) { - c = ' '; + std::cout << session.name << ": " << session.len << ": " << session.str << '\n'; } - for (char& c : output) { - c = 0; - } - - for (int i = 0; i < auth_cookie_length; i++) { - std::cout << std::hex << (0xFF & (int)auth_cookie[i]) << ' '; - } - std::cout << '\n'; - - gcry_kdf_derive(password.c_str(), password.size(), GCRY_KDF_PBKDF2, GCRY_KDF_ARGON2ID, salt.c_str(), salt.size(), iterations, length, key); - - gcry_cipher_hd_t handle; - - gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0); - - gcry_cipher_setkey(handle, (const void*) &key, length); - - gcry_cipher_setiv(handle, (const void*)&iv, 16); - - unsigned long err = gcry_cipher_decrypt(handle, (unsigned char*)output, 2048, auth_cookie.c_str() + 3, auth_cookie_length - 3); - - if (err) { - std::cout << gcry_strerror(err) << std::endl; - exit(2); - } - - for (char& c : output) { - if (c == '\017') { - c = 0; - } - } - - out.emplace_back(output); - - gcry_cipher_setiv(handle, (const void*)&iv, 16); - - gcry_cipher_decrypt(handle, (unsigned char*)output, 2048, session_cookie.c_str() + 3, session_cookie_length - 3); - - out.emplace_back(output); + out.push_back(auth); + out.push_back(session); return out; } #endif #endif -std::vector get_cookies_from_files(const std::filesystem::path& auth_cookie_path, const std::filesystem::path& session_cookie_path, bool verbose = false) { +std::vector get_cookies_from_files(const std::filesystem::path& auth_cookie_path, const std::filesystem::path& session_cookie_path, bool verbose = false) { std::fstream auth_cookie_file("auth_cookie"); std::fstream session_cookie_file("session_cookie"); std::string auth_cookie; std::string session_cookie; - std::vector out; + std::vector out; auth_cookie_file >> auth_cookie; if (verbose) { @@ -261,7 +257,7 @@ std::vector get_cookies_from_files(const std::filesystem::path& aut return out; } -std::vector get_cookies(bool verbose = false) { +std::vector get_cookies(bool verbose = false) { #ifdef DROPOUT_DL_SQLITE std::filesystem::path firefox_profile("firefox_profile"); @@ -292,116 +288,13 @@ std::vector get_cookies(bool verbose = false) { } } -class options { -public: - - std::string url; - bool verbose = false; - bool cookies_forced = false; - bool series = false; - bool season = false; - std::string quality; - std::string filename; - std::string output_directory; - std::string episode; - std::vector cookies; - - static std::vector convert_program_args(int argc, char** argv) { - std::vector out; - for (int i = 1; i < argc; i++) { - out.emplace_back(argv[i]); - } - return out; - } - - options(int argc, char** argv) { - std::vector args = convert_program_args(argc, argv); - - for (int i = 0; i < args.size(); i++) { - std::string arg = args[i]; - - if (arg.substr(0, 2) != "--") { - url = arg; - continue; - } - arg = arg.substr(2); - if (arg == "verbose") { - verbose = true; - } else if (arg == "quality") { - if (i + 1 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --quality used with too few following arguments\n"; - exit(8); - } - quality = args[++i]; - } - else if (arg == "force-cookies") { - if (i + 2 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --force-cookies used with too few following arguments\n"; - exit(8); - } - cookies.emplace_back(args[++i]); - cookies.emplace_back(args[++i]); - cookies_forced = true; - } - else if (arg == "output") { - if (i + 1 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --output used with too few following arguments\n"; - exit(8); - } - filename = args[++i]; - } - else if (arg == "output-directory") { - if (i + 1 >= args.size()) { - std::cerr << "ARGUMENT PARSE ERROR: --output-directory used with too few following arguments\n"; - exit(8); - } - output_directory = args[++i]; - } - else if (arg == "series") { - series = true; - } - else if (arg == "season") { - season = true; - } - else if (arg == "help") { - std::cout << "Usage: dropout-dl [OPTIONS] [OPTIONS]\n" - "\n" - "Options:\n" - "\t--help Display this message\n" - "\t--quality Set the quality of the downloaded video. Quality can be set to 'all' which\n" - "\t will download all qualities and place them into separate folders\n" - "\t--output Set the output filename\n" - "\t--output-directory Set the directory where files are output\n" - "\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--season Interpret the url as a link to a season and download all episodes from all seasons\n" - << std::endl; - - exit(0); - } - } - - if (output_directory.empty()) { - output_directory = "."; - } - - if (season && series) { - std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n"; - } - if (quality.empty()) { - quality = "1080p"; - } - } -}; - int main(int argc, char** argv) { - options options(argc, argv); + dropout_dl::options options(argc, argv); - std::cout << "quality: " << options.quality << std::endl; - std::cout << "verbose: " << options.verbose << std::endl; - std::cout << "url: \"" << options.url << '"' << std::endl; +// std::cout << "quality: " << options.quality << std::endl; +// std::cout << "verbose: " << options.verbose << std::endl; +// std::cout << "url: \"" << options.url << '"' << std::endl; std::string firefox_profile; std::string chrome_profile; diff --git a/src/season.cpp b/src/season.cpp index ab8bcf1..7c17aee 100644 --- a/src/season.cpp +++ b/src/season.cpp @@ -5,7 +5,7 @@ #include "season.h" namespace dropout_dl { - episode get_episode(const std::string& html_data, int& start_point, const std::vector& cookies) { + episode get_episode(const std::string& html_data, int& start_point, const std::vector& cookies) { int link_start = 0; for (int i = start_point; i > 0; i--) { if (substr_is(html_data, i, " season::get_episodes(const std::string &html_data, const std::vector& cookies) { + std::vector season::get_episodes(const std::string &html_data, const std::vector& cookies) { std::vector out; std::string site_video(R"(class="browse-item-link" data-track-event="site_video")"); diff --git a/src/season.h b/src/season.h index 583eab9..ae77a16 100644 --- a/src/season.h +++ b/src/season.h @@ -18,11 +18,11 @@ namespace dropout_dl { std::string page_data; std::vector episodes; - static std::vector get_episodes(const std::string& html_data, const std::vector& cookies); + static std::vector get_episodes(const std::string& html_data, const std::vector& cookies); void download(const std::string& quality, const std::string& series_directory); - season(const std::string& url, const std::string& name, const std::vector& cookies, const std::string& series_name = "") { + 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; diff --git a/src/series.cpp b/src/series.cpp index 9531751..83036c2 100644 --- a/src/series.cpp +++ b/src/series.cpp @@ -40,7 +40,7 @@ namespace dropout_dl { return "-1"; } - std::vector series::get_seasons(const std::string &html_data, const std::vector& cookies) { + std::vector series::get_seasons(const std::string &html_data, const std::vector& cookies) { std::vector out; std::string search_class("js-switch-season"); @@ -127,7 +127,7 @@ namespace dropout_dl { } - season series::get_season(const std::string &url, const std::vector& cookies) { + season series::get_season(const std::string &url, const std::vector& cookies) { std::string html_data = get_generic_page(url); std::string search_class("js-switch-season"); diff --git a/src/series.h b/src/series.h index 16b1619..fcebf87 100644 --- a/src/series.h +++ b/src/series.h @@ -20,13 +20,13 @@ namespace dropout_dl { 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 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); + static season get_season(const std::string& url, const std::vector& cookies); void download(const std::string& quality, const std::string& base); - explicit series(const std::string& url, const std::vector& cookies) { + explicit series(const std::string& url, const std::vector& cookies) { this->url = url; this->page_data = get_generic_page(url); this->name = get_series_name(page_data);