#include #include #include #include #include #include #include "filesystem.hpp" #include "functions.hpp" #include "mainwindow.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_get_names.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 = 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 ); delete 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 Gtk::Dialog dialog( "Rename confirmation", *this ); auto content = dialog.get_content_area(); Gtk::TextView tx; content->pack_start( tx ); tx.set_editable( false ); dialog.add_button( "_No", Gtk::RESPONSE_CANCEL ); dialog.add_button( "_Yes", Gtk::RESPONSE_OK ); 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( const Glib::RefPtr< Gtk::Application > &ptr ) : app( ptr ) { set_title( "TV Rename" ); set_default_size( 400, 310 ); 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"; } } add( m_layout ); // set widgets' location m_layout.put( m_label_show, 5, 5 ); m_layout.put( m_label_language, 190, 5 ); m_layout.put( m_entry_show, 5, 25 ); m_layout.put( m_combo_language, 190, 25 ); m_layout.put( m_label_dir, 5, 60 ); m_layout.put( m_entry_dir, 5, 80 ); m_layout.put( m_button_dir, 190, 80 ); m_layout.put( m_label_pattern, 5, 115 ); m_layout.put( m_entry_pattern, 5, 135 ); m_layout.put( m_button_pattern, 190, 135 ); m_layout.put( m_check_linux, 95, 169 ); m_layout.put( m_button_process, 5, 173 ); m_layout.put( m_check_trust, 95, 187 ); m_layout.put( m_label_possible, 5, 210 ); m_layout.put( m_combo_possible, 5, 230 ); m_layout.put( m_button_get_names, 5, 265 ); m_layout.put( m_button_quit, 315, 275 ); // set button texts m_button_process.set_label( "Process" ); m_button_get_names.set_label( "Get names" ); 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_get_names.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_get_names.signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::getNames ) ); 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 m_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(); }