tv_rename/main.cpp

408 lines
12 KiB
C++
Raw Normal View History

2019-01-19 12:40:10 +00:00
#include <iostream>
2020-03-12 19:52:23 +00:00
#include <locale.h>
2019-02-04 16:39:48 +00:00
#include <set>
#ifdef _WIN32
#include <windows.h>
2020-04-01 11:46:28 +00:00
#include "resources_windows.h"
2019-02-04 16:39:48 +00:00
#include <fcntl.h>
#include <io.h>
using char_t = wchar_t;
using string = std::wstring;
constexpr const char_t *dir_divider = L"\\";
2019-02-04 16:39:48 +00:00
#define cerr std::wcerr
#define cout std::wcout
#define cin std::wcin
#else
2020-03-12 19:52:23 +00:00
#include "resources_linux.h"
#include <errno.h>
2020-04-01 14:13:39 +00:00
#include <error.h>
2020-01-15 16:12:54 +00:00
#include <getopt.h>
2019-02-04 16:39:48 +00:00
using char_t = char;
using string = std::string;
constexpr const char_t *dir_divider = "/";
2019-02-04 16:39:48 +00:00
#define cerr std::cerr
#define cout std::cout
#define cin std::cin
2020-03-12 19:52:23 +00:00
#define DOMAIN "tv_rename"
2019-02-04 16:39:48 +00:00
#endif
2020-01-15 16:12:54 +00:00
#include "filesystem.hpp"
#include "functions.hpp"
#include "tv_rename.hpp"
// DB flags
2020-04-01 14:13:39 +00:00
#define DB_ADD 0x0001
2020-01-15 16:12:54 +00:00
#define DB_REFRESH 0x0002
2020-04-01 14:13:39 +00:00
#define DB_UPDATE 0x0004
#define DB_CLEAN 0x0008
#define DB_REMOVE 0x0010
2020-01-15 16:12:54 +00:00
#define DB_PATTERN 0x0020
2019-06-04 19:54:00 +00:00
2020-01-15 21:23:11 +00:00
#define API_KEY "42B66F5E-C6BF-423F-ADF9-CC97163472F6"
2020-01-15 16:12:54 +00:00
// return 0 - all went as expected, 1 - request of help or print languages,
// -1 - error
2019-02-04 16:39:48 +00:00
int handleArgument( char_t c, string &show, std::set< int > &seasons_num,
2020-01-15 16:12:54 +00:00
string &path, string &language, string &pattern,
size_t &tv_flags, size_t &db_flags, string &db_pattern,
char_t *optional, int &i ) {
2019-02-04 16:39:48 +00:00
switch ( c ) {
case 's':
show = optional;
i++;
break;
case 'n':
parseSeasonNumbers( seasons_num, optional );
i++;
break;
case 'c':
2020-01-15 16:12:54 +00:00
tv_flags |= TV_CHDIR;
2019-02-04 16:39:48 +00:00
break;
case 'p':
2019-06-04 19:54:00 +00:00
path = optional;
2019-02-04 16:39:48 +00:00
i++;
// if path provided, assume it's correct
2020-01-15 16:12:54 +00:00
tv_flags &= ~TV_CHDIR;
2019-02-04 16:39:48 +00:00
break;
case 't':
2020-01-15 16:12:54 +00:00
tv_flags |= TV_TRUST;
2019-02-04 16:39:48 +00:00
break;
case 'x':
2020-01-15 16:12:54 +00:00
tv_flags |= TV_LINUX;
break;
case 'd':
tv_flags |= TV_DVD;
2019-02-04 16:39:48 +00:00
break;
case 'l':
2020-01-15 21:23:11 +00:00
language = optional;
2019-02-04 16:39:48 +00:00
i++;
break;
case 'h':
printHelp();
return 1;
2020-01-15 16:12:54 +00:00
case '0':
2020-01-15 21:23:11 +00:00
return 2;
2019-02-04 16:39:48 +00:00
case '1':
pattern = optional;
i++;
break;
2019-06-04 19:54:00 +00:00
case 'a':
db_flags |= DB_ADD;
break;
case 'r':
db_flags |= DB_REFRESH;
break;
case 'u':
db_flags |= DB_UPDATE;
break;
case '2':
db_flags |= DB_CLEAN;
break;
case '3':
db_flags |= DB_REMOVE;
break;
case '4':
2020-01-15 16:12:54 +00:00
db_pattern = optional;
db_flags |= DB_PATTERN;
i++;
break;
2020-03-12 19:52:23 +00:00
case '5':
printPatternHelp();
return 1;
2019-02-04 16:39:48 +00:00
default:
return -1;
}
return 0;
}
#ifdef _WIN32
string getOptions( const char_t *option ) {
if ( option[0] != '-' )
return L"";
else if ( option[1] != '-' )
2019-02-04 16:39:48 +00:00
return option + 1;
else if ( !wcscmp( option, L"--show" ) )
2019-02-04 16:39:48 +00:00
return L"s";
else if ( !wcscmp( option, L"--season" ) )
return L"n";
else if ( !wcscmp( option, L"--correct-path" ) )
return L"c";
else if ( !wcscmp( option, L"--trust" ) )
return L"t";
else if ( !wcscmp( option, L"--linux" ) )
return L"x";
2020-01-15 16:12:54 +00:00
else if ( !wcscmp( option, L"--dvd" ) )
return L"d";
2019-02-04 16:39:48 +00:00
else if ( !wcscmp( option, L"--lang" ) )
return L"l";
2020-01-15 16:12:54 +00:00
else if ( !wcscmp( option, L"--help" ) )
return L"h";
2019-02-04 16:39:48 +00:00
else if ( !wcscmp( option, L"--print-langs" ) )
return L"0";
else if ( !wcscmp( option, L"--name-pattern" ) )
return L"1";
2019-06-04 19:54:00 +00:00
else if ( !wcscmp( option, L"--db-add" ) )
return L"a";
else if ( !wcscmp( option, L"--db-refresh" ) )
return L"r";
else if ( !wcscmp( option, L"--db-update" ) )
return L"u";
else if ( !wcscmp( option, L"--db-clean" ) )
return L"2";
else if ( !wcscmp( option, L"--db-remove" ) )
return L"3";
2020-01-15 16:12:54 +00:00
else if ( !wcscmp( option, L"--db-name-pattern" ) )
return L"4";
2020-03-12 19:52:23 +00:00
else if ( !wcscmp( option, L"--pattern-help" ) )
return L"5";
2019-02-04 16:39:48 +00:00
return L"";
}
// there's no getopt for windows, so just use wcscmp
int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
2020-01-15 16:12:54 +00:00
string &language, string &pattern, size_t &tv_flags,
size_t &db_flags, string &db_pattern, const int argc,
char_t **argv ) {
string options{};
char_t *optional;
2019-02-04 16:39:48 +00:00
for ( auto i = 1; i < argc; i++ ) {
options = getOptions( argv[i] );
if ( options == L"" ) {
options = L"p";
optional = argv[i];
} else {
optional = ( i < argc - 1 ) ? argv[i + 1] : nullptr;
}
2019-02-04 16:39:48 +00:00
for ( const auto &x : options ) {
2020-01-15 16:12:54 +00:00
auto res =
handleArgument( x, show, seasons_num, path, language, pattern,
tv_flags, db_flags, db_pattern, optional, i );
2019-02-04 16:39:48 +00:00
if ( res != 0 )
return res;
}
}
return 0;
}
#else
// parse command line arguments using getopt
int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
2020-01-15 16:12:54 +00:00
string &language, string &pattern, size_t &tv_flags,
size_t &db_flags, string &db_pattern, int argc,
char **argv ) {
2019-01-19 12:40:10 +00:00
static struct option long_options[] = {
2020-01-15 16:12:54 +00:00
{ "show", required_argument, 0, 's' },
{ "season", required_argument, 0, 'n' },
{ "correct-path", no_argument, 0, 'c' },
{ "trust", no_argument, 0, 't' },
{ "linux", no_argument, 0, 'x' },
{ "dvd", no_argument, 0, 'd' },
{ "lang", required_argument, 0, 'l' },
{ "help", no_argument, 0, 'h' },
{ "print-langs", no_argument, 0, '0' },
{ "name-pattern", required_argument, 0, '1' },
{ "db-add", no_argument, 0, 'a' },
{ "db-refresh", no_argument, 0, 'r' },
{ "db-update", no_argument, 0, 'u' },
{ "db-clean", no_argument, 0, '2' },
{ "db-remove", no_argument, 0, '3' },
{ "db-name-pattern", required_argument, 0, '4' },
2020-03-12 19:52:23 +00:00
{ "pattern-help", no_argument, 0, '5' },
2019-06-01 19:54:45 +00:00
{ 0, 0, 0, 0 }
2019-01-19 12:40:10 +00:00
};
2019-02-04 16:39:48 +00:00
int i{}; // this is useless, but needed for handleArgument
while ( 1 ) {
int option_index{ 0 };
2020-01-15 16:12:54 +00:00
auto c = getopt_long( argc, argv, "s:n:cp:txl:arudh", long_options,
2019-02-04 16:39:48 +00:00
&option_index );
if ( c == -1 )
2019-01-19 12:40:10 +00:00
break;
2020-01-15 16:12:54 +00:00
auto res =
handleArgument( c, show, seasons_num, path, language, pattern,
tv_flags, db_flags, db_pattern, optarg, i );
2019-02-04 16:39:48 +00:00
if ( res != 0 )
return res;
2019-01-19 12:40:10 +00:00
}
if ( optind < argc ) {
path = string( argv[optind] );
// if path provided, assume it's correct
2020-01-15 16:12:54 +00:00
tv_flags &= ~TV_CHDIR;
}
2019-01-19 12:40:10 +00:00
return 0;
}
2020-05-11 13:34:40 +00:00
#endif
void ensureShowNotEmpty( string &show, const string &path ) {
if ( show.empty() ) {
show = FSLib::canonical( path );
auto pos = show.find_last_of( dir_divider );
if ( pos != string::npos )
show = show.substr( ++pos );
cout << _( SHOW_CONFIRM ) << ' ' << show << std::endl;
string response;
cin >> response;
cin.ignore( 1, '\n' );
cin.clear();
if ( response[0] != 'y' && response[0] != 'Y' ) {
cout << _( SHOW_REENTER ) << std::endl;
std::getline( cin, show );
}
}
}
2019-02-04 16:39:48 +00:00
// windows needs wmain for unicode support
#ifdef _WIN32
int wmain
#else
int main
#endif
( int argc, char_t **argv ) {
#ifdef _WIN32
// set console to unicode
2020-04-15 08:53:37 +00:00
_setmode( _fileno( stdout ), _O_U8TEXT );
2020-03-12 19:52:23 +00:00
#else
if ( setlocale( LC_ALL, "" ) == NULL )
2020-04-12 19:11:14 +00:00
error( 1, errno, "%s", _( INVALID_LOCALE ).c_str() );
if ( FSLib::exists( "/usr/share/locale/en_US/LC_MESSAGES/tv_rename.mo" ) ) {
if ( bindtextdomain( DOMAIN, "/usr/share/locale" ) == NULL )
2020-04-12 19:11:14 +00:00
error( 1, errno, "%s", _( MEM_ALLOC_FAILED ).c_str() );
if ( textdomain( DOMAIN ) == NULL )
2020-04-12 19:11:14 +00:00
error( 1, errno, "%s", _( MEM_ALLOC_FAILED ).c_str() );
} else {
if ( bindtextdomain( DOMAIN, "./locale" ) == NULL )
2020-04-12 19:11:14 +00:00
error( 1, errno, "%s", _( MEM_ALLOC_FAILED ).c_str() );
if ( textdomain( DOMAIN ) == NULL )
2020-04-12 19:11:14 +00:00
error( 1, errno, "%s", _( MEM_ALLOC_FAILED ).c_str() );
}
2019-02-04 16:39:48 +00:00
#endif
string show{};
std::set< int > seasons_num{};
2020-01-15 16:12:54 +00:00
size_t tv_flags{};
2019-06-04 19:54:00 +00:00
size_t db_flags{};
2019-02-04 16:39:48 +00:00
string path{ TEXT( "." ) };
string language{ TEXT( "en" ) };
string pattern{ TEXT( "%filename - %epname" ) };
2019-06-04 19:54:00 +00:00
string db_pattern{};
2019-01-19 12:40:10 +00:00
2020-01-15 21:23:11 +00:00
auto ret = parseCommandLine( show, seasons_num, path, language, pattern,
tv_flags, db_flags, db_pattern, argc, argv );
if ( ret == -1 )
return 1;
else if ( ret == 1 )
return 0;
2020-01-15 16:12:54 +00:00
2020-01-15 21:23:11 +00:00
authenticate( API_KEY );
if ( ret == 2 ) {
printLangs();
return 0;
}
if ( !findLanguage( language.c_str() ) ) {
2020-03-12 19:52:23 +00:00
cerr << _( INVALID_CHOICE ) << std::endl;
2020-01-15 21:23:11 +00:00
printLangs();
return -1;
2019-01-19 12:40:10 +00:00
}
if ( !FSLib::isDirectory( path ) && FSLib::exists( path ) ) {
// specified file, not directory
2020-01-15 19:10:47 +00:00
std::map< int, string > file_set{};
size_t season_pos{}, ep_pos{};
if ( !searchSeason( path.c_str(), season_pos, ep_pos ) ) {
2020-03-12 19:52:23 +00:00
cerr << _( PATTERN_ERROR ) << std::endl;
return 1;
} else {
2020-01-15 19:10:47 +00:00
file_set[std::stoi( path.c_str() + ep_pos )] = path;
auto season = std::stoi( path.c_str() + season_pos );
2019-06-01 19:54:45 +00:00
singleSeason( path, show, season, TEXT( "" ), language, pattern,
2020-01-15 19:10:47 +00:00
tv_flags, &file_set, true );
return 0;
}
} else if ( !FSLib::isDirectory( path ) ) {
2020-01-15 16:12:54 +00:00
tv_flags |= TV_CHDIR;
}
2019-01-19 12:40:10 +00:00
2019-06-04 19:54:00 +00:00
if ( !db_pattern.empty() ) {
changeDBPattern( db_pattern );
}
if ( db_flags & DB_REMOVE && db_flags & DB_ADD ) {
2020-03-12 19:52:23 +00:00
cerr << _( ADD_REMOVE_FLAG_ERROR ) << std::endl;
2019-06-04 19:54:00 +00:00
return 1;
}
if ( db_flags & DB_REMOVE ) {
removeFromDB( FSLib::canonical( path ) );
}
if ( db_flags & DB_ADD ) {
ensureShowNotEmpty( show, path );
2020-01-15 21:23:11 +00:00
addToDB( show, path, language, tv_flags & TV_LINUX, tv_flags & TV_DVD );
2020-03-12 19:52:23 +00:00
cout << _( ADDED_TO_DB ) << std::endl;
2019-06-04 19:54:00 +00:00
}
if ( db_flags & DB_REFRESH ) {
2020-01-15 16:12:54 +00:00
refreshDB( tv_flags & TV_LINUX );
2020-03-12 19:52:23 +00:00
cout << _( REFRESHED_DB ) << std::endl;
2019-06-04 19:54:00 +00:00
}
if ( db_flags & DB_UPDATE ) {
2020-01-15 16:12:54 +00:00
updateDB( tv_flags & TV_LINUX );
2020-03-12 19:52:23 +00:00
cout << _( UPDATED_DB ) << std::endl;
2019-06-04 19:54:00 +00:00
}
if ( db_flags & DB_CLEAN ) {
cleanDB();
2020-03-12 19:52:23 +00:00
cout << _( CLEANED_DB ) << std::endl;
2019-06-04 19:54:00 +00:00
}
// if db operations happened don't continue
if ( db_flags )
return 0;
2020-01-15 16:12:54 +00:00
while ( tv_flags & TV_CHDIR ) {
2019-02-04 16:39:48 +00:00
if ( !FSLib::isDirectory( path ) ) {
2020-03-12 19:52:23 +00:00
cout << _( DIR_FAIL_REENTER ) << std::endl;
2019-02-04 16:39:48 +00:00
std::getline( cin, path );
2019-01-19 12:40:10 +00:00
continue;
}
2020-03-12 19:52:23 +00:00
cout << _( DIR_CONFIRM ) << ' ' << FSLib::canonical( path )
2019-02-04 16:39:48 +00:00
<< std::endl;
string response;
cin >> response;
cin.ignore( 1, '\n' );
cin.clear();
2019-01-19 12:40:10 +00:00
if ( response[0] == 'y' || response[0] == 'Y' ) {
2020-01-15 16:12:54 +00:00
tv_flags &= ~TV_CHDIR;
2019-01-19 12:40:10 +00:00
} else {
2020-03-12 19:52:23 +00:00
cout << _( DIR_REENTER ) << std::endl;
2019-02-04 16:39:48 +00:00
std::getline( cin, path );
2019-01-19 12:40:10 +00:00
}
}
ensureShowNotEmpty( show, path );
2019-01-19 12:40:10 +00:00
2019-02-04 16:39:48 +00:00
if ( seasons_num.size() == 1 ) {
singleSeason( path, show, *seasons_num.begin(), string(), language,
2020-01-15 16:12:54 +00:00
pattern, tv_flags, nullptr, true );
2019-01-19 12:40:10 +00:00
} else if ( seasons_num.size() != 0 ) {
2020-01-15 16:12:54 +00:00
multipleSeasons( path, show, seasons_num, language, pattern, tv_flags );
2019-01-19 12:40:10 +00:00
} else {
2020-01-15 16:12:54 +00:00
allSeasons( path, show, language, pattern, tv_flags );
2019-01-19 12:40:10 +00:00
}
}