Meta: Changed Spaces to Tabs
This commit is contained in:
parent
35b4c181b3
commit
ea57f4477b
1187
src/episode.cpp
1187
src/episode.cpp
File diff suppressed because it is too large
Load Diff
762
src/episode.h
762
src/episode.h
|
@ -16,444 +16,444 @@
|
||||||
|
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
|
|
||||||
class cookie {
|
class cookie {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param data - A string to write to return value of the command to
|
* @param data - A string to write to return value of the command to
|
||||||
* @param argc - The number of results from the command
|
* @param argc - The number of results from the command
|
||||||
* @param argv - The results of the command
|
* @param argv - The results of the command
|
||||||
* @param azColName - The column names from the command
|
* @param azColName - The column names from the command
|
||||||
* @return 0 on success or -1 on failure
|
* @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.
|
* 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)
|
static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName)
|
||||||
{
|
{
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
|
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*(std::string*)data = argv[0];
|
*(std::string*)data = argv[0];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the value from the sqlite database or "?" if not set.
|
* The name of the value from the sqlite database or "?" if not set.
|
||||||
*/
|
*/
|
||||||
std::string name;
|
std::string name;
|
||||||
/**
|
/**
|
||||||
* The value of the cookie
|
* The value of the cookie
|
||||||
*/
|
*/
|
||||||
std::string value;
|
std::string value;
|
||||||
/**
|
/**
|
||||||
* The length of the value of the cookie
|
* The length of the value of the cookie
|
||||||
*/
|
*/
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param name - Name of the value from the sqlite database
|
* @param name - Name of the value from the sqlite database
|
||||||
*
|
*
|
||||||
* Create a cookie with no value and length of 0
|
* Create a cookie with no value and length of 0
|
||||||
*/
|
*/
|
||||||
explicit cookie(const std::string& name) {
|
explicit cookie(const std::string& name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->value = "";
|
this->value = "";
|
||||||
this->len = 0;
|
this->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param name - Name of the value from the sqlite database
|
* @param name - Name of the value from the sqlite database
|
||||||
* @param cookie - Value of the cookie
|
* @param cookie - Value of the cookie
|
||||||
*
|
*
|
||||||
* Sets the name and value using the parameters and gets the length from the value
|
* Sets the name and value using the parameters and gets the length from the value
|
||||||
*/
|
*/
|
||||||
cookie(const std::string& name, const std::string& cookie) {
|
cookie(const std::string& name, const std::string& cookie) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->value = cookie;
|
this->value = cookie;
|
||||||
this->len = cookie.size();
|
this->len = cookie.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param cookie - Value of the cookie
|
* @param cookie - Value of the cookie
|
||||||
* @param length - Length of the cookie
|
* @param length - Length of the cookie
|
||||||
*
|
*
|
||||||
* Sets the value and length using the parameters and sets the name as "?"
|
* Sets the value and length using the parameters and sets the name as "?"
|
||||||
*/
|
*/
|
||||||
cookie(const std::string& cookie, int length) {
|
cookie(const std::string& cookie, int length) {
|
||||||
this->value = cookie;
|
this->value = cookie;
|
||||||
this->name = "?";
|
this->name = "?";
|
||||||
this->len = length;
|
this->len = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param name - Name of the value from the sqlite database
|
* @param name - Name of the value from the sqlite database
|
||||||
* @param cookie - Value of the cookie
|
* @param cookie - Value of the cookie
|
||||||
* @param length - Length of the cookie
|
* @param length - Length of the cookie
|
||||||
*
|
*
|
||||||
* Sets the name, value, and length using the parameters leaving nothing unset.
|
* Sets the name, value, and length using the parameters leaving nothing unset.
|
||||||
*/
|
*/
|
||||||
cookie(const std::string& name, const std::string& cookie, int length) {
|
cookie(const std::string& name, const std::string& cookie, int length) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->value = cookie;
|
this->value = cookie;
|
||||||
this->len = length;
|
this->len = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param db - An sqlite3 database
|
* @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 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
|
* @param value - The name of the value to fill the cookie with
|
||||||
*
|
*
|
||||||
* Retrieve the value of a cookie from the provided sqlite database.
|
* 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);
|
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 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 salt - Salt is "saltysalt" for both MacOS and Linux
|
||||||
* @param length - Length of 16 is standard 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
|
* @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.
|
* 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);
|
void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16);
|
||||||
|
|
||||||
private:
|
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.
|
* 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();
|
void url_decode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the leading version (e.g. "v10") from the cookie
|
* Remove the leading version (e.g. "v10") from the cookie
|
||||||
*/
|
*/
|
||||||
void format_from_chrome();
|
void format_from_chrome();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param string - The string which is being searched
|
* @param string - The string which is being searched
|
||||||
* @param start - The starting index of the substring
|
* @param start - The starting index of the substring
|
||||||
* @param test_str - The string which is being tested
|
* @param test_str - The string which is being tested
|
||||||
* @return whether or not the substring is at the start index
|
* @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
|
* 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);
|
bool substr_is(const std::string& string, int start, const std::string& test_str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param str - The base string which is being modified
|
* @param str - The base string which is being modified
|
||||||
* @param from - what is being replaced
|
* @param from - what is being replaced
|
||||||
* @param to - what to place it with
|
* @param to - what to place it with
|
||||||
*
|
*
|
||||||
* Replaces every instance of the <b>from</b> string with the <b>to</b> string.
|
* 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);
|
void replace_all(std::string& str, const std::string& from, const std::string& to);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param str - A string
|
* @param str - A string
|
||||||
* @return <b>str</b> with any leading or following whitespace
|
* @return <b>str</b> with any leading or following whitespace
|
||||||
*
|
*
|
||||||
* Removes leading and following whitespace from a string and returns the modified string
|
* Removes leading and following whitespace from a string and returns the modified string
|
||||||
*/
|
*/
|
||||||
std::string remove_leading_and_following_whitespace(const std::string& str);
|
std::string remove_leading_and_following_whitespace(const std::string& str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param str - A string
|
* @param str - A string
|
||||||
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
|
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
|
||||||
*
|
*
|
||||||
* E.G. \' would be replaced with '
|
* E.G. \' would be replaced with '
|
||||||
*/
|
*/
|
||||||
std::string replace_html_character_codes(const std::string& str);
|
std::string replace_html_character_codes(const std::string& str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param str - A string
|
* @param str - A string
|
||||||
* @return <b>str</b> with junk removed or replace
|
* @return <b>str</b> with junk removed or replace
|
||||||
*
|
*
|
||||||
* Removed leading and following whitespace and replaces html character codes
|
* Removed leading and following whitespace and replaces html character codes
|
||||||
*/
|
*/
|
||||||
std::string format_name_string(const std::string& str);
|
std::string format_name_string(const std::string& str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param str - A string
|
* @param str - A string
|
||||||
* @return <b>str</b> properly formatted to be a filename
|
* @return <b>str</b> properly formatted to be a filename
|
||||||
*
|
*
|
||||||
* Removes non-alphanumeric characters and spaces
|
* Removes non-alphanumeric characters and spaces
|
||||||
*/
|
*/
|
||||||
std::string format_filename(const std::string& str);
|
std::string format_filename(const std::string& str);
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
msec_t time_ms(void);
|
msec_t time_ms(void);
|
||||||
#else
|
#else
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return The time in milliseconds
|
* @return The time in milliseconds
|
||||||
*/
|
*/
|
||||||
long time_ms();
|
long time_ms();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param filename - Name of the file that is being downloaded
|
* @param filename - Name of the file that is being downloaded
|
||||||
* @param total_to_download - The total amount of bytes that are 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 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 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)
|
* @param uploaded - The current amount of bytes that have been uploaded (This project does not upload so this is not used)
|
||||||
* @return 0
|
* @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.
|
* 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.
|
* 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);
|
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 contents - What we're writing
|
||||||
* @param size - The amount that is being written
|
* @param size - The amount that is being written
|
||||||
* @param nmemb - The number of bytes per unit of size
|
* @param nmemb - The number of bytes per unit of size
|
||||||
* @param userp - Where the information in <b>contents</b> is written to
|
* @param userp - Where the information in <b>contents</b> is written to
|
||||||
* @return size * nmemb
|
* @return size * nmemb
|
||||||
*
|
*
|
||||||
* Used by curl. Writes the information gathered by curl into the userp string. This function was not written by me.
|
* 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);
|
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url - Url which is being downloaded
|
* @param url - Url which is being downloaded
|
||||||
* @param verbose - Whether or not to be verbose (not recommended)
|
* @param verbose - Whether or not to be verbose (not recommended)
|
||||||
* @return The page data as a string
|
* @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.
|
* 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);
|
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.
|
* 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 {
|
class episode {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// The name of the series that the episode belongs to
|
/// The name of the series that the episode belongs to
|
||||||
std::string series;
|
std::string series;
|
||||||
/// The directory for the series
|
/// The directory for the series
|
||||||
std::string series_directory;
|
std::string series_directory;
|
||||||
/// The name of the episode
|
/// The name of the episode
|
||||||
std::string name;
|
std::string name;
|
||||||
/// The number of the episode in the season. This can be a number or a string
|
/// The number of the episode in the season. This can be a number or a string
|
||||||
std::string episode_number;
|
std::string episode_number;
|
||||||
/// The url for the main episode page
|
/// The url for the main episode page
|
||||||
std::string episode_url;
|
std::string episode_url;
|
||||||
/// The data of the main episode page
|
/// The data of the main episode page
|
||||||
std::string episode_data;
|
std::string episode_data;
|
||||||
/// The url for the main embedded page. This contains page the link to the config page
|
/// The url for the main embedded page. This contains page the link to the config page
|
||||||
std::string embedded_url;
|
std::string embedded_url;
|
||||||
/// The data of the main embedded page. This contains the link to the config page
|
/// The data of the main embedded page. This contains the link to the config page
|
||||||
std::string embedded_page_data;
|
std::string embedded_page_data;
|
||||||
/// The url for the main config page. This contains page the link to the mp4 video of the episode
|
/// The url for the main config page. This contains page the link to the mp4 video of the episode
|
||||||
std::string config_url;
|
std::string config_url;
|
||||||
/// The data of the main config page. This contains the link to the mp4 video of the episode
|
/// The data of the main config page. This contains the link to the mp4 video of the episode
|
||||||
std::string config_data;
|
std::string config_data;
|
||||||
/// The list of the qualities available for the episode. This is a parallel array with the quality_urls vector
|
/// The list of the qualities available for the episode. This is a parallel array with the quality_urls vector
|
||||||
std::vector<std::string> qualities;
|
std::vector<std::string> qualities;
|
||||||
/// The list of the urls correlating with the qualities array.
|
/// The list of the urls correlating with the qualities array.
|
||||||
std::vector<std::string> quality_urls;
|
std::vector<std::string> quality_urls;
|
||||||
|
|
||||||
/// Whether or not to be verbose
|
/// Whether or not to be verbose
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
// Curl
|
// Curl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url - The url of the episode page
|
* @param url - The url of the episode page
|
||||||
* @param auth_cookie - The authentication cookie with name "__cf_bm"
|
* @param auth_cookie - The authentication cookie with name "__cf_bm"
|
||||||
* @param session_cookie - The session cookie with name "_session"
|
* @param session_cookie - The session cookie with name "_session"
|
||||||
* @param verbose - Whether or not to be verbose (not recommended)
|
* @param verbose - Whether or not to be verbose (not recommended)
|
||||||
* @return The episode page data
|
* @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);
|
static std::string get_episode_page(const std::string& url, const std::string& auth_cookie, const std::string& session_cookie, bool verbose = false);
|
||||||
|
|
||||||
// Parsing
|
// Parsing
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - Episode page data
|
* @param html_data - Episode page data
|
||||||
* @return The name of the series
|
* @return The name of the series
|
||||||
*
|
*
|
||||||
* Get the name of the series from the episode page
|
* Get the name of the series from the episode page
|
||||||
*/
|
*/
|
||||||
static std::string get_series_name(const std::string& html_data);
|
static std::string get_series_name(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - Episode page data
|
* @param html_data - Episode page data
|
||||||
* @return The name of the episode
|
* @return The name of the episode
|
||||||
*
|
*
|
||||||
* Get the name of the episode from the episode page
|
* Get the name of the episode from the episode page
|
||||||
*/
|
*/
|
||||||
static std::string get_episode_name(const std::string& html_data);
|
static std::string get_episode_name(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - Episode page data
|
* @param html_data - Episode page data
|
||||||
* @return The number of the episode
|
* @return The number of the episode
|
||||||
*
|
*
|
||||||
* Get the number of the episode from the episode page
|
* Get the number of the episode from the episode page
|
||||||
*/
|
*/
|
||||||
static std::string get_episode_number(const std::string& html_data);
|
static std::string get_episode_number(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - Episode page data
|
* @param html_data - Episode page data
|
||||||
* @return The url of the embedded page
|
* @return The url of the embedded page
|
||||||
*
|
*
|
||||||
* Get the url of the embedded page from the episode page
|
* Get the url of the embedded page from the episode page
|
||||||
*/
|
*/
|
||||||
static std::string get_embed_url(const std::string& html_data);
|
static std::string get_embed_url(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - Embedded page data
|
* @param html_data - Embedded page data
|
||||||
* @return The url of the config page
|
* @return The url of the config page
|
||||||
*
|
*
|
||||||
* Get the url of the config page from the embedded page data
|
* Get the url of the config page from the embedded page data
|
||||||
*/
|
*/
|
||||||
static std::string get_config_url(const std::string& html_data);
|
static std::string get_config_url(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return A vector of 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.
|
* 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.
|
* 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();
|
std::vector<std::string> get_qualities();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param quality - The quality of the video
|
* @param quality - The quality of the video
|
||||||
* @return The url to 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.
|
* 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);
|
std::string get_video_url(const std::string& quality);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param quality - The quality of the video
|
* @param quality - The quality of the video
|
||||||
* @param filename - The filename which will be displayed will downloading the video
|
* @param filename - The filename which will be displayed will downloading the video
|
||||||
* @return The video data
|
* @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.
|
* 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 = "");
|
std::string get_video_data(const std::string& quality, const std::string& filename = "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param quality - The quality of the video
|
* @param quality - The quality of the video
|
||||||
* @param series_directory - The directory which the episode is downloaded into
|
* @param series_directory - The directory which the episode is downloaded into
|
||||||
* @param filename - The name of the file (Will default if empty)
|
* @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.
|
* 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.
|
* 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 = "");
|
void download(const std::string& quality, const std::string& series_directory, std::string filename = "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param episode_url - Link to the episode
|
* @param episode_url - Link to the episode
|
||||||
* @param cookies - The current cookies from the browser
|
* @param cookies - The current cookies from the browser
|
||||||
* @param verbose - Whether or not be verbose
|
* @param verbose - Whether or not be verbose
|
||||||
*
|
*
|
||||||
* Create an episode object from the link using to cookies to get all the necessary information.
|
* Create an episode object from the link using to cookies to get all the necessary information.
|
||||||
* This constructor initializes all the object data.
|
* This constructor initializes all the object data.
|
||||||
*/
|
*/
|
||||||
episode(const std::string& episode_url, std::vector<cookie> cookies, bool verbose = false) {
|
episode(const std::string& episode_url, std::vector<cookie> cookies, bool verbose = false) {
|
||||||
|
|
||||||
this->episode_url = episode_url;
|
this->episode_url = episode_url;
|
||||||
this->verbose = verbose;
|
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) {
|
if (verbose) {
|
||||||
std::cout << "Got name: " << name << '\n';
|
std::cout << "Got name: " << name << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == "ERROR") {
|
if (name == "ERROR") {
|
||||||
std::cerr << "EPISODE ERROR: Invalid Episode URL\n";
|
std::cerr << "EPISODE ERROR: Invalid Episode URL\n";
|
||||||
exit(6);
|
exit(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->episode_number = get_episode_number(episode_data);
|
this->episode_number = get_episode_number(episode_data);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Got episode: " << this->episode_number << '\n';
|
std::cout << "Got episode: " << this->episode_number << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
this->series = get_series_name(episode_data);
|
this->series = get_series_name(episode_data);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Got series: " << this->series << '\n';
|
std::cout << "Got series: " << this->series << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
this->series_directory = format_filename(this->series);
|
this->series_directory = format_filename(this->series);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Got series directory: " << this->series_directory << '\n';
|
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, "&", "&");
|
replace_all(this->embedded_url, "&", "&");
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Got embedded url: " << this->embedded_url << '\n';
|
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) {
|
if (this->embedded_page_data.find("you are not authorized") != std::string::npos) {
|
||||||
std::cerr << "ERROR: Could not access video. Try refreshing cookies.\n";
|
std::cerr << "ERROR: Could not access video. Try refreshing cookies.\n";
|
||||||
exit(6);
|
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) {
|
if (verbose) {
|
||||||
std::cout << "Got config url: " << this->config_url << '\n';
|
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.
|
* Creates an episode object with no data. This should only be used for invalid states.
|
||||||
*/
|
*/
|
||||||
episode() = default;
|
episode() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // dropout_dl
|
} // dropout_dl
|
||||||
|
|
520
src/main.cpp
520
src/main.cpp
|
@ -8,126 +8,126 @@
|
||||||
|
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for handling and storing the program arguments.
|
* A class for handling and storing the program arguments.
|
||||||
*/
|
*/
|
||||||
class options {
|
class options {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
std::string url;
|
std::string url;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool cookies_forced = false;
|
bool cookies_forced = false;
|
||||||
bool series = false;
|
bool series = false;
|
||||||
bool season = false;
|
bool season = false;
|
||||||
std::string quality;
|
std::string quality;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string output_directory;
|
std::string output_directory;
|
||||||
std::string episode;
|
std::string episode;
|
||||||
std::vector<cookie> cookies;
|
std::vector<cookie> cookies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param argc - The number of provided program arguments
|
* @param argc - The number of provided program arguments
|
||||||
* @param argv - The provided program arguments
|
* @param argv - The provided program arguments
|
||||||
* @return A vector of arguments in the c++ string format
|
* @return A vector of arguments in the c++ string format
|
||||||
*
|
*
|
||||||
* Converts the C style program arguments to a vector of strings
|
* Converts the C style program arguments to a vector of strings
|
||||||
*/
|
*/
|
||||||
static std::vector<std::string> convert_program_args(int argc, char** argv) {
|
static std::vector<std::string> convert_program_args(int argc, char** argv) {
|
||||||
std::vector<std::string> out;
|
std::vector<std::string> out;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
out.emplace_back(argv[i]);
|
out.emplace_back(argv[i]);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param argc - The number of provided program arguments
|
* @param argc - The number of provided program arguments
|
||||||
* @param argv - The provided program arguments
|
* @param argv - The provided program arguments
|
||||||
*
|
*
|
||||||
* Parses and handles the program arguments and creates an options object.
|
* Parses and handles the program arguments and creates an options object.
|
||||||
*/
|
*/
|
||||||
options(int argc, char** argv) {
|
options(int argc, char** argv) {
|
||||||
std::vector<std::string> args = convert_program_args(argc, argv);
|
std::vector<std::string> args = convert_program_args(argc, argv);
|
||||||
|
|
||||||
for (int i = 0; i < args.size(); i++) {
|
for (int i = 0; i < args.size(); i++) {
|
||||||
std::string arg = args[i];
|
std::string arg = args[i];
|
||||||
|
|
||||||
if (arg.substr(0, 2) != "--") {
|
if (arg.substr(0, 2) != "--") {
|
||||||
url = arg;
|
url = arg;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
arg = arg.substr(2);
|
arg = arg.substr(2);
|
||||||
if (arg == "verbose") {
|
if (arg == "verbose") {
|
||||||
verbose = true;
|
verbose = true;
|
||||||
} else if (arg == "quality") {
|
} else if (arg == "quality") {
|
||||||
if (i + 1 >= args.size()) {
|
if (i + 1 >= args.size()) {
|
||||||
std::cerr << "ARGUMENT PARSE ERROR: --quality used with too few following arguments\n";
|
std::cerr << "ARGUMENT PARSE ERROR: --quality used with too few following arguments\n";
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
quality = args[++i];
|
quality = args[++i];
|
||||||
}
|
}
|
||||||
else if (arg == "force-cookies") {
|
else if (arg == "force-cookies") {
|
||||||
if (i + 2 >= args.size()) {
|
if (i + 2 >= args.size()) {
|
||||||
std::cerr << "ARGUMENT PARSE ERROR: --force-cookies used with too few following arguments\n";
|
std::cerr << "ARGUMENT PARSE ERROR: --force-cookies used with too few following arguments\n";
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
cookies.emplace_back(args[++i]);
|
cookies.emplace_back(args[++i]);
|
||||||
cookies.emplace_back(args[++i]);
|
cookies.emplace_back(args[++i]);
|
||||||
cookies_forced = true;
|
cookies_forced = true;
|
||||||
}
|
}
|
||||||
else if (arg == "output") {
|
else if (arg == "output") {
|
||||||
if (i + 1 >= args.size()) {
|
if (i + 1 >= args.size()) {
|
||||||
std::cerr << "ARGUMENT PARSE ERROR: --output used with too few following arguments\n";
|
std::cerr << "ARGUMENT PARSE ERROR: --output used with too few following arguments\n";
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
filename = args[++i];
|
filename = args[++i];
|
||||||
}
|
}
|
||||||
else if (arg == "output-directory") {
|
else if (arg == "output-directory") {
|
||||||
if (i + 1 >= args.size()) {
|
if (i + 1 >= args.size()) {
|
||||||
std::cerr << "ARGUMENT PARSE ERROR: --output-directory used with too few following arguments\n";
|
std::cerr << "ARGUMENT PARSE ERROR: --output-directory used with too few following arguments\n";
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
output_directory = args[++i];
|
output_directory = args[++i];
|
||||||
}
|
}
|
||||||
else if (arg == "series") {
|
else if (arg == "series") {
|
||||||
series = true;
|
series = true;
|
||||||
}
|
}
|
||||||
else if (arg == "season") {
|
else if (arg == "season") {
|
||||||
season = true;
|
season = true;
|
||||||
}
|
}
|
||||||
else if (arg == "help") {
|
else if (arg == "help") {
|
||||||
std::cout << "Usage: dropout-dl [OPTIONS] <url> [OPTIONS]\n"
|
std::cout << "Usage: dropout-dl [OPTIONS] <url> [OPTIONS]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
"\t--help Display this message\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--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 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 Set the output filename. Only works for single episode downloads\n"
|
||||||
"\t--output-directory Set the directory where files are output\n"
|
"\t--output-directory Set the directory where files are output\n"
|
||||||
"\t--verbose Display debug information while running\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--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--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"
|
"\t--season Interpret the url as a link to a season and download all episodes from all seasons\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output_directory.empty()) {
|
if (output_directory.empty()) {
|
||||||
output_directory = ".";
|
output_directory = ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (season && series) {
|
if (season && series) {
|
||||||
std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n";
|
std::cerr << "ARGUMENT PARSE ERROR: Season and Series arguments used\n";
|
||||||
}
|
}
|
||||||
if (quality.empty()) {
|
if (quality.empty()) {
|
||||||
quality = "1080p";
|
quality = "1080p";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DROPOUT_DL_SQLITE
|
#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::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::fstream firefox_profile_file(firefox_profile_path);
|
||||||
std::string firefox_profile;
|
std::string firefox_profile;
|
||||||
|
|
||||||
dropout_dl::cookie auth("__cf_bm");
|
dropout_dl::cookie auth("__cf_bm");
|
||||||
dropout_dl::cookie session("_session");
|
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) {
|
if (verbose) {
|
||||||
std::cout << "Getting firefox cookies from firefox sqlite db\n";
|
std::cout << "Getting firefox cookies from firefox sqlite db\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::filesystem::is_directory("tmp"))
|
if (!std::filesystem::is_directory("tmp"))
|
||||||
std::filesystem::create_directories("tmp");
|
std::filesystem::create_directories("tmp");
|
||||||
std::filesystem::remove("tmp/firefox_cookies.sqlite");
|
std::filesystem::remove("tmp/firefox_cookies.sqlite");
|
||||||
std::filesystem::copy_file(firefox_profile + "/cookies.sqlite", "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);
|
int rc = sqlite3_open("tmp/firefox_cookies.sqlite", &db);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
|
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Firefox database opened successfully\n";
|
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")) {
|
if (std::filesystem::is_empty("tmp")) {
|
||||||
std::filesystem::remove("tmp/");
|
std::filesystem::remove("tmp/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cerr << "FIREFOX COOKIE ERROR: Attempted to get cookies from firefox without profile." << std::endl;
|
std::cerr << "FIREFOX COOKIE ERROR: Attempted to get cookies from firefox without profile." << std::endl;
|
||||||
exit(4);
|
exit(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
|
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(auth);
|
||||||
out.push_back(session);
|
out.push_back(session);
|
||||||
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DROPOUT_DL_GCRYPT
|
#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::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::fstream chrome_profile_file(chrome_profile_path);
|
||||||
std::string chrome_profile;
|
std::string chrome_profile;
|
||||||
|
|
||||||
dropout_dl::cookie auth("__cf_bm");
|
dropout_dl::cookie auth("__cf_bm");
|
||||||
dropout_dl::cookie session("_session");
|
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) {
|
if (verbose) {
|
||||||
std::cout << "Getting chrome cookies from chrome sqlite db\n";
|
std::cout << "Getting chrome cookies from chrome sqlite db\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc = sqlite3_open((chrome_profile + "/Cookies").c_str(), &db);
|
int rc = sqlite3_open((chrome_profile + "/Cookies").c_str(), &db);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
|
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n';
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "Chrome database opened successfully\n";
|
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 {
|
else {
|
||||||
std::cerr << "CHROME COOKIE ERROR: Attempted to get cookies from chrome without profile." << std::endl;
|
std::cerr << "CHROME COOKIE ERROR: Attempted to get cookies from chrome without profile." << std::endl;
|
||||||
exit(4);
|
exit(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.chrome_decrypt();
|
auth.chrome_decrypt();
|
||||||
|
|
||||||
session.chrome_decrypt();
|
session.chrome_decrypt();
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << auth.name << ": " << auth.len << ": " << auth.value << '\n';
|
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(auth);
|
||||||
out.push_back(session);
|
out.push_back(session);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#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::vector<dropout_dl::cookie> get_cookies(bool verbose = false) {
|
||||||
|
|
||||||
std::filesystem::path firefox_profile("firefox_profile");
|
std::filesystem::path firefox_profile("_firefox_profile");
|
||||||
std::filesystem::path chrome_profile("chrome_profile");
|
std::filesystem::path chrome_profile("chrome_profile");
|
||||||
|
|
||||||
if (std::filesystem::exists(firefox_profile)) {
|
if (std::filesystem::exists(firefox_profile)) {
|
||||||
|
|
||||||
#ifdef DROPOUT_DL_SQLITE
|
#ifdef DROPOUT_DL_SQLITE
|
||||||
return get_cookies_from_firefox(firefox_profile, verbose);
|
return get_cookies_from_firefox(firefox_profile, verbose);
|
||||||
#else
|
#else
|
||||||
std::cout << "WARNING: Firefox profile file exists but sqlite is not installed" << std::endl;
|
std::cout << "WARNING: Firefox profile file exists but sqlite is not installed" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (std::filesystem::exists(chrome_profile)) {
|
if (std::filesystem::exists(chrome_profile)) {
|
||||||
#if defined(DROPOUT_DL_GCRYPT) & defined(DROPOUT_DL_SQLITE)
|
#if defined(DROPOUT_DL_GCRYPT) & defined(DROPOUT_DL_SQLITE)
|
||||||
return get_cookies_from_chrome(chrome_profile, verbose);
|
return get_cookies_from_chrome(chrome_profile, verbose);
|
||||||
#else
|
#else
|
||||||
std::cout << "WARNING: Chrome profile file exists but libgcrypt or sqlite is not installed" << std::endl;
|
std::cout << "WARNING: Chrome profile file exists but libgcrypt or sqlite is not installed" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::cerr << "ERROR: dropout.tv cookies could not be found" << std::endl;
|
std::cerr << "ERROR: dropout.tv cookies could not be found" << std::endl;
|
||||||
exit(7);
|
exit(7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
dropout_dl::options options(argc, argv);
|
dropout_dl::options options(argc, argv);
|
||||||
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
std::cout << "quality: " << options.quality << std::endl;
|
std::cout << "quality: " << options.quality << std::endl;
|
||||||
std::cout << "verbose: " << options.verbose << std::endl;
|
std::cout << "verbose: " << options.verbose << std::endl;
|
||||||
std::cout << "url: \"" << options.url << '"' << std::endl;
|
std::cout << "url: \"" << options.url << '"' << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string firefox_profile;
|
std::string firefox_profile;
|
||||||
std::string chrome_profile;
|
std::string chrome_profile;
|
||||||
|
|
||||||
std::string video_data;
|
std::string video_data;
|
||||||
|
|
||||||
if (options.url.empty()) {
|
if (options.url.empty()) {
|
||||||
std::cout << "Enter episode url: ";
|
std::cout << "Enter episode url: ";
|
||||||
std::cin >> options.url;
|
std::cin >> options.url;
|
||||||
}
|
}
|
||||||
else if (options.verbose) {
|
else if (options.verbose) {
|
||||||
std::cout << "Got episode url: " << options.url << " from program arguments\n";
|
std::cout << "Got episode url: " << options.url << " from program arguments\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.cookies_forced) {
|
if (!options.cookies_forced) {
|
||||||
options.cookies = get_cookies(options.verbose);
|
options.cookies = get_cookies(options.verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.series) {
|
if (options.series) {
|
||||||
dropout_dl::series series(options.url, options.cookies);
|
dropout_dl::series series(options.url, options.cookies);
|
||||||
|
|
||||||
series.download(options.quality, options.output_directory);
|
series.download(options.quality, options.output_directory);
|
||||||
}
|
}
|
||||||
else if (options.season) {
|
else if (options.season) {
|
||||||
dropout_dl::season season = dropout_dl::series::get_season(options.url, options.cookies);
|
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);
|
season.download(options.quality, options.output_directory + "/" + series_directory);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dropout_dl::episode ep(options.url, options.cookies, options.verbose);
|
dropout_dl::episode ep(options.url, options.cookies, options.verbose);
|
||||||
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
std::cout << "filename: " << options.filename << '\n';
|
std::cout << "filename: " << options.filename << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::filesystem::is_directory(options.output_directory)) {
|
if (!std::filesystem::is_directory(options.output_directory)) {
|
||||||
std::filesystem::create_directories(options.output_directory);
|
std::filesystem::create_directories(options.output_directory);
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
std::cout << "Creating series directory" << '\n';
|
std::cout << "Creating series directory" << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.filename.empty()) {
|
if (options.filename.empty()) {
|
||||||
options.filename = dropout_dl::format_filename(ep.name + ".mp4");
|
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;
|
||||||
}
|
}
|
||||||
|
|
104
src/season.cpp
104
src/season.cpp
|
@ -5,64 +5,64 @@
|
||||||
#include "season.h"
|
#include "season.h"
|
||||||
|
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
episode get_episode(const std::string& html_data, int& start_point, const std::vector<cookie>& cookies) {
|
episode get_episode(const std::string& html_data, int& start_point, const std::vector<cookie>& cookies) {
|
||||||
int link_start = 0;
|
int link_start = 0;
|
||||||
for (int i = start_point; i > 0; i--) {
|
for (int i = start_point; i > 0; i--) {
|
||||||
if (substr_is(html_data, i, "<a")) {
|
if (substr_is(html_data, i, "<a")) {
|
||||||
link_start = i;
|
link_start = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (substr_is(html_data, i, "<")) {
|
else if (substr_is(html_data, i, "<")) {
|
||||||
// Invalid episode place. Return empty value.
|
// Invalid episode place. Return empty value.
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = link_start; i < html_data.size(); i++) {
|
for (int i = link_start; i < html_data.size(); i++) {
|
||||||
if (substr_is(html_data, i, "href=\"")) {
|
if (substr_is(html_data, i, "href=\"")) {
|
||||||
i += 6;
|
i += 6;
|
||||||
for (int j = 0; j + i < html_data.size(); j++) {
|
for (int j = 0; j + i < html_data.size(); j++) {
|
||||||
if (html_data[i + j] == '"') {
|
if (html_data[i + j] == '"') {
|
||||||
start_point += 15;
|
start_point += 15;
|
||||||
return {html_data.substr(i, j), cookies};
|
return {html_data.substr(i, j), cookies};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cerr << "SEASON PARSE ERROR: Error finding episode" << std::endl;
|
std::cerr << "SEASON PARSE ERROR: Error finding episode" << std::endl;
|
||||||
exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<episode> season::get_episodes(const std::string &html_data, const std::vector<cookie>& cookies) {
|
std::vector<episode> season::get_episodes(const std::string &html_data, const std::vector<cookie>& cookies) {
|
||||||
std::vector<episode> out;
|
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++) {
|
for (int i = 0; i < html_data.size(); i++) {
|
||||||
if (substr_is(html_data, i, site_video)) {
|
if (substr_is(html_data, i, site_video)) {
|
||||||
episode e = get_episode(html_data, i, cookies);
|
episode e = get_episode(html_data, i, cookies);
|
||||||
if (e.episode_url.empty()) {
|
if (e.episode_url.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::cout << '\t' << e.episode_number << ": " << e.name << '\n';
|
std::cout << '\t' << e.episode_number << ": " << e.name << '\n';
|
||||||
out.push_back(e);
|
out.push_back(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void season::download(const std::string &quality, const std::string &series_directory) {
|
void season::download(const std::string &quality, const std::string &series_directory) {
|
||||||
if (!std::filesystem::is_directory(series_directory)) {
|
if (!std::filesystem::is_directory(series_directory)) {
|
||||||
std::filesystem::create_directories(series_directory);
|
std::filesystem::create_directories(series_directory);
|
||||||
std::cout << "Creating series directory" << '\n';
|
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) {
|
for (auto& ep : episodes) {
|
||||||
ep.download(quality, dir);
|
ep.download(quality, dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // dropout_dl
|
} // dropout_dl
|
||||||
|
|
94
src/season.h
94
src/season.h
|
@ -10,56 +10,56 @@
|
||||||
|
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
|
|
||||||
class season {
|
class season {
|
||||||
public:
|
public:
|
||||||
/// The name of the season
|
/// The name of the season
|
||||||
std::string name;
|
std::string name;
|
||||||
/// The name of the series
|
/// The name of the series
|
||||||
std::string series_name;
|
std::string series_name;
|
||||||
/// The link to the season page
|
/// The link to the season page
|
||||||
std::string url;
|
std::string url;
|
||||||
/// The season page data
|
/// The season page data
|
||||||
std::string page_data;
|
std::string page_data;
|
||||||
/// The list of all the episodes in the season
|
/// The list of all the episodes in the season
|
||||||
std::vector<episode> episodes;
|
std::vector<episode> episodes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - The season page data
|
* @param html_data - The season page data
|
||||||
* @param cookies - The browser cookies
|
* @param cookies - The browser cookies
|
||||||
* @return A vector of all episodes in the season
|
* @return A vector of all episodes in the season
|
||||||
*
|
*
|
||||||
* Gets all the episodes of the season and returns in a vector
|
* 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);
|
static std::vector<episode> get_episodes(const std::string& html_data, const std::vector<cookie>& cookies);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param quality - The quality of the videos
|
* @param quality - The quality of the videos
|
||||||
* @param series_directory - The directory of the series
|
* @param series_directory - The directory of the series
|
||||||
*
|
*
|
||||||
* Downloads all the episodes of the season. Appends the season to the series directory
|
* 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);
|
void download(const std::string& quality, const std::string& series_directory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url - The url to the webpage of the season
|
* @param url - The url to the webpage of the season
|
||||||
* @param name - The name of the season
|
* @param name - The name of the season
|
||||||
* @param cookies - The browser cookies
|
* @param cookies - The browser cookies
|
||||||
* @param series_name - The name of the series
|
* @param series_name - The name of the series
|
||||||
*
|
*
|
||||||
* Creates a season object and populates the needed information.
|
* 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 = "") {
|
season(const std::string& url, const std::string& name, const std::vector<cookie>& cookies, const std::string& series_name = "") {
|
||||||
this->url = url;
|
this->url = url;
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->series_name = series_name;
|
this->series_name = series_name;
|
||||||
std::cout << series_name << ": " << name << ": " << "\n";
|
std::cout << series_name << ": " << name << ": " << "\n";
|
||||||
this->page_data = get_generic_page(url);
|
this->page_data = get_generic_page(url);
|
||||||
this->episodes = get_episodes(page_data, cookies);
|
this->episodes = get_episodes(page_data, cookies);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // dropout_dl
|
} // dropout_dl
|
||||||
|
|
||||||
|
|
370
src/series.cpp
370
src/series.cpp
|
@ -6,216 +6,216 @@
|
||||||
|
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
|
|
||||||
std::string series::get_series_name(const std::string& html_data) {
|
std::string series::get_series_name(const std::string& html_data) {
|
||||||
std::string collection_title("collection-title");
|
std::string collection_title("collection-title");
|
||||||
std::string close_tag(">");
|
std::string close_tag(">");
|
||||||
std::string close_a("</h1>");
|
std::string close_a("</h1>");
|
||||||
|
|
||||||
for (int i = 0; i < html_data.size(); i++) {
|
for (int i = 0; i < html_data.size(); i++) {
|
||||||
if (substr_is(html_data, i, collection_title)) {
|
if (substr_is(html_data, i, collection_title)) {
|
||||||
for (int j = i + collection_title.size(); j < html_data.size(); j++) {
|
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 (html_data[j] == '\n' || html_data[j] == ' ' || html_data[j] == '\t') continue;
|
||||||
if (substr_is(html_data, j, close_tag)) {
|
if (substr_is(html_data, j, close_tag)) {
|
||||||
for (int l = 0; l < html_data.size() - j; l++) {
|
for (int l = 0; l < html_data.size() - j; l++) {
|
||||||
if (substr_is(html_data, j + l, close_a)) {
|
if (substr_is(html_data, j + l, close_a)) {
|
||||||
return format_name_string(html_data.substr(j + 1, l - 1));
|
return format_name_string(html_data.substr(j + 1, l - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "ERROR";
|
return "ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<season> series::get_seasons(const std::string &html_data, const std::vector<cookie>& cookies) {
|
std::vector<season> series::get_seasons(const std::string &html_data, const std::vector<cookie>& cookies) {
|
||||||
std::vector<season> out;
|
std::vector<season> out;
|
||||||
|
|
||||||
std::string search_class("js-switch-season");
|
std::string search_class("js-switch-season");
|
||||||
std::string open_select("<select");
|
std::string open_select("<select");
|
||||||
std::string close_tag(">");
|
std::string close_tag(">");
|
||||||
std::string close_select("</select>");
|
std::string close_select("</select>");
|
||||||
|
|
||||||
std::string open_option("<option");
|
std::string open_option("<option");
|
||||||
std::string close_option("</option>");
|
std::string close_option("</option>");
|
||||||
std::string value("value=");
|
std::string value("value=");
|
||||||
|
|
||||||
bool seasons_dropdown = false;
|
bool seasons_dropdown = false;
|
||||||
std::string season_url;
|
std::string season_url;
|
||||||
std::string season_name;
|
std::string season_name;
|
||||||
for (int i = 0; i < html_data.size(); i++) {
|
for (int i = 0; i < html_data.size(); i++) {
|
||||||
if (substr_is(html_data, i, open_select)) {
|
if (substr_is(html_data, i, open_select)) {
|
||||||
for (int j = i; j < html_data.size(); j++) {
|
for (int j = i; j < html_data.size(); j++) {
|
||||||
if (substr_is(html_data, j, search_class)) {
|
if (substr_is(html_data, j, search_class)) {
|
||||||
i = j;
|
i = j;
|
||||||
seasons_dropdown = true;
|
seasons_dropdown = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (substr_is(html_data, j, close_tag)) {
|
else if (substr_is(html_data, j, close_tag)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (seasons_dropdown) {
|
if (seasons_dropdown) {
|
||||||
if (substr_is(html_data, i, value)) {
|
if (substr_is(html_data, i, value)) {
|
||||||
i += value.size() + 1;
|
i += value.size() + 1;
|
||||||
for (int j = 0; j + i < html_data.size(); j++) {
|
for (int j = 0; j + i < html_data.size(); j++) {
|
||||||
if (html_data[i + j] == '"') {
|
if (html_data[i + j] == '"') {
|
||||||
season_url = html_data.substr(i, j);
|
season_url = html_data.substr(i, j);
|
||||||
i += j;
|
i += j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!season_url.empty() && substr_is(html_data, i, close_tag)) {
|
else if (!season_url.empty() && substr_is(html_data, i, close_tag)) {
|
||||||
i += close_tag.size() + 1;
|
i += close_tag.size() + 1;
|
||||||
for (int j = 0; i + j < html_data.size(); j++) {
|
for (int j = 0; i + j < html_data.size(); j++) {
|
||||||
if (html_data[i + j] == '\n') {
|
if (html_data[i + j] == '\n') {
|
||||||
season_name = html_data.substr(i, j);
|
season_name = html_data.substr(i, j);
|
||||||
|
|
||||||
// Remove leading and trailing whitespace
|
// Remove leading and trailing whitespace
|
||||||
bool leading_whitespace = true;
|
bool leading_whitespace = true;
|
||||||
int name_start;
|
int name_start;
|
||||||
int name_end;
|
int name_end;
|
||||||
for (int k = 0; k < season_name.size(); k++) {
|
for (int k = 0; k < season_name.size(); k++) {
|
||||||
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
||||||
name_start = k;
|
name_start = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int k = season_name.size() - 1; k > 0; k--) {
|
for (int k = season_name.size() - 1; k > 0; k--) {
|
||||||
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
||||||
name_end = k;
|
name_end = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
season_name = season_name.substr(name_start, season_name.size() - name_start - name_end);
|
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_url.clear();
|
||||||
season_name.clear();
|
season_name.clear();
|
||||||
|
|
||||||
i = i + j;
|
i = i + j;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr_is(html_data, i, close_select)) {
|
if (substr_is(html_data, i, close_select)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
season series::get_season(const std::string &url, const std::vector<cookie>& cookies) {
|
season series::get_season(const std::string &url, const std::vector<cookie>& cookies) {
|
||||||
std::string html_data = get_generic_page(url);
|
std::string html_data = get_generic_page(url);
|
||||||
|
|
||||||
std::string search_class("js-switch-season");
|
std::string search_class("js-switch-season");
|
||||||
std::string open_select("<select");
|
std::string open_select("<select");
|
||||||
std::string close_tag(">");
|
std::string close_tag(">");
|
||||||
std::string close_select("</select>");
|
std::string close_select("</select>");
|
||||||
|
|
||||||
std::string open_option("<option");
|
std::string open_option("<option");
|
||||||
std::string close_option("</option>");
|
std::string close_option("</option>");
|
||||||
std::string value("value=");
|
std::string value("value=");
|
||||||
|
|
||||||
bool seasons_dropdown = false;
|
bool seasons_dropdown = false;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
std::string season_url;
|
std::string season_url;
|
||||||
std::string season_name;
|
std::string season_name;
|
||||||
for (int i = 0; i < html_data.size(); i++) {
|
for (int i = 0; i < html_data.size(); i++) {
|
||||||
if (substr_is(html_data, i, open_select)) {
|
if (substr_is(html_data, i, open_select)) {
|
||||||
for (int j = i; j < html_data.size(); j++) {
|
for (int j = i; j < html_data.size(); j++) {
|
||||||
if (substr_is(html_data, j, search_class)) {
|
if (substr_is(html_data, j, search_class)) {
|
||||||
i = j;
|
i = j;
|
||||||
seasons_dropdown = true;
|
seasons_dropdown = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (substr_is(html_data, j, close_tag)) {
|
else if (substr_is(html_data, j, close_tag)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (seasons_dropdown) {
|
if (seasons_dropdown) {
|
||||||
if (substr_is(html_data, i, value)) {
|
if (substr_is(html_data, i, value)) {
|
||||||
i += value.size() + 1;
|
i += value.size() + 1;
|
||||||
for (int j = 0; j + i < html_data.size(); j++) {
|
for (int j = 0; j + i < html_data.size(); j++) {
|
||||||
if (html_data[i + j] == '"') {
|
if (html_data[i + j] == '"') {
|
||||||
season_url = html_data.substr(i, j);
|
season_url = html_data.substr(i, j);
|
||||||
i += j;
|
i += j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!season_url.empty() && substr_is(html_data, i, "selected")) {
|
else if (!season_url.empty() && substr_is(html_data, i, "selected")) {
|
||||||
selected = true;
|
selected = true;
|
||||||
}
|
}
|
||||||
else if (!season_url.empty() && substr_is(html_data, i, close_tag)) {
|
else if (!season_url.empty() && substr_is(html_data, i, close_tag)) {
|
||||||
i += close_tag.size() + 1;
|
i += close_tag.size() + 1;
|
||||||
for (int j = 0; i + j < html_data.size(); j++) {
|
for (int j = 0; i + j < html_data.size(); j++) {
|
||||||
if (html_data[i + j] == '\n') {
|
if (html_data[i + j] == '\n') {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
season_name = html_data.substr(i, j);
|
season_name = html_data.substr(i, j);
|
||||||
|
|
||||||
// Remove leading and trailing whitespace
|
// Remove leading and trailing whitespace
|
||||||
bool leading_whitespace = true;
|
bool leading_whitespace = true;
|
||||||
int name_start;
|
int name_start;
|
||||||
int name_end;
|
int name_end;
|
||||||
for (int k = 0; k < season_name.size(); k++) {
|
for (int k = 0; k < season_name.size(); k++) {
|
||||||
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
||||||
name_start = k;
|
name_start = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int k = season_name.size() - 1; k > 0; k--) {
|
for (int k = season_name.size() - 1; k > 0; k--) {
|
||||||
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
if (season_name[k] != ' ' && season_name[k] != '\t' && season_name[k] != '\n') {
|
||||||
name_end = k;
|
name_end = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
season_name = season_name.substr(name_start,
|
season_name = season_name.substr(name_start,
|
||||||
season_name.size() - name_start - name_end);
|
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_url.clear();
|
||||||
season_name.clear();
|
season_name.clear();
|
||||||
|
|
||||||
i = i + j;
|
i = i + j;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr_is(html_data, i, close_select)) {
|
if (substr_is(html_data, i, close_select)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "SEASON PARSE ERROR: No selected season found\n";
|
std::cerr << "SEASON PARSE ERROR: No selected season found\n";
|
||||||
exit(9);
|
exit(9);
|
||||||
}
|
}
|
||||||
|
|
||||||
void series::download(const std::string &quality, const std::string& base) {
|
void series::download(const std::string &quality, const std::string& base) {
|
||||||
if (!std::filesystem::is_directory(base + "/" + series_directory)) {
|
if (!std::filesystem::is_directory(base + "/" + series_directory)) {
|
||||||
std::filesystem::create_directories(base + "/" + series_directory);
|
std::filesystem::create_directories(base + "/" + series_directory);
|
||||||
std::cout << "Creating series directory" << '\n';
|
std::cout << "Creating series directory" << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& season : seasons) {
|
for (auto& season : seasons) {
|
||||||
season.download(quality, base + "/" + series_directory);
|
season.download(quality, base + "/" + series_directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // dropout_dl
|
} // dropout_dl
|
||||||
|
|
136
src/series.h
136
src/series.h
|
@ -11,80 +11,80 @@
|
||||||
namespace dropout_dl {
|
namespace dropout_dl {
|
||||||
|
|
||||||
|
|
||||||
/// A class for handling all series information and functions.
|
/// A class for handling all series information and functions.
|
||||||
class series {
|
class series {
|
||||||
public:
|
public:
|
||||||
/// The name of the series
|
/// The name of the series
|
||||||
std::string name;
|
std::string name;
|
||||||
/// The link to the series page
|
/// The link to the series page
|
||||||
std::string url;
|
std::string url;
|
||||||
/// The series page data
|
/// The series page data
|
||||||
std::string page_data;
|
std::string page_data;
|
||||||
/// The directory which will contain the seasons of the series
|
/// The directory which will contain the seasons of the series
|
||||||
std::string series_directory;
|
std::string series_directory;
|
||||||
/// A vector containing all the season that this series include
|
/// A vector containing all the season that this series include
|
||||||
std::vector<season> seasons;
|
std::vector<season> seasons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - The series page data
|
* @param html_data - The series page data
|
||||||
* @return The name of the series
|
* @return The name of the series
|
||||||
*
|
*
|
||||||
* Scrapes the series page for 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);
|
static std::string get_series_name(const std::string& html_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param html_data - The series page data
|
* @param html_data - The series page data
|
||||||
* @param cookies - The cookies from a browser
|
* @param cookies - The cookies from a browser
|
||||||
* @return A list of all seasons in the series
|
* @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.
|
* 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.
|
* These season object contain all the episodes of the season as episode objects.
|
||||||
* The cookies this function takes are passed to the 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);
|
static std::vector<season> get_seasons(const std::string& html_data, const std::vector<cookie>& cookies);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url - The url to the season
|
* @param url - The url to the season
|
||||||
* @param cookies - The browser cookies
|
* @param cookies - The browser cookies
|
||||||
* @return A season object
|
* @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
|
* 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);
|
static season get_season(const std::string& url, const std::vector<cookie>& cookies);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param quality - The quality of the video
|
* @param quality - The quality of the video
|
||||||
* @param base - The base directory to download to
|
* @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>
|
* 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 = ".");
|
void download(const std::string& quality, const std::string& base = ".");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url - The link to the series page
|
* @param url - The link to the series page
|
||||||
* @param cookies - The browser cookies
|
* @param cookies - The browser cookies
|
||||||
*
|
*
|
||||||
* Creates a series object and populates the needed variables
|
* Creates a series object and populates the needed variables
|
||||||
*/
|
*/
|
||||||
explicit series(const std::string& url, const std::vector<dropout_dl::cookie>& cookies) {
|
explicit series(const std::string& url, const std::vector<dropout_dl::cookie>& cookies) {
|
||||||
this->url = url;
|
this->url = url;
|
||||||
this->page_data = get_generic_page(url);
|
this->page_data = get_generic_page(url);
|
||||||
this->name = get_series_name(page_data);
|
this->name = get_series_name(page_data);
|
||||||
if (name == "ERROR") {
|
if (name == "ERROR") {
|
||||||
std::cerr << "SERIES PARSE ERROR: Could not parse series name\n";
|
std::cerr << "SERIES PARSE ERROR: Could not parse series name\n";
|
||||||
exit(10);
|
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
|
} // dropout_dl
|
||||||
|
|
Loading…
Reference in New Issue