Somewhat working with API (push before major refactor)

This commit is contained in:
zvon 2020-01-15 10:00:25 +01:00
parent f1063bb14e
commit 81cfc9a53b
8 changed files with 23184 additions and 301 deletions

View File

@ -55,25 +55,6 @@ constexpr const char_t *dir_divider = "/";
#endif // UNIX
#ifndef GUI
constexpr std::array< const char_t *, 46 > languages{
TEXT( "en" ), TEXT( "English" ), TEXT( "sv" ), TEXT( "Svenska" ),
TEXT( "no" ), TEXT( "Norsk" ), TEXT( "da" ), TEXT( "Dansk" ),
TEXT( "fi" ), TEXT( "Suomeksi" ), TEXT( "nl" ), TEXT( "Nederlands" ),
TEXT( "de" ), TEXT( "Deutsch" ), TEXT( "it" ), TEXT( "Italiano" ),
TEXT( "es" ), TEXT( "Español" ), TEXT( "fr" ), TEXT( "Français" ),
TEXT( "pl" ), TEXT( "Polski" ), TEXT( "hu" ), TEXT( "Magyar" ),
TEXT( "el" ), TEXT( "Greek" ), TEXT( "tr" ), TEXT( "Turkish" ),
TEXT( "ru" ), TEXT( "Russian" ), TEXT( "he" ), TEXT( "Hebrew" ),
TEXT( "ja" ), TEXT( "Japanese" ), TEXT( "pt" ), TEXT( "Portuguese" ),
TEXT( "zh" ), TEXT( "Chinese" ), TEXT( "cs" ), TEXT( "Czech" ),
TEXT( "sl" ), TEXT( "Slovenian" ), TEXT( "hr" ), TEXT( "Croatian" ),
TEXT( "ko" ), TEXT( "Korea" )
};
#endif // not GUI
#ifdef _WIN32
// functions to convert between string and wstring
@ -90,7 +71,6 @@ std::wstring utf8_to_wstring( const std::string &utf8 ) {
#endif // _WIN32
// encode url so it's valid even with UTF-8 characters
string encodeUrl( const string &url ) {
// stolen from here -
// https://stackoverflow.com/questions/154536/encode-decode-urls-in-c
@ -202,48 +182,6 @@ void iterateFS( std::map< int, std::set< string > > &seasons,
}
}
string getDefUrl( string &show, const string &language, Curl &c ) {
std::replace( show.begin(), show.end(), ' ', '+' );
string base_url = TEXT( "https://www.thetvdb.com" );
#ifdef _WIN32
string source_code = utf8_to_wstring(
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language ) );
#else
string source_code =
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language );
#endif
size_t order{}, pos{};
std::vector< std::pair< string, string > > urls;
// find all possible shows
while ( true ) {
pos = source_code.find( TEXT( "/ser" ), pos );
if ( pos != string::npos ) {
auto end = source_code.find( TEXT( ">" ), pos );
end--;
auto end2 = source_code.find( TEXT( "<" ), end + 2 );
// store shows in urls, first is name, second is url
urls.emplace_back( source_code.substr( end + 2, end2 - end - 2 ),
source_code.substr( pos, end - pos ) );
cout << ++order << ". " << urls.back().first << std::endl;
pos = end2;
} else {
break;
}
}
cout << "Which TV Show is the right one? ";
cin >> pos;
cin.clear();
cin.ignore( 1, '\n' );
show = urls[pos - 1].first;
return base_url + urls[pos - 1].second;
}
// find all files for provided season in `path` and store it in `files`
void findSeason( std::set< string > &files, int season, const string &path ) {
#ifdef _WIN32
@ -308,6 +246,7 @@ void printHelp() {
<< " or 'all'" << std::endl;
cout << " for all seasons in selected directory"
<< std::endl;
cout << " --dvd use dvd ordering instead of aired ordering" << std::endl;
cout << " --name-pattern <string> Pattern to which change the file name."
<< std::endl;
cout << " Possible sequences are:" << std::endl;
@ -389,62 +328,6 @@ void parseSeasonNumbers( std::set< int > &seasons_num,
}
}
// print possible language codes and their corresponding language
void printLangs() {
for ( size_t i = 0; i < languages.size(); i += 2 ) {
cout << languages[i] << " - " << languages[i + 1] << std::endl;
}
}
// make sure language is a valide language code
bool findLanguage( const char_t *language ) {
for ( size_t i = 0; i < languages.size(); i += 2 ) {
#ifdef _WIN32
if ( !wcscmp( language, languages[i] ) )
#else
if ( !strcmp( language, languages[i] ) )
#endif
return true;
}
return false;
}
#else // GUI
// functions that are needed for GUI but not for CLI
// get possible shows for search query in `show`
std::vector< std::pair< string, string > >
getPossibleShows( string show, const string &language, Curl &c ) {
// encode show name so it can be resolved as url
std::replace( show.begin(), show.end(), ' ', '+' );
show = encodeUrl( show );
#ifdef _WIN32
auto source_code = utf8_to_wstring(
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) + show +
TEXT( "&l=" ) + language ) );
#else
auto source_code = c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
show + TEXT( "&l=" ) + language );
#endif
size_t pos{};
std::vector< std::pair< string, string > > urls;
while ( true ) {
pos = source_code.find( TEXT( "/ser" ), pos );
if ( pos != string::npos ) {
auto end = source_code.find( TEXT( ">" ), pos );
auto end2 = source_code.find( TEXT( "<" ), end + 1 );
end--;
urls.emplace_back(
source_code.substr( end + 2, end2 - ( end + 2 ) ),
source_code.substr( pos, end - pos ) );
pos = end + 2;
} else {
break;
}
}
return urls;
}
#endif // ndef GUI
#ifndef _WIN32
@ -616,14 +499,15 @@ void prepareDB( const std::string &_pattern ) {
<< " write permission for " << getDBName() << std::endl;
throw e;
}
db.exec( "CREATE TABLE IF NOT EXISTS SHOWS (ID INTEGER NOT NULL "
"PRIMARY KEY AUTOINCREMENT, URL TEXT NOT NULL, SHOW TEXT NOT NULL,"
db.exec(
"CREATE TABLE IF NOT EXISTS SHOWS (ID INTEGER NOT NULL "
"PRIMARY KEY AUTOINCREMENT, TVID TEXT NOT NULL, SHOW TEXT NOT NULL,"
"PATH TEXT NOT NULL UNIQUE, LANGUAGE TEXT NOT NULL);" );
db.exec( "CREATE TABLE IF NOT EXISTS EPISODES (SHOWID INTEGER NOT NULL,"
"PATH TEXT NOT NULL UNIQUE, FOREIGN KEY(SHOWID) "
"REFERENCES SHOWS(ID));" );
const string *pattern;
if( _pattern.empty() ) {
if ( _pattern.empty() ) {
cout << "Insert name pattern for database:" << std::endl;
auto *p = new string;
std::getline( cin, *p );
@ -631,22 +515,22 @@ void prepareDB( const std::string &_pattern ) {
} else {
pattern = &_pattern;
}
db.exec( TEXT( "INSERT INTO SHOWS ( URL, SHOW, PATH, LANGUAGE ) "
"VALUES ( 'pattern', 'pattern', '" ) + sanitize( *pattern )
+ TEXT( "', 'pattern' );" ) );
if( pattern != &_pattern ) {
db.exec( TEXT( "INSERT INTO SHOWS ( TVID, SHOW, PATH, LANGUAGE ) "
"VALUES ( 'pattern', 'pattern', '" ) +
sanitize( *pattern ) + TEXT( "', 'pattern' );" ) );
if ( pattern != &_pattern ) {
delete pattern;
}
}
#ifndef GUI
void addToDB( string &show, const string &path, const string &language,
bool linux, Curl &c ) {
bool linux ) {
if ( !FSLib::exists( getDBName() ) )
prepareDB();
#else
void addToDB( string &show, const string &path, const string &language,
const string &url, const string &pattern, bool linux, Curl &c ) {
const string &id, const string &pattern, bool linux ) {
if ( !FSLib::exists( getDBName() ) )
prepareDB( pattern );
#endif
@ -659,18 +543,19 @@ void addToDB( string &show, const string &path, const string &language,
throw e;
}
#ifndef GUI
auto url = getDefUrl( show, language, c );
auto id = getShowId( show, language );
show = showNameFromId( id, language );
#endif
string show_id{};
db.exec( TEXT( "INSERT OR IGNORE INTO SHOWS ( URL, SHOW, PATH, LANGUAGE ) "
db.exec( TEXT( "INSERT OR IGNORE INTO SHOWS ( TVID, SHOW, PATH, LANGUAGE ) "
"VALUES ( '" ) +
sanitize( url ) + TEXT( "', '" ) + sanitize( show ) +
sanitize( id ) + TEXT( "', '" ) + sanitize( show ) +
TEXT( "', '" ) + sanitize( absolute ) + TEXT( "', '" ) +
sanitize( language ) + TEXT( "' );" ) );
show_id = std::to_string( db.lastRowID() );
#ifndef GUI
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ),
pattern );
#endif
@ -687,8 +572,8 @@ void addToDB( string &show, const string &path, const string &language,
ProgressBar::print( 0 );
#endif
for ( const auto &x : seasons ) {
singleSeason( absolute, show, x.first, url, language, pattern, linux,
true, c, &x.second, false );
singleSeason( absolute, show, x.first, id, language, pattern, linux,
true, &x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
@ -757,7 +642,7 @@ void cleanUpLine() {
#endif
}
void refreshDB( bool linux, Curl &c ) {
void refreshDB( bool linux ) {
std::vector< std::unordered_map< string, string > > shows;
SQLite::Database db{};
try {
@ -767,12 +652,13 @@ void refreshDB( bool linux, Curl &c ) {
throw e;
}
db.exec( "DELETE FROM EPISODES;" );
db.exec( TEXT( "SELECT ID, URL, SHOW, PATH, LANGUAGE FROM SHOWS WHERE URL "
db.exec(
TEXT( "SELECT ID, TVID, SHOW, PATH, LANGUAGE FROM SHOWS WHERE TVID "
"!= 'pattern';" ),
shows );
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ),
pattern );
cout << "Refreshing database" << std::endl << std::endl << std::endl;
@ -798,8 +684,8 @@ void refreshDB( bool linux, Curl &c ) {
size_t i{};
for ( const auto &x : seasons ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
x.first, show[TEXT( "URL" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
x.first, show[TEXT( "TVID" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true,
&x.second, false );
i++;
#ifndef GUI
@ -824,7 +710,7 @@ void refreshDB( bool linux, Curl &c ) {
}
}
void updateDB( bool linux, Curl &c ) {
void updateDB( bool linux ) {
std::vector< std::unordered_map< string, string > > shows;
SQLite::Database db{};
try {
@ -833,12 +719,12 @@ void updateDB( bool linux, Curl &c ) {
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
db.exec( TEXT( "SELECT ID, URL, SHOW, PATH, LANGUAGE FROM SHOWS WHERE URL "
"!= 'pattern';" ),
db.exec( TEXT( "SELECT ID, TVID, SHOW, PATH, LANGUAGE FROM SHOWS WHERE TVID"
" != 'pattern';" ),
shows );
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ),
pattern );
cout << "Updating database" << std::endl << std::endl << std::endl;
@ -879,8 +765,8 @@ void updateDB( bool linux, Curl &c ) {
size_t i{};
for ( const auto &x : new_eps ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
x.first, show[TEXT( "URL" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
x.first, show[TEXT( "TVID" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true,
&x.second, false );
i++;
#ifndef GUI
@ -914,7 +800,7 @@ void changeDBPattern( const string &pattern ) {
throw e;
}
db.exec( TEXT( "UPDATE SHOWS SET PATH = '" ) + pattern +
TEXT( "' WHERE URL == 'pattern';" ) );
TEXT( "' WHERE TVID == 'pattern';" ) );
}
void cleanDB() {
@ -926,12 +812,12 @@ void cleanDB() {
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
db.exec( TEXT( "SELECT ID, URL, SHOW, PATH, LANGUAGE FROM SHOWS WHERE URL "
"!= 'pattern';" ),
db.exec( TEXT( "SELECT ID, TVID, SHOW, PATH, LANGUAGE FROM SHOWS WHERE TVID"
" != 'pattern';" ),
shows );
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ),
pattern );
for ( auto &show : shows ) {
@ -976,7 +862,6 @@ void removeFromDB( const string &path ) {
db.exec( TEXT( "DELETE FROM SHOWS WHERE ID == " ) + show_id + TEXT( ";" ) );
}
std::vector< std::unordered_map< std::string, std::string > > dbGetShows() {
SQLite::Database db{};
try {
@ -986,12 +871,12 @@ std::vector< std::unordered_map< std::string, std::string > > dbGetShows() {
throw e;
}
std::vector< std::unordered_map< std::string, std::string > > ret;
db.exec( TEXT( "SELECT * FROM SHOWS;"), ret );
db.exec( TEXT( "SELECT * FROM SHOWS;" ), ret );
return ret;
}
void changeDB( size_t index, const string &path, const string &language,
const string &url, Curl &c ) {
const string &id ) {
SQLite::Database db{};
auto absolute = FSLib::canonical( path );
try {
@ -1001,31 +886,21 @@ void changeDB( size_t index, const string &path, const string &language,
throw e;
}
#ifndef WIN32
string show_id = std::to_string(index);
string show_id = std::to_string( index );
#else
string show_id = std::to_wstring(index);
string show_id = std::to_wstring( index );
#endif
#ifdef _WIN32
string source_code = utf8_to_wstring(
c.execute( url );
#else
string source_code =
c.execute( url );
auto real_show = showNameFromId( id, language );
#endif
auto pos = source_code.find( "series_title" );
pos = source_code.find( ' ', pos );
pos = source_code.find_first_not_of( " ", pos );
auto end = source_code.find( '<', pos );
end--;
while( source_code[end] == ' ' )
end--;
end++;
auto real_show = source_code.substr(pos, end - pos);
db.exec( TEXT( "UPDATE SHOWS SET URL = '" ) + sanitize( url ) + TEXT("', SHOW = '") + sanitize(real_show) + TEXT("', PATH = '") + sanitize(absolute) + TEXT("', LANGUAGE = '") +
db.exec( TEXT( "UPDATE SHOWS SET TVID = '" ) + sanitize( id ) + TEXT("', SHOW = '") + sanitize(real_show) + TEXT("', PATH = '") + sanitize(absolute) + TEXT("', LANGUAGE = '") +
sanitize( language ) + TEXT( "' WHERE ID == " + show_id + ";" ) );
}
void refreshSingleDB( const size_t &index, bool linux, Curl &c ) {
void refreshSingleDB( const size_t &index, bool linux ) {
std::vector< std::unordered_map< string, string > > shows;
string show_id = std::to_string( index );
SQLite::Database db{};
@ -1036,15 +911,19 @@ void refreshSingleDB( const size_t &index, bool linux, Curl &c ) {
throw e;
}
db.exec( "DELETE FROM EPISODES WHERE SHOWID == " + show_id + ";" );
db.exec( TEXT( "SELECT URL, SHOW, PATH, LANGUAGE FROM SHOWS WHERE ID == " + show_id + ";" ), shows );
db.exec( TEXT( "SELECT TVID, SHOW, PATH, LANGUAGE FROM SHOWS WHERE ID == " +
show_id + ";" ),
shows );
std::unordered_map< string, string > &show = shows[0];
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ),
pattern );
cout << "Refreshing " << show[TEXT( "SHOW" )] << std::endl << std::endl << std::endl;
cout << "Refreshing " << show[TEXT( "SHOW" )] << std::endl
<< std::endl
<< std::endl;
#ifdef WIN32
cout << std::endl;
#endif
@ -1063,10 +942,9 @@ void refreshSingleDB( const size_t &index, bool linux, Curl &c ) {
#endif
size_t i{};
for ( const auto &x : seasons ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
x.first, show[TEXT( "URL" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
&x.second, false );
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )], x.first,
show[TEXT( "TVID" )], show[TEXT( "LANGUAGE" )],
pattern, linux, true, &x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
@ -1082,8 +960,8 @@ void refreshSingleDB( const size_t &index, bool linux, Curl &c ) {
for ( auto &episode : season.second ) {
db.exec( TEXT( "INSERT INTO EPISODES ( SHOWID, PATH ) "
"VALUES ( " ) +
show_id + TEXT( ", '" ) +
sanitize( episode ) + TEXT( "' );" ) );
show_id + TEXT( ", '" ) + sanitize( episode ) +
TEXT( "' );" ) );
}
}
}

View File

@ -29,7 +29,6 @@ using char_t = char;
#endif
string getDefUrl( string &show, const string &language, Curl &c );
void findSeason( std::set< string > &files, int season, const string &path );
void findSeasons( std::map< int, std::set< string > > &seasons,
const string &path, const std::set< int > &season_numbers );
@ -38,13 +37,12 @@ void findSeasons( std::map< int, std::set< string > > &seasons,
// CLI functions
void printHelp();
string encodeUrl( const string &url );
bool searchSpecificSeason( const char_t *const path, const string &number );
bool searchSeason( const char_t *const path, size_t &season_pos );
bool searchSeason( const char_t *const path );
void parseSeasonNumbers( std::set< int > &seasons_num, const char_t *argument );
void printLangs();
bool findLanguage( const char_t *language );
string encodeUrl( const string &url );
@ -52,7 +50,7 @@ string encodeUrl( const string &url );
// GUI functions
std::vector< std::pair< string, string > >
getPossibleShows( string show, const string &language, Curl &c );
getPossibleShows( string show, const string &language );
#endif // GUI
@ -61,20 +59,20 @@ string userHome();
void prepareDB( const std::string &_pattern = "" );
#ifndef GUI
void addToDB( string &show, const string &path, const string &language,
bool linux, Curl &c );
bool linux );
#else
void addToDB( string &show, const string &path, const string &language,
const string &url, const string &pattern, bool linux, Curl &c );
const string &url, const string &pattern, bool linux );
std::vector< std::unordered_map< std::string, std::string > > dbGetShows();
#endif
void removeFromDB( const string &path );
void changeDBPattern( const string &pattern );
void refreshDB( bool linux, Curl &c );
void updateDB( bool linux, Curl &c );
void refreshDB( bool linux );
void updateDB( bool linux );
void cleanDB();
void changeDB( size_t index, const string &path, const string &language,
const string &url, Curl &c);
void refreshSingleDB( const size_t &index, bool linux, Curl &c );
const string &url );
void refreshSingleDB( const size_t &index, bool linux );
void iterateFS( std::map< int, std::set< string > > &seasons,
const string &path );

22875
json.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ constexpr size_t DB_PATTERN = 0x0020;
int handleArgument( char_t c, string &show, std::set< int > &seasons_num,
bool &change_dir, string &path, bool &trust, bool &linux,
string &language, string &pattern, size_t &db_flags,
string &language, string &pattern, bool &dvd, size_t &db_flags,
string &db_pattern, char_t *optional, int &i ) {
switch ( c ) {
case 's':
@ -79,12 +79,14 @@ int handleArgument( char_t c, string &show, std::set< int > &seasons_num,
language = optional;
} else {
cerr << "Invalid language choice" << std::endl;
authenticate( "XXXXX" );
printLangs();
return -1;
}
i++;
break;
case '0':
authenticate( "XXXXX" );
printLangs();
return 1;
case 'h':
@ -114,6 +116,9 @@ int handleArgument( char_t c, string &show, std::set< int > &seasons_num,
case '3':
db_flags |= DB_REMOVE;
break;
case '4':
dvd = true;
break;
default:
return -1;
}
@ -177,7 +182,7 @@ int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
}
for ( const auto &x : options ) {
auto res = handleArgument( x, show, seasons_num, change_dir, path,
trust, linux, language, pattern,
trust, linux, language, pattern, dvd,
db_flags, db_pattern, optional, i );
if ( res != 0 )
return res;
@ -191,7 +196,7 @@ int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
// parse command line arguments using getopt
int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
bool &change_dir, string &language, string &pattern,
bool &linux, bool &trust, size_t &db_flags,
bool &linux, bool &trust, bool &dvd, size_t &db_flags,
string &db_pattern, int argc, char **argv ) {
static struct option long_options[] = {
{ "show", required_argument, 0, 's' },
@ -207,7 +212,8 @@ int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
{ "db-update", no_argument, 0, 'u' },
{ "db-name-pattern", required_argument, 0, 'd' },
{ "db-clean", no_argument, 0, '2' },
{ "db-remove", required_argument, 0, '3' },
{ "db-remove", no_argument, 0, '3' },
{ "dvd", no_argument, 0, '4' },
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
@ -216,12 +222,12 @@ int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
while ( 1 ) {
int option_index{ 0 };
auto c = getopt_long( argc, argv, "s:n:cp:txl:01:arud23h", long_options,
auto c = getopt_long( argc, argv, "s:n:cp:txl:01:arud234h", long_options,
&option_index );
if ( c == -1 )
break;
auto res = handleArgument( c, show, seasons_num, change_dir, path,
trust, linux, language, pattern, db_flags,
trust, linux, language, pattern, dvd, db_flags,
db_pattern, optarg, i );
if ( res != 0 )
return res;
@ -254,16 +260,16 @@ int main
bool change_dir{ true };
bool linux{ false };
bool trust{ false };
bool dvd{ false };
size_t db_flags{};
string path{ TEXT( "." ) };
string language{ TEXT( "en" ) };
string pattern{ TEXT( "%filename - %epname" ) };
string db_pattern{};
Curl c;
{
auto tmp = parseCommandLine( show, seasons_num, path, change_dir,
language, pattern, linux, trust, db_flags,
language, pattern, linux, trust, dvd, db_flags,
db_pattern, argc, argv );
if ( tmp == -1 )
return 1;
@ -271,6 +277,8 @@ int main
return 0;
}
authenticate( "XXXXX" );
if ( !FSLib::isDirectory( path ) && FSLib::exists( path ) ) {
// specified file, not directory
auto *file_set = new std::set< string >;
@ -283,7 +291,7 @@ int main
} else {
auto season = std::stoi( path.c_str() + season_pos );
singleSeason( path, show, season, TEXT( "" ), language, pattern,
linux, trust, c, file_set );
linux, trust, file_set, true, dvd );
return 0;
}
} else if ( !FSLib::isDirectory( path ) ) {
@ -304,15 +312,15 @@ int main
}
if ( db_flags & DB_ADD ) {
addToDB( show, path, language, linux, c );
addToDB( show, path, language, linux );
cout << "Added to database" << std::endl;
}
if ( db_flags & DB_REFRESH ) {
refreshDB( linux, c );
refreshDB( linux );
cout << "Refreshed database" << std::endl;
}
if ( db_flags & DB_UPDATE ) {
updateDB( linux, c );
updateDB( linux );
cout << "Updated database" << std::endl;
}
if ( db_flags & DB_CLEAN ) {
@ -364,11 +372,11 @@ int main
if ( seasons_num.size() == 1 ) {
singleSeason( path, show, *seasons_num.begin(), string(), language,
pattern, linux, trust, c );
pattern, linux, trust, nullptr, true, dvd );
} else if ( seasons_num.size() != 0 ) {
multipleSeasons( path, show, seasons_num, language, pattern, linux,
trust, c );
trust, dvd );
} else {
allSeasons( path, show, language, pattern, linux, trust, c );
allSeasons( path, show, language, pattern, linux, trust, dvd );
}
}

View File

@ -62,15 +62,52 @@ Curl::Curl() {
Curl::~Curl() {
curl_easy_cleanup( curl_handle );
curl_slist_free_all( chunk );
curl_global_cleanup();
}
std::string Curl::execute( const std::string &url ) {
void cleanUp( CURL *curl_handle ) {
curl_easy_setopt( curl_handle, CURLOPT_POST, 0 );
curl_easy_setopt( curl_handle, CURLOPT_POSTFIELDS, "" );
}
std::string Curl::get( const std::string &url ) {
// get rid of garbage
cleanUp( curl_handle );
std::string source;
source.reserve( 100000 );
curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA,
static_cast< void * >( &source ) );
curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() );
curl_easy_setopt( curl_handle, CURLOPT_HTTPGET, 1 );
auto res = curl_easy_perform( curl_handle );
if ( res != CURLE_OK ) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res )
<< std::endl;
return "";
}
return source;
}
void Curl::addHeader( const std::string &header ) {
chunk = curl_slist_append( chunk, header.c_str() );
curl_easy_setopt( curl_handle, CURLOPT_HTTPHEADER, chunk );
}
void Curl::clearHeader() {
curl_slist_free_all( chunk );
chunk = NULL;
curl_easy_setopt( curl_handle, CURLOPT_HTTPHEADER, chunk );
}
std::string Curl::post( const std::string &url, const std::string &data ) {
std::string source;
source.reserve( 100000 );
curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() );
curl_easy_setopt( curl_handle, CURLOPT_POST, 1 );
curl_easy_setopt( curl_handle, CURLOPT_POSTFIELDS, data.c_str() );
curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA,
static_cast< void * >( &source ) );
auto res = curl_easy_perform( curl_handle );
if ( res != CURLE_OK ) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res )

View File

@ -1,18 +1,17 @@
#ifndef NETWORK_HPP
#define NETWORK_HPP
#include <string>
#ifdef _WIN32
#include <Windows.h>
#include <WinInet.h>
#include <Windows.h>
using string = std::wstring;
#else
#include <curl/curl.h>
#include <string>
using string = std::string;
@ -22,13 +21,17 @@ class Curl {
public:
Curl();
~Curl();
std::string execute( const string &url );
std::string get( const string &url );
std::string post( const std::string &url, const std::string &data );
void addHeader( const std::string &header );
void clearHeader();
private:
#ifdef _WIN32
HINTERNET connect = nullptr;
#else
CURL *curl_handle;
struct curl_slist *chunk = NULL;
#endif
};

View File

@ -24,6 +24,9 @@
#include "functions.hpp"
#include "tv_rename.hpp"
#include "json.hpp"
using json = nlohmann::json;
#ifdef _WIN32
constexpr const char_t *dir_divider = L"\\";
@ -44,81 +47,122 @@ constexpr const char_t *dir_divider = "/";
#endif
// get names for all episodes for a given season
std::vector< string > parseEpisodeNames( const string &season_code,
const string &language ) {
std::vector< string > episodes;
size_t pos = 0;
while ( true ) {
pos =
season_code.find( TEXT( "ge=\"" ) + language + TEXT( "\" " ), pos );
if ( pos != string::npos ) {
if ( language == TEXT( "en" ) )
pos += 17;
else
pos += 29;
while ( iswspace( season_code[pos] ) )
pos++;
std::string api_token = "";
Curl c;
auto end = season_code.find( TEXT( "<" ), pos );
end--;
std::vector< std::pair< string, string > >
searchShow( const string &show, const string &language ) {
c.addHeader( "Accept: application/json" );
c.addHeader( "Authorization: Bearer " + api_token );
c.addHeader( "Accept-Language: " + language );
while ( iswspace( season_code[end] ) )
end--;
auto encoded_show = encodeUrl( show );
end++;
episodes.push_back( season_code.substr( pos, end - pos ) );
auto j = json::parse(
c.get( "https://api.thetvdb.com/search/series?name=" + encoded_show ) );
std::vector< json > results;
if( j["data"].is_array() ) {
results = j["data"].get< std::vector< json > >();
} else {
cout << j << std::endl;
return {};
}
std::vector< std::pair< string, string > > ret;
// find all possible shows
for ( auto &x : results ) {
ret.emplace_back( x["seriesName"].get< string >(),
std::to_string( x["id"].get< int >() ) );
}
c.clearHeader();
return ret;
}
// get show's ID
string getShowId( string &show, const string &language ) {
size_t order{}, pos{};
auto search_results = searchShow( show, language );
for( const auto &x : search_results ) {
cout << ++order << ". " << x.first << std::endl;
}
cout << "Which TV Show is the right one? ";
cin >> pos;
cin.clear();
cin.ignore( 1, '\n' );
return search_results[pos-1].second;
}
string showNameFromId( const string &id, const string &language ) {
c.addHeader( "Accept: application/json" );
c.addHeader( "Authorization: Bearer " + api_token );
c.addHeader( "Accept-Language: " + language );
auto j = json::parse( c.get( "https://api.thetvdb.com/series/" + id ) );
return j["data"].get< json >()["seriesName"].get< string >();
}
// get names for all episodes for a given season
std::vector< string > getEpisodeNames( const string &id, const string &season,
const string &language, bool dvd = false ) {
c.addHeader( "Accept: application/json" );
c.addHeader( "Authorization: Bearer " + api_token );
c.addHeader( "Accept-Language: " + language );
string page = "1";
string season_query = "airedSeason=";
if( dvd )
season_query = "dvdSeason=";
std::vector< string > episodes;
do {
episodes.resize( episodes.size() * 2 );
auto j = json::parse( c.get( "https://api.thetvdb.com/series/" + id +
"/episodes/query?" + season_query + season
+ "&page=" + page ) );
if( j["data"].is_array() ) {
auto epdata = j["data"].get< std::vector< json > >();
if( episodes.size() < epdata.size() )
episodes.resize( epdata.size() );
for ( auto &x : epdata ) {
if( x["episodeName"].is_string() ) {
if( dvd ) {
size_t index = x["dvdEpisodeNumber"].get< size_t >();
if( index > episodes.size() )
episodes.resize( index );
index--;
episodes[index] = x["episodeName"].get< string >();
} else {
size_t index = x["airedEpisodeNumber"].get< size_t >();
if( index > episodes.size() )
episodes.resize( index );
index--;
episodes[index] = x["episodeName"].get<string>();
}
}
}
} else {
cerr << "Couldn't find episode names for season " << season << " of show " << showNameFromId( id, language ) << std::endl;
}
if( j["links"]["next"].is_null() )
break;
}
}
page = std::to_string( j["links"]["next"].get< size_t >() );
} while( 1 );
c.clearHeader();
return episodes;
}
std::vector< std::pair< string, std::pair< string, string > > >
getRenamedFiles( const string &show, int season, string url,
getRenamedFiles( const string &show, int season, const string id,
const string &language, const string &pattern,
const bool &linux, Curl &c, const std::set< string > &files ) {
const bool &linux, const std::set< string > &files, bool dvd = false ) {
#ifdef _WIN32
auto season_num = std::to_wstring( season );
#else
auto season_num = std::to_string( season );
#endif
url += TEXT( "/seasons/" );
url += season_num;
// get source code of season's page
auto season_code = c.execute( url );
// remove newlines
season_code.erase(
std::remove_if( season_code.begin(), season_code.end(),
[]( char x ) { return x == '\r' || x == '\n'; } ),
season_code.end() );
// first 900 chars or so are useless to us
season_code = season_code.substr( 900 );
// get only the episode names
auto pos = season_code.find( "\"translations\"" );
if ( pos != string::npos ) {
season_code = season_code.substr( pos );
pos = season_code.find( "</table>" );
if ( pos != string::npos )
season_code = season_code.substr( 0, pos );
else
return {};
} else {
return {};
}
#ifdef _WIN32
auto episodes =
parseEpisodeNames( utf8_to_wstring( season_code ), language );
#else
auto episodes = parseEpisodeNames( season_code, language );
auto season_num = std::to_string( season );
auto episodes = getEpisodeNames( id, season_num, language, dvd );
#endif
if ( episodes.empty() )
@ -130,6 +174,7 @@ getRenamedFiles( const string &show, int season, string url,
std::vector< std::pair< string, std::pair< string, string > > >
renamed_files;
size_t pos = 0;
for ( const auto &x : files ) {
auto last = x.find_last_of( dir_divider );
string og_name;
@ -148,8 +193,8 @@ getRenamedFiles( const string &show, int season, string url,
} else {
continue;
}
num--;
num -= 1;
if ( num < episodes.size() ) {
auto pos = og_name.find_last_of( TEXT( "." ) );
// get desired filename
@ -204,12 +249,12 @@ getRenamedFiles( const string &show, int season, string url,
return renamed_files;
}
void singleSeason( const string &path, string &show, int season, string url,
void singleSeason( const string &path, string &show, int season, string id,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c,
std::set< string > const *files_ptr, bool print ) {
if ( url.empty() )
url = getDefUrl( show, language, c );
const bool &linux, const bool &trust,
std::set< string > const *files_ptr, bool print, bool dvd ) {
if ( id.empty() )
id = getShowId( show, language );
std::set< string > *found_files = nullptr;
@ -219,13 +264,12 @@ void singleSeason( const string &path, string &show, int season, string url,
files_ptr = found_files;
}
auto renamed_files =
getRenamedFiles( show, season, std::move( url ), language, pattern,
linux, c, *files_ptr );
auto renamed_files = getRenamedFiles( show, season, id, language, pattern,
linux, *files_ptr, dvd );
if ( print ) {
for ( auto renamed = renamed_files.begin();
renamed != renamed_files.end(); ++renamed ) {
if( print || !trust ) {
for ( auto renamed = renamed_files.begin(); renamed != renamed_files.end();
++renamed ) {
cout << renamed->second.first << " --> " << renamed->second.second
<< std::endl;
}
@ -257,31 +301,67 @@ void singleSeason( const string &path, string &show, int season, string url,
void multipleSeasons( const string &path, string &show,
const std::map< int, std::set< string > > &seasons,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c ) {
auto url = getDefUrl( show, language, c );
const bool &linux, const bool &trust, bool dvd ) {
auto id = getShowId( show, language );
for ( const auto &x : seasons ) {
singleSeason( path, show, x.first, url, language, pattern, linux, trust,
c, &x.second );
singleSeason( path, show, x.first, id, language, pattern, linux, trust,
&x.second, dvd );
}
}
void multipleSeasons( const string &path, string &show,
const std::set< int > seasons, const string &language,
const string &pattern, const bool &linux,
const bool &trust, Curl &c ) {
const bool &trust, bool dvd ) {
std::map< int, std::set< string > > season_map;
findSeasons( season_map, path, seasons );
multipleSeasons( path, show, season_map, language, pattern, linux, trust,
c );
multipleSeasons( path, show, season_map, language, pattern, linux, trust, dvd );
}
void allSeasons( const string &path, string &show, const string &language,
const string &pattern, const bool &linux, const bool &trust,
Curl &c ) {
const string &pattern, const bool &linux, const bool &trust, bool dvd ) {
std::map< int, std::set< string > > seasons;
// get all season number from this directory and subdirectories
iterateFS( seasons, path );
multipleSeasons( path, show, seasons, language, pattern, linux, trust, c );
multipleSeasons( path, show, seasons, language, pattern, linux, trust, dvd );
}
std::vector< std::pair< std::string, std::string > > getLangs() {
std::vector< std::pair< std::string, std::string > > langs;
c.addHeader( "Accept: application/json" );
c.addHeader( "Authorization: Bearer " + api_token );
auto j = json::parse( c.get( "https://api.thetvdb.com/languages" ) );
c.clearHeader();
auto langs_json = j["data"].get< std::vector< json > >();
for ( auto &x : langs_json ) {
langs.emplace_back( x["abbreviation"].get< std::string >(),
x["name"].get< std::string >() );
}
return langs;
}
void printLangs() {
for ( auto &x : getLangs() )
cout << x.first << " - " << x.second << std::endl;
}
bool findLanguage( const char_t *language ) {
for ( auto &x : getLangs() ) {
if ( x.first == language )
return true;
}
return false;
}
bool authenticate( const std::string &api_key ) {
c.addHeader( "Accept: application/json" );
c.addHeader( "Content-Type: application/json" );
auto j = json::parse( c.post( "https://api.thetvdb.com/login",
"{ \"apikey\": \"" + api_key + "\" }" ) );
api_token = j["token"].get< std::string >();
c.clearHeader();
// TODO check return code
return true;
}
#endif

View File

@ -2,10 +2,7 @@
#define TV_RENAME_HPP
#include <set>
#ifdef GUI
#include <vector>
#endif
#include "network.hpp"
@ -21,11 +18,11 @@ using char_t = char;
#endif
void singleSeason( const string &path, string &show, int season, string url,
void singleSeason( const string &path, string &show, int season, string id,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c,
std::set< string > const *files = nullptr,
bool print = true );
const bool &linux, const bool &trust,
std::set< string > const *files_ptr = nullptr,
bool print = true, bool dvd = false );
#ifdef GUI
@ -39,11 +36,18 @@ getRenamedFiles( const string &show, int season, string url,
void multipleSeasons( const string &path, string &show,
const std::set< int > seasons, const string &language,
const string &pattern, const bool &linux,
const bool &trust, Curl &c );
const bool &trust, bool dvd = false );
void allSeasons( const string &path, string &show, const string &language,
const string &pattern, const bool &linux, const bool &trust,
Curl &c );
const string &pattern, const bool &linux, const bool &trust, bool dvd = false );
#endif
void printLangs();
bool findLanguage( const char_t *language );
bool authenticate( const std::string &api_key );
string getShowId( string &show, const string &language );
string showNameFromId( const string &id, const string &language );
std::vector< std::pair< string, string > > searchShow( const string &show,
const string &language );
#endif // TV_RENAME_HPP