From c8e93aa3cc28ec59a2f7a52c2fc3000fe74f0c00 Mon Sep 17 00:00:00 2001 From: zvon Date: Wed, 23 Jan 2019 20:46:03 +0100 Subject: [PATCH] Add option to compile with GUI --- .gitignore | 1 + Makefile | 12 ++ filesystem.cpp | 16 ++- filesystem.hpp | 4 +- functions.cpp | 108 +++++++++++---- functions.hpp | 22 ++- gui.cpp | 11 ++ mainwindow.cpp | 343 +++++++++++++++++++++++++++++++++++++++++++++++ mainwindow.hpp | 91 +++++++++++++ seasonwindow.cpp | 73 ++++++++++ seasonwindow.hpp | 34 +++++ tv_rename.cpp | 48 ++++++- tv_rename.hpp | 10 ++ 13 files changed, 736 insertions(+), 37 deletions(-) create mode 100644 gui.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.hpp create mode 100644 seasonwindow.cpp create mode 100644 seasonwindow.hpp diff --git a/.gitignore b/.gitignore index b419975..f7176b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o tv_rename +tv_rename_gui test diff --git a/Makefile b/Makefile index aae4cb4..2b5cd0a 100644 --- a/Makefile +++ b/Makefile @@ -24,3 +24,15 @@ network.o: network.cpp tv_rename.o: tv_rename.cpp $(CXX) $(CFLAGS) -c tv_rename.cpp +gui: gui.cpp mainwindow.cpp seasonwindow.cpp network.o functions_gui.o filesystem_gui.o tv_rename_gui.o + $(CXX) $(CFLAGS) -o tv_rename_gui gui.cpp mainwindow.cpp seasonwindow.cpp network.o functions_gui.o filesystem_gui.o tv_rename_gui.o `pkg-config gtkmm-3.0 --cflags --libs` -lcurl -DGUI + +filesystem_gui.o: filesystem.cpp + $(CXX) $(CFLAGS) -c filesystem.cpp -o filesystem_gui.o -DGUI + +functions_gui.o: functions.cpp + $(CXX) $(CFLAGS) -c functions.cpp -o functions_gui.o -DGUI + +tv_rename_gui.o: tv_rename.cpp + $(CXX) $(CFLAGS) -c tv_rename.cpp -o tv_rename_gui.o -DGUI + diff --git a/filesystem.cpp b/filesystem.cpp index abca16b..cd37e38 100644 --- a/filesystem.cpp +++ b/filesystem.cpp @@ -3,17 +3,13 @@ #include #include +#ifndef GUI + bool FSLib::exists(const std::string &path) { struct stat path_stat; return stat( path.c_str(), &path_stat ) == 0; } -bool FSLib::isDirectory(const std::string &path) { - struct stat path_stat; - stat( path.c_str(), &path_stat ); - return S_ISDIR(path_stat.st_mode); -} - std::string FSLib::canonical(const std::string &path) { char *canonical_path = static_cast(malloc(PATH_MAX)); if( realpath(path.c_str(), canonical_path) == nullptr ) { @@ -25,6 +21,14 @@ std::string FSLib::canonical(const std::string &path) { return canonical_string; } +#endif + +bool FSLib::isDirectory(const std::string &path) { + struct stat path_stat; + stat( path.c_str(), &path_stat ); + return S_ISDIR(path_stat.st_mode); +} + bool FSLib::rename(const std::string &file_a, const std::string &file_b) { return ::rename( file_a.c_str(), file_b.c_str() ) == 0; } diff --git a/filesystem.hpp b/filesystem.hpp index 16a58a4..4cac677 100644 --- a/filesystem.hpp +++ b/filesystem.hpp @@ -6,9 +6,11 @@ #include namespace FSLib{ +#ifndef GUI bool exists(const std::string &path); - bool isDirectory(const std::string &path); std::string canonical(const std::string &path); +#endif + bool isDirectory(const std::string &path); bool rename(const std::string &file_a, const std::string &file_b); class Directory { diff --git a/functions.cpp b/functions.cpp index 87f3f25..2ac5120 100644 --- a/functions.cpp +++ b/functions.cpp @@ -2,12 +2,24 @@ #include "filesystem.hpp" #include #include +#include + +#ifndef GUI + #include #include #include -#include #include +#else + +#include +#include + +#endif + +#ifndef GUI + constexpr std::array languages{ "en", "English", "sv", "Svenska", "no", "Norsk", "da", "Dansk", "fi", "Suomeksi", "nl", "Nederlands", "de", "Deutsch", "it", "Italiano", "es", "Español", "fr", "Français", @@ -16,6 +28,25 @@ constexpr std::array languages{ "sl", "Slovenian", "hr", "Croatian", "ko","Korea" }; +#endif + +// encode url so it's valid even with UTF-8 characters +std::string encodeUrl( const std::string &url ) { + //stolen from here - https://stackoverflow.com/questions/154536/encode-decode-urls-in-c + std::ostringstream encoded; + encoded.fill('0'); + encoded << std::hex; + for( auto &x : url ) { + if( std::isalnum(x) || x == '-' || x == '_' || x == '.' || x == '~' ) { + encoded << x; + continue; + } + encoded << std::uppercase << '%' << std::setw(2); + encoded << int(static_cast(x)) << std::nouppercase; + } + return encoded.str(); +} + // return true if filename has specified season // set ep_pos to position where episode number starts bool searchSpecificSeason(const char *const p, size_t &ep_pos, const std::string &number) { @@ -72,6 +103,21 @@ bool searchSeason(const char *const p) { return searchSeason(p, tmp); } +void iterateFS(std::map> &seasons, const std::string &path) { + size_t season_pos{std::string::npos}; // season_pos - position of first digit of the season + for( const auto p: FSLib::Directory(path) ) { + if(FSLib::isDirectory(path + "/" + p)) { + iterateFS(seasons, path + "/" + p); + continue; + } + + if( searchSeason(p, season_pos) ) + seasons[atoi(p+season_pos)].insert(path + "/" + p); + } +} + +#ifndef GUI + void findSeason(std::set &files, int season, const std::string &path) { auto number = std::to_string(season); @@ -102,19 +148,6 @@ void findSeasons(std::map> &seasons, const std::strin } } -void iterateFS(std::map> &seasons, const std::string &path) { - size_t season_pos{std::string::npos}; // season_pos - position of first digit of the season - for( const auto p: FSLib::Directory(path) ) { - if(FSLib::isDirectory(path + "/" + p)) { - iterateFS(seasons, path + "/" + p); - continue; - } - - if( searchSeason(p, season_pos) ) - seasons[atoi(p+season_pos)].insert(path + "/" + p); - } -} - std::string getDefUrl( std::string show, const std::string &language, Curl &c ) { std::replace(show.begin(), show.end(), ' ', '+'); auto source_code = c.execute("https://www.thetvdb.com/search?q=" + encodeUrl(show) + "&l=" + language); @@ -205,22 +238,45 @@ bool findLanguage( const char *language ) { return false; } -std::string encodeUrl( const std::string &url ) { - //stolen from here - https://stackoverflow.com/questions/154536/encode-decode-urls-in-c - std::ostringstream encoded; - encoded.fill('0'); - encoded << std::hex; - for( auto &x : url ) { - if( std::isalnum(x) || x == '-' || x == '_' || x == '.' || x == '~' ) { - encoded << x; - continue; +#else + +std::vector> getPossibleShows( std::string show, const std::string &language, Curl &c ) { + std::replace(show.begin(), show.end(), ' ', '+'); + auto source_code = c.execute("https://www.thetvdb.com/search?q=" + encodeUrl(show) + "&l=" + language); + size_t pos{}; + std::vector> urls; + while( true ) { + pos = source_code.find("/ser", pos); + if( pos != std::string::npos ) { + auto end = source_code.find(">", pos); + auto end2 = source_code.find("<", end+1); + end--; + urls.emplace_back(source_code.substr(end+2, end2 - (end+2)), source_code.substr(pos, end - pos)); + pos = end + 2; + } else { + break; } - encoded << std::uppercase << '%' << std::setw(2); - encoded << int(static_cast(x)) << std::nouppercase; } - return encoded.str(); + return urls; } +std::string userHome() { + uid_t user_uid; + { + uid_t eid; + uid_t sid; + getresuid( &user_uid, &eid, &sid ); // don't need eid and sid + } + 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 + std::string compilePattern(const std::string &pattern, int season, int episode, const std::string &filename, const std::string &episodeName, const std::string &showName) { std::string output; diff --git a/functions.hpp b/functions.hpp index 0c55d4b..8df1f5f 100644 --- a/functions.hpp +++ b/functions.hpp @@ -6,13 +6,19 @@ #include #include "network.hpp" +#ifdef GUI + +#include + +#endif + +#ifndef GUI + std::string getDefUrl( std::string show, const std::string &language, Curl &c ); void findSeason(std::set &files, int season, const std::string &path); void findSeasons(std::map> &seasons, const std::string &path, const std::set &season_numbers); -void iterateFS(std::map> &seasons, const std::string &path); void printHelp(); -bool searchSpecificSeason(const char *const p, size_t &ep_pos, const std::string &number); bool searchSpecificSeason(const char *const p, const std::string &number); bool searchSeason(const char *const p, size_t &season_pos); bool searchSeason(const char *const p); @@ -22,6 +28,18 @@ void printLangs(); bool findLanguage( const char *language ); std::string encodeUrl( const std::string &url ); + +#else + +std::vector> getPossibleShows( std::string show, const std::string &language, Curl &c ); +std::string userHome(); + +#endif + +void iterateFS(std::map> &seasons, const std::string &path); + +bool searchSpecificSeason(const char *const p, size_t &ep_pos, const std::string &number); + std::string compilePattern(const std::string &pattern, int season, int episode, const std::string &filename, const std::string &episodeName, const std::string &showName); #endif diff --git a/gui.cpp b/gui.cpp new file mode 100644 index 0000000..bc8603c --- /dev/null +++ b/gui.cpp @@ -0,0 +1,11 @@ +#include + +#include "mainwindow.hpp" + +int main(int argc, char **argv) { + auto app = Gtk::Application::create(argc, argv, "org.idonthaveanorganization.tvrename"); + + MainWindow mw(app); + + return app->run(mw); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..1f8f7f7 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,343 @@ +#include "mainwindow.hpp" +#include "functions.hpp" +#include "filesystem.hpp" +#include "tv_rename.hpp" +#include +#include +#include +#include +#include +#include + +constexpr std::array 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 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::set &orig, const std::vector> &renamed) { + auto orig_it = orig.begin(); + + for(auto renamed_it = renamed.begin(); renamed_it != renamed.end(); ++renamed_it) { + std::cout << *orig_it << " --> " << renamed_it->first + "/" + renamed_it->second << std::endl; + FSLib::rename(*orig_it, renamed_it->first + "/" + renamed_it->second); + ++orig_it; + } +} + +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::cout << "https://www.thetvdb.com" << static_cast((*iter)[m_columns_url.m_col_url]) << std::endl; + + 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((*iter)[m_columns_url.m_col_show]), x, + "https://www.thetvdb.com" + static_cast((*iter)[m_columns_url.m_col_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(files[x], 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.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.c_str()); + } + + auto response = dialog.run(); + + // if user clicked "Yes" in dialog, rename files + switch(response) { + case Gtk::RESPONSE_OK: + renameFiles(files[x], renamed_files); + default: + break; + } + } +} + +MainWindow::MainWindow(const Glib::RefPtr &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(); +} + diff --git a/mainwindow.hpp b/mainwindow.hpp new file mode 100644 index 0000000..bb9c827 --- /dev/null +++ b/mainwindow.hpp @@ -0,0 +1,91 @@ +#ifndef GTKMM_MAIN_WINDOW +#define GTKMM_MAIN_WINDOW + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "network.hpp" +#include "seasonwindow.hpp" + +class MainWindow : public Gtk::Window { +public: + MainWindow(const Glib::RefPtr &ptr); + virtual ~MainWindow() = default; + +private: + void quit(); + void process(); + void getNames(); + void finishedSelection(); + void chooseFile(); + void patternHelp(); + +protected: + Gtk::Button m_button_dir; + Gtk::Button m_button_get_names; + Gtk::Button m_button_quit; + Gtk::Button m_button_process; + Gtk::Button m_button_pattern; + + Gtk::CheckButton m_check_linux; + Gtk::CheckButton m_check_trust; + + Gtk::ComboBox m_combo_language; + Gtk::ComboBox m_combo_possible; + + Gtk::Entry m_entry_show; + Gtk::Entry m_entry_dir; + Gtk::Entry m_entry_pattern; + + Gtk::Label m_label_language; + Gtk::Label m_label_possible; + Gtk::Label m_label_show; + Gtk::Label m_label_dir; + Gtk::Label m_label_pattern; + + Gtk::Layout m_layout; + + Curl c; + + class LanguageColumns : public Gtk::TreeModel::ColumnRecord { + public: + + LanguageColumns() { + add(m_col_code); + add(m_col_language); + } + + Gtk::TreeModelColumn m_col_code; + Gtk::TreeModelColumn m_col_language; + }; + + class UrlColumns: public Gtk::TreeModel::ColumnRecord { + public: + UrlColumns() { + add(m_col_url); + add(m_col_show); + } + Gtk::TreeModelColumn m_col_url; + Gtk::TreeModelColumn m_col_show; + }; + + LanguageColumns m_columns_language; + UrlColumns m_columns_url; + + Glib::RefPtr app; + + SeasonWindow *sw; + std::vector selected; + std::map> files; + std::string path; + std::string language_code; + std::string default_pattern; +}; + +#endif // GTKMM_EXAMPLE_HELLOWORLD_H diff --git a/seasonwindow.cpp b/seasonwindow.cpp new file mode 100644 index 0000000..27e39b8 --- /dev/null +++ b/seasonwindow.cpp @@ -0,0 +1,73 @@ +#include +#include "seasonwindow.hpp" + +void SeasonWindow::confirm() { + // go through all checkbuttons and save numbers + // of checked boxes into returned vector + // then quit + for( auto &x : m_checks ) { + if( x.get_active() ) { + returned.push_back(std::stoi(x.get_label())); + } + } + hide(); +} + +void SeasonWindow::select_all() { + // set all check boxes to checked + for( auto &x : m_checks ) { + x.set_active(true); + } +} + +void SeasonWindow::select_none() { + // set all check boxes to unchecked + for( auto &x : m_checks ) { + x.set_active(false); + } +} + +SeasonWindow::SeasonWindow(const std::vector &seasons, std::vector &_returned) : returned(_returned) { + set_title("Choose seasons"); + + set_default_size(250, 250); + set_resizable(false); + + add(m_layout); + + size_t x{5}, y{25}; + + // create a check box for each season + for( auto &s : seasons ) { + m_checks.emplace_back(std::to_string(s)); + m_layout.put(m_checks.back(), x, y); + + if( x == 185 ) { + x = 5; + y += 20; + } else { + x += 60; + } + } + + m_layout.put(m_label, 5, 5); + m_label.set_label("Select seasons:"); + + m_layout.put(m_confirm, 165, 215); + m_layout.put(m_all, 130, 175); + m_layout.put(m_none, 5, 175); + + m_confirm.set_size_request(80,30); + m_all.set_size_request(80,30); + m_none.set_size_request(80,30); + + m_confirm.set_label("Select"); + m_all.set_label("Select All"); + m_none.set_label("Unselect All"); + + m_confirm.signal_clicked().connect(sigc::mem_fun(*this, &SeasonWindow::confirm)); + m_all.signal_clicked().connect(sigc::mem_fun(*this, &SeasonWindow::select_all)); + m_none.signal_clicked().connect(sigc::mem_fun(*this, &SeasonWindow::select_none)); + + show_all_children(); +} diff --git a/seasonwindow.hpp b/seasonwindow.hpp new file mode 100644 index 0000000..df98244 --- /dev/null +++ b/seasonwindow.hpp @@ -0,0 +1,34 @@ +#ifndef GTKMM_SEASON_WINDOW +#define GTKMM_SEASON_WINDOW + +#include +#include +#include +#include +#include + +class SeasonWindow : public Gtk::Window { +public: + SeasonWindow(const std::vector &seasons, std::vector &_returned); + virtual ~SeasonWindow() = default; + +private: + void confirm(); + void select_all(); + void select_none(); + +protected: + Gtk::Button m_confirm; + Gtk::Button m_all; + Gtk::Button m_none; + + Gtk::Label m_label; + + Gtk::Layout m_layout; + + std::vector m_checks; + + std::vector &returned; +}; + +#endif diff --git a/tv_rename.cpp b/tv_rename.cpp index 031a741..f172eed 100644 --- a/tv_rename.cpp +++ b/tv_rename.cpp @@ -1,10 +1,15 @@ #include +#include "functions.hpp" +#include "tv_rename.hpp" + +#ifndef GUI + #include #include #include #include "filesystem.hpp" -#include "functions.hpp" -#include "tv_rename.hpp" + +#endif std::vector parseEpisodeNames( const std::string &season_code, const std::string &language) { std::vector episodes; @@ -31,9 +36,20 @@ std::vector parseEpisodeNames( const std::string &season_code, cons return episodes; } +#ifndef GUI void singleSeason( const std::string &path, const std::string &show, int season, std::string url, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c, std::set const *files) { +#else +std::vector> singleSeason( const std::string &show, int season, std::string url, const std::string &language, const std::string &pattern, const bool &linux, Curl &c, const std::set &files) { + if( files.empty() ) + return {}; +#endif if( url.empty() ) +#ifndef GUI url = getDefUrl(show, language, c); +#else + return {}; +#endif + url += "/seasons/" + std::to_string(season); //get source code of season's page auto season_code = c.execute(url); @@ -49,16 +65,29 @@ void singleSeason( const std::string &path, const std::string &show, int season, if( pos != std::string::npos ) season_code = season_code.substr(0,pos); else +#ifndef GUI return; +#else + return {}; +#endif } else { +#ifndef GUI return; +#else + return {}; +#endif } auto episodes = parseEpisodeNames(season_code, language); if( episodes.empty() ) +#ifndef GUI return; +#else + return {}; +#endif +#ifndef GUI std::set found_files; std::set renamed_files; @@ -71,6 +100,11 @@ void singleSeason( const std::string &path, const std::string &show, int season, return; for( const auto &x : *files ) { +#else + std::vector> renamed_files; + + for( const auto &x : files ) { +#endif auto last = x.find_last_of("/"); std::string name; std::string dir; @@ -113,10 +147,15 @@ void singleSeason( const std::string &path, const std::string &show, int season, } } } +#ifndef GUI renamed_files.insert(dir + "/" + name); +#else + renamed_files.emplace_back(dir, name); +#endif } } +#ifndef GUI for(auto renamed = renamed_files.begin(); renamed != renamed_files.end(); ++renamed) { std::cout << *renamed << std::endl; } @@ -137,8 +176,12 @@ void singleSeason( const std::string &path, const std::string &show, int season, FSLib::rename(*orig, *renamed); ++orig; } +#else + return renamed_files; +#endif } +#ifndef GUI void multipleSeasons( const std::string &path, const std::string &show, const std::map> &seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c) { auto url = getDefUrl(show, language, c); for( const auto &x : seasons ) { @@ -158,4 +201,5 @@ void allSeasons( const std::string &path, const std::string &show, const std::st iterateFS(seasons, path); multipleSeasons( path, show, seasons, language, pattern, linux, trust, c); } +#endif diff --git a/tv_rename.hpp b/tv_rename.hpp index 4f941d0..4fbdb41 100644 --- a/tv_rename.hpp +++ b/tv_rename.hpp @@ -4,8 +4,18 @@ #include #include "network.hpp" +#ifdef GUI + +#include + +std::vector> singleSeason( const std::string &show, int season, std::string url, const std::string &language, const std::string &pattern, const bool &linux, Curl &c, const std::set &files); + +#else + void singleSeason( const std::string &path, const std::string &show, int season, std::string url, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c, std::set const *files=nullptr); void multipleSeasons( const std::string &path, const std::string &show, const std::set seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c); void allSeasons( const std::string &path, const std::string &show, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c); #endif + +#endif