2019-01-23 19:46:03 +00:00
|
|
|
#include "mainwindow.hpp"
|
|
|
|
#include "functions.hpp"
|
|
|
|
#include "filesystem.hpp"
|
|
|
|
#include "tv_rename.hpp"
|
|
|
|
#include <gtkmm/liststore.h>
|
|
|
|
#include <gtkmm/filechooserdialog.h>
|
|
|
|
#include <gtkmm/messagedialog.h>
|
|
|
|
#include <gtkmm/textview.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
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 fields are 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 fields are 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`)
|
|
|
|
*/
|
2019-01-23 21:57:52 +00:00
|
|
|
void renameFiles(const std::vector<std::pair<std::string, std::pair<std::string, std::string>>> &renamed) {
|
2019-01-23 19:46:03 +00:00
|
|
|
for(auto renamed_it = renamed.begin(); renamed_it != renamed.end(); ++renamed_it) {
|
2019-01-23 21:57:52 +00:00
|
|
|
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);
|
2019-01-23 19:46:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-01-23 21:57:52 +00:00
|
|
|
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;
|
2019-01-23 19:46:03 +00:00
|
|
|
|
|
|
|
std::string input_pattern = m_entry_pattern.get_text();
|
|
|
|
|
|
|
|
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 = singleSeason( static_cast<Glib::ustring>((*iter)[m_columns_url.m_col_show]), x,
|
2019-01-23 21:57:52 +00:00
|
|
|
"https://www.thetvdb.com" + url,
|
2019-01-23 19:46:03 +00:00
|
|
|
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() ) {
|
2019-01-23 21:57:52 +00:00
|
|
|
renameFiles(renamed_files);
|
2019-01-23 19:46:03 +00:00
|
|
|
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());
|
2019-01-23 21:57:52 +00:00
|
|
|
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());
|
2019-01-23 19:46:03 +00:00
|
|
|
|
|
|
|
for( size_t i = 1; i < renamed_files.size(); i++ ) {
|
|
|
|
buff->insert_at_cursor("\n");
|
2019-01-23 21:57:52 +00:00
|
|
|
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());
|
2019-01-23 19:46:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto response = dialog.run();
|
|
|
|
|
|
|
|
// if user clicked "Yes" in dialog, rename files
|
|
|
|
switch(response) {
|
|
|
|
case Gtk::RESPONSE_OK:
|
2019-01-23 21:57:52 +00:00
|
|
|
renameFiles(renamed_files);
|
2019-01-23 19:46:03 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::MainWindow(const Glib::RefPtr<Gtk::Application> &ptr) : app(ptr) {
|
|
|
|
set_title("TV Rename");
|
|
|
|
|
|
|
|
set_default_size(400, 310);
|
|
|
|
set_resizable(false);
|
|
|
|
|
|
|
|
{
|
|
|
|
std::ifstream file(userHome() + "/.cache/tv_rename_pattern");
|
|
|
|
if( file ) {
|
|
|
|
std::getline( file, default_pattern );
|
|
|
|
} else {
|
|
|
|
default_pattern = "%filename - %epname";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
add(m_layout);
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|