Add option to compile with GUI

This commit is contained in:
zvon 2019-01-23 20:46:03 +01:00
parent 0762a7d21d
commit c8e93aa3cc
13 changed files with 736 additions and 37 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.o *.o
tv_rename tv_rename
tv_rename_gui
test test

View File

@ -24,3 +24,15 @@ network.o: network.cpp
tv_rename.o: tv_rename.cpp tv_rename.o: tv_rename.cpp
$(CXX) $(CFLAGS) -c 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

View File

@ -3,17 +3,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <linux/limits.h> #include <linux/limits.h>
#ifndef GUI
bool FSLib::exists(const std::string &path) { bool FSLib::exists(const std::string &path) {
struct stat path_stat; struct stat path_stat;
return stat( path.c_str(), &path_stat ) == 0; 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) { std::string FSLib::canonical(const std::string &path) {
char *canonical_path = static_cast<char *>(malloc(PATH_MAX)); char *canonical_path = static_cast<char *>(malloc(PATH_MAX));
if( realpath(path.c_str(), canonical_path) == nullptr ) { if( realpath(path.c_str(), canonical_path) == nullptr ) {
@ -25,6 +21,14 @@ std::string FSLib::canonical(const std::string &path) {
return canonical_string; 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) { bool FSLib::rename(const std::string &file_a, const std::string &file_b) {
return ::rename( file_a.c_str(), file_b.c_str() ) == 0; return ::rename( file_a.c_str(), file_b.c_str() ) == 0;
} }

View File

@ -6,9 +6,11 @@
#include <string.h> #include <string.h>
namespace FSLib{ namespace FSLib{
#ifndef GUI
bool exists(const std::string &path); bool exists(const std::string &path);
bool isDirectory(const std::string &path);
std::string canonical(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); bool rename(const std::string &file_a, const std::string &file_b);
class Directory { class Directory {

View File

@ -2,12 +2,24 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <sstream>
#ifndef GUI
#include <iostream> #include <iostream>
#include <curl/curl.h> #include <curl/curl.h>
#include <stdlib.h> #include <stdlib.h>
#include <sstream>
#include <vector> #include <vector>
#else
#include <unistd.h>
#include <pwd.h>
#endif
#ifndef GUI
constexpr std::array<const char *, 46> languages{ constexpr std::array<const char *, 46> languages{
"en", "English", "sv", "Svenska", "no", "Norsk", "da", "Dansk", "fi", "Suomeksi", "en", "English", "sv", "Svenska", "no", "Norsk", "da", "Dansk", "fi", "Suomeksi",
"nl", "Nederlands", "de", "Deutsch", "it", "Italiano", "es", "Español", "fr", "Français", "nl", "Nederlands", "de", "Deutsch", "it", "Italiano", "es", "Español", "fr", "Français",
@ -16,6 +28,25 @@ constexpr std::array<const char *, 46> languages{
"sl", "Slovenian", "hr", "Croatian", "ko","Korea" "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<unsigned char>(x)) << std::nouppercase;
}
return encoded.str();
}
// return true if filename has specified season // return true if filename has specified season
// set ep_pos to position where episode number starts // set ep_pos to position where episode number starts
bool searchSpecificSeason(const char *const p, size_t &ep_pos, const std::string &number) { 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); return searchSeason(p, tmp);
} }
void iterateFS(std::map<int, std::set<std::string>> &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<std::string> &files, int season, const std::string &path) { void findSeason(std::set<std::string> &files, int season, const std::string &path) {
auto number = std::to_string(season); auto number = std::to_string(season);
@ -102,19 +148,6 @@ void findSeasons(std::map<int, std::set<std::string>> &seasons, const std::strin
} }
} }
void iterateFS(std::map<int, std::set<std::string>> &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::string getDefUrl( std::string show, const std::string &language, Curl &c ) {
std::replace(show.begin(), show.end(), ' ', '+'); std::replace(show.begin(), show.end(), ' ', '+');
auto source_code = c.execute("https://www.thetvdb.com/search?q=" + encodeUrl(show) + "&l=" + language); 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; return false;
} }
std::string encodeUrl( const std::string &url ) { #else
//stolen from here - https://stackoverflow.com/questions/154536/encode-decode-urls-in-c
std::ostringstream encoded; std::vector<std::pair<std::string, std::string>> getPossibleShows( std::string show, const std::string &language, Curl &c ) {
encoded.fill('0'); std::replace(show.begin(), show.end(), ' ', '+');
encoded << std::hex; auto source_code = c.execute("https://www.thetvdb.com/search?q=" + encodeUrl(show) + "&l=" + language);
for( auto &x : url ) { size_t pos{};
if( std::isalnum(x) || x == '-' || x == '_' || x == '.' || x == '~' ) { std::vector<std::pair<std::string, std::string>> urls;
encoded << x; while( true ) {
continue; 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<unsigned char>(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 compilePattern(const std::string &pattern, int season, int episode, const std::string &filename, const std::string &episodeName, const std::string &showName) {
std::string output; std::string output;

View File

@ -6,13 +6,19 @@
#include <map> #include <map>
#include "network.hpp" #include "network.hpp"
#ifdef GUI
#include <vector>
#endif
#ifndef GUI
std::string getDefUrl( std::string show, const std::string &language, Curl &c ); std::string getDefUrl( std::string show, const std::string &language, Curl &c );
void findSeason(std::set<std::string> &files, int season, const std::string &path); void findSeason(std::set<std::string> &files, int season, const std::string &path);
void findSeasons(std::map<int, std::set<std::string>> &seasons, const std::string &path, const std::set<int> &season_numbers); void findSeasons(std::map<int, std::set<std::string>> &seasons, const std::string &path, const std::set<int> &season_numbers);
void iterateFS(std::map<int, std::set<std::string>> &seasons, const std::string &path);
void printHelp(); 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 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, size_t &season_pos);
bool searchSeason(const char *const p); bool searchSeason(const char *const p);
@ -22,6 +28,18 @@ void printLangs();
bool findLanguage( const char *language ); bool findLanguage( const char *language );
std::string encodeUrl( const std::string &url ); std::string encodeUrl( const std::string &url );
#else
std::vector<std::pair<std::string, std::string>> getPossibleShows( std::string show, const std::string &language, Curl &c );
std::string userHome();
#endif
void iterateFS(std::map<int, std::set<std::string>> &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); std::string compilePattern(const std::string &pattern, int season, int episode, const std::string &filename, const std::string &episodeName, const std::string &showName);
#endif #endif

11
gui.cpp Normal file
View File

@ -0,0 +1,11 @@
#include <gtkmm/application.h>
#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);
}

343
mainwindow.cpp Normal file
View File

@ -0,0 +1,343 @@
#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`)
*/
void renameFiles(const std::set<std::string> &orig, const std::vector<std::pair<std::string, std::string>> &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<Glib::ustring>((*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<Glib::ustring>((*iter)[m_columns_url.m_col_show]), x,
"https://www.thetvdb.com" + static_cast<Glib::ustring>((*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<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();
}

91
mainwindow.hpp Normal file
View File

@ -0,0 +1,91 @@
#ifndef GTKMM_MAIN_WINDOW
#define GTKMM_MAIN_WINDOW
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/combobox.h>
#include <gtkmm/entry.h>
#include <gtkmm/layout.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
#include <set>
#include "network.hpp"
#include "seasonwindow.hpp"
class MainWindow : public Gtk::Window {
public:
MainWindow(const Glib::RefPtr<Gtk::Application> &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<std::string> m_col_code;
Gtk::TreeModelColumn<std::string> m_col_language;
};
class UrlColumns: public Gtk::TreeModel::ColumnRecord {
public:
UrlColumns() {
add(m_col_url);
add(m_col_show);
}
Gtk::TreeModelColumn<Glib::ustring> m_col_url;
Gtk::TreeModelColumn<Glib::ustring> m_col_show;
};
LanguageColumns m_columns_language;
UrlColumns m_columns_url;
Glib::RefPtr<Gtk::Application> app;
SeasonWindow *sw;
std::vector<int> selected;
std::map<int, std::set<std::string>> files;
std::string path;
std::string language_code;
std::string default_pattern;
};
#endif // GTKMM_EXAMPLE_HELLOWORLD_H

73
seasonwindow.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <string>
#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<int> &seasons, std::vector<int> &_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();
}

34
seasonwindow.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef GTKMM_SEASON_WINDOW
#define GTKMM_SEASON_WINDOW
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/layout.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
class SeasonWindow : public Gtk::Window {
public:
SeasonWindow(const std::vector<int> &seasons, std::vector<int> &_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<Gtk::CheckButton> m_checks;
std::vector<int> &returned;
};
#endif

View File

@ -1,10 +1,15 @@
#include <algorithm> #include <algorithm>
#include "functions.hpp"
#include "tv_rename.hpp"
#ifndef GUI
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "filesystem.hpp" #include "filesystem.hpp"
#include "functions.hpp"
#include "tv_rename.hpp" #endif
std::vector<std::string> parseEpisodeNames( const std::string &season_code, const std::string &language) { std::vector<std::string> parseEpisodeNames( const std::string &season_code, const std::string &language) {
std::vector<std::string> episodes; std::vector<std::string> episodes;
@ -31,9 +36,20 @@ std::vector<std::string> parseEpisodeNames( const std::string &season_code, cons
return episodes; 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<std::string> const *files) { 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<std::string> const *files) {
#else
std::vector<std::pair<std::string, std::string>> 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<std::string> &files) {
if( files.empty() )
return {};
#endif
if( url.empty() ) if( url.empty() )
#ifndef GUI
url = getDefUrl(show, language, c); url = getDefUrl(show, language, c);
#else
return {};
#endif
url += "/seasons/" + std::to_string(season); url += "/seasons/" + std::to_string(season);
//get source code of season's page //get source code of season's page
auto season_code = c.execute(url); 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 ) if( pos != std::string::npos )
season_code = season_code.substr(0,pos); season_code = season_code.substr(0,pos);
else else
#ifndef GUI
return; return;
#else
return {};
#endif
} else { } else {
#ifndef GUI
return; return;
#else
return {};
#endif
} }
auto episodes = parseEpisodeNames(season_code, language); auto episodes = parseEpisodeNames(season_code, language);
if( episodes.empty() ) if( episodes.empty() )
#ifndef GUI
return; return;
#else
return {};
#endif
#ifndef GUI
std::set<std::string> found_files; std::set<std::string> found_files;
std::set<std::string> renamed_files; std::set<std::string> renamed_files;
@ -71,6 +100,11 @@ void singleSeason( const std::string &path, const std::string &show, int season,
return; return;
for( const auto &x : *files ) { for( const auto &x : *files ) {
#else
std::vector<std::pair<std::string, std::string>> renamed_files;
for( const auto &x : files ) {
#endif
auto last = x.find_last_of("/"); auto last = x.find_last_of("/");
std::string name; std::string name;
std::string dir; 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); 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) { for(auto renamed = renamed_files.begin(); renamed != renamed_files.end(); ++renamed) {
std::cout << *renamed << std::endl; 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); FSLib::rename(*orig, *renamed);
++orig; ++orig;
} }
#else
return renamed_files;
#endif
} }
#ifndef GUI
void multipleSeasons( const std::string &path, const std::string &show, const std::map<int, std::set<std::string>> &seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c) { void multipleSeasons( const std::string &path, const std::string &show, const std::map<int, std::set<std::string>> &seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c) {
auto url = getDefUrl(show, language, c); auto url = getDefUrl(show, language, c);
for( const auto &x : seasons ) { 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); iterateFS(seasons, path);
multipleSeasons( path, show, seasons, language, pattern, linux, trust, c); multipleSeasons( path, show, seasons, language, pattern, linux, trust, c);
} }
#endif

View File

@ -4,8 +4,18 @@
#include <set> #include <set>
#include "network.hpp" #include "network.hpp"
#ifdef GUI
#include <vector>
std::vector<std::pair<std::string, std::string>> 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<std::string> &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<std::string> const *files=nullptr); 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<std::string> const *files=nullptr);
void multipleSeasons( const std::string &path, const std::string &show, const std::set<int> seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c); void multipleSeasons( const std::string &path, const std::string &show, const std::set<int> 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); 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
#endif