#include #include #include #include #include #include #include #include "filesystem.hpp" #include "functions.hpp" #include "mainwindow.hpp" #include "databasewindow.hpp" #include "tv_rename.hpp" constexpr std::array< const char *, 46 > languages{ "en", "English", "sv", "Svenska", "no", "Norsk", "da", "Dansk", "fi", "Suomeksi", "nl", "Nederlands", "de", "Deutsch", "it", "Italiano", "es", "Español", "fr", "Français", "pl", "Polski", "hu", "Magyar", "el", "Greek", "tr", "Turkish", "ru", "Russian", "he", "Hebrew", "ja", "Japanese", "pt", "Portuguese", "zh", "Chinese", "cs", "Czech", "sl", "Slovenian", "hr", "Croatian", "ko", "Korea" }; void MainWindow::chooseFile() { // create a dialog for choosing directory Gtk::FileChooserDialog dialog( "Select a directory", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER ); dialog.set_transient_for( *this ); // add cancel and select buttons dialog.add_button( "_Cancel", Gtk::RESPONSE_CANCEL ); dialog.add_button( "Select", Gtk::RESPONSE_OK ); auto result = dialog.run(); switch ( result ) { case Gtk::RESPONSE_OK: m_entry_dir->set_text( dialog.get_filename() ); std::cout << dialog.get_filename() << std::endl; break; case Gtk::RESPONSE_CANCEL: std::cout << "Canceled" << std::endl; break; default: std::cout << "Closed dialog" << std::endl; break; } } void MainWindow::quit() { std::cout << "Quitting" << std::endl; hide(); } void MainWindow::patternHelp() { Gtk::MessageDialog dialog( *this, "Pattern escape sequences" ); dialog.set_secondary_text( "%filename - original filename (without type extension)\n" "%show - show name from thetvdb\n" "%epname - episode name from thetvdb\n" "%season - season number\n" "%episode - episode number\n" "Both season number and episode number can be padded with zeros, just " "add width of padding" " right after %, like this: %2season.\n" "Default pattern is \"%filename - %epname\", you might want to change " "this to" " \"S%2seasonE%2episode - %epname\" or \"%show - S%2seasonE%2episode - " "%epname\"" ); dialog.run(); } void MainWindow::process() { // check required field is filled out if ( m_entry_show->get_text().empty() ) { Gtk::MessageDialog dialog( *this, "Show field is empty" ); dialog.run(); return; } // language code language_code = ( *m_combo_language->get_active() )[m_columns_language.m_col_code]; // fill up m_combo_possible with possible tv shows auto possible_shows = getPossibleShows( std::string( m_entry_show->get_text() ), language_code, c ); // if no possible shows were found, tell the user if ( possible_shows.size() == 0 ) { Gtk::MessageDialog dialog( *this, "No results found for given show name" ); dialog.run(); return; } // show widgets m_label_possible->show(); m_button_rename->show(); m_button_db_add->show(); m_combo_possible->show(); // fill up combo box with results from thetvdb auto model = Gtk::ListStore::create( m_columns_url ); m_combo_possible->set_model( model ); auto row = *( model->append() ); row[m_columns_url.m_col_show] = possible_shows[0].first; row[m_columns_url.m_col_url] = possible_shows[0].second; m_combo_possible->set_active( row ); for ( size_t i = 1; i < possible_shows.size(); i++ ) { auto row = *( model->append() ); row[m_columns_url.m_col_show] = possible_shows[i].first; row[m_columns_url.m_col_url] = possible_shows[i].second; } } void MainWindow::getNames() { // check required field is filled out if ( m_entry_dir->get_text().empty() ) { Gtk::MessageDialog dialog( *this, "Directory field is empty" ); dialog.run(); return; } // check directory exists if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) { Gtk::MessageDialog dialog( *this, "Directory doesn't exist" ); dialog.run(); return; } path = m_entry_dir->get_text(); selected.clear(); files.clear(); std::vector< int > options; // get all files in path and seperate them in map `files` by season iterateFS( files, path ); for ( auto &x : files ) { options.push_back( x.first ); } // create a window with possible seasons to rename // store selected seasons in `selected` sw.reset( new SeasonWindow( options, selected ) ); sw->signal_hide().connect( sigc::mem_fun( *this, &MainWindow::finishedSelection ) ); app->add_window( *sw ); sw->show(); } /* change names of original files to generated new names * orig - original filenames * renamed - renamed filenames (sorted in the same order as `orig`) */ void renameFiles( const std::vector< std::pair< std::string, std::pair< std::string, std::string > > > &renamed ) { for ( auto renamed_it = renamed.begin(); renamed_it != renamed.end(); ++renamed_it ) { std::cout << renamed_it->first << "/" << renamed_it->second.first << " --> " << renamed_it->first << "/" << renamed_it->second.second << std::endl; FSLib::rename( renamed_it->first + "/" + renamed_it->second.first, renamed_it->first + "/" + renamed_it->second.second ); } } void MainWindow::finishedSelection() { // remove created SeasonWindow and delete it from memory app->remove_window( *sw ); auto iter = m_combo_possible->get_active(); // debug output std::cout << ( *iter )[m_columns_url.m_col_show] << " " << language_code << std::endl; std::string url = static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_url] ); // shouldn't ever happen, but just to be sure if ( url.empty() ) return; std::cout << "https://www.thetvdb.com" << url << std::endl; std::string input_pattern = m_entry_pattern->get_text(); // store pattern to cache if it's different from default if ( input_pattern != default_pattern ) { std::ofstream file( userHome() + "/.cache/tv_rename_pattern" ); if ( file ) { file << input_pattern; } } for ( auto &x : selected ) { // get renamed files for given season auto renamed_files = getRenamedFiles( static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_show] ), x, "https://www.thetvdb.com" + url, language_code, ( input_pattern.empty() ? default_pattern : input_pattern ), !m_check_linux->get_active(), c, files[x] ); if ( renamed_files.empty() ) continue; // if trust checkbox is ticked, rename files if ( m_check_trust->get_active() ) { renameFiles( renamed_files ); continue; } // create a custom dialog box with textview of new episode names std::unique_ptr dialog( new Gtk::Dialog( "Rename confirmation", *this ) ); dialog->set_default_size( 550, 350 ); dialog->set_resizable( false ); auto content = dialog->get_content_area(); std::unique_ptr sw( new Gtk::ScrolledWindow ); std::unique_ptr tx( new Gtk::TextView ); content->pack_start( *sw ); sw->add( *tx ); tx->set_editable( false ); tx->set_cursor_visible( false ); dialog->add_button( "_No", Gtk::RESPONSE_CANCEL ); dialog->add_button( "_Yes", Gtk::RESPONSE_OK ); sw->show(); tx->show(); auto buff = tx->get_buffer(); buff->place_cursor( buff->begin() ); buff->insert_at_cursor( renamed_files[0].second.first.c_str() ); buff->insert_at_cursor( " --> " ); buff->insert_at_cursor( renamed_files[0].second.second.c_str() ); for ( size_t i = 1; i < renamed_files.size(); i++ ) { buff->insert_at_cursor( "\n" ); buff->insert_at_cursor( renamed_files[i].second.first.c_str() ); buff->insert_at_cursor( " --> " ); buff->insert_at_cursor( renamed_files[i].second.second.c_str() ); } auto response = dialog->run(); // if user clicked "Yes" in dialog, rename files switch ( response ) { case Gtk::RESPONSE_OK: renameFiles( renamed_files ); default: break; } } } MainWindow::~MainWindow() { auto children = get_children(); size_t max = children.size(); size_t index{}; while( index < max ) { if( auto *p = dynamic_cast(children[index]) ) { auto temp = p->get_children(); children.insert( children.end(), temp.begin(), temp.end() ); max = children.size(); } index++; } std::cout << children.size() << std::endl; for( int i = max - 1; i >= 0; i-- ) { delete children[i]; } } MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr ) : app( ptr ) { set_title( "TV Rename" ); set_default_size( 400, 345 ); set_resizable( false ); { // if cached pattern exists, load that instead of default std::ifstream file( userHome() + "/.cache/tv_rename_pattern" ); if ( file ) { std::getline( file, default_pattern ); } else { default_pattern = "%filename - %epname"; } } auto *box = new Gtk::Box(Gtk::ORIENTATION_VERTICAL); auto *menu = new Gtk::MenuBar(); auto *layout = new Gtk::Layout(); add( *box ); box->pack_start(*menu, false, false); box->pack_start(*layout, true, true); auto *item = new Gtk::MenuItem(); auto *submenu = new Gtk::Menu(); menu->append(*item); // File menu item->set_label("File"); item->set_submenu(*submenu); // Exit item for File menu item = new Gtk::MenuItem(); item->set_label("Exit"); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::quit ) ); submenu->append(*item); // Database menu item = new Gtk::MenuItem(); submenu = new Gtk::Menu(); item->set_label("Database"); item->set_submenu(*submenu); menu->append(*item); // Update database item = new Gtk::MenuItem(); item->set_label("Update database"); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbUpdate ) ); submenu->append(*item); // Refresh database item = new Gtk::MenuItem(); item->set_label("Refresh database"); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbRefresh ) ); submenu->append(*item); // Clean database item = new Gtk::MenuItem(); item->set_label("Clean database"); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbClean ) ); submenu->append(*item); // Manage database item = new Gtk::MenuItem(); item->set_label("Manage database"); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbManage ) ); submenu->append(*item); // set widgets' location layout->put( *m_label_show, 5, 5 ); layout->put( *m_label_language, 190, 5 ); layout->put( *m_entry_show, 5, 25 ); layout->put( *m_combo_language, 190, 25 ); layout->put( *m_label_dir, 5, 60 ); layout->put( *m_entry_dir, 5, 80 ); layout->put( *m_button_dir, 190, 80 ); layout->put( *m_label_pattern, 5, 115 ); layout->put( *m_entry_pattern, 5, 135 ); layout->put( *m_button_pattern, 190, 135 ); layout->put( *m_check_linux, 95, 169 ); layout->put( *m_button_process, 5, 173 ); layout->put( *m_check_trust, 95, 187 ); layout->put( *m_label_possible, 5, 210 ); layout->put( *m_combo_possible, 5, 230 ); layout->put( *m_button_rename, 5, 270 ); layout->put( *m_button_db_add, 95, 270 ); layout->put( *m_button_quit, 315, 280 ); // set button texts m_button_process->set_label( "Process" ); m_button_rename->set_label( "Rename" ); m_button_db_add->set_label( "Add to database" ); m_button_quit->set_label( "Quit" ); m_button_dir->set_label( "Choose directory" ); m_button_pattern->set_label( "Pattern help" ); m_check_linux->set_label( "Replace windows-illegal characters" ); m_check_trust->set_label( "Don't ask for rename confirmation" ); // set label texts m_label_show->set_label( "Show:" ); m_label_language->set_label( "Language:" ); m_label_possible->set_label( "Possible shows:" ); m_label_dir->set_label( "Directory:" ); m_label_pattern->set_label( "Pattern:" ); // set dimensions m_combo_language->set_size_request( 120 ); m_combo_possible->set_size_request( 200 ); m_entry_show->set_size_request( 170, 30 ); m_entry_dir->set_size_request( 170, 30 ); m_button_dir->set_size_request( 80, 30 ); m_button_quit->set_size_request( 80, 30 ); m_button_process->set_size_request( 80, 30 ); m_button_rename->set_size_request( 80, 30 ); m_button_db_add->set_size_request( 80, 30 ); // set default pattern m_entry_pattern->set_text( default_pattern ); // put languages in combo box { auto model = Gtk::ListStore::create( m_columns_language ); m_combo_language->set_model( model ); auto row = *( model->append() ); row[m_columns_language.m_col_code] = "en"; row[m_columns_language.m_col_language] = "English"; m_combo_language->set_active( row ); for ( size_t i = 2; i < languages.size(); i += 2 ) { row = *( model->append() ); row[m_columns_language.m_col_code] = languages[i]; row[m_columns_language.m_col_language] = languages[i + 1]; } } // set column to be shown in comboboxes m_combo_language->pack_start( m_columns_language.m_col_language ); m_combo_possible->pack_start( m_columns_url.m_col_show ); // set signals m_button_dir->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::chooseFile ) ); m_button_quit->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::quit ) ); m_button_process->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::process ) ); m_button_rename->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::getNames ) ); m_button_db_add->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::dbAdd ) ); m_entry_show->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::process ) ); m_entry_dir->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::process ) ); m_button_pattern->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::patternHelp ) ); // show everything except possible shows and items related to them box->show(); menu->show(); menu->show_all_children(); layout->show(); m_label_show->show(); m_label_language->show(); m_entry_show->show(); m_entry_dir->show(); m_combo_language->show(); m_button_process->show(); m_button_quit->show(); m_button_dir->show(); m_check_linux->show(); m_check_linux->set_active( true ); m_check_trust->show(); m_button_pattern->show(); m_entry_pattern->show(); m_label_pattern->show(); m_label_dir->show(); } void MainWindow::dbUpdate() { updateDB( !m_check_linux->get_active(), c ); } void MainWindow::dbClean() { cleanDB(); } void MainWindow::dbRefresh() { refreshDB( !m_check_linux->get_active(), c ); } void MainWindow::dbAdd() { // check required field is filled out if ( m_entry_dir->get_text().empty() ) { Gtk::MessageDialog dialog( *this, "Directory field is empty" ); dialog.run(); return; } // check directory exists if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) { Gtk::MessageDialog dialog( *this, "Directory doesn't exist" ); dialog.run(); return; } auto iter = m_combo_possible->get_active(); std::string input_pattern = m_entry_pattern->get_text(); std::string show = static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_show] ); std::string language_code = static_cast< Glib::ustring >( ( *m_combo_language->get_active() )[m_columns_language.m_col_code] ); std::string url = "https://www.thetvdb.com" + static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_url] ); std::cout << show << " " << language_code << " " << url << std::endl; addToDB( show, m_entry_dir->get_text(), language_code, url, m_entry_pattern->get_text(), !m_check_linux->get_active(), c ); } void MainWindow::dbManage() { auto *dbWindow = new DatabaseWindow( !m_check_linux->get_active(), c ); app->add_window( *dbWindow ); auto app_ptr = app; dbWindow->signal_hide().connect( [dbWindow, app_ptr](){ app_ptr->remove_window( *dbWindow ); delete dbWindow; }); dbWindow->show(); }