2022-09-11 00:19:41 +00:00
# include <iostream>
# include <fstream>
# include <filesystem>
2022-09-29 00:56:10 +00:00
# include "episode.h"
2022-09-29 00:02:10 +00:00
# ifdef DROPOUT_DL_SQLITE
2022-09-11 17:21:24 +00:00
# include <sqlite3.h>
2022-09-29 00:02:10 +00:00
# ifdef DROPOUT_DL_GCRYPT
# include <gcrypt.h>
# endif
# endif
2022-09-11 00:19:41 +00:00
2022-09-29 00:02:10 +00:00
static int sqlite_write_callback ( void * data , int argc , char * * argv , char * * azColName )
2022-09-11 17:21:24 +00:00
{
if ( argc < 1 ) {
std : : cerr < < " ERROR: sqlite could not find dropout.tv cookie " < < std : : endl ;
return - 1 ;
}
else {
2022-09-29 00:02:10 +00:00
* ( std : : string * ) data = argv [ 0 ] ;
2022-09-11 17:21:24 +00:00
return 0 ;
}
}
2022-09-29 00:02:10 +00:00
# ifdef DROPOUT_DL_SQLITE
2022-09-29 01:02:56 +00:00
std : : vector < std : : string > get_cookies_from_firefox ( const std : : filesystem : : path & firefox_profile_path , bool verbose = false ) {
2022-09-11 17:21:24 +00:00
2022-09-29 01:02:56 +00:00
std : : fstream firefox_profile_file ( firefox_profile_path ) ;
2022-09-29 00:02:10 +00:00
std : : string firefox_profile ;
2022-09-11 00:19:41 +00:00
2022-09-29 00:02:10 +00:00
std : : string auth_cookie ;
std : : string session_cookie ;
2022-09-11 00:19:41 +00:00
2022-09-29 00:02:10 +00:00
std : : vector < std : : string > out ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
firefox_profile_file > > firefox_profile ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
if ( std : : filesystem : : is_directory ( firefox_profile ) ) {
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
sqlite3 * db ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
if ( verbose ) {
std : : cout < < " Getting firefox cookies from firefox sqlite db \n " ;
}
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
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 " ;
}
}
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
char * err_code = nullptr ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
std : : string sql ( " SELECT value FROM moz_cookies WHERE host LIKE '%dropout.tv%' AND name='__cf_bm'; " ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
rc = sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & auth_cookie , & err_code ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
out . emplace_back ( auth_cookie ) ;
2022-09-14 01:01:55 +00:00
2022-09-29 00:02:10 +00:00
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " SQL error: %s \n " , err_code ) ;
sqlite3_free ( err_code ) ;
sqlite3_close ( db ) ;
exit ( 2 ) ;
} else if ( verbose ) {
std : : cout < < " Got __cf_bm cookie from firefox sqlite db \n " ;
}
sql = " SELECT value FROM moz_cookies WHERE host LIKE '%dropout.tv%' AND name='_session'; " ;
rc = sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & session_cookie , & err_code ) ;
out . emplace_back ( session_cookie ) ;
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " SQL error: %s \n " , err_code ) ;
sqlite3_free ( err_code ) ;
sqlite3_close ( db ) ;
exit ( 3 ) ;
} else if ( verbose ) {
std : : cout < < " Got _session cookie from firefox sqlite db \n " ;
}
sqlite3_close ( db ) ;
}
return out ;
}
# ifdef DROPOUT_DL_GCRYPT
2022-09-29 01:02:56 +00:00
std : : vector < std : : string > get_cookies_from_chrome ( const std : : filesystem : : path & chrome_profile_path , bool verbose = false ) {
2022-09-29 00:02:10 +00:00
2022-09-29 01:02:56 +00:00
std : : fstream chrome_profile_file ( chrome_profile_path ) ;
2022-09-29 00:02:10 +00:00
std : : string chrome_profile ;
std : : string auth_cookie ;
int auth_cookie_length ;
std : : string session_cookie ;
int session_cookie_length ;
std : : vector < std : : string > out ;
getline ( chrome_profile_file , chrome_profile ) ;
if ( std : : filesystem : : is_directory ( chrome_profile ) ) {
sqlite3 * db ;
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 {
2022-09-14 01:01:55 +00:00
if ( verbose ) {
2022-09-29 00:02:10 +00:00
std : : cout < < " Chrome database opened successfully \n " ;
2022-09-14 01:01:55 +00:00
}
2022-09-29 00:02:10 +00:00
}
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
char * err_code = nullptr ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
std : : string len ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
std : : string sql = " SELECT length(encrypted_value) FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='__cf_bm'; " ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & len , & err_code ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
auth_cookie_length = std : : stoi ( len ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
sql = " SELECT encrypted_value FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='__cf_bm'; " ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
rc = sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & auth_cookie , & err_code ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " SQL error: %s \n " , err_code ) ;
sqlite3_free ( err_code ) ;
2022-09-11 17:21:24 +00:00
sqlite3_close ( db ) ;
2022-09-29 00:02:10 +00:00
exit ( 2 ) ;
} else if ( verbose ) {
std : : cout < < " Got __cf_bm cookie from chrome sqlite db \n " < < auth_cookie < < ' \n ' ;
}
2022-09-11 17:21:24 +00:00
2022-09-29 00:02:10 +00:00
sql = " SELECT length(encrypted_value) FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='_session'; " ;
sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & len , & err_code ) ;
session_cookie_length = std : : stoi ( len ) ;
sql = " SELECT encrypted_value FROM cookies WHERE host_key LIKE '%dropout.tv%' AND name='_session'; " ;
rc = sqlite3_exec ( db , sql . c_str ( ) , sqlite_write_callback , & session_cookie , & err_code ) ;
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " SQL error: %s \n " , err_code ) ;
sqlite3_free ( err_code ) ;
sqlite3_close ( db ) ;
exit ( 3 ) ;
} else if ( verbose ) {
std : : cout < < " Got _session cookie from chrome sqlite db \n " ;
2022-09-11 17:21:24 +00:00
}
2022-09-29 00:02:10 +00:00
sqlite3_close ( db ) ;
// system(("echo \"SELECT value FROM moz_cookies WHERE originAttributes LIKE '%dropout.tv%';\" | sqlite3 " + firefox_profile + "/cookies.sqlite > cookie").c_str());
2022-09-11 17:21:24 +00:00
}
2022-09-29 00:02:10 +00:00
// For mac os this is your keychain password
// For linux leave as "peanuts"
std : : string password = " peanuts " ;
std : : string salt = " saltysalt " ;
int length = 16 ;
int iterations = 1 ;
uint8_t key [ 32 ] ;
char output [ 2048 ] ;
char iv [ 16 ] ;
for ( char & c : iv ) {
c = ' ' ;
2022-09-11 17:21:24 +00:00
}
2022-09-29 00:02:10 +00:00
for ( char & c : output ) {
c = 0 ;
2022-09-11 17:21:24 +00:00
}
2022-09-29 00:02:10 +00:00
for ( int i = 0 ; i < auth_cookie_length ; i + + ) {
std : : cout < < std : : hex < < ( 0xFF & ( int ) auth_cookie [ i ] ) < < ' ' ;
}
std : : cout < < ' \n ' ;
gcry_kdf_derive ( password . c_str ( ) , password . size ( ) , GCRY_KDF_PBKDF2 , GCRY_KDF_ARGON2ID , salt . c_str ( ) , salt . size ( ) , iterations , length , key ) ;
gcry_cipher_hd_t handle ;
gcry_cipher_open ( & handle , GCRY_CIPHER_AES , GCRY_CIPHER_MODE_CBC , 0 ) ;
gcry_cipher_setkey ( handle , ( const void * ) & key , length ) ;
gcry_cipher_setiv ( handle , ( const void * ) & iv , 16 ) ;
unsigned long err = gcry_cipher_decrypt ( handle , ( unsigned char * ) output , 2048 , auth_cookie . c_str ( ) + 3 , auth_cookie_length - 3 ) ;
if ( err ) {
std : : cout < < gcry_strerror ( err ) < < std : : endl ;
exit ( 2 ) ;
}
for ( char & c : output ) {
if ( c = = ' \017 ' ) {
c = 0 ;
2022-09-11 17:21:24 +00:00
}
}
2022-09-29 00:02:10 +00:00
out . emplace_back ( output ) ;
gcry_cipher_setiv ( handle , ( const void * ) & iv , 16 ) ;
gcry_cipher_decrypt ( handle , ( unsigned char * ) output , 2048 , session_cookie . c_str ( ) + 3 , session_cookie_length - 3 ) ;
out . emplace_back ( output ) ;
return out ;
}
# endif
# endif
2022-09-29 01:02:56 +00:00
std : : vector < std : : string > get_cookies_from_files ( const std : : filesystem : : path & auth_cookie_path , const std : : filesystem : : path & session_cookie_path , bool verbose = false ) {
2022-09-29 00:02:10 +00:00
std : : fstream auth_cookie_file ( " auth_cookie " ) ;
std : : fstream session_cookie_file ( " session_cookie " ) ;
std : : string auth_cookie ;
std : : string session_cookie ;
std : : vector < std : : string > out ;
auth_cookie_file > > auth_cookie ;
if ( verbose ) {
std : : cout < < " Got __cf_bm cookie from auth_cookie file db \n " ;
}
out . emplace_back ( auth_cookie ) ;
session_cookie_file > > session_cookie ;
if ( verbose ) {
std : : cout < < " Got _session cookie from auth_cookie file db \n " ;
}
out . emplace_back ( session_cookie ) ;
return out ;
}
std : : vector < std : : string > get_cookies ( bool verbose = false ) {
2022-09-29 01:02:56 +00:00
# ifdef DROPOUT_DL_SQLITE
std : : filesystem : : path firefox_profile ( " firefox_profile " ) ;
# ifdef DROPOUT_DL_GCRYPT
std : : filesystem : : path chrome_profile ( " chrome_profile " ) ;
# endif
# endif
std : : filesystem : : path auth_cookie ( " auth_cookie " ) ;
std : : filesystem : : path session_cookie ( " session_cookie " ) ;
# ifdef DROPOUT_DL_SQLITE
if ( std : : filesystem : : exists ( firefox_profile ) ) {
return get_cookies_from_firefox ( firefox_profile , verbose ) ;
} else
# ifdef DROPOUT_DL_GCRYPT
if ( std : : filesystem : : exists ( chrome_profile ) ) {
return get_cookies_from_chrome ( chrome_profile , verbose ) ;
} else
# endif
# endif
if ( std : : filesystem : : exists ( auth_cookie ) & & std : : filesystem : : exists ( session_cookie ) ) {
return get_cookies_from_files ( auth_cookie , session_cookie , verbose ) ;
2022-09-29 00:02:10 +00:00
}
else {
std : : cerr < < " ERROR: dropout.tv cookies could not be found " < < std : : endl ;
2022-09-29 01:02:56 +00:00
exit ( 7 ) ;
2022-09-29 00:02:10 +00:00
}
}
2022-09-29 03:10:35 +00:00
/*
* < select class = " js - switch - season btn - dropdown - transparent margin - right - small " data-switch-season= " " >
< option value = " https://www.dropout.tv/game-changer/season:1 " selected = " " >
Season 1
< / option >
< option value = " https://www.dropout.tv/game-changer/season:2 " >
Season 2
< / option >
< option value = " https://www.dropout.tv/game-changer/season:3 " >
Season 3
< / option >
< option value = " https://www.dropout.tv/game-changer/season:4 " >
Season 4
< / option >
< option value = " https://www.dropout.tv/game-changer/season:7 " >
Bonus Content
< / option >
< / select >
*/
class options {
public :
std : : string url ;
bool verbose = false ;
bool cookies_forced = false ;
std : : string quality ;
2022-09-29 03:27:48 +00:00
std : : string filename ;
std : : string series_dir ;
2022-09-29 03:10:35 +00:00
std : : vector < std : : string > cookies ;
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 ;
}
options ( int argc , char * * argv ) {
std : : vector < std : : string > args = convert_program_args ( argc , argv ) ;
2022-09-29 03:27:48 +00:00
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
const auto & arg = args [ i ] ;
if ( arg . substr ( 0 , 2 ) ! = " -- " ) {
2022-09-29 03:10:35 +00:00
url = arg ;
}
else {
if ( arg = = " --verbose " ) {
verbose = true ;
} else if ( arg = = " --quality " ) {
2022-09-29 03:27:48 +00:00
if ( i + 1 > = args . size ( ) ) {
std : : cerr < < " ARGUMENT PARSE ERROR: --quality used with too few following arguments \n " ;
exit ( 8 ) ;
}
quality = args [ + + i ] ;
2022-09-29 03:10:35 +00:00
}
else if ( arg = = " --force-cookies " ) {
2022-09-29 03:27:48 +00:00
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 ] ) ;
2022-09-29 03:10:35 +00:00
cookies_forced = true ;
}
2022-09-29 03:27:48 +00:00
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 ) ;
}
series_dir = args [ + + i ] ;
}
2022-09-29 03:10:35 +00:00
else if ( arg = = " --help " ) {
std : : cout < < " Usage: dropout-dl [OPTIONS] <url> [OPTIONS] \n "
" \n "
" Options: \n "
2022-09-29 03:27:48 +00:00
" \t --help Display this message \n "
" \t --quality Set the quality of the downloaded video. Quality can be set to 'all' which \n "
" \t will download all qualities and place them into separate folders \n "
" \t --output Set the output filename \n "
" \t --output-directory Set the directory where files are output \n "
" \t --verbose Display debug information while running \n "
" \t --force-cookies Interpret the next to arguments as authentication cookie and session cookie \n "
2022-09-29 03:10:35 +00:00
< < std : : endl ;
exit ( 0 ) ;
}
}
}
if ( quality . empty ( ) ) {
quality = " 1080p " ;
}
}
} ;
2022-09-29 00:02:10 +00:00
int main ( int argc , char * * argv ) {
2022-09-29 03:10:35 +00:00
options options ( argc , argv ) ;
std : : cout < < " quality: " < < options . quality < < std : : endl ;
std : : cout < < " verbose: " < < options . verbose < < std : : endl ;
std : : cout < < " url: \" " < < options . url < < ' " ' < < std : : endl ;
2022-09-29 00:02:10 +00:00
std : : string firefox_profile ;
std : : string chrome_profile ;
2022-09-11 00:19:41 +00:00
std : : string video_data ;
2022-09-29 03:10:35 +00:00
if ( options . url . empty ( ) ) {
2022-09-11 00:19:41 +00:00
std : : cout < < " Enter episode url: " ;
2022-09-29 03:10:35 +00:00
std : : cin > > options . url ;
}
else if ( options . verbose ) {
std : : cout < < " Got episode url: " < < options . url < < " from program arguments \n " ;
2022-09-11 00:19:41 +00:00
}
2022-09-29 03:10:35 +00:00
if ( ! options . cookies_forced ) {
options . cookies = get_cookies ( options . verbose ) ;
}
2022-09-11 00:19:41 +00:00
2022-09-29 03:10:35 +00:00
dropout_dl : : episode ep ( options . url , options . cookies , options . verbose ) ;
2022-09-11 17:21:24 +00:00
2022-09-29 03:27:48 +00:00
if ( options . filename . empty ( ) ) {
options . filename = " S " + ( ep . season_number . size ( ) < 2 ? " 0 " + ep . season_number : ep . season_number ) + " E " +
( ep . episode_number . size ( ) < 2 ? " 0 " + ep . episode_number : ep . episode_number ) + ep . name +
" .mp4 " ;
std : : replace ( options . filename . begin ( ) , options . filename . end ( ) , ' ' , ' _ ' ) ;
std : : replace ( options . filename . begin ( ) , options . filename . end ( ) , ' , ' , ' _ ' ) ;
}
if ( options . verbose ) {
std : : cout < < " filename: " < < options . filename < < ' \n ' ;
}
if ( options . series_dir . empty ( ) ) {
options . series_dir = ep . series ;
}
2022-09-29 00:56:10 +00:00
if ( ! std : : filesystem : : is_directory ( ep . series ) ) {
std : : filesystem : : create_directories ( ep . series ) ;
2022-09-29 03:10:35 +00:00
if ( options . verbose ) {
2022-09-29 00:56:10 +00:00
std : : cout < < " Creating series directory " < < ' \n ' ;
2022-09-11 00:19:41 +00:00
}
}
2022-09-29 03:10:35 +00:00
if ( options . quality = = " all " ) {
2022-09-29 02:22:20 +00:00
for ( const auto & possible_quality : ep . qualities ) {
2022-09-29 03:27:48 +00:00
if ( ! std : : filesystem : : is_directory ( options . series_dir + " / " + possible_quality ) ) {
std : : filesystem : : create_directories ( options . series_dir + " / " + possible_quality ) ;
2022-09-29 03:10:35 +00:00
if ( options . verbose ) {
2022-09-29 02:22:20 +00:00
std : : cout < < " Creating series directory " < < ' \n ' ;
}
}
2022-09-29 03:27:48 +00:00
std : : fstream out ( options . series_dir + " / " + possible_quality + " / " + options . filename ,
2022-09-29 02:22:20 +00:00
std : : ios_base : : in | std : : ios_base : : out | std : : ios_base : : trunc ) ;
2022-09-11 00:19:41 +00:00
2022-09-29 02:22:20 +00:00
out < < ep . get_video_data ( possible_quality ) < < std : : endl ;
}
}
else {
2022-09-29 03:27:48 +00:00
std : : fstream out ( options . series_dir + " / " + options . filename , std : : ios_base : : in | std : : ios_base : : out | std : : ios_base : : trunc ) ;
2022-09-29 02:22:20 +00:00
2022-09-29 03:10:35 +00:00
out < < ep . get_video_data ( options . quality ) < < std : : endl ;
2022-09-29 02:22:20 +00:00
}
2022-09-11 00:19:41 +00:00
return 0 ;
}