#include #include #include #include #include #include #include #include #include "filesystem.hpp" #include "functions.hpp" #include "progress.hpp" #include "sqlitepp.hpp" #include "tv_rename.hpp" #ifdef _WIN32 #include #include #define cout std::wcout #define cerr std::wcerr #define cin std::wcin constexpr const char_t *dir_divider = L"\\"; #else // UNIX #include #include #include #include #define TEXT( a ) a #define cout std::cout #define cerr std::cerr #define cin std::cin constexpr const char_t *dir_divider = "/"; #endif // UNIX #ifdef _WIN32 // functions to convert between string and wstring std::string wstring_to_utf8( const std::wstring &wstring ) { std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > wconv; return wconv.to_bytes( wstring ); } std::wstring utf8_to_wstring( const std::string &utf8 ) { std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > wconv; return wconv.from_bytes( utf8 ); } #endif // _WIN32 string encodeUrl( const string &url ) { // stolen from here - // https://stackoverflow.com/questions/154536/encode-decode-urls-in-c #ifdef _WIN32 std::wstringstream encoded; auto url_c = wstring_to_utf8( url ); #else std::stringstream encoded; const auto &url_c = url; #endif encoded.fill( '0' ); encoded << std::hex; for ( const auto &x : url_c ) { if ( isalnum( static_cast< unsigned char >( x ) ) || x == '-' || x == '_' || x == '.' || x == '~' || x == '+' ) { encoded << x; continue; } encoded << std::uppercase << '%' << std::setw( 2 ); encoded << int( static_cast< unsigned char >( x ) ) << std::nouppercase; } return encoded.str(); } // return true if file contains S[0-9]+E[0-9]+ and set // season_pos to start of season number bool searchSeason( const char_t *const path, size_t &season_pos, size_t &ep_pos ) { size_t cur_pos{}; while ( path[cur_pos] != '\0' ) { if ( ( path[cur_pos] == 's' || path[cur_pos] == 'S' ) && iswdigit( path[cur_pos + 1] ) ) { cur_pos++; season_pos = cur_pos; // after ++ because we want the first pos to // point to season's number while ( iswdigit( path[cur_pos] ) ) cur_pos++; if ( ( path[cur_pos] == 'e' || path[cur_pos] == 'E' ) && iswdigit( path[cur_pos + 1] ) ) { ep_pos = cur_pos + 1; return true; } } cur_pos++; } return false; } bool searchSeason( const char_t *const path, size_t &season_pos ) { size_t tmp; return searchSeason( path, season_pos, tmp ); } void iterateFS( std::map< int, std::map< int, string > > &seasons, const string &path ) { // season_pos - position of first digit of the season size_t season_pos{ string::npos }; size_t ep_pos{ string::npos }; for ( const auto p : FSLib::Directory( path ) ) { // if p is directory, iterate through it if ( FSLib::isDirectory( path + dir_divider + p ) ) { iterateFS( seasons, path + dir_divider + p ); continue; } // if file is a correct format, add it to file list // for its season if ( searchSeason( p, season_pos, ep_pos ) ) seasons[std::stoi( p + season_pos )][std::stoi( p + ep_pos )] = path + dir_divider + p; } } #ifndef GUI // following functions are only needed for CLI version void printHelp() { cout << "Usage:" << std::endl; cout << " tv_rename [options] [path]" << std::endl << std::endl; cout << " -h, --help show this help message and exit" << std::endl << std::endl; cout << " path can be either a file or a directory, if it's a directory" << std::endl; cout << " all files in it and its subdirectories will be renamed" << std::endl << std::endl; cout << "OPTIONS" << std::endl; cout << " -s, --show TV show from which you want the"; cout << " episode names" << std::endl; cout << " -n, --season Season number/s (if multiple seasons," << std::endl; cout << " must be seperated by one space)" << " or 'all'" << std::endl; cout << " for all seasons in selected directory" << std::endl; cout << " -d, --dvd use dvd ordering instead of aired " "ordering" << std::endl; cout << " --name-pattern Pattern to which change the file name." << std::endl; cout << " Possible sequences are:" << std::endl; cout << " %filename - original filename" << std::endl; cout << " (without filetype" << " extension)" << std::endl; cout << " %show - show name from thetvdb" << std::endl; cout << " %epname - episode name from thetvdb" << std::endl; cout << " %season - season number" << std::endl; cout << " it's possible to specify leading" << " 0 like this:" << std::endl; cout << " %2season (number means how many" << " zeros)" << std::endl; cout << " %episode - episode number" << std::endl; cout << " it's possible to specify leading" << " 0 like this:" << std::endl; cout << " %2episode (number means how many" << " zeros)" << std::endl; cout << " Default pattern is \"$filename -" << " $epname\"" << std::endl; cout << " -c, --correct-path This is the correct path," << " stop asking me!" << std::endl; cout << " -t, --trust Don't ask whether the names are correct" << std::endl; cout << " -x, --linux Don't replace characters characters" << " that are" << std::endl; cout << " illegal in Windows" << std::endl; cout << " -l, --lang Select which language the episode" << " names shoud be in" << std::endl; cout << " --print-langs Pring available languages" << std::endl; cout << std::endl; cout << "DATABASE OPTIONS" << std::endl; cout << " --db-add Add path to the database" << std::endl; cout << " --db-refresh Refresh episode names for all paths" << " in the database" << std::endl; cout << " --db-update Check all paths in the database," << std::endl; cout << " if they contain new files," << " rename them" << std::endl; cout << " --db-name-pattern Change name pattern used for files" << std::endl << " managed by database" << std::endl; cout << " --db-clean Remove deleted files from the" << " database" << std::endl; cout << " --db-remove Remove path from the database" << std::endl; } // parse command line argument --seasons (e.g. '1 2 3 4 5') // and store season numbers as integers in seasons_num void parseSeasonNumbers( std::set< int > &seasons_num, const char_t *argument ) { size_t pos{ 0 }; while ( !iswdigit( argument[pos] ) && argument[pos] != '\0' ) pos++; if ( argument[pos] == '\0' ) { seasons_num.clear(); return; } int temp; #ifdef _WIN32 std::wstringstream iss( argument + pos ); #else std::stringstream iss( argument + pos ); #endif while ( iss >> temp ) { seasons_num.insert( temp ); } } #endif // ndef GUI #ifdef _WIN32 // get user's %APPDATA% folder location string userHome() { wchar_t *dir = static_cast< wchar_t * >( CoTaskMemAlloc( MAX_PATH ) ); auto res = SHGetKnownFolderPath( FOLDERID_RoamingAppData, 0, NULL, &dir ); if ( res == S_OK ) { string dir_s = dir; CoTaskMemFree( dir ); return dir_s; } CoTaskMemFree( dir ); return L""; } #else // UNIX // get user's home directory string userHome() { uid_t user_uid; // current user's uid { uid_t eid; uid_t sid; getresuid( &user_uid, &eid, &sid ); // don't need eid and sid } // password file entry auto user_passwd = getpwuid( user_uid ); if ( user_passwd == nullptr ) throw std::runtime_error( "User with uid " + std::to_string( user_uid ) + " doesn't exist!" ); return user_passwd->pw_dir; } #endif // UNIX // create file name based on given pattern string compilePattern( const string &pattern, int season, int episode, const string &filename, const string &episodeName, const string &showName ) { string output; #ifdef _WIN32 auto season_num = std::to_wstring( season ); auto ep_num = std::to_wstring( episode ); #else auto season_num = std::to_string( season ); auto ep_num = std::to_string( episode ); #endif for ( size_t i = 0; i < pattern.size(); i++ ) { // if current character is % check if a pattern follows, otherwise // put % if ( pattern[i] == '%' ) { // check for numbers right after % indicating size of zero // padding for numbers auto pos = pattern.find_first_not_of( TEXT( "0123456789" ), i + 1 ); if ( pattern.find( TEXT( "season" ), pos - 1 ) == pos && pos != i + 1 ) { // if season is AFTER numbers, put season number padded // with zeros // get number of leading zeros auto leading = std::stoi( pattern.c_str() + i + 1 ); // move i to the last char of 'season' i = pos + 5; // get number of zeros to be put before the season number leading -= season_num.size(); if ( leading < 0 ) leading = 0; // add padded season to output output += string( leading, '0' ) + season_num; } else if ( pattern.find( TEXT( "season" ), i ) == i + 1 ) { // if season isn't after numbers, just put season number to // output i += 6; output += season_num; } else if ( pattern.find( TEXT( "episode" ), pos - 1 ) == pos && pos != i + 1 ) { // same principle as with season after number auto leading = std::stoi( pattern.c_str() + i + 1 ); i = pos + 6; leading -= ep_num.size(); if ( leading < 0 ) leading = 0; output += string( leading, '0' ) + ep_num; } else if ( pattern.find( TEXT( "episode" ), i ) == i + 1 ) { // if episode isn't after number, just put the episode number to // output i += 7; output += ep_num; } else if ( pattern.find( TEXT( "epname" ), i ) == i + 1 ) { // episode name from thetvdb i += 6; output += episodeName; } else if ( pattern.find( TEXT( "show" ), i ) == i + 1 ) { // show name from thetvdb i += 4; output += showName; } else if ( pattern.find( TEXT( "filename" ), i ) == i + 1 ) { // original file name i += 8; output += filename; } else { // output % if no escape sequence was found output += '%'; } } else if ( pattern[i] == '\\' ) { // possibility to escape % if ( pattern[i + 1] == '%' ) { output += '%'; i++; } else if ( pattern[i + 1] == '\\' ) { output += '\\'; i++; } else { output += '\\'; } } else { // if char isn't % or / just add it to the output string output += pattern[i]; } } return output; } #ifdef WIN32 std::wstring getDBName() { return userHome() + L"\\tv_rename\\database.db"; } #else std::string getDBName() { return userHome() + "/.cache/tv_rename.db"; } #endif string sanitize( const string &str ) { string ret; size_t prev_pos{}; size_t pos = str.find_first_of( '\'' ); while ( pos != string::npos ) { ret += str.substr( prev_pos, pos - prev_pos ); prev_pos = pos + 1; pos = str.find_first_of( '\'', prev_pos ); ret += TEXT( "\'\'" ); } ret += str.substr( prev_pos, pos ); return ret; } void prepareDB( const std::string &_pattern ) { SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE ); } catch ( std::exception &e ) { cerr << "Couldn't create database, make sure you have" << " write permission for " << getDBName() << std::endl; throw e; } 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, DVD INTEGER 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() ) { cout << "Insert name pattern for database:" << std::endl; auto *p = new string; std::getline( cin, *p ); pattern = p; } else { pattern = &_pattern; } db.exec( TEXT( "INSERT INTO SHOWS ( TVID, SHOW, PATH, LANGUAGE, DVD ) " "VALUES ( 'pattern', 'pattern', '" ) + sanitize( *pattern ) + TEXT( "', 'pattern', 0 );" ) ); if ( pattern != &_pattern ) { delete pattern; } } #ifndef GUI void addToDB( string &show, const string &path, const string &language, bool linux, bool dvd ) { if ( !FSLib::exists( getDBName() ) ) prepareDB(); #else void addToDB( string &show, const string &path, const string &language, const string &id, const string &pattern, bool linux, bool dvd ) { if ( !FSLib::exists( getDBName() ) ) prepareDB( pattern ); #endif SQLite::Database db{}; auto absolute = FSLib::canonical( path ); try { db.open( getDBName(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } #ifndef GUI // gui gives id and correct show name auto id = getShowId( show, language ); show = showNameFromId( id, language ); #endif db.exec( TEXT( "INSERT OR IGNORE INTO SHOWS ( TVID, SHOW, PATH, LANGUAGE, DVD ) " "VALUES ( '" ) + sanitize( id ) + TEXT( "', '" ) + sanitize( show ) + TEXT( "', '" ) + sanitize( absolute ) + TEXT( "', '" ) + sanitize( language ) + "', " + ( dvd ? TEXT( "1" ) : TEXT( "0" ) ) + TEXT( " );" ) ); string show_id = std::to_string( db.lastRowID() ); #ifndef GUI string pattern{}; db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ), pattern ); #endif std::map< int, std::map< int, string > > seasons; // get all seasons and episodes iterateFS( seasons, absolute ); auto size = seasons.size(); size_t i = 0; cout << "Renaming" << std::endl; #ifdef WIN32 cout << std::endl; #endif #ifndef GUI ProgressBar::print( 0 ); #endif for ( auto &x : seasons ) { singleSeason( absolute, show, x.first, id, language, pattern, linux, true, &x.second, true, dvd ); i++; #ifndef GUI ProgressBar::print( ( i * 100 ) / size ); #endif } #ifndef GUI cout << std::endl; #endif size = seasons.size(); i = 0; cout << "Adding to database" << std::endl; #ifdef WIN32 cout << std::endl; #endif #ifndef GUI ProgressBar::print( 0 ); #endif for ( auto &season : seasons ) { for ( auto &episode : season.second ) { db.exec( TEXT( "INSERT OR IGNORE INTO EPISODES ( SHOWID, PATH ) " "VALUES ( " ) + show_id + TEXT( ", '" ) + sanitize( episode.second ) + TEXT( "' );" ) ); } i++; #ifndef GUI ProgressBar::print( ( i * 100 ) / size ); #endif } #ifndef GUI cout << std::endl; #endif } #ifdef WIN32 void cleanUpLine() { CONSOLE_SCREEN_BUFFER_INFO csbi; int width; GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &csbi ); width = csbi.srWindow.Right - csbi.srWindow.Left + 1; static HANDLE h = NULL; if ( !h ) h = GetStdHandle( STD_OUTPUT_HANDLE ); CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo( h, &info ); COORD c = { 0, info.dwCursorPosition.Y - 3 }; SetConsoleCursorPosition( h, c ); cout << string( width, ' ' ) << std::endl << std::endl; SetConsoleCursorPosition( h, c ); } #else void cleanUpLine() { struct winsize w; ioctl( 0, TIOCGWINSZ, &w ); auto width = w.ws_col; cout << "\x1b[2A"; cout << string( width, ' ' ) << std::endl << std::endl; cout << "\x1b[2A"; } #endif void refreshDB( bool linux ) { std::vector< std::unordered_map< string, string > > shows; SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } db.exec( "DELETE FROM EPISODES;" ); db.exec( TEXT( "SELECT ID, TVID, SHOW, PATH, LANGUAGE, DVD FROM SHOWS WHERE TVID " "!= 'pattern';" ), shows ); string pattern{}; db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ), pattern ); cout << "Refreshing database" << std::endl << std::endl << std::endl; #ifdef WIN32 cout << std::endl; #endif for ( auto &show : shows ) { if ( FSLib::exists( show[TEXT( "PATH" )] ) ) { cleanUpLine(); cout << "Refreshing " << show[TEXT( "SHOW" )] << std::endl; #ifdef WIN32 cout << std::endl; #endif #ifndef GUI ProgressBar::print( 0 ); #endif std::map< int, std::map< int, string > > seasons; // get all season number from this directory and subdirectories iterateFS( seasons, show[TEXT( "PATH" )] ); #ifndef GUI auto size = seasons.size(); #endif size_t i{}; for ( auto &x : seasons ) { singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )], x.first, show[TEXT( "TVID" )], show[TEXT( "LANGUAGE" )], pattern, linux, true, &x.second, false, show[TEXT( "DVD" )] == TEXT( "1" ) ); i++; #ifndef GUI ProgressBar::print( ( i * 100 ) / size ); #endif } #ifndef GUI ProgressBar::print( 100 ); cout << std::endl; #endif for ( auto &season : seasons ) { for ( auto &episode : season.second ) { db.exec( TEXT( "INSERT INTO EPISODES ( SHOWID, PATH ) " "VALUES ( " ) + show[TEXT( "ID" )] + TEXT( ", '" ) + sanitize( episode.second ) + TEXT( "' );" ) ); } } } } } void updateDB( bool linux ) { std::vector< std::unordered_map< string, string > > shows; SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } db.exec( TEXT( "SELECT ID, TVID, SHOW, PATH, LANGUAGE, DVD FROM SHOWS WHERE TVID" " != 'pattern';" ), shows ); string pattern{}; db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE TVID == 'pattern';" ), pattern ); cout << "Updating database" << std::endl << std::endl << std::endl; #ifdef WIN32 cout << std::endl; #endif for ( auto &show : shows ) { if ( !FSLib::exists( show[TEXT( "PATH" )] ) ) { continue; } cleanUpLine(); cout << "Updating " << show[TEXT( "SHOW" )] << std::endl; #ifdef WIN32 cout << std::endl; #endif std::unordered_set< string > episodes; db.exec( TEXT( "SELECT PATH FROM EPISODES WHERE SHOWID == " ) + show[TEXT( "ID" )] + TEXT( ";" ), episodes ); std::map< int, std::map< int, string > > seasons; std::map< int, std::map< int, string > > new_eps; // get all season numbers from this directory and subdirectories iterateFS( seasons, show[TEXT( "PATH" )] ); for ( const auto &x : seasons ) { for ( const auto &episode : x.second ) { if ( episodes.find( episode.second ) == episodes.end() ) { new_eps[x.first].insert( episode ); } } } #ifndef GUI ProgressBar::print( 0 ); #endif if ( !new_eps.empty() ) { #ifndef GUI auto size = new_eps.size(); #endif size_t i{}; for ( auto &x : new_eps ) { singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )], x.first, show[TEXT( "TVID" )], show[TEXT( "LANGUAGE" )], pattern, linux, true, &x.second, false, show[TEXT( "DVD" )] == TEXT( "1" ) ); i++; #ifndef GUI ProgressBar::print( ( i * 100 ) / size ); #endif } seasons.clear(); iterateFS( seasons, show[TEXT( "PATH" )] ); for ( auto &season : seasons ) { for ( auto &episode : season.second ) { db.exec( TEXT( "INSERT OR IGNORE INTO EPISODES ( SHOWID, " "PATH ) VALUES ( " ) + show[TEXT( "ID" )] + TEXT( ", '" ) + sanitize( episode.second ) + TEXT( "' );" ) ); } } } #ifndef GUI ProgressBar::print( 100 ); cout << std::endl; #endif } } void changeDBPattern( const string &pattern ) { SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } db.exec( TEXT( "UPDATE SHOWS SET PATH = '" ) + pattern + TEXT( "' WHERE TVID == 'pattern';" ) ); } void cleanDB() { std::vector< std::unordered_map< string, string > > shows; SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } db.exec( TEXT( "SELECT ID, PATH FROM SHOWS WHERE TVID" " != 'pattern';" ), shows ); for ( auto &show : shows ) { bool dir_exists = FSLib::exists( show[TEXT( "PATH" )] ); std::unordered_set< string > episodes; db.exec( TEXT( "SELECT PATH FROM EPISODES WHERE SHOWID == " ) + show[TEXT( "ID" )] + TEXT( ";" ), episodes ); for ( const auto &episode : episodes ) { if ( !dir_exists || !FSLib::exists( episode ) ) { db.exec( TEXT( "DELETE FROM EPISODES WHERE PATH == '" ) + sanitize( episode ) + TEXT( "';" ) ); } } episodes.clear(); db.exec( TEXT( "SELECT PATH FROM EPISODES WHERE SHOWID == " ) + show[TEXT( "ID" )] + TEXT( ";" ), episodes ); if ( episodes.empty() ) { db.exec( TEXT( "DELETE FROM SHOWS WHERE ID == " ) + show[TEXT( "ID" )] + TEXT( ";" ) ); } } } void removeFromDB( const string &path ) { SQLite::Database db{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } string show_id{}; db.exec( TEXT( "SELECT ID FROM SHOWS WHERE PATH == '" ) + sanitize( path ) + TEXT( "';" ), show_id ); db.exec( TEXT( "DELETE FROM EPISODES WHERE SHOWID == " ) + show_id + TEXT( ";" ) ); 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 { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } std::vector< std::unordered_map< std::string, std::string > > ret; db.exec( TEXT( "SELECT * FROM SHOWS;" ), ret ); return ret; } void changeDB( size_t index, const string &path, const string &language, const string &id, bool dvd ) { SQLite::Database db{}; auto absolute = FSLib::canonical( path ); try { db.open( getDBName(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } #ifndef WIN32 string show_id = std::to_string( index ); #else string show_id = std::to_wstring( index ); #endif auto real_show = showNameFromId( id, language ); db.exec( TEXT( "UPDATE SHOWS SET TVID = '" ) + sanitize( id ) + TEXT( "', SHOW = '" ) + sanitize( real_show ) + TEXT( "', PATH = '" ) + sanitize( absolute ) + TEXT( "', LANGUAGE = '" ) + sanitize( language ) + TEXT( "', DVD = " + ( dvd ? TEXT( "1" ) : TEXT( "0" ) ) + " WHERE ID == " + show_id + ";" ) ); } 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{}; try { db.open( getDBName(), SQLite::OPEN_READWRITE ); } catch ( std::exception &e ) { cerr << "Can't open database, make sure it exists" << std::endl; throw e; } db.exec( "DELETE FROM EPISODES WHERE SHOWID == " + show_id + ";" ); db.exec( TEXT( "SELECT TVID, SHOW, PATH, LANGUAGE, DVD 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 TVID == 'pattern';" ), pattern ); cout << "Refreshing " << show[TEXT( "SHOW" )] << std::endl << std::endl << std::endl; #ifdef WIN32 cout << std::endl; #endif if ( FSLib::exists( show[TEXT( "PATH" )] ) ) { #ifdef WIN32 cout << std::endl; #endif #ifndef GUI ProgressBar::print( 0 ); #endif std::map< int, std::map< int, string > > seasons; // get all season number from this directory and subdirectories iterateFS( seasons, show[TEXT( "PATH" )] ); #ifndef GUI auto size = seasons.size(); #endif size_t i{}; for ( auto &x : seasons ) { singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )], x.first, show[TEXT( "TVID" )], show[TEXT( "LANGUAGE" )], pattern, linux, true, &x.second, false, show[TEXT( "DVD" )] == TEXT( "1" ) ); i++; #ifndef GUI ProgressBar::print( ( i * 100 ) / size ); #endif } #ifndef GUI ProgressBar::print( 100 ); cout << std::endl; #endif seasons.clear(); iterateFS( seasons, show[TEXT( "PATH" )] ); for ( auto &season : seasons ) { for ( auto &episode : season.second ) { db.exec( TEXT( "INSERT INTO EPISODES ( SHOWID, PATH ) " "VALUES ( " ) + show_id + TEXT( ", '" ) + sanitize( episode.second ) + TEXT( "' );" ) ); } } } }