Meta: Changed Spaces to Tabs

This commit is contained in:
Moss 2022-12-21 19:41:53 -08:00
parent 35b4c181b3
commit ea57f4477b
No known key found for this signature in database
GPG Key ID: F539D4A506C954F9
7 changed files with 1587 additions and 1586 deletions

File diff suppressed because it is too large Load Diff

View File

@ -16,444 +16,444 @@
namespace dropout_dl {
class cookie {
public:
/**
*
* @param data - A string to write to return value of the command to
* @param argc - The number of results from the command
* @param argv - The results of the command
* @param azColName - The column names from the command
* @return 0 on success or -1 on failure
*
* Used by sqlite. Write the first result of the sqlite command to the data string. If there are no results print an error and return -1.
*/
static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName)
{
if (argc < 1) {
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
return -1;
}
else {
*(std::string*)data = argv[0];
return 0;
}
}
class cookie {
public:
/**
*
* @param data - A string to write to return value of the command to
* @param argc - The number of results from the command
* @param argv - The results of the command
* @param azColName - The column names from the command
* @return 0 on success or -1 on failure
*
* Used by sqlite. Write the first result of the sqlite command to the data string. If there are no results print an error and return -1.
*/
static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName)
{
if (argc < 1) {
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
return -1;
}
else {
*(std::string*)data = argv[0];
return 0;
}
}
/**
* The name of the value from the sqlite database or "?" if not set.
*/
std::string name;
/**
* The value of the cookie
*/
std::string value;
/**
* The length of the value of the cookie
*/
int len;
/**
* The name of the value from the sqlite database or "?" if not set.
*/
std::string name;
/**
* The value of the cookie
*/
std::string value;
/**
* The length of the value of the cookie
*/
int len;
/**
*
* @param name - Name of the value from the sqlite database
*
* Create a cookie with no value and length of 0
*/
explicit cookie(const std::string& name) {
this->name = name;
this->value = "";
this->len = 0;
}
/**
*
* @param name - Name of the value from the sqlite database
*
* Create a cookie with no value and length of 0
*/
explicit cookie(const std::string& name) {
this->name = name;
this->value = "";
this->len = 0;
}
/**
*
* @param name - Name of the value from the sqlite database
* @param cookie - Value of the cookie
*
* Sets the name and value using the parameters and gets the length from the value
*/
cookie(const std::string& name, const std::string& cookie) {
this->name = name;
this->value = cookie;
this->len = cookie.size();
}
/**
*
* @param name - Name of the value from the sqlite database
* @param cookie - Value of the cookie
*
* Sets the name and value using the parameters and gets the length from the value
*/
cookie(const std::string& name, const std::string& cookie) {
this->name = name;
this->value = cookie;
this->len = cookie.size();
}
/**
*
* @param cookie - Value of the cookie
* @param length - Length of the cookie
*
* Sets the value and length using the parameters and sets the name as "?"
*/
cookie(const std::string& cookie, int length) {
this->value = cookie;
this->name = "?";
this->len = length;
}
/**
*
* @param cookie - Value of the cookie
* @param length - Length of the cookie
*
* Sets the value and length using the parameters and sets the name as "?"
*/
cookie(const std::string& cookie, int length) {
this->value = cookie;
this->name = "?";
this->len = length;
}
/**
*
* @param name - Name of the value from the sqlite database
* @param cookie - Value of the cookie
* @param length - Length of the cookie
*
* Sets the name, value, and length using the parameters leaving nothing unset.
*/
cookie(const std::string& name, const std::string& cookie, int length) {
this->name = name;
this->value = cookie;
this->len = length;
}
/**
*
* @param name - Name of the value from the sqlite database
* @param cookie - Value of the cookie
* @param length - Length of the cookie
*
* Sets the name, value, and length using the parameters leaving nothing unset.
*/
cookie(const std::string& name, const std::string& cookie, int length) {
this->name = name;
this->value = 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 <value>" and "WHERE name='<name>'"
* @param value - The name of the value to fill the cookie with
*
* Retrieve the value of a cookie from the provided sqlite database.
*
*/
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);
/**
*
* @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 <value>" and "WHERE name='<name>'"
* @param value - The name of the value to fill the cookie with
*
* Retrieve the value of a cookie from the provided sqlite database.
*
*/
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);
/**
*
* @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
*
* Decrypt chrome cookies and format them to be usable as regular cookies. Currently this has only been tested for the _session and __cf_bm cookies from dropout.tv but I see no reason this would not work for anything else.
*/
void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16);
/**
*
* @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
*
* Decrypt chrome cookies and format them to be usable as regular cookies. Currently this has only been tested for the _session and __cf_bm cookies from dropout.tv but I see no reason this would not work for anything else.
*/
void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16);
private:
/**
* Remove url encoded text from a cookie. Currently this only checks for %3D ('=') as that is the only thing I've come across in cookies during the entirety of this project.
*/
void url_decode();
private:
/**
* Remove url encoded text from a cookie. Currently this only checks for %3D ('=') as that is the only thing I've come across in cookies during the entirety of this project.
*/
void url_decode();
/**
* Remove the leading version (e.g. "v10") from the cookie
*/
void format_from_chrome();
};
/**
* Remove the leading version (e.g. "v10") from the cookie
*/
void format_from_chrome();
};
/**
*
* @param string - The string which is being searched
* @param start - The starting index of the substring
* @param test_str - The string which is being tested
* @return whether or not the substring is at the start index
*
* Checks if <b>test_str</b> is a substring of <b>string</b> at the start index
*/
bool substr_is(const std::string& string, int start, const std::string& test_str);
/**
*
* @param string - The string which is being searched
* @param start - The starting index of the substring
* @param test_str - The string which is being tested
* @return whether or not the substring is at the start index
*
* Checks if <b>test_str</b> is a substring of <b>string</b> at the start index
*/
bool substr_is(const std::string& string, int start, const std::string& test_str);
/**
*
* @param str - The base string which is being modified
* @param from - what is being replaced
* @param to - what to place it with
*
* Replaces every instance of the <b>from</b> string with the <b>to</b> string.
*/
void replace_all(std::string& str, const std::string& from, const std::string& to);
/**
*
* @param str - The base string which is being modified
* @param from - what is being replaced
* @param to - what to place it with
*
* Replaces every instance of the <b>from</b> string with the <b>to</b> string.
*/
void replace_all(std::string& str, const std::string& from, const std::string& to);
/**
*
* @param str - A string
* @return <b>str</b> with any leading or following whitespace
*
* Removes leading and following whitespace from a string and returns the modified string
*/
std::string remove_leading_and_following_whitespace(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> with any leading or following whitespace
*
* Removes leading and following whitespace from a string and returns the modified string
*/
std::string remove_leading_and_following_whitespace(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
*
* E.G. \&#39; would be replaced with '
*/
std::string replace_html_character_codes(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
*
* E.G. \&#39; would be replaced with '
*/
std::string replace_html_character_codes(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> with junk removed or replace
*
* Removed leading and following whitespace and replaces html character codes
*/
std::string format_name_string(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> with junk removed or replace
*
* Removed leading and following whitespace and replaces html character codes
*/
std::string format_name_string(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> properly formatted to be a filename
*
* Removes non-alphanumeric characters and spaces
*/
std::string format_filename(const std::string& str);
/**
*
* @param str - A string
* @return <b>str</b> properly formatted to be a filename
*
* Removes non-alphanumeric characters and spaces
*/
std::string format_filename(const std::string& str);
#if defined(__WIN32__)
#include <windows.h>
msec_t time_ms(void);
#else
#include <sys/time.h>
/**
*
* @return The time in milliseconds
*/
long time_ms();
#endif
#if defined(__WIN32__)
#include <windows.h>
msec_t time_ms(void);
#else
#include <sys/time.h>
/**
*
* @return The time in milliseconds
*/
long time_ms();
#endif
/**
*
* @param filename - Name of the file that is being downloaded
* @param total_to_download - The total amount of bytes that are being downloaded
* @param downloaded - The current amount of bytes that have been downloaded
* @param total_to_upload - The total amount of bytes that are being uploaded (This project does not upload so this is not used)
* @param uploaded - The current amount of bytes that have been uploaded (This project does not upload so this is not used)
* @return 0
*
* Used by curl. Displays the filename followed by a bar which show the percent downloaded followed by the number of Mib downloaded out of the total.
* The function takes the upload amount because that is required by curl but they are just ignored for this since we never upload anything.
*/
static int curl_progress_func(void* filename, double total_to_download, double downloaded, double total_to_upload, double uploaded);
/**
*
* @param filename - Name of the file that is being downloaded
* @param total_to_download - The total amount of bytes that are being downloaded
* @param downloaded - The current amount of bytes that have been downloaded
* @param total_to_upload - The total amount of bytes that are being uploaded (This project does not upload so this is not used)
* @param uploaded - The current amount of bytes that have been uploaded (This project does not upload so this is not used)
* @return 0
*
* Used by curl. Displays the filename followed by a bar which show the percent downloaded followed by the number of Mib downloaded out of the total.
* The function takes the upload amount because that is required by curl but they are just ignored for this since we never upload anything.
*/
static int curl_progress_func(void* filename, double total_to_download, double downloaded, double total_to_upload, double uploaded);
/**
*
* @param contents - What we're writing
* @param size - The amount that is being written
* @param nmemb - The number of bytes per unit of size
* @param userp - Where the information in <b>contents</b> is written to
* @return size * nmemb
*
* Used by curl. Writes the information gathered by curl into the userp string. This function was not written by me.
*/
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
/**
*
* @param contents - What we're writing
* @param size - The amount that is being written
* @param nmemb - The number of bytes per unit of size
* @param userp - Where the information in <b>contents</b> is written to
* @return size * nmemb
*
* Used by curl. Writes the information gathered by curl into the userp string. This function was not written by me.
*/
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
/**
*
* @param url - Url which is being downloaded
* @param verbose - Whether or not to be verbose (not recommended)
* @return The page data as a string
*
* This function downloads the provided url and returns it as a string. Does not use cookies. This was ripped directly from a firefox network request for an episode page and modified minimally.
*/
std::string get_generic_page(const std::string& url, bool verbose = false);
/**
*
* @param url - Url which is being downloaded
* @param verbose - Whether or not to be verbose (not recommended)
* @return The page data as a string
*
* This function downloads the provided url and returns it as a string. Does not use cookies. This was ripped directly from a firefox network request for an episode page and modified minimally.
*/
std::string get_generic_page(const std::string& url, bool verbose = false);
/**
* A class for handling all episode information. This class is wildly overkill if downloading an entire series as it gather the series name and season for every episode. This is not an issue here because all the information it gathers it already available while gathering the video url and the majority of the time taken while parsing an episode is from downloading the three required webpages.
*/
class episode {
/**
* A class for handling all episode information. This class is wildly overkill if downloading an entire series as it gather the series name and season for every episode. This is not an issue here because all the information it gathers it already available while gathering the video url and the majority of the time taken while parsing an episode is from downloading the three required webpages.
*/
class episode {
public:
/// The name of the series that the episode belongs to
std::string series;
/// The directory for the series
std::string series_directory;
/// The name of the episode
std::string name;
/// The number of the episode in the season. This can be a number or a string
std::string episode_number;
/// The url for the main episode page
std::string episode_url;
/// The data of the main episode page
std::string episode_data;
/// The url for the main embedded page. This contains page the link to the config page
std::string embedded_url;
/// The data of the main embedded page. This contains the link to the config page
std::string embedded_page_data;
/// The url for the main config page. This contains page the link to the mp4 video of the episode
std::string config_url;
/// The data of the main config page. This contains the link to the mp4 video of the episode
std::string config_data;
/// The list of the qualities available for the episode. This is a parallel array with the quality_urls vector
std::vector<std::string> qualities;
/// The list of the urls correlating with the qualities array.
std::vector<std::string> quality_urls;
public:
/// The name of the series that the episode belongs to
std::string series;
/// The directory for the series
std::string series_directory;
/// The name of the episode
std::string name;
/// The number of the episode in the season. This can be a number or a string
std::string episode_number;
/// The url for the main episode page
std::string episode_url;
/// The data of the main episode page
std::string episode_data;
/// The url for the main embedded page. This contains page the link to the config page
std::string embedded_url;
/// The data of the main embedded page. This contains the link to the config page
std::string embedded_page_data;
/// The url for the main config page. This contains page the link to the mp4 video of the episode
std::string config_url;
/// The data of the main config page. This contains the link to the mp4 video of the episode
std::string config_data;
/// The list of the qualities available for the episode. This is a parallel array with the quality_urls vector
std::vector<std::string> qualities;
/// The list of the urls correlating with the qualities array.
std::vector<std::string> quality_urls;
/// Whether or not to be verbose
bool verbose = false;
/// Whether or not to be verbose
bool verbose = false;
// Curl
// Curl
/**
*
* @param url - The url of the episode page
* @param auth_cookie - The authentication cookie with name "__cf_bm"
* @param session_cookie - The session cookie with name "_session"
* @param verbose - Whether or not to be verbose (not recommended)
* @return The episode page data
*/
static std::string get_episode_page(const std::string& url, const std::string& auth_cookie, const std::string& session_cookie, bool verbose = false);
/**
*
* @param url - The url of the episode page
* @param auth_cookie - The authentication cookie with name "__cf_bm"
* @param session_cookie - The session cookie with name "_session"
* @param verbose - Whether or not to be verbose (not recommended)
* @return The episode page data
*/
static std::string get_episode_page(const std::string& url, const std::string& auth_cookie, const std::string& session_cookie, bool verbose = false);
// Parsing
/**
*
* @param html_data - Episode page data
* @return The name of the series
*
* Get the name of the series from the episode page
*/
static std::string get_series_name(const std::string& html_data);
// Parsing
/**
*
* @param html_data - Episode page data
* @return The name of the series
*
* Get the name of the series from the episode page
*/
static std::string get_series_name(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The name of the episode
*
* Get the name of the episode from the episode page
*/
static std::string get_episode_name(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The name of the episode
*
* Get the name of the episode from the episode page
*/
static std::string get_episode_name(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The number of the episode
*
* Get the number of the episode from the episode page
*/
static std::string get_episode_number(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The number of the episode
*
* Get the number of the episode from the episode page
*/
static std::string get_episode_number(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The url of the embedded page
*
* Get the url of the embedded page from the episode page
*/
static std::string get_embed_url(const std::string& html_data);
/**
*
* @param html_data - Episode page data
* @return The url of the embedded page
*
* Get the url of the embedded page from the episode page
*/
static std::string get_embed_url(const std::string& html_data);
/**
*
* @param html_data - Embedded page data
* @return The url of the config page
*
* Get the url of the config page from the embedded page data
*/
static std::string get_config_url(const std::string& html_data);
/**
*
* @param html_data - Embedded page data
* @return The url of the config page
*
* Get the url of the config page from the embedded page data
*/
static std::string get_config_url(const std::string& html_data);
/**
*
* @return A vector of qualities
*
* Gets the available qualities for the episode and populate the <b>qualities</b> and <b>quality_urls</b> vectors.
* If this function has already been run it simply returns the already populated <b>qualities</b> vector unless said vector has been cleared.
*/
std::vector<std::string> get_qualities();
/**
*
* @return A vector of qualities
*
* Gets the available qualities for the episode and populate the <b>qualities</b> and <b>quality_urls</b> vectors.
* If this function has already been run it simply returns the already populated <b>qualities</b> vector unless said vector has been cleared.
*/
std::vector<std::string> get_qualities();
/**
*
* @param quality - The quality of the video
* @return The url to the video
*
* Get a link to the video of the episode with the given <b>quality</b>. <b>Quality</b> must be contained in the <b>qualities</b> vector otherwise this function will give an error and exit the program after listing the available qualities.
*/
std::string get_video_url(const std::string& quality);
/**
*
* @param quality - The quality of the video
* @return The url to the video
*
* Get a link to the video of the episode with the given <b>quality</b>. <b>Quality</b> must be contained in the <b>qualities</b> vector otherwise this function will give an error and exit the program after listing the available qualities.
*/
std::string get_video_url(const std::string& quality);
/**
*
* @param quality - The quality of the video
* @param filename - The filename which will be displayed will downloading the video
* @return The video data
*
* Download the episode with the given quality and return the raw video data as a string. The <b>filename</b> parameter is only used for displaying while downloading the video so that the user knows what is being downloaded. The <b>filename</b> argument is entirely optional and this function will not place the video into a file whether the value is given or not.
*/
std::string get_video_data(const std::string& quality, const std::string& filename = "");
/**
*
* @param quality - The quality of the video
* @param filename - The filename which will be displayed will downloading the video
* @return The video data
*
* Download the episode with the given quality and return the raw video data as a string. The <b>filename</b> parameter is only used for displaying while downloading the video so that the user knows what is being downloaded. The <b>filename</b> argument is entirely optional and this function will not place the video into a file whether the value is given or not.
*/
std::string get_video_data(const std::string& quality, const std::string& filename = "");
/**
*
* @param quality - The quality of the video
* @param series_directory - The directory which the episode is downloaded into
* @param filename - The name of the file (Will default if empty)
*
* Downloads the episode using the get_video_data function and places it into the <b>filename</b> file in the <b>series_directory</b> directory.
* If the <b>filename</b> parameter is left empty it will default to the E\<episode_number\>\<name\>.mp4 format.
*/
void download(const std::string& quality, const std::string& series_directory, std::string filename = "");
/**
*
* @param quality - The quality of the video
* @param series_directory - The directory which the episode is downloaded into
* @param filename - The name of the file (Will default if empty)
*
* Downloads the episode using the get_video_data function and places it into the <b>filename</b> file in the <b>series_directory</b> directory.
* If the <b>filename</b> parameter is left empty it will default to the E\<episode_number\>\<name\>.mp4 format.
*/
void download(const std::string& quality, const std::string& series_directory, std::string filename = "");
/**
*
* @param episode_url - Link to the episode
* @param cookies - The current cookies from the browser
* @param verbose - Whether or not be verbose
*
* Create an episode object from the link using to cookies to get all the necessary information.
* This constructor initializes all the object data.
*/
episode(const std::string& episode_url, std::vector<cookie> cookies, bool verbose = false) {
/**
*
* @param episode_url - Link to the episode
* @param cookies - The current cookies from the browser
* @param verbose - Whether or not be verbose
*
* Create an episode object from the link using to cookies to get all the necessary information.
* This constructor initializes all the object data.
*/
episode(const std::string& episode_url, std::vector<cookie> cookies, bool verbose = false) {
this->episode_url = episode_url;
this->verbose = verbose;
this->episode_url = episode_url;
this->verbose = verbose;
episode_data = get_episode_page(episode_url, cookies[0].value, cookies[1].value);
episode_data = get_episode_page(episode_url, cookies[0].value, cookies[1].value);
name = get_episode_name(episode_data);
name = get_episode_name(episode_data);
if (verbose) {
std::cout << "Got name: " << name << '\n';
}
if (verbose) {
std::cout << "Got name: " << name << '\n';
}
if (name == "ERROR") {
std::cerr << "EPISODE ERROR: Invalid Episode URL\n";
exit(6);
}
if (name == "ERROR") {
std::cerr << "EPISODE ERROR: Invalid Episode URL\n";
exit(6);
}
this->episode_number = get_episode_number(episode_data);
this->episode_number = get_episode_number(episode_data);
if (verbose) {
std::cout << "Got episode: " << this->episode_number << '\n';
}
if (verbose) {
std::cout << "Got episode: " << this->episode_number << '\n';
}
this->series = get_series_name(episode_data);
this->series = get_series_name(episode_data);
if (verbose) {
std::cout << "Got series: " << this->series << '\n';
}
if (verbose) {
std::cout << "Got series: " << this->series << '\n';
}
this->series_directory = format_filename(this->series);
this->series_directory = format_filename(this->series);
if (verbose) {
std::cout << "Got series directory: " << this->series_directory << '\n';
}
if (verbose) {
std::cout << "Got series directory: " << this->series_directory << '\n';
}
this->embedded_url = get_embed_url(episode_data);
this->embedded_url = get_embed_url(episode_data);
replace_all(this->embedded_url, "&amp;", "&");
replace_all(this->embedded_url, "&amp;", "&");
if (verbose) {
std::cout << "Got embedded url: " << this->embedded_url << '\n';
}
if (verbose) {
std::cout << "Got embedded url: " << this->embedded_url << '\n';
}
this->embedded_page_data = get_generic_page(this->embedded_url);
this->embedded_page_data = get_generic_page(this->embedded_url);
if (this->embedded_page_data.find("you are not authorized") != std::string::npos) {
std::cerr << "ERROR: Could not access video. Try refreshing cookies.\n";
exit(6);
}
if (this->embedded_page_data.find("you are not authorized") != std::string::npos) {
std::cerr << "ERROR: Could not access video. Try refreshing cookies.\n";
exit(6);
}
this->config_url = get_config_url(this->embedded_page_data);
this->config_url = get_config_url(this->embedded_page_data);
replace_all(this->config_url, "\\u0026", "&");
replace_all(this->config_url, "\\u0026", "&");
if (verbose) {
std::cout << "Got config url: " << this->config_url << '\n';
}
if (verbose) {
std::cout << "Got config url: " << this->config_url << '\n';
}
this->config_data = get_generic_page(this->config_url);
this->config_data = get_generic_page(this->config_url);
this->get_qualities();
}
this->get_qualities();
}
/**
* Creates an episode object with no data. This should only be used for invalid states.
*/
episode() = default;
};
/**
* Creates an episode object with no data. This should only be used for invalid states.
*/
episode() = default;
};
} // dropout_dl

View File

@ -8,126 +8,126 @@
namespace dropout_dl {
/**
* A class for handling and storing the program arguments.
*/
class options {
public:
/**
* A class for handling and storing the program arguments.
*/
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<cookie> cookies;
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<cookie> cookies;
/**
*
* @param argc - The number of provided program arguments
* @param argv - The provided program arguments
* @return A vector of arguments in the c++ string format
*
* Converts the C style program arguments to a vector of strings
*/
static std::vector<std::string> convert_program_args(int argc, char** argv) {
std::vector<std::string> out;
for (int i = 1; i < argc; i++) {
out.emplace_back(argv[i]);
}
return out;
}
/**
*
* @param argc - The number of provided program arguments
* @param argv - The provided program arguments
* @return A vector of arguments in the c++ string format
*
* Converts the C style program arguments to a vector of strings
*/
static std::vector<std::string> convert_program_args(int argc, char** argv) {
std::vector<std::string> out;
for (int i = 1; i < argc; i++) {
out.emplace_back(argv[i]);
}
return out;
}
/**
*
* @param argc - The number of provided program arguments
* @param argv - The provided program arguments
*
* Parses and handles the program arguments and creates an options object.
*/
options(int argc, char** argv) {
std::vector<std::string> args = convert_program_args(argc, argv);
/**
*
* @param argc - The number of provided program arguments
* @param argv - The provided program arguments
*
* Parses and handles the program arguments and creates an options object.
*/
options(int argc, char** argv) {
std::vector<std::string> args = convert_program_args(argc, argv);
for (int i = 0; i < args.size(); i++) {
std::string arg = args[i];
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] <url> [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;
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] <url> [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);
}
}
exit(0);
}
}
if (output_directory.empty()) {
output_directory = ".";
}
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";
}
}
};
if (season && series) {
std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n";
}
if (quality.empty()) {
quality = "1080p";
}
}
};
}
#ifdef DROPOUT_DL_SQLITE
@ -141,69 +141,69 @@ namespace dropout_dl {
*/
std::vector<dropout_dl::cookie> 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::fstream firefox_profile_file(firefox_profile_path);
std::string firefox_profile;
dropout_dl::cookie auth("__cf_bm");
dropout_dl::cookie session("_session");
dropout_dl::cookie auth("__cf_bm");
dropout_dl::cookie session("_session");
std::vector<dropout_dl::cookie> out;
std::vector<dropout_dl::cookie> out;
firefox_profile_file >> firefox_profile;
firefox_profile_file >> firefox_profile;
if (std::filesystem::is_directory(firefox_profile)) {
if (std::filesystem::is_directory(firefox_profile)) {
sqlite3 *db;
sqlite3 *db;
if (verbose) {
std::cout << "Getting firefox cookies from firefox sqlite db\n";
}
if (verbose) {
std::cout << "Getting firefox cookies from firefox sqlite db\n";
}
if (!std::filesystem::is_directory("tmp"))
std::filesystem::create_directories("tmp");
std::filesystem::remove("tmp/firefox_cookies.sqlite");
std::filesystem::copy_file(firefox_profile + "/cookies.sqlite", "tmp/firefox_cookies.sqlite");
if (!std::filesystem::is_directory("tmp"))
std::filesystem::create_directories("tmp");
std::filesystem::remove("tmp/firefox_cookies.sqlite");
std::filesystem::copy_file(firefox_profile + "/cookies.sqlite", "tmp/firefox_cookies.sqlite");
int rc = sqlite3_open("tmp/firefox_cookies.sqlite", &db);
if (rc) {
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
exit(1);
} else {
if (verbose) {
std::cout << "Firefox database opened successfully\n";
}
}
int rc = sqlite3_open("tmp/firefox_cookies.sqlite", &db);
if (rc) {
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
exit(1);
} else {
if (verbose) {
std::cout << "Firefox database opened successfully\n";
}
}
std::string len;
std::string len;
auth.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value");
auth.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value");
session.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value");
session.get_value_from_db(db, "FROM moz_cookies WHERE host LIKE '%dropout.tv%'", "value");
sqlite3_close(db);
sqlite3_close(db);
std::filesystem::remove("tmp/firefox_cookies.sqlite");
std::filesystem::remove("tmp/firefox_cookies.sqlite");
if (std::filesystem::is_empty("tmp")) {
std::filesystem::remove("tmp/");
}
}
else {
std::cerr << "FIREFOX COOKIE ERROR: Attempted to get cookies from firefox without profile." << std::endl;
exit(4);
}
if (std::filesystem::is_empty("tmp")) {
std::filesystem::remove("tmp/");
}
}
else {
std::cerr << "FIREFOX COOKIE ERROR: Attempted to get cookies from firefox without profile." << std::endl;
exit(4);
}
if (verbose) {
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
if (verbose) {
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
std::cout << session.name << ": " << session.len << ": " << session.value << '\n';
}
std::cout << session.name << ": " << session.len << ": " << session.value << '\n';
}
out.push_back(auth);
out.push_back(session);
out.push_back(auth);
out.push_back(session);
return out;
return out;
}
#ifdef DROPOUT_DL_GCRYPT
@ -219,62 +219,62 @@ std::vector<dropout_dl::cookie> get_cookies_from_firefox(const std::filesystem::
*/
std::vector<dropout_dl::cookie> 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::fstream chrome_profile_file(chrome_profile_path);
std::string chrome_profile;
dropout_dl::cookie auth("__cf_bm");
dropout_dl::cookie session("_session");
dropout_dl::cookie auth("__cf_bm");
dropout_dl::cookie session("_session");
std::vector<dropout_dl::cookie> out;
std::vector<dropout_dl::cookie> out;
getline(chrome_profile_file, chrome_profile);
getline(chrome_profile_file, chrome_profile);
if (std::filesystem::is_directory(chrome_profile)) {
if (std::filesystem::is_directory(chrome_profile)) {
sqlite3 *db;
sqlite3 *db;
if (verbose) {
std::cout << "Getting chrome cookies from chrome sqlite db\n";
}
if (verbose) {
std::cout << "Getting chrome cookies from chrome sqlite db\n";
}
int rc = sqlite3_open((chrome_profile + "/Cookies").c_str(), &db);
if (rc) {
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
exit(1);
} else {
if (verbose) {
std::cout << "Chrome database opened successfully\n";
}
}
int rc = sqlite3_open((chrome_profile + "/Cookies").c_str(), &db);
if (rc) {
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
exit(1);
} else {
if (verbose) {
std::cout << "Chrome database opened successfully\n";
}
}
std::string len;
std::string len;
auth.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value");
auth.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value");
session.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value");
session.get_value_from_db(db, "FROM cookies WHERE host_key LIKE '%dropout.tv%'", "encrypted_value");
sqlite3_close(db);
sqlite3_close(db);
}
else {
std::cerr << "CHROME COOKIE ERROR: Attempted to get cookies from chrome without profile." << std::endl;
exit(4);
}
}
else {
std::cerr << "CHROME COOKIE ERROR: Attempted to get cookies from chrome without profile." << std::endl;
exit(4);
}
auth.chrome_decrypt();
auth.chrome_decrypt();
session.chrome_decrypt();
session.chrome_decrypt();
if (verbose) {
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
if (verbose) {
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
std::cout << session.name << ": " << session.len << ": " << session.value << '\n';
}
std::cout << session.name << ": " << session.len << ": " << session.value << '\n';
}
out.push_back(auth);
out.push_back(session);
out.push_back(auth);
out.push_back(session);
return out;
return out;
}
#endif
#endif
@ -289,93 +289,93 @@ std::vector<dropout_dl::cookie> get_cookies_from_chrome(const std::filesystem::p
*/
std::vector<dropout_dl::cookie> get_cookies(bool verbose = false) {
std::filesystem::path firefox_profile("firefox_profile");
std::filesystem::path chrome_profile("chrome_profile");
std::filesystem::path firefox_profile("_firefox_profile");
std::filesystem::path chrome_profile("chrome_profile");
if (std::filesystem::exists(firefox_profile)) {
if (std::filesystem::exists(firefox_profile)) {
#ifdef DROPOUT_DL_SQLITE
return get_cookies_from_firefox(firefox_profile, verbose);
#else
std::cout << "WARNING: Firefox profile file exists but sqlite is not installed" << std::endl;
#endif
}
if (std::filesystem::exists(chrome_profile)) {
#if defined(DROPOUT_DL_GCRYPT) & defined(DROPOUT_DL_SQLITE)
return get_cookies_from_chrome(chrome_profile, verbose);
#else
std::cout << "WARNING: Chrome profile file exists but libgcrypt or sqlite is not installed" << std::endl;
#endif
}
#ifdef DROPOUT_DL_SQLITE
return get_cookies_from_firefox(firefox_profile, verbose);
#else
std::cout << "WARNING: Firefox profile file exists but sqlite is not installed" << std::endl;
#endif
}
if (std::filesystem::exists(chrome_profile)) {
#if defined(DROPOUT_DL_GCRYPT) & defined(DROPOUT_DL_SQLITE)
return get_cookies_from_chrome(chrome_profile, verbose);
#else
std::cout << "WARNING: Chrome profile file exists but libgcrypt or sqlite is not installed" << std::endl;
#endif
}
{
std::cerr << "ERROR: dropout.tv cookies could not be found" << std::endl;
exit(7);
}
{
std::cerr << "ERROR: dropout.tv cookies could not be found" << std::endl;
exit(7);
}
}
int main(int argc, char** argv) {
dropout_dl::options options(argc, argv);
dropout_dl::options options(argc, argv);
if (options.verbose) {
std::cout << "quality: " << options.quality << std::endl;
std::cout << "verbose: " << options.verbose << std::endl;
std::cout << "url: \"" << options.url << '"' << std::endl;
}
if (options.verbose) {
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;
std::string firefox_profile;
std::string chrome_profile;
std::string video_data;
std::string video_data;
if (options.url.empty()) {
std::cout << "Enter episode url: ";
std::cin >> options.url;
}
else if (options.verbose) {
std::cout << "Got episode url: " << options.url << " from program arguments\n";
}
if (options.url.empty()) {
std::cout << "Enter episode url: ";
std::cin >> options.url;
}
else if (options.verbose) {
std::cout << "Got episode url: " << options.url << " from program arguments\n";
}
if (!options.cookies_forced) {
options.cookies = get_cookies(options.verbose);
}
if (!options.cookies_forced) {
options.cookies = get_cookies(options.verbose);
}
if (options.series) {
dropout_dl::series series(options.url, options.cookies);
if (options.series) {
dropout_dl::series series(options.url, options.cookies);
series.download(options.quality, options.output_directory);
}
else if (options.season) {
dropout_dl::season season = dropout_dl::series::get_season(options.url, options.cookies);
series.download(options.quality, options.output_directory);
}
else if (options.season) {
dropout_dl::season season = dropout_dl::series::get_season(options.url, options.cookies);
std::string series_directory = dropout_dl::format_filename(season.series_name);
std::string series_directory = dropout_dl::format_filename(season.series_name);
std::cout << "ser: " << season.series_name << "\ndir: " << series_directory << '\n';
std::cout << "ser: " << season.series_name << "\ndir: " << series_directory << '\n';
season.download(options.quality, options.output_directory + "/" + series_directory);
}
else {
dropout_dl::episode ep(options.url, options.cookies, options.verbose);
season.download(options.quality, options.output_directory + "/" + series_directory);
}
else {
dropout_dl::episode ep(options.url, options.cookies, options.verbose);
if (options.verbose) {
std::cout << "filename: " << options.filename << '\n';
}
if (options.verbose) {
std::cout << "filename: " << options.filename << '\n';
}
if (!std::filesystem::is_directory(options.output_directory)) {
std::filesystem::create_directories(options.output_directory);
if (options.verbose) {
std::cout << "Creating series directory" << '\n';
}
}
if (!std::filesystem::is_directory(options.output_directory)) {
std::filesystem::create_directories(options.output_directory);
if (options.verbose) {
std::cout << "Creating series directory" << '\n';
}
}
if (options.filename.empty()) {
options.filename = dropout_dl::format_filename(ep.name + ".mp4");
}
if (options.filename.empty()) {
options.filename = dropout_dl::format_filename(ep.name + ".mp4");
}
ep.download(options.quality, options.output_directory, options.filename);
}
ep.download(options.quality, options.output_directory, options.filename);
}
return 0;
return 0;
}

View File

@ -5,64 +5,64 @@
#include "season.h"
namespace dropout_dl {
episode get_episode(const std::string& html_data, int& start_point, const std::vector<cookie>& cookies) {
int link_start = 0;
for (int i = start_point; i > 0; i--) {
if (substr_is(html_data, i, "<a")) {
link_start = i;
break;
}
else if (substr_is(html_data, i, "<")) {
// Invalid episode place. Return empty value.
return {};
}
}
episode get_episode(const std::string& html_data, int& start_point, const std::vector<cookie>& cookies) {
int link_start = 0;
for (int i = start_point; i > 0; i--) {
if (substr_is(html_data, i, "<a")) {
link_start = i;
break;
}
else if (substr_is(html_data, i, "<")) {
// Invalid episode place. Return empty value.
return {};
}
}
for (int i = link_start; i < html_data.size(); i++) {
if (substr_is(html_data, i, "href=\"")) {
i += 6;
for (int j = 0; j + i < html_data.size(); j++) {
if (html_data[i + j] == '"') {
start_point += 15;
return {html_data.substr(i, j), cookies};
}
}
}
}
std::cerr << "SEASON PARSE ERROR: Error finding episode" << std::endl;
exit(8);
}
for (int i = link_start; i < html_data.size(); i++) {
if (substr_is(html_data, i, "href=\"")) {
i += 6;
for (int j = 0; j + i < html_data.size(); j++) {
if (html_data[i + j] == '"') {
start_point += 15;
return {html_data.substr(i, j), cookies};
}
}
}
}
std::cerr << "SEASON PARSE ERROR: Error finding episode" << std::endl;
exit(8);
}
std::vector<episode> season::get_episodes(const std::string &html_data, const std::vector<cookie>& cookies) {
std::vector<episode> out;
std::vector<episode> season::get_episodes(const std::string &html_data, const std::vector<cookie>& cookies) {
std::vector<episode> out;
std::string site_video(R"(class="browse-item-link" data-track-event="site_video")");
std::string site_video(R"(class="browse-item-link" data-track-event="site_video")");
for (int i = 0; i < html_data.size(); i++) {
if (substr_is(html_data, i, site_video)) {
episode e = get_episode(html_data, i, cookies);
if (e.episode_url.empty()) {
continue;
}
std::cout << '\t' << e.episode_number << ": " << e.name << '\n';
out.push_back(e);
}
}
for (int i = 0; i < html_data.size(); i++) {
if (substr_is(html_data, i, site_video)) {
episode e = get_episode(html_data, i, cookies);
if (e.episode_url.empty()) {
continue;
}
std::cout << '\t' << e.episode_number << ": " << e.name << '\n';
out.push_back(e);
}
}
return out;
}
return out;
}
void season::download(const std::string &quality, const std::string &series_directory) {
if (!std::filesystem::is_directory(series_directory)) {
std::filesystem::create_directories(series_directory);
std::cout << "Creating series directory" << '\n';
}
void season::download(const std::string &quality, const std::string &series_directory) {
if (!std::filesystem::is_directory(series_directory)) {
std::filesystem::create_directories(series_directory);
std::cout << "Creating series directory" << '\n';
}
std::string dir = format_filename(series_directory + "/" + this->name);
std::string dir = format_filename(series_directory + "/" + this->name);
for (auto& ep : episodes) {
ep.download(quality, dir);
}
}
for (auto& ep : episodes) {
ep.download(quality, dir);
}
}
} // dropout_dl

View File

@ -10,56 +10,56 @@
namespace dropout_dl {
class season {
public:
/// The name of the season
std::string name;
/// The name of the series
std::string series_name;
/// The link to the season page
std::string url;
/// The season page data
std::string page_data;
/// The list of all the episodes in the season
std::vector<episode> episodes;
class season {
public:
/// The name of the season
std::string name;
/// The name of the series
std::string series_name;
/// The link to the season page
std::string url;
/// The season page data
std::string page_data;
/// The list of all the episodes in the season
std::vector<episode> episodes;
/**
*
* @param html_data - The season page data
* @param cookies - The browser cookies
* @return A vector of all episodes in the season
*
* Gets all the episodes of the season and returns in a vector
*/
static std::vector<episode> get_episodes(const std::string& html_data, const std::vector<cookie>& cookies);
/**
*
* @param html_data - The season page data
* @param cookies - The browser cookies
* @return A vector of all episodes in the season
*
* Gets all the episodes of the season and returns in a vector
*/
static std::vector<episode> get_episodes(const std::string& html_data, const std::vector<cookie>& cookies);
/**
*
* @param quality - The quality of the videos
* @param series_directory - The directory of the series
*
* Downloads all the episodes of the season. Appends the season to the series directory
*/
void download(const std::string& quality, const std::string& series_directory);
/**
*
* @param quality - The quality of the videos
* @param series_directory - The directory of the series
*
* Downloads all the episodes of the season. Appends the season to the series directory
*/
void download(const std::string& quality, const std::string& series_directory);
/**
*
* @param url - The url to the webpage of the season
* @param name - The name of the season
* @param cookies - The browser cookies
* @param series_name - The name of the series
*
* Creates a season object and populates the needed information.
*/
season(const std::string& url, const std::string& name, const std::vector<cookie>& cookies, const std::string& series_name = "") {
this->url = url;
this->name = name;
this->series_name = series_name;
std::cout << series_name << ": " << name << ": " << "\n";
this->page_data = get_generic_page(url);
this->episodes = get_episodes(page_data, cookies);
}
};
/**
*
* @param url - The url to the webpage of the season
* @param name - The name of the season
* @param cookies - The browser cookies
* @param series_name - The name of the series
*
* Creates a season object and populates the needed information.
*/
season(const std::string& url, const std::string& name, const std::vector<cookie>& cookies, const std::string& series_name = "") {
this->url = url;
this->name = name;
this->series_name = series_name;
std::cout << series_name << ": " << name << ": " << "\n";
this->page_data = get_generic_page(url);
this->episodes = get_episodes(page_data, cookies);
}
};
} // dropout_dl

View File

@ -6,216 +6,216 @@
namespace dropout_dl {
std::string series::get_series_name(const std::string& html_data) {
std::string collection_title("collection-title");
std::string close_tag(">");
std::string close_a("</h1>");
std::string series::get_series_name(const std::string& html_data) {
std::string collection_title("collection-title");
std::string close_tag(">");
std::string close_a("</h1>");
for (int i = 0; i < html_data.size(); i++) {
if (substr_is(html_data, i, collection_title)) {
for (int j = i + collection_title.size(); j < html_data.size(); j++) {
if (html_data[j] == '\n' || html_data[j] == ' ' || html_data[j] == '\t') continue;
if (substr_is(html_data, j, close_tag)) {
for (int l = 0; l < html_data.size() - j; l++) {
if (substr_is(html_data, j + l, close_a)) {
return format_name_string(html_data.substr(j + 1, l - 1));
}
}
}
}
}
}
return "ERROR";
}
for (int i = 0; i < html_data.size(); i++) {
if (substr_is(html_data, i, collection_title)) {
for (int j = i + collection_title.size(); j < html_data.size(); j++) {
if (html_data[j] == '\n' || html_data[j] == ' ' || html_data[j] == '\t') continue;
if (substr_is(html_data, j, close_tag)) {
for (int l = 0; l < html_data.size() - j; l++) {
if (substr_is(html_data, j + l, close_a)) {
return format_name_string(html_data.substr(j + 1, l - 1));
}
}
}
}
}
}
return "ERROR";
}
std::vector<season> series::get_seasons(const std::string &html_data, const std::vector<cookie>& cookies) {
std::vector<season> out;
std::vector<season> series::get_seasons(const std::string &html_data, const std::vector<cookie>& cookies) {
std::vector<season> out;
std::string search_class("js-switch-season");
std::string open_select("<select");
std::string close_tag(">");
std::string close_select("</select>");
std::string search_class("js-switch-season");
std::string open_select("<select");
std::string close_tag(">");
std::string close_select("</select>");
std::string open_option("<option");
std::string close_option("</option>");
std::string value("value=");
std::string open_option("<option");
std::string close_option("</option>");
std::string value("value=");
bool seasons_dropdown = 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, close_tag)) {
i += close_tag.size() + 1;
for (int j = 0; i + j < html_data.size(); j++) {
if (html_data[i + j] == '\n') {
season_name = html_data.substr(i, j);
bool seasons_dropdown = 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, close_tag)) {
i += close_tag.size() + 1;
for (int j = 0; i + j < html_data.size(); j++) {
if (html_data[i + j] == '\n') {
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);
// 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);
out.emplace_back(season_url, season_name, cookies);
out.emplace_back(season_url, season_name, cookies);
std::cout << out.back().name << ": " << out.back().url << '\n';
std::cout << out.back().name << ": " << out.back().url << '\n';
season_url.clear();
season_name.clear();
season_url.clear();
season_name.clear();
i = i + j;
i = i + j;
break;
}
}
}
break;
}
}
}
if (substr_is(html_data, i, close_select)) {
break;
}
}
}
if (substr_is(html_data, i, close_select)) {
break;
}
}
}
return out;
}
return out;
}
season series::get_season(const std::string &url, const std::vector<cookie>& cookies) {
std::string html_data = get_generic_page(url);
season series::get_season(const std::string &url, const std::vector<cookie>& cookies) {
std::string html_data = get_generic_page(url);
std::string search_class("js-switch-season");
std::string open_select("<select");
std::string close_tag(">");
std::string close_select("</select>");
std::string search_class("js-switch-season");
std::string open_select("<select");
std::string close_tag(">");
std::string close_select("</select>");
std::string open_option("<option");
std::string close_option("</option>");
std::string value("value=");
std::string open_option("<option");
std::string close_option("</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);
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);
// 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)};
}
return {season_url, season_name, cookies, get_series_name(html_data)};
}
season_url.clear();
season_name.clear();
season_url.clear();
season_name.clear();
i = i + j;
i = i + j;
break;
}
}
}
break;
}
}
}
if (substr_is(html_data, i, close_select)) {
break;
}
}
}
if (substr_is(html_data, i, close_select)) {
break;
}
}
}
std::cerr << "SEASON PARSE ERROR: No selected season found\n";
exit(9);
}
std::cerr << "SEASON PARSE ERROR: No selected season found\n";
exit(9);
}
void series::download(const std::string &quality, const std::string& base) {
if (!std::filesystem::is_directory(base + "/" + series_directory)) {
std::filesystem::create_directories(base + "/" + series_directory);
std::cout << "Creating series directory" << '\n';
}
void series::download(const std::string &quality, const std::string& base) {
if (!std::filesystem::is_directory(base + "/" + series_directory)) {
std::filesystem::create_directories(base + "/" + series_directory);
std::cout << "Creating series directory" << '\n';
}
for (auto& season : seasons) {
season.download(quality, base + "/" + series_directory);
}
}
for (auto& season : seasons) {
season.download(quality, base + "/" + series_directory);
}
}
} // dropout_dl

View File

@ -11,80 +11,80 @@
namespace dropout_dl {
/// A class for handling all series information and functions.
class series {
public:
/// The name of the series
std::string name;
/// The link to the series page
std::string url;
/// The series page data
std::string page_data;
/// The directory which will contain the seasons of the series
std::string series_directory;
/// A vector containing all the season that this series include
std::vector<season> seasons;
/// A class for handling all series information and functions.
class series {
public:
/// The name of the series
std::string name;
/// The link to the series page
std::string url;
/// The series page data
std::string page_data;
/// The directory which will contain the seasons of the series
std::string series_directory;
/// A vector containing all the season that this series include
std::vector<season> seasons;
/**
*
* @param html_data - The series page data
* @return The name of the series
*
* Scrapes the series page for the name of the series
*/
static std::string get_series_name(const std::string& html_data);
/**
*
* @param html_data - The series page data
* @return The name of the series
*
* Scrapes the series page for the name of the series
*/
static std::string get_series_name(const std::string& html_data);
/**
*
* @param html_data - The series page data
* @param cookies - The cookies from a browser
* @return A list of all seasons in the series
*
* Scrapes the series page for the names and link of all the season. Creates season objects for each of these.
* These season object contain all the episodes of the season as episode objects.
* The cookies this function takes are passed to the episode objects.
*/
static std::vector<season> get_seasons(const std::string& html_data, const std::vector<cookie>& cookies);
/**
*
* @param html_data - The series page data
* @param cookies - The cookies from a browser
* @return A list of all seasons in the series
*
* Scrapes the series page for the names and link of all the season. Creates season objects for each of these.
* These season object contain all the episodes of the season as episode objects.
* The cookies this function takes are passed to the episode objects.
*/
static std::vector<season> get_seasons(const std::string& html_data, const std::vector<cookie>& cookies);
/**
*
* @param url - The url to the season
* @param cookies - The browser cookies
* @return A season object
*
* Gets the season page, which is really just a series page, and creates a season object with all the episodes of the season
*/
static season get_season(const std::string& url, const std::vector<cookie>& cookies);
/**
*
* @param url - The url to the season
* @param cookies - The browser cookies
* @return A season object
*
* Gets the season page, which is really just a series page, and creates a season object with all the episodes of the season
*/
static season get_season(const std::string& url, const std::vector<cookie>& cookies);
/**
*
* @param quality - The quality of the video
* @param base - The base directory to download to
*
* Downloads the series into the <b>base</b> directory with the format <i>\<base\>/\<series name\>/\<season name\>/\<episode\></i>
*/
void download(const std::string& quality, const std::string& base = ".");
/**
*
* @param quality - The quality of the video
* @param base - The base directory to download to
*
* Downloads the series into the <b>base</b> directory with the format <i>\<base\>/\<series name\>/\<season name\>/\<episode\></i>
*/
void download(const std::string& quality, const std::string& base = ".");
/**
*
* @param url - The link to the series page
* @param cookies - The browser cookies
*
* Creates a series object and populates the needed variables
*/
explicit series(const std::string& url, const std::vector<dropout_dl::cookie>& cookies) {
this->url = url;
this->page_data = get_generic_page(url);
this->name = get_series_name(page_data);
if (name == "ERROR") {
std::cerr << "SERIES PARSE ERROR: Could not parse series name\n";
exit(10);
}
/**
*
* @param url - The link to the series page
* @param cookies - The browser cookies
*
* Creates a series object and populates the needed variables
*/
explicit series(const std::string& url, const std::vector<dropout_dl::cookie>& cookies) {
this->url = url;
this->page_data = get_generic_page(url);
this->name = get_series_name(page_data);
if (name == "ERROR") {
std::cerr << "SERIES PARSE ERROR: Could not parse series name\n";
exit(10);
}
this->series_directory = format_filename(name);
this->series_directory = format_filename(name);
this->seasons = get_seasons(page_data, cookies);
}
};
this->seasons = get_seasons(page_data, cookies);
}
};
} // dropout_dl