Add option to compile with GUI
This commit is contained in:
parent
0762a7d21d
commit
c8e93aa3cc
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.o
|
||||
tv_rename
|
||||
tv_rename_gui
|
||||
test
|
||||
|
12
Makefile
12
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
|
||||
|
||||
|
@ -3,17 +3,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#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<char *>(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;
|
||||
}
|
||||
|
@ -6,9 +6,11 @@
|
||||
#include <string.h>
|
||||
|
||||
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 {
|
||||
|
108
functions.cpp
108
functions.cpp
@ -2,12 +2,24 @@
|
||||
#include "filesystem.hpp"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
#include <iostream>
|
||||
#include <curl/curl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
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",
|
||||
@ -16,6 +28,25 @@ constexpr std::array<const char *, 46> 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<unsigned char>(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<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) {
|
||||
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::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<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>> 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<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 output;
|
||||
|
||||
|
@ -6,13 +6,19 @@
|
||||
#include <map>
|
||||
#include "network.hpp"
|
||||
|
||||
#ifdef GUI
|
||||
|
||||
#include <vector>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
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 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();
|
||||
|
||||
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<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);
|
||||
|
||||
#endif
|
||||
|
11
gui.cpp
Normal file
11
gui.cpp
Normal 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
343
mainwindow.cpp
Normal 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
91
mainwindow.hpp
Normal 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
73
seasonwindow.cpp
Normal 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
34
seasonwindow.hpp
Normal 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
|
@ -1,10 +1,15 @@
|
||||
#include <algorithm>
|
||||
#include "functions.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#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> episodes;
|
||||
@ -31,9 +36,20 @@ std::vector<std::string> 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<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() )
|
||||
#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<std::string> found_files;
|
||||
std::set<std::string> 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<std::pair<std::string, std::string>> 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<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);
|
||||
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
|
||||
|
||||
|
@ -4,8 +4,18 @@
|
||||
#include <set>
|
||||
#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 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);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user