Add Windows support
This commit is contained in:
parent
1b20d90c0b
commit
55a8f32f49
17
Makefile
17
Makefile
@ -8,7 +8,7 @@ default: tv_rename
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o tv_rename
|
||||
rm -f *.o tv_rename tv_rename_gui
|
||||
|
||||
.PHONY: install
|
||||
install: tv_rename
|
||||
@ -50,3 +50,18 @@ functions_gui.o: functions.cpp
|
||||
tv_rename_gui.o: tv_rename.cpp
|
||||
$(CXX) $(CFLAGS) -c tv_rename.cpp -o tv_rename_gui.o -DGUI
|
||||
|
||||
|
||||
.PHONY: windows
|
||||
windows: tv_rename.exe
|
||||
|
||||
tv_rename.exe: tv_rename.cpp filesystem.cpp functions.cpp network.cpp main.cpp
|
||||
$(CXX) -MD -EHsc -Fe"tv_rename" tv_rename.cpp filesystem.cpp functions.cpp network.cpp main.cpp -D_WIN32 -DUNICODE -link wininet.lib shlwapi.lib
|
||||
|
||||
.PHONY: windows_gui
|
||||
windows_gui: tv_rename_gui.exe
|
||||
|
||||
tv_rename_gui.exe: tv_rename_gui.res tv_rename_gui.cpp tv_rename.cpp filesystem.cpp functions.cpp network.cpp
|
||||
$(CXX) -MD -EHsc -Fe"tv_rename_gui" tv_rename_gui.cpp tv_rename.cpp filesystem.cpp functions.cpp network.cpp -D_WIN32 -DUNICODE -DGUI -link wininet.lib shlwapi.lib ole32.lib shell32.lib gdi32.lib user32.lib tv_rename_gui.res
|
||||
|
||||
tv_rename_gui.res: tv_rename_gui.rc
|
||||
rc tv_rename_gui.rc
|
||||
|
149
filesystem.cpp
149
filesystem.cpp
@ -1,44 +1,91 @@
|
||||
#include "filesystem.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/limits.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef GUI
|
||||
#ifdef _WIN32
|
||||
#include <Shlwapi.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
bool FSLib::exists(const std::string &path) {
|
||||
struct stat path_stat;
|
||||
#include "filesystem.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define S_ISDIR( a ) a & _S_IFDIR
|
||||
|
||||
using stat_t = struct _stat;
|
||||
|
||||
#else
|
||||
|
||||
using stat_t = struct stat;
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifndef GUI // these functions aren't needed in GUI
|
||||
|
||||
bool FSLib::exists( const string &path ) {
|
||||
stat_t path_stat;
|
||||
|
||||
#ifdef _WIN32
|
||||
return _wstat( path.c_str(), &path_stat ) == 0;
|
||||
#else
|
||||
return stat( path.c_str(), &path_stat ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
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 ) {
|
||||
free(canonical_path);
|
||||
return "";
|
||||
string FSLib::canonical( const string &path ) {
|
||||
|
||||
#ifdef _WIN32
|
||||
auto PATH_MAX = MAX_PATH;
|
||||
#endif
|
||||
|
||||
char_t *canonical_path = new char_t[PATH_MAX];
|
||||
|
||||
#ifdef _WIN32
|
||||
auto failed = !PathCanonicalizeW( canonical_path, path.c_str() );
|
||||
#else
|
||||
auto failed = realpath( path.c_str(), canonical_path ) == nullptr;
|
||||
#endif
|
||||
|
||||
if ( failed ) {
|
||||
delete[] canonical_path;
|
||||
return string();
|
||||
}
|
||||
std::string canonical_string{canonical_path};
|
||||
free(canonical_path);
|
||||
|
||||
string canonical_string{ canonical_path };
|
||||
delete[] canonical_path;
|
||||
return canonical_string;
|
||||
}
|
||||
|
||||
#endif // ndef GUI
|
||||
|
||||
bool FSLib::isDirectory( const string &path ) {
|
||||
stat_t path_stat;
|
||||
|
||||
#ifdef _WIN32
|
||||
_wstat( path.c_str(), &path_stat );
|
||||
#else
|
||||
stat( path.c_str(), &path_stat );
|
||||
#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);
|
||||
return S_ISDIR( path_stat.st_mode );
|
||||
}
|
||||
|
||||
bool FSLib::rename(const std::string &file_a, const std::string &file_b) {
|
||||
bool FSLib::rename( const string &file_a, const string &file_b ) {
|
||||
#ifdef _WIN32
|
||||
return MoveFileExW( file_a.c_str(), file_b.c_str(),
|
||||
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING );
|
||||
#else
|
||||
return ::rename( file_a.c_str(), file_b.c_str() ) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
FSLib::Directory::iterator FSLib::Directory::begin() {
|
||||
return Iterator(dir_path);
|
||||
return Iterator( *this );
|
||||
}
|
||||
|
||||
FSLib::Directory::const_iterator FSLib::Directory::begin() const {
|
||||
return Iterator(dir_path);
|
||||
return Iterator( *this );
|
||||
}
|
||||
|
||||
FSLib::Directory::const_iterator FSLib::Directory::cbegin() const {
|
||||
@ -46,44 +93,82 @@ FSLib::Directory::const_iterator FSLib::Directory::cbegin() const {
|
||||
}
|
||||
|
||||
FSLib::Directory::iterator FSLib::Directory::end() {
|
||||
return Iterator(dir_path, nullptr);
|
||||
#ifdef _WIN32
|
||||
return Iterator( true );
|
||||
#else
|
||||
return Iterator( *this, nullptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
FSLib::Directory::const_iterator FSLib::Directory::end() const {
|
||||
return Iterator(dir_path, nullptr);
|
||||
#ifdef _WIN32
|
||||
return Iterator( true );
|
||||
#else
|
||||
return Iterator( *this, nullptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
FSLib::Directory::const_iterator FSLib::Directory::cend() const {
|
||||
return end();
|
||||
}
|
||||
|
||||
const char *FSLib::Directory::path() const {
|
||||
const char_t *FSLib::Directory::path() const {
|
||||
return dir_path.c_str();
|
||||
}
|
||||
|
||||
char const *FSLib::Directory::Iterator::operator*() const {
|
||||
char_t const *FSLib::Directory::Iterator::operator*() const {
|
||||
#ifdef _WIN32
|
||||
return data.cFileName;
|
||||
#else
|
||||
return current_entry->d_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
FSLib::Directory::Iterator &FSLib::Directory::Iterator::operator++() {
|
||||
if( current_entry == nullptr )
|
||||
if ( ended == true )
|
||||
return *this;
|
||||
current_entry = readdir(d);
|
||||
if( current_entry != nullptr && ( !strcmp(current_entry->d_name, ".") || !strcmp(current_entry->d_name, "..") ) )
|
||||
// skip . and ..
|
||||
if ( FindNextFileW( hFind, &data ) == 0 ) {
|
||||
ended = true;
|
||||
} else if ( !wcscmp( data.cFileName, L"." ) ||
|
||||
!wcscmp( data.cFileName, L".." ) ) {
|
||||
return operator++();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
FSLib::Directory::Iterator &FSLib::Directory::Iterator::operator++() {
|
||||
if ( current_entry == nullptr )
|
||||
return *this;
|
||||
current_entry = readdir( d );
|
||||
// skip . and ..
|
||||
if ( current_entry != nullptr &&
|
||||
( !strcmp( current_entry->d_name, "." ) ||
|
||||
!strcmp( current_entry->d_name, ".." ) ) )
|
||||
return operator++();
|
||||
return *this;
|
||||
}
|
||||
|
||||
FSLib::Directory::Iterator FSLib::Directory::Iterator::operator++(int) {
|
||||
Iterator ret(*this);
|
||||
#endif
|
||||
|
||||
FSLib::Directory::Iterator FSLib::Directory::Iterator::operator++( int ) {
|
||||
Iterator ret( *this );
|
||||
operator++();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FSLib::Directory::Iterator::operator==(const Iterator &i_other) const {
|
||||
bool FSLib::Directory::Iterator::operator==( const Iterator &i_other ) const {
|
||||
#ifdef _WIN32
|
||||
return i_other.ended == ended;
|
||||
#else
|
||||
return i_other.current_entry == current_entry;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FSLib::Directory::Iterator::operator!=(const Iterator &i_other) const {
|
||||
return i_other.current_entry != current_entry;
|
||||
bool FSLib::Directory::Iterator::operator!=( const Iterator &i_other ) const {
|
||||
return !( i_other == *this );
|
||||
}
|
||||
|
130
filesystem.hpp
130
filesystem.hpp
@ -1,58 +1,122 @@
|
||||
#ifndef FSLIB_H
|
||||
#define FSLIB_H
|
||||
|
||||
#include <string>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
namespace FSLib{
|
||||
#ifndef GUI
|
||||
bool exists(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 {
|
||||
public:
|
||||
Directory(const std::string &path_) : dir_path(path_) {}
|
||||
// set apropriate data types for each operating system
|
||||
#ifdef _WIN32
|
||||
|
||||
using string = std::wstring;
|
||||
using char_t = wchar_t;
|
||||
|
||||
#else
|
||||
|
||||
using string = std::string;
|
||||
using char_t = char;
|
||||
|
||||
#endif
|
||||
|
||||
// windows version stolen from
|
||||
// http://www.martinbroadhurst.com/list-the-files-in-a-directory-in-c.html
|
||||
|
||||
namespace FSLib {
|
||||
|
||||
#ifndef GUI
|
||||
bool exists( const string &path );
|
||||
string canonical( const string &path );
|
||||
#endif
|
||||
|
||||
bool isDirectory( const string &path );
|
||||
bool rename( const string &file_a, const string &file_b );
|
||||
|
||||
class Directory {
|
||||
public:
|
||||
Directory( const string &path_ ) : dir_path( path_ ) {
|
||||
#ifdef _WIN32
|
||||
// need to append \\* for windows to search files in directory
|
||||
dir_path.append( L"\\*" );
|
||||
#endif
|
||||
}
|
||||
Directory() = delete;
|
||||
Directory(const Directory &d) = default;
|
||||
Directory(Directory &&d) = default;
|
||||
Directory( const Directory &d ) = default;
|
||||
Directory( Directory &&d ) = default;
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator( const Directory &d_ ) : d(opendir(d_.path())) {
|
||||
current_entry = readdir(d);
|
||||
if( current_entry != nullptr && ( !strcmp(current_entry->d_name, ".") || !strcmp(current_entry->d_name, "..") ) )
|
||||
++(*this);
|
||||
#ifdef _WIN32
|
||||
Iterator( const Directory &d_ ) {
|
||||
hFind = FindFirstFileW( d_.path(), &data );
|
||||
if ( hFind != INVALID_HANDLE_VALUE ) {
|
||||
if ( !wcscmp( data.cFileName, L"." ) ||
|
||||
!wcscmp( data.cFileName, L".." ) ) {
|
||||
++( *this );
|
||||
}
|
||||
} else {
|
||||
ended = true;
|
||||
}
|
||||
}
|
||||
// this is definitely not a good way to create the "end" iterator
|
||||
// but it was the only way I thought of with my limited knowledge of
|
||||
// windows.h
|
||||
Iterator( bool ended_ ) : ended( ended_ ) {}
|
||||
|
||||
~Iterator() {
|
||||
if ( hFind )
|
||||
FindClose( hFind );
|
||||
}
|
||||
#else
|
||||
Iterator( const Directory &d_ ) : d( opendir( d_.path() ) ) {
|
||||
current_entry = readdir( d );
|
||||
if ( current_entry != nullptr &&
|
||||
( !strcmp( current_entry->d_name, "." ) ||
|
||||
!strcmp( current_entry->d_name, ".." ) ) )
|
||||
++( *this );
|
||||
}
|
||||
|
||||
Iterator( const Directory &d_, const struct dirent *current_entry_) : d(opendir(d_.path())), current_entry(current_entry_) {}
|
||||
Iterator( const Directory &d_, const struct dirent *current_entry_ )
|
||||
: d( opendir( d_.path() ) ), current_entry( current_entry_ ) {}
|
||||
|
||||
~Iterator() {
|
||||
closedir( d );
|
||||
}
|
||||
#endif
|
||||
|
||||
Iterator() = delete;
|
||||
|
||||
Iterator(const Iterator &i) = default;
|
||||
Iterator( const Iterator &i ) = default;
|
||||
|
||||
Iterator(Iterator &&i) = default;
|
||||
Iterator( Iterator &&i ) = default;
|
||||
|
||||
~Iterator() {
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
char const *operator*() const;
|
||||
char_t const *operator*() const;
|
||||
|
||||
Iterator &operator++();
|
||||
|
||||
Iterator operator++(int);
|
||||
Iterator operator++( int );
|
||||
|
||||
bool operator==(const Iterator &i_other) const;
|
||||
bool operator==( const Iterator &i_other ) const;
|
||||
|
||||
bool operator!=(const Iterator &i_other) const;
|
||||
bool operator!=( const Iterator &i_other ) const;
|
||||
|
||||
private:
|
||||
#ifndef _WIN32
|
||||
DIR *d;
|
||||
const struct dirent *current_entry;
|
||||
#else
|
||||
HANDLE hFind;
|
||||
WIN32_FIND_DATA data;
|
||||
bool ended{ false };
|
||||
#endif
|
||||
};
|
||||
|
||||
using iterator = Iterator;
|
||||
@ -70,11 +134,11 @@ namespace FSLib{
|
||||
|
||||
const_iterator cend() const;
|
||||
|
||||
const char *path() const;
|
||||
const char_t *path() const;
|
||||
|
||||
private:
|
||||
std::string dir_path;
|
||||
};
|
||||
} //end FSLib
|
||||
private:
|
||||
string dir_path;
|
||||
};
|
||||
} // namespace FSLib
|
||||
|
||||
#endif
|
||||
|
535
functions.cpp
535
functions.cpp
@ -1,258 +1,417 @@
|
||||
#include "functions.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <shlobj.h>
|
||||
|
||||
#else
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
#include <iostream>
|
||||
#include <curl/curl.h>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif
|
||||
#endif // GUI
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "functions.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define cout std::wcout
|
||||
#define cerr std::wcerr
|
||||
#define cin std::wcin
|
||||
|
||||
constexpr const char_t *dir_divider = L"\\";
|
||||
|
||||
#else
|
||||
|
||||
#define TEXT( a ) a
|
||||
|
||||
#define cout std::cout
|
||||
#define cerr std::cerr
|
||||
#define cin std::cin
|
||||
|
||||
constexpr const char_t *dir_divider = "/";
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#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",
|
||||
"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"
|
||||
constexpr std::array< const char_t *, 46 > languages{
|
||||
TEXT( "en" ), TEXT( "English" ), TEXT( "sv" ), TEXT( "Svenska" ),
|
||||
TEXT( "no" ), TEXT( "Norsk" ), TEXT( "da" ), TEXT( "Dansk" ),
|
||||
TEXT( "fi" ), TEXT( "Suomeksi" ), TEXT( "nl" ), TEXT( "Nederlands" ),
|
||||
TEXT( "de" ), TEXT( "Deutsch" ), TEXT( "it" ), TEXT( "Italiano" ),
|
||||
TEXT( "es" ), TEXT( "Español" ), TEXT( "fr" ), TEXT( "Français" ),
|
||||
TEXT( "pl" ), TEXT( "Polski" ), TEXT( "hu" ), TEXT( "Magyar" ),
|
||||
TEXT( "el" ), TEXT( "Greek" ), TEXT( "tr" ), TEXT( "Turkish" ),
|
||||
TEXT( "ru" ), TEXT( "Russian" ), TEXT( "he" ), TEXT( "Hebrew" ),
|
||||
TEXT( "ja" ), TEXT( "Japanese" ), TEXT( "pt" ), TEXT( "Portuguese" ),
|
||||
TEXT( "zh" ), TEXT( "Chinese" ), TEXT( "cs" ), TEXT( "Czech" ),
|
||||
TEXT( "sl" ), TEXT( "Slovenian" ), TEXT( "hr" ), TEXT( "Croatian" ),
|
||||
TEXT( "ko" ), TEXT( "Korea" )
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // not GUI
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// functions to convert between string and wstring
|
||||
|
||||
std::string wstring_to_utf8( const std::wstring &wstring ) {
|
||||
std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > wconv;
|
||||
return wconv.to_bytes( wstring );
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wstring( const std::string &utf8 ) {
|
||||
std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > wconv;
|
||||
return wconv.from_bytes( utf8 );
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
// 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');
|
||||
string encodeUrl( const string &url ) {
|
||||
// stolen from here -
|
||||
// https://stackoverflow.com/questions/154536/encode-decode-urls-in-c
|
||||
#ifdef _WIN32
|
||||
std::wstringstream encoded;
|
||||
auto url_c = wstring_to_utf8( url );
|
||||
#else
|
||||
std::stringstream encoded;
|
||||
const auto &url_c = url;
|
||||
#endif
|
||||
encoded.fill( '0' );
|
||||
encoded << std::hex;
|
||||
for( auto &x : url ) {
|
||||
if( std::isalnum(x) || x == '-' || x == '_' || x == '.' || x == '~' ) {
|
||||
for ( const auto &x : url_c ) {
|
||||
if ( isalnum( static_cast< unsigned char >( x ) ) || x == '-' ||
|
||||
x == '_' || x == '.' || x == '~' ) {
|
||||
encoded << x;
|
||||
continue;
|
||||
}
|
||||
encoded << std::uppercase << '%' << std::setw(2);
|
||||
encoded << int(static_cast<unsigned char>(x)) << std::nouppercase;
|
||||
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) {
|
||||
bool searchSpecificSeason( const char_t *const path, size_t &ep_pos,
|
||||
const string &number ) {
|
||||
size_t cur_pos{};
|
||||
bool found_season{false};
|
||||
while( p[cur_pos] != '\0' ) {
|
||||
if( (p[cur_pos] == 's' || p[cur_pos] == 'S') && isdigit(p[cur_pos+1]) ) {
|
||||
// search for S[0-9]+E[0-9]+
|
||||
while ( path[cur_pos] != '\0' ) {
|
||||
if ( ( path[cur_pos] == 's' || path[cur_pos] == 'S' ) &&
|
||||
iswdigit( path[cur_pos + 1] ) ) {
|
||||
cur_pos++;
|
||||
while( p[cur_pos] == '0' )
|
||||
while ( path[cur_pos] == '0' )
|
||||
cur_pos++;
|
||||
size_t offset{};
|
||||
while( offset < number.size() && p[cur_pos + offset] == number[offset] )
|
||||
offset++;
|
||||
cur_pos += offset;
|
||||
if( offset != number.size() )
|
||||
// make sure season's number is the same as provided in argument
|
||||
// `number`
|
||||
#ifdef _WIN32
|
||||
if ( wcsncmp( path + cur_pos, number.c_str(), number.size() ) )
|
||||
#else
|
||||
if ( strncmp( path + cur_pos, number.c_str(), number.size() ) )
|
||||
#endif
|
||||
continue;
|
||||
if( (p[cur_pos] == 'e' || p[cur_pos] == 'E') && isdigit(p[cur_pos+1]) ) {
|
||||
found_season = true;
|
||||
cur_pos += number.size();
|
||||
if ( ( path[cur_pos] == 'e' || path[cur_pos] == 'E' ) &&
|
||||
iswdigit( path[cur_pos + 1] ) ) {
|
||||
ep_pos = cur_pos + 1;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cur_pos++;
|
||||
}
|
||||
return found_season;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool searchSpecificSeason(const char *const p, const std::string &number) {
|
||||
bool searchSpecificSeason( const char_t *const p, const string &number ) {
|
||||
size_t tmp;
|
||||
return searchSpecificSeason(p, tmp, number);
|
||||
return searchSpecificSeason( p, tmp, number );
|
||||
}
|
||||
|
||||
bool searchSeason(const char *const p, size_t &season_pos) {
|
||||
// return true if file contains S[0-9]+E[0-9]+ nad set
|
||||
// season_pos to start of season number
|
||||
bool searchSeason( const char_t *const path, size_t &season_pos ) {
|
||||
size_t cur_pos{};
|
||||
bool found_season{false};
|
||||
while( p[cur_pos] != '\0' ) {
|
||||
if( (p[cur_pos] == 's' || p[cur_pos] == 'S') && isdigit(p[cur_pos+1]) ) {
|
||||
while ( path[cur_pos] != '\0' ) {
|
||||
if ( ( path[cur_pos] == 's' || path[cur_pos] == 'S' ) &&
|
||||
iswdigit( path[cur_pos + 1] ) ) {
|
||||
cur_pos++;
|
||||
season_pos = cur_pos; // after ++ because we want the first pos to point to season's number
|
||||
while( isdigit(p[cur_pos]) )
|
||||
season_pos = cur_pos; // after ++ because we want the first pos to
|
||||
// point to season's number
|
||||
while ( iswdigit( path[cur_pos] ) )
|
||||
cur_pos++;
|
||||
if( (p[cur_pos] == 'e' || p[cur_pos] == 'E') && isdigit(p[cur_pos+1]) ) {
|
||||
found_season = true;
|
||||
break;
|
||||
if ( ( path[cur_pos] == 'e' || path[cur_pos] == 'E' ) &&
|
||||
iswdigit( path[cur_pos + 1] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cur_pos++;
|
||||
}
|
||||
return found_season;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool searchSeason(const char *const p) {
|
||||
bool searchSeason( const char_t *const path ) {
|
||||
size_t tmp{};
|
||||
return searchSeason(p, tmp);
|
||||
return searchSeason( path, 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);
|
||||
void iterateFS( std::map< int, std::set< string > > &seasons,
|
||||
const string &path ) {
|
||||
// season_pos - position of first digit of the season
|
||||
size_t season_pos{ string::npos };
|
||||
for ( const auto p : FSLib::Directory( path ) ) {
|
||||
// if p is directory, iterate through it
|
||||
if ( FSLib::isDirectory( path + dir_divider + p ) ) {
|
||||
iterateFS( seasons, path + dir_divider + p );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( searchSeason(p, season_pos) )
|
||||
seasons[atoi(p+season_pos)].insert(path + "/" + p);
|
||||
// if file is a correct format, add it to file list
|
||||
// for its season
|
||||
if ( searchSeason( p, season_pos ) )
|
||||
seasons[std::stoi( p + season_pos )].insert(
|
||||
path + dir_divider + p );
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GUI
|
||||
// following functions are only needed for CLI version
|
||||
|
||||
void findSeason(std::set<std::string> &files, int season, const std::string &path) {
|
||||
auto number = std::to_string(season);
|
||||
// find all files for provided season in `path` and store it in `files`
|
||||
void findSeason( std::set< string > &files, int season, const string &path ) {
|
||||
#ifdef _WIN32
|
||||
auto number = std::to_wstring( season );
|
||||
#else
|
||||
auto number = std::to_string( season );
|
||||
#endif
|
||||
|
||||
for( const auto p: FSLib::Directory(path) ) {
|
||||
if(FSLib::isDirectory(path + "/" + p)) {
|
||||
findSeason(files, season, path + "/" + p);
|
||||
for ( const auto p : FSLib::Directory( path ) ) {
|
||||
// if p is directory, iterate through it
|
||||
if ( FSLib::isDirectory( path + dir_divider + p ) ) {
|
||||
findSeason( files, season, path + dir_divider + p );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( searchSpecificSeason(p, number) )
|
||||
files.insert(path + "/" + p);
|
||||
if ( searchSpecificSeason( p, number ) )
|
||||
files.insert( path + dir_divider + p );
|
||||
}
|
||||
}
|
||||
|
||||
void findSeasons(std::map<int, std::set<std::string>> &seasons, const std::string &path, const std::set<int> &season_numbers) {
|
||||
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)) {
|
||||
findSeasons(seasons, path + "/" + p, season_numbers);
|
||||
// find all files that comply with the S[0-9]+E[0-9]+ pattern
|
||||
// and their season is in season_numbers and store tem in `seasons`
|
||||
void findSeasons( std::map< int, std::set< string > > &seasons,
|
||||
const string &path, const std::set< int > &season_numbers ) {
|
||||
// season_pos - position of first digit of the season
|
||||
size_t season_pos{ string::npos };
|
||||
for ( const auto p : FSLib::Directory( path ) ) {
|
||||
// if p is directory, iterate through it
|
||||
if ( FSLib::isDirectory( path + dir_divider + p ) ) {
|
||||
findSeasons( seasons, path + dir_divider + p, season_numbers );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( searchSeason(p, season_pos) ) {
|
||||
auto num = atoi(p+season_pos);
|
||||
if( season_numbers.find(num) != season_numbers.end() )
|
||||
seasons[num].insert(path + "/" + p);
|
||||
if ( searchSeason( p, season_pos ) ) {
|
||||
auto num = std::stoi( p + season_pos );
|
||||
if ( season_numbers.find( num ) != season_numbers.end() )
|
||||
seasons[num].insert( path + dir_divider + 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);
|
||||
string getDefUrl( string &show, const string &language, Curl &c ) {
|
||||
std::replace( show.begin(), show.end(), ' ', '+' );
|
||||
|
||||
string base_url = TEXT( "https://www.thetvdb.com" );
|
||||
|
||||
#ifdef _WIN32
|
||||
string source_code = utf8_to_wstring(
|
||||
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
|
||||
encodeUrl( show ) + TEXT( "&l=" ) + language ) );
|
||||
#else
|
||||
string source_code =
|
||||
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
|
||||
encodeUrl( show ) + TEXT( "&l=" ) + language );
|
||||
#endif
|
||||
|
||||
size_t order{}, 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);
|
||||
std::vector< std::pair< string, string > > urls;
|
||||
|
||||
// find all possible shows
|
||||
while ( true ) {
|
||||
pos = source_code.find( TEXT( "/ser" ), pos );
|
||||
if ( pos != string::npos ) {
|
||||
auto end = source_code.find( TEXT( ">" ), pos );
|
||||
end--;
|
||||
auto end2 = source_code.find("<", end + 2);
|
||||
urls.emplace_back(source_code.substr(end + 2, end2 - end - 2), source_code.substr(pos, end - pos));
|
||||
std::cout << ++order << ". " << urls.back().first << std::endl;
|
||||
auto end2 = source_code.find( TEXT( "<" ), end + 2 );
|
||||
// store shows in urls, first is name, second is url
|
||||
urls.emplace_back( source_code.substr( end + 2, end2 - end - 2 ),
|
||||
source_code.substr( pos, end - pos ) );
|
||||
cout << ++order << ". " << urls.back().first << std::endl;
|
||||
pos = end2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << "Which TV Show is the right one? ";
|
||||
std::cin >> pos;
|
||||
std::cin.clear();
|
||||
std::cin.ignore(1, '\n');
|
||||
show = urls[pos-1].first;
|
||||
return "https://www.thetvdb.com" + urls[pos-1].second;
|
||||
cout << "Which TV Show is the right one? ";
|
||||
cin >> pos;
|
||||
cin.clear();
|
||||
cin.ignore( 1, '\n' );
|
||||
show = urls[pos - 1].first;
|
||||
return base_url + urls[pos - 1].second;
|
||||
}
|
||||
|
||||
void printHelp() {
|
||||
std::cout << "usage: tv_rename [--help] [--show show name] [--season season number]" << std::endl;
|
||||
std::cout << " [--correct-path] [--show-path show path] [--trust]" << std::endl;
|
||||
std::cout << " [--linux] [--lang language] [--print-langs]" << std::endl;
|
||||
std::cout << std::endl << "Rename TV episodes" << std::endl << std::endl << "optional arguments:" << std::endl;
|
||||
std::cout << " -h, --help\t\tshow this help message and exit" << std::endl;
|
||||
std::cout << " --show show name, -s show name" << std::endl;
|
||||
std::cout << "\t\t\tTV show from which you want episode names (needs to be" << std::endl;
|
||||
std::cout << "\t\t\tin quotation marks if it has more than one word)" << std::endl;
|
||||
std::cout << " --season season number, -n season number" << std::endl;
|
||||
std::cout << "\t\t\tSeason number/s (if multiple seasons, put them in" << std::endl;
|
||||
std::cout << "\t\t\tquotation marks and seperate by one space)" << std::endl;
|
||||
std::cout << "\t\t\tor 'all' for all seasons in selected subdirectory" << std::endl;
|
||||
std::cout << " --show-path show path, -p show path" << std::endl;
|
||||
std::cout << "\t\t\tPath of the directory with episodes" << std::endl;
|
||||
std::cout << " --correct-path, -c\tThis is the correct path, stop asking me!" << std::endl;
|
||||
std::cout << " --name-pattern pattern" << std::endl;
|
||||
std::cout << "\t\t\tPattern to which change the file name. Possible sequences are:" << std::endl;
|
||||
std::cout << "\t\t\t\t\%filename - original filename (without filetype extension)" << std::endl;
|
||||
std::cout << "\t\t\t\t\%show - show name from thetvdb" << std::endl;
|
||||
std::cout << "\t\t\t\t\%epname - episode name from thetvdb" << std::endl;
|
||||
std::cout << "\t\t\t\t\%season - season number" << std::endl;
|
||||
std::cout << "\t\t\t\t\tpossible to specify leading 0 like this: \%2season (number means how many leading zeros)" << std::endl;
|
||||
std::cout << "\t\t\t\t\%episode - episode number" << std::endl;
|
||||
std::cout << "\t\t\t\t\tpossible to specify leading 0 like this: \%2episode (number means how many leading zeros)" << std::endl;
|
||||
std::cout << "\t\t\tDefault pattern is \"$filename - $epname\"" << std::endl;
|
||||
std::cout << " --trust, -t\t\tDon't ask whether the names are correct" << std::endl;
|
||||
std::cout << " --linux, -x\t\tDon't replace characters characters that are illegal in Windows" << std::endl;
|
||||
std::cout << " --lang language, -l language" << std::endl;
|
||||
std::cout << "\t\t\tSelect which language the episode names shoud be in" << std::endl;
|
||||
std::cout << " --print-langs\t\tPring available language" << std::endl;
|
||||
cout << "usage: tv_rename [--help] [--show show name] [--season season "
|
||||
"number]"
|
||||
<< std::endl;
|
||||
cout
|
||||
<< " [--correct-path] [--show-path show path] [--trust]"
|
||||
<< std::endl;
|
||||
cout << " [--linux] [--lang language] [--print-langs]"
|
||||
<< std::endl;
|
||||
cout << std::endl
|
||||
<< "Rename TV episodes" << std::endl
|
||||
<< std::endl
|
||||
<< "optional arguments:" << std::endl;
|
||||
cout << " -h, --help\t\tshow this help message and exit" << std::endl;
|
||||
cout << " --show show name, -s show name" << std::endl;
|
||||
cout << "\t\t\tTV show from which you want episode names (needs to be"
|
||||
<< std::endl;
|
||||
cout << "\t\t\tin quotation marks if it has more than one word)"
|
||||
<< std::endl;
|
||||
cout << " --season season number, -n season number" << std::endl;
|
||||
cout << "\t\t\tSeason number/s (if multiple seasons, put them in"
|
||||
<< std::endl;
|
||||
cout << "\t\t\tquotation marks and seperate by one space)" << std::endl;
|
||||
cout << "\t\t\tor 'all' for all seasons in selected subdirectory"
|
||||
<< std::endl;
|
||||
cout << " --show-path show path, -p show path" << std::endl;
|
||||
cout << "\t\t\tPath of the directory with episodes" << std::endl;
|
||||
cout << " --correct-path, -c\tThis is the correct path, stop asking me!"
|
||||
<< std::endl;
|
||||
cout << " --name-pattern pattern" << std::endl;
|
||||
cout << "\t\t\tPattern to which change the file name. Possible sequences "
|
||||
"are:"
|
||||
<< std::endl;
|
||||
cout << "\t\t\t\t%filename - original filename (without filetype extension)"
|
||||
<< std::endl;
|
||||
cout << "\t\t\t\t%show - show name from thetvdb" << std::endl;
|
||||
cout << "\t\t\t\t%epname - episode name from thetvdb" << std::endl;
|
||||
cout << "\t\t\t\t%season - season number" << std::endl;
|
||||
cout << "\t\t\t\ttpossible to specify leading 0 like this: %2season "
|
||||
"(number means how many leading zeros)"
|
||||
<< std::endl;
|
||||
cout << "\t\t\t\t%episode - episode number" << std::endl;
|
||||
cout << "\t\t\t\t\tpossible to specify leading 0 like this: %2episode "
|
||||
"(number means how many leading zeros)"
|
||||
<< std::endl;
|
||||
cout << "\t\t\tDefault pattern is \"$filename - $epname\"" << std::endl;
|
||||
cout << " --trust, -t\t\tDon't ask whether the names are correct"
|
||||
<< std::endl;
|
||||
cout << " --linux, -x\t\tDon't replace characters characters that are "
|
||||
"illegal in Windows"
|
||||
<< std::endl;
|
||||
cout << " --lang language, -l language" << std::endl;
|
||||
cout << "\t\t\tSelect which language the episode names shoud be in"
|
||||
<< std::endl;
|
||||
cout << " --print-langs\t\tPring available language" << std::endl;
|
||||
}
|
||||
|
||||
void parseSeasonNumbers(std::set<int> &seasons_num, const char *argument) {
|
||||
size_t pos{0};
|
||||
// parse command line argument --seasons (e.g. '1 2 3 4 5')
|
||||
// and store season numbers as integers in seasons_num
|
||||
void parseSeasonNumbers( std::set< int > &seasons_num,
|
||||
const char_t *argument ) {
|
||||
size_t pos{ 0 };
|
||||
|
||||
while(!isdigit(argument[pos]) && argument[pos] != '\0')
|
||||
while ( !iswdigit( argument[pos] ) && argument[pos] != '\0' )
|
||||
pos++;
|
||||
|
||||
if( argument[pos] == '\0' ) {
|
||||
if ( argument[pos] == '\0' ) {
|
||||
seasons_num.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int temp;
|
||||
std::istringstream iss(argument + pos);
|
||||
while(iss >> temp) {
|
||||
seasons_num.insert(temp);
|
||||
|
||||
#ifdef _WIN32
|
||||
std::wstringstream iss( argument + pos );
|
||||
#else
|
||||
std::stringstream iss( argument + pos );
|
||||
#endif
|
||||
|
||||
while ( iss >> temp ) {
|
||||
seasons_num.insert( temp );
|
||||
}
|
||||
}
|
||||
|
||||
// print possible language codes and their corresponding language
|
||||
void printLangs() {
|
||||
for( size_t i = 0; i < languages.size(); i += 2 ) {
|
||||
std::cout << languages[i] << " - " << languages[i+1] << std::endl;
|
||||
for ( size_t i = 0; i < languages.size(); i += 2 ) {
|
||||
cout << languages[i] << " - " << languages[i + 1] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool findLanguage( const char *language ) {
|
||||
for( size_t i = 0; i < languages.size(); i += 2 ) {
|
||||
if( !strcmp(language, languages[i]) )
|
||||
// make sure language is a valide language code
|
||||
bool findLanguage( const char_t *language ) {
|
||||
for ( size_t i = 0; i < languages.size(); i += 2 ) {
|
||||
#ifdef _WIN32
|
||||
if ( !wcscmp( language, languages[i] ) )
|
||||
#else
|
||||
if ( !strcmp( language, languages[i] ) )
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
// functions that are needed for GUI but not for CLI
|
||||
|
||||
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);
|
||||
// get possible shows for search query in `show`
|
||||
std::vector< std::pair< string, string > >
|
||||
getPossibleShows( string show, const string &language, Curl &c ) {
|
||||
std::replace( show.begin(), show.end(), ' ', '+' );
|
||||
#ifdef _WIN32
|
||||
auto source_code = utf8_to_wstring(
|
||||
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
|
||||
encodeUrl( show ) + TEXT( "&l=" ) + language ) );
|
||||
#else
|
||||
auto source_code =
|
||||
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
|
||||
encodeUrl( show ) + TEXT( "&l=" ) + language );
|
||||
#endif
|
||||
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);
|
||||
std::vector< std::pair< string, string > > urls;
|
||||
while ( true ) {
|
||||
pos = source_code.find( TEXT( "/ser" ), pos );
|
||||
if ( pos != string::npos ) {
|
||||
auto end = source_code.find( TEXT( ">" ), pos );
|
||||
auto end2 = source_code.find( TEXT( "<" ), end + 1 );
|
||||
end--;
|
||||
urls.emplace_back(source_code.substr(end+2, end2 - (end+2)), source_code.substr(pos, end - pos));
|
||||
urls.emplace_back(
|
||||
source_code.substr( end + 2, end2 - ( end + 2 ) ),
|
||||
source_code.substr( pos, end - pos ) );
|
||||
pos = end + 2;
|
||||
} else {
|
||||
break;
|
||||
@ -261,13 +420,17 @@ std::vector<std::pair<std::string, std::string>> getPossibleShows( std::string s
|
||||
return urls;
|
||||
}
|
||||
|
||||
std::string userHome() {
|
||||
uid_t user_uid;
|
||||
#ifndef _WIN32
|
||||
|
||||
// get user's home directory
|
||||
string userHome() {
|
||||
uid_t user_uid; // current user's uid
|
||||
{
|
||||
uid_t eid;
|
||||
uid_t sid;
|
||||
getresuid( &user_uid, &eid, &sid ); // don't need eid and sid
|
||||
}
|
||||
// password file entry
|
||||
auto user_passwd = getpwuid( user_uid );
|
||||
|
||||
if ( user_passwd == nullptr )
|
||||
@ -276,63 +439,94 @@ std::string userHome() {
|
||||
return user_passwd->pw_dir;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// get user's %APPDATA% folder location
|
||||
string userHome() {
|
||||
wchar_t *dir = static_cast< wchar_t * >( CoTaskMemAlloc( MAX_PATH ) );
|
||||
auto res = SHGetKnownFolderPath( FOLDERID_RoamingAppData, 0, NULL, &dir );
|
||||
if ( res == S_OK ) {
|
||||
string dir_s = dir;
|
||||
CoTaskMemFree( dir );
|
||||
return dir_s;
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
#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;
|
||||
#endif // ndef GUI
|
||||
|
||||
for( size_t i = 0; i < pattern.size(); i++ ) {
|
||||
if( pattern[i] == '%' ) {
|
||||
// if current character is % check if a pattern follows, otherwise put %
|
||||
// check for numbers right after % indicating size of zero padding for numbers
|
||||
auto pos = pattern.find_first_not_of("0123456789", i+1);
|
||||
// create file name based on given pattern
|
||||
string compilePattern( const string &pattern, int season, int episode,
|
||||
const string &filename, const string &episodeName,
|
||||
const string &showName ) {
|
||||
string output;
|
||||
|
||||
if( pattern.find( "season", pos - 1 ) == pos ) {
|
||||
#ifdef _WIN32
|
||||
auto season_num = std::to_wstring( season );
|
||||
auto ep_num = std::to_wstring( episode );
|
||||
#else
|
||||
auto season_num = std::to_string( season );
|
||||
auto ep_num = std::to_string( episode );
|
||||
#endif
|
||||
|
||||
for ( size_t i = 0; i < pattern.size(); i++ ) {
|
||||
// if current character is % check if a pattern follows, otherwise
|
||||
// put %
|
||||
if ( pattern[i] == '%' ) {
|
||||
// check for numbers right after % indicating size of zero
|
||||
// padding for numbers
|
||||
auto pos = pattern.find_first_not_of( TEXT( "0123456789" ), i + 1 );
|
||||
|
||||
if ( pattern.find( TEXT( "season" ), pos - 1 ) == pos &&
|
||||
pos != i + 1 ) {
|
||||
// if season is AFTER numbers, put season number padded
|
||||
// with zeros
|
||||
|
||||
// get number of leading zeros
|
||||
auto leading = std::atoi( pattern.c_str() + i + 1 );
|
||||
auto leading = std::stoi( pattern.c_str() + i + 1 );
|
||||
// move i to the last char of 'season'
|
||||
i = pos + 5;
|
||||
auto season_num = std::to_string(season);
|
||||
|
||||
// get number of zeros to be put before the season number
|
||||
leading -= season_num.size();
|
||||
if( leading < 0 )
|
||||
if ( leading < 0 )
|
||||
leading = 0;
|
||||
|
||||
// add padded season to output
|
||||
output += std::string(leading, '0') + season_num;
|
||||
} else if ( pattern.find( "season", i ) == i + 1 ) {
|
||||
// if season isn't after numbers, just put season number to output
|
||||
output += string( leading, '0' ) + season_num;
|
||||
} else if ( pattern.find( TEXT( "season" ), i ) == i + 1 ) {
|
||||
// if season isn't after numbers, just put season number to
|
||||
// output
|
||||
i += 6;
|
||||
output += std::to_string(season);
|
||||
} else if ( pattern.find( "episode", pos - 1 ) == pos ) {
|
||||
output += season_num;
|
||||
} else if ( pattern.find( TEXT( "episode" ), pos - 1 ) == pos &&
|
||||
pos != i + 1 ) {
|
||||
// same principle as with season after number
|
||||
auto leading = std::atoi( pattern.c_str() + i + 1 );
|
||||
auto leading = std::stoi( pattern.c_str() + i + 1 );
|
||||
|
||||
i = pos + 6;
|
||||
auto ep_num = std::to_string(episode);
|
||||
|
||||
leading -= ep_num.size();
|
||||
if( leading < 0 )
|
||||
if ( leading < 0 )
|
||||
leading = 0;
|
||||
|
||||
output += std::string(leading, '0') + ep_num;
|
||||
} else if ( pattern.find( "episode", i ) == i + 1 ) {
|
||||
// if episode isn't after number, just put the episode number to output
|
||||
output += string( leading, '0' ) + ep_num;
|
||||
} else if ( pattern.find( TEXT( "episode" ), i ) == i + 1 ) {
|
||||
// if episode isn't after number, just put the episode number to
|
||||
// output
|
||||
i += 7;
|
||||
output += std::to_string(episode);
|
||||
} else if ( pattern.find( "epname", i ) == i + 1 ) {
|
||||
output += ep_num;
|
||||
} else if ( pattern.find( TEXT( "epname" ), i ) == i + 1 ) {
|
||||
// episode name from thetvdb
|
||||
i += 6;
|
||||
output += episodeName;
|
||||
} else if ( pattern.find( "show", i ) == i + 1 ) {
|
||||
} else if ( pattern.find( TEXT( "show" ), i ) == i + 1 ) {
|
||||
// show name from thetvdb
|
||||
i += 4;
|
||||
output += showName;
|
||||
} else if ( pattern.find( "filename", i ) == i + 1 ) {
|
||||
} else if ( pattern.find( TEXT( "filename" ), i ) == i + 1 ) {
|
||||
// original file name
|
||||
i += 8;
|
||||
output += filename;
|
||||
@ -342,10 +536,10 @@ std::string compilePattern(const std::string &pattern, int season, int episode,
|
||||
}
|
||||
} else if ( pattern[i] == '\\' ) {
|
||||
// possibility to escape %
|
||||
if( pattern[i+1] == '%' ) {
|
||||
if ( pattern[i + 1] == '%' ) {
|
||||
output += '%';
|
||||
i++;
|
||||
} else if ( pattern[i+1] == '\\' ) {
|
||||
} else if ( pattern[i + 1] == '\\' ) {
|
||||
output += '\\';
|
||||
i++;
|
||||
} else {
|
||||
@ -359,4 +553,3 @@ std::string compilePattern(const std::string &pattern, int season, int episode,
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#ifndef GETPAGE_H
|
||||
#define GETPAGE_H
|
||||
#ifndef TV_FUNCTIONS_H
|
||||
#define TV_FUNCTIONS_H
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include "network.hpp"
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#ifdef GUI
|
||||
|
||||
@ -12,34 +12,62 @@
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef GUI
|
||||
#include "network.hpp"
|
||||
|
||||
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 printHelp();
|
||||
#ifdef _WIN32
|
||||
|
||||
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);
|
||||
|
||||
void parseSeasonNumbers(std::set<int> &seasons_num, const char *argument);
|
||||
void printLangs();
|
||||
bool findLanguage( const char *language );
|
||||
|
||||
std::string encodeUrl( const std::string &url );
|
||||
using string = std::wstring;
|
||||
using char_t = wchar_t;
|
||||
|
||||
#else
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getPossibleShows( std::string show, const std::string &language, Curl &c );
|
||||
std::string userHome();
|
||||
using string = std::string;
|
||||
using char_t = char;
|
||||
|
||||
#endif
|
||||
|
||||
void iterateFS(std::map<int, std::set<std::string>> &seasons, const std::string &path);
|
||||
#ifdef _WIN32
|
||||
|
||||
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::wstring utf8_to_wstring( const std::string &utf8 );
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef GUI
|
||||
// CLI functions
|
||||
|
||||
string getDefUrl( string &show, const string &language, Curl &c );
|
||||
void findSeason( std::set< string > &files, int season, const string &path );
|
||||
void findSeasons( std::map< int, std::set< string > > &seasons,
|
||||
const string &path, const std::set< int > &season_numbers );
|
||||
void printHelp();
|
||||
|
||||
bool searchSpecificSeason( const char_t *const path, const string &number );
|
||||
bool searchSeason( const char_t *const path, size_t &season_pos );
|
||||
bool searchSeason( const char_t *const path );
|
||||
|
||||
void parseSeasonNumbers( std::set< int > &seasons_num, const char_t *argument );
|
||||
void printLangs();
|
||||
bool findLanguage( const char_t *language );
|
||||
|
||||
string encodeUrl( const string &url );
|
||||
|
||||
#else
|
||||
// GUI functions
|
||||
|
||||
std::vector< std::pair< string, string > >
|
||||
getPossibleShows( string show, const string &language, Curl &c );
|
||||
string userHome();
|
||||
|
||||
#endif
|
||||
|
||||
void iterateFS( std::map< int, std::set< string > > &seasons,
|
||||
const string &path );
|
||||
|
||||
bool searchSpecificSeason( const char_t *const path, size_t &ep_pos,
|
||||
const string &number );
|
||||
|
||||
string compilePattern( const string &pattern, int season, int episode,
|
||||
const string &filename, const string &episodeName,
|
||||
const string &showName );
|
||||
|
||||
#endif
|
||||
|
9
gui.cpp
9
gui.cpp
@ -2,10 +2,11 @@
|
||||
|
||||
#include "mainwindow.hpp"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto app = Gtk::Application::create(argc, argv, "org.idonthaveanorganization.tvrename");
|
||||
int main( int argc, char **argv ) {
|
||||
auto app = Gtk::Application::create(
|
||||
argc, argv, "org.idonthaveanorganization.tvrename" );
|
||||
|
||||
MainWindow mw(app);
|
||||
MainWindow mw( app );
|
||||
|
||||
return app->run(mw);
|
||||
return app->run( mw );
|
||||
}
|
||||
|
258
main.cpp
258
main.cpp
@ -1,40 +1,63 @@
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
|
||||
int parseCommandLine(std::string &show, std::set<int> &seasons_num, std::string &path, bool &change_dir, std::string &language, std::string &pattern, bool &linux, bool &trust, int argc, char **argv) {
|
||||
static struct option long_options[] = {
|
||||
{"show", required_argument, 0, 's' },
|
||||
{"season", required_argument, 0, 'n' },
|
||||
{"correct-path", no_argument, 0, 'c' },
|
||||
{"show-path", required_argument, 0, 'p' },
|
||||
{"trust", no_argument, 0, 't' },
|
||||
{"linux", no_argument, 0, 'x' },
|
||||
{"lang", required_argument, 0, 'l' },
|
||||
{"print-langs", no_argument, 0, '0' },
|
||||
{"name-pattern", required_argument, 0, '1' },
|
||||
{"help", no_argument, 0, 'h' }
|
||||
};
|
||||
#ifdef _WIN32
|
||||
|
||||
while(1) {
|
||||
int option_index{0};
|
||||
auto c = getopt_long(argc, argv, "s:n:cp:txl:01:h", long_options, &option_index);
|
||||
if( c == -1 )
|
||||
break;
|
||||
switch(c) {
|
||||
using char_t = wchar_t;
|
||||
using string = std::wstring;
|
||||
|
||||
#define cerr std::wcerr
|
||||
#define cout std::wcout
|
||||
#define cin std::wcin
|
||||
|
||||
#else
|
||||
|
||||
using char_t = char;
|
||||
using string = std::string;
|
||||
|
||||
#define cerr std::cerr
|
||||
#define cout std::cout
|
||||
#define cin std::cin
|
||||
|
||||
#define TEXT( a ) a
|
||||
|
||||
#endif
|
||||
|
||||
int handleArgument( char_t c, string &show, std::set< int > &seasons_num,
|
||||
bool &change_dir, string &path, bool &trust, bool &linux,
|
||||
string &language, string &pattern, char_t *optional,
|
||||
int &i ) {
|
||||
switch ( c ) {
|
||||
case 's':
|
||||
show = optarg;
|
||||
show = optional;
|
||||
i++;
|
||||
break;
|
||||
case 'n':
|
||||
parseSeasonNumbers(seasons_num, optarg);
|
||||
parseSeasonNumbers( seasons_num, optional );
|
||||
i++;
|
||||
break;
|
||||
case 'c':
|
||||
change_dir = false;
|
||||
break;
|
||||
case 'p':
|
||||
path = std::string(optarg);
|
||||
path = string( optional );
|
||||
i++;
|
||||
// if path provided, assume it's correct
|
||||
change_dir = false;
|
||||
break;
|
||||
@ -45,13 +68,14 @@ int parseCommandLine(std::string &show, std::set<int> &seasons_num, std::string
|
||||
linux = true;
|
||||
break;
|
||||
case 'l':
|
||||
if( findLanguage(optarg) ) {
|
||||
language = optarg;
|
||||
if ( findLanguage( optional ) ) {
|
||||
language = optional;
|
||||
} else {
|
||||
std::cerr << "Invalid language choice" << std::endl;
|
||||
cerr << "Invalid language choice" << std::endl;
|
||||
printLangs();
|
||||
return -1;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '0':
|
||||
printLangs();
|
||||
@ -60,78 +84,178 @@ int parseCommandLine(std::string &show, std::set<int> &seasons_num, std::string
|
||||
printHelp();
|
||||
return 1;
|
||||
case '1':
|
||||
pattern = optarg;
|
||||
pattern = optional;
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
string getOptions( const char_t *option ) {
|
||||
if ( option[1] != '-' )
|
||||
return option + 1;
|
||||
if ( !wcscmp( option, L"--show" ) )
|
||||
return L"s";
|
||||
else if ( !wcscmp( option, L"--season" ) )
|
||||
return L"n";
|
||||
else if ( !wcscmp( option, L"--correct-path" ) )
|
||||
return L"c";
|
||||
else if ( !wcscmp( option, L"--show-path" ) )
|
||||
return L"p";
|
||||
else if ( !wcscmp( option, L"--trust" ) )
|
||||
return L"t";
|
||||
else if ( !wcscmp( option, L"--linux" ) )
|
||||
return L"x";
|
||||
else if ( !wcscmp( option, L"--lang" ) )
|
||||
return L"l";
|
||||
else if ( !wcscmp( option, L"--print-langs" ) )
|
||||
return L"0";
|
||||
else if ( !wcscmp( option, L"--name-pattern" ) )
|
||||
return L"1";
|
||||
else if ( !wcscmp( option, L"--help" ) )
|
||||
return L"h";
|
||||
return L"";
|
||||
}
|
||||
|
||||
// there's no getopt for windows, so just use wcscmp
|
||||
int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
|
||||
bool &change_dir, string &language, string &pattern,
|
||||
bool &linux, bool &trust, const int argc,
|
||||
char_t **argv ) {
|
||||
for ( auto i = 1; i < argc; i++ ) {
|
||||
auto options = getOptions( argv[i] );
|
||||
char_t *optional = ( i < argc - 1 ) ? argv[i + 1] : nullptr;
|
||||
for ( const auto &x : options ) {
|
||||
auto res =
|
||||
handleArgument( x, show, seasons_num, change_dir, path, trust,
|
||||
linux, language, pattern, optional, i );
|
||||
if ( res != 0 )
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// parse command line arguments using getopt
|
||||
int parseCommandLine( string &show, std::set< int > &seasons_num, string &path,
|
||||
bool &change_dir, string &language, string &pattern,
|
||||
bool &linux, bool &trust, int argc, char **argv ) {
|
||||
static struct option long_options[] = {
|
||||
{ "show", required_argument, 0, 's' },
|
||||
{ "season", required_argument, 0, 'n' },
|
||||
{ "correct-path", no_argument, 0, 'c' },
|
||||
{ "show-path", required_argument, 0, 'p' },
|
||||
{ "trust", no_argument, 0, 't' },
|
||||
{ "linux", no_argument, 0, 'x' },
|
||||
{ "lang", required_argument, 0, 'l' },
|
||||
{ "print-langs", no_argument, 0, '0' },
|
||||
{ "name-pattern", required_argument, 0, '1' },
|
||||
{ "help", no_argument, 0, 'h' }
|
||||
};
|
||||
|
||||
int i{}; // this is useless, but needed for handleArgument
|
||||
|
||||
while ( 1 ) {
|
||||
int option_index{ 0 };
|
||||
auto c = getopt_long( argc, argv, "s:n:cp:txl:01:h", long_options,
|
||||
&option_index );
|
||||
if ( c == -1 )
|
||||
break;
|
||||
auto res = handleArgument( c, show, seasons_num, change_dir, path,
|
||||
trust, linux, language, pattern, optarg, i );
|
||||
if ( res != 0 )
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::string show{};
|
||||
std::set<int> seasons_num;
|
||||
std::string path{"."};
|
||||
bool change_dir{true};
|
||||
std::string language{"en"};
|
||||
bool linux{false};
|
||||
bool trust{false};
|
||||
std::string pattern{"%filename - %epname"};
|
||||
#endif
|
||||
|
||||
// windows needs wmain for unicode support
|
||||
#ifdef _WIN32
|
||||
int wmain
|
||||
#else
|
||||
int main
|
||||
#endif
|
||||
( int argc, char_t **argv ) {
|
||||
#ifdef _WIN32
|
||||
// set console to unicode
|
||||
_setmode( _fileno( stdout ), _O_U16TEXT );
|
||||
#endif
|
||||
string show{};
|
||||
std::set< int > seasons_num{};
|
||||
bool change_dir{ true };
|
||||
bool linux{ false };
|
||||
bool trust{ false };
|
||||
string path{ TEXT( "." ) };
|
||||
string language{ TEXT( "en" ) };
|
||||
string pattern{ TEXT( "%filename - %epname" ) };
|
||||
Curl c;
|
||||
|
||||
{
|
||||
auto tmp = parseCommandLine(show, seasons_num, path, change_dir, language, pattern, linux, trust, argc, argv);
|
||||
if( tmp == -1 )
|
||||
auto tmp =
|
||||
parseCommandLine( show, seasons_num, path, change_dir, language,
|
||||
pattern, linux, trust, argc, argv );
|
||||
if ( tmp == -1 )
|
||||
return 1;
|
||||
else if ( tmp == 1 )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !FSLib::isDirectory(path) )
|
||||
if ( !FSLib::isDirectory( path ) )
|
||||
change_dir = true;
|
||||
|
||||
while( change_dir ) {
|
||||
if( !FSLib::isDirectory(path) ) {
|
||||
std::cout << "This directory doesn't exist, please insert a correct path: " << std::endl;
|
||||
std::getline(std::cin, path);
|
||||
while ( change_dir ) {
|
||||
if ( !FSLib::isDirectory( path ) ) {
|
||||
cout << "This directory doesn't exist, please insert a correct "
|
||||
"path: "
|
||||
<< std::endl;
|
||||
std::getline( cin, path );
|
||||
continue;
|
||||
}
|
||||
std::cout << "Is this the right directory? " << FSLib::canonical(path) << std::endl;
|
||||
std::string response;
|
||||
std::cin >> response;
|
||||
std::cin.ignore(1,'\n');
|
||||
std::cin.clear();
|
||||
cout << "Is this the right directory? " << FSLib::canonical( path )
|
||||
<< std::endl;
|
||||
string response;
|
||||
cin >> response;
|
||||
cin.ignore( 1, '\n' );
|
||||
cin.clear();
|
||||
if ( response[0] == 'y' || response[0] == 'Y' ) {
|
||||
change_dir = false;
|
||||
} else {
|
||||
std::cout << "Insert correct path:" << std::endl;
|
||||
std::getline(std::cin, path);
|
||||
cout << "Insert correct path:" << std::endl;
|
||||
std::getline( cin, path );
|
||||
}
|
||||
}
|
||||
|
||||
if( show.empty() ) {
|
||||
auto pos = show.find_last_of('/');
|
||||
if( pos != std::string::npos )
|
||||
show = show.substr(++pos);
|
||||
std::cout << "Is this the right show name? " << show << std::endl;
|
||||
std::string response;
|
||||
std::cin >> response;
|
||||
std::cin.ignore(1, '\n');
|
||||
std::cin.clear();
|
||||
if( response[0] != 'y' && response[0] != 'Y' ) {
|
||||
std::cout << "Insert the correct show name: " << std::endl;
|
||||
std::getline(std::cin, show);
|
||||
if ( show.empty() ) {
|
||||
auto pos = show.find_last_of( '/' );
|
||||
if ( pos != string::npos )
|
||||
show = show.substr( ++pos );
|
||||
cout << "Is this the right show name? " << show << std::endl;
|
||||
string response;
|
||||
cin >> response;
|
||||
cin.ignore( 1, '\n' );
|
||||
cin.clear();
|
||||
if ( response[0] != 'y' && response[0] != 'Y' ) {
|
||||
cout << "Insert the correct show name: " << std::endl;
|
||||
std::getline( cin, show );
|
||||
}
|
||||
}
|
||||
|
||||
if( seasons_num.size() == 1 ) {
|
||||
singleSeason(path, show, *seasons_num.begin(), "", language, pattern, linux, trust, c);
|
||||
if ( seasons_num.size() == 1 ) {
|
||||
singleSeason( path, show, *seasons_num.begin(), string(), language,
|
||||
pattern, linux, trust, c );
|
||||
} else if ( seasons_num.size() != 0 ) {
|
||||
multipleSeasons(path, show, seasons_num, language, pattern, linux, trust, c);
|
||||
multipleSeasons( path, show, seasons_num, language, pattern, linux,
|
||||
trust, c );
|
||||
} else {
|
||||
allSeasons(path, show, language, pattern, linux, trust, c);
|
||||
allSeasons( path, show, language, pattern, linux, trust, c );
|
||||
}
|
||||
}
|
||||
|
||||
|
258
mainwindow.cpp
258
mainwindow.cpp
@ -1,36 +1,38 @@
|
||||
#include "mainwindow.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <fstream>
|
||||
#include <gtkmm/filechooserdialog.h>
|
||||
#include <gtkmm/liststore.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"
|
||||
#include "filesystem.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "mainwindow.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
|
||||
constexpr std::array< const char *, 46 > languages{
|
||||
"en", "English", "sv", "Svenska", "no", "Norsk", "da", "Dansk",
|
||||
"fi", "Suomeksi", "nl", "Nederlands", "de", "Deutsch", "it", "Italiano",
|
||||
"es", "Español", "fr", "Français", "pl", "Polski", "hu", "Magyar",
|
||||
"el", "Greek", "tr", "Turkish", "ru", "Russian", "he", "Hebrew",
|
||||
"ja", "Japanese", "pt", "Portuguese", "zh", "Chinese", "cs", "Czech",
|
||||
"sl", "Slovenian", "hr", "Croatian", "ko", "Korea"
|
||||
};
|
||||
|
||||
void MainWindow::chooseFile() {
|
||||
// create a dialog for choosing directory
|
||||
Gtk::FileChooserDialog dialog("Select a directory",
|
||||
Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||
dialog.set_transient_for(*this);
|
||||
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);
|
||||
dialog.add_button( "_Cancel", Gtk::RESPONSE_CANCEL );
|
||||
dialog.add_button( "Select", Gtk::RESPONSE_OK );
|
||||
|
||||
auto result = dialog.run();
|
||||
|
||||
switch(result) {
|
||||
switch ( result ) {
|
||||
case Gtk::RESPONSE_OK:
|
||||
m_entry_dir.set_text(dialog.get_filename());
|
||||
m_entry_dir.set_text( dialog.get_filename() );
|
||||
std::cout << dialog.get_filename() << std::endl;
|
||||
break;
|
||||
case Gtk::RESPONSE_CANCEL:
|
||||
@ -48,74 +50,80 @@ void MainWindow::quit() {
|
||||
}
|
||||
|
||||
void MainWindow::patternHelp() {
|
||||
Gtk::MessageDialog dialog(*this, "Pattern escape sequences");
|
||||
dialog.set_secondary_text( "%filename - original filename (without type extension)\n"
|
||||
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"
|
||||
"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\""
|
||||
);
|
||||
"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");
|
||||
// check required field is filled out
|
||||
if ( m_entry_show.get_text().empty() ) {
|
||||
Gtk::MessageDialog dialog( *this, "Show field is empty" );
|
||||
dialog.run();
|
||||
return;
|
||||
}
|
||||
|
||||
// language code
|
||||
language_code = (*m_combo_language.get_active())[m_columns_language.m_col_code];
|
||||
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 );
|
||||
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");
|
||||
if ( possible_shows.size() == 0 ) {
|
||||
Gtk::MessageDialog dialog( *this,
|
||||
"No results found for given show name" );
|
||||
dialog.run();
|
||||
return;
|
||||
}
|
||||
|
||||
//show widgets
|
||||
// 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);
|
||||
auto model = Gtk::ListStore::create( m_columns_url );
|
||||
|
||||
m_combo_possible.set_model(model);
|
||||
m_combo_possible.set_model( model );
|
||||
|
||||
auto row = *(model->append());
|
||||
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);
|
||||
m_combo_possible.set_active( row );
|
||||
|
||||
for( size_t i = 1; i < possible_shows.size(); i++ ) {
|
||||
auto row = *(model->append());
|
||||
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");
|
||||
// check required field is filled out
|
||||
if ( m_entry_dir.get_text().empty() ) {
|
||||
Gtk::MessageDialog dialog( *this, "Directory field is empty" );
|
||||
dialog.run();
|
||||
return;
|
||||
}
|
||||
// check directory exists
|
||||
if( !FSLib::isDirectory(m_entry_dir.get_text()) ) {
|
||||
Gtk::MessageDialog dialog(*this, "Directory doesn't exist");
|
||||
if ( !FSLib::isDirectory( m_entry_dir.get_text() ) ) {
|
||||
Gtk::MessageDialog dialog( *this, "Directory doesn't exist" );
|
||||
dialog.run();
|
||||
return;
|
||||
}
|
||||
@ -125,21 +133,22 @@ void MainWindow::getNames() {
|
||||
selected.clear();
|
||||
files.clear();
|
||||
|
||||
std::vector<int> options;
|
||||
std::vector< int > options;
|
||||
|
||||
// get all files in path and seperate them in map `files` by season
|
||||
iterateFS(files, path);
|
||||
iterateFS( files, path );
|
||||
|
||||
for( auto &x : files ) {
|
||||
options.push_back(x.first);
|
||||
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));
|
||||
sw = new SeasonWindow( options, selected );
|
||||
sw->signal_hide().connect(
|
||||
sigc::mem_fun( *this, &MainWindow::finishedSelection ) );
|
||||
|
||||
app->add_window(*sw);
|
||||
app->add_window( *sw );
|
||||
sw->show();
|
||||
}
|
||||
|
||||
@ -147,113 +156,123 @@ void MainWindow::getNames() {
|
||||
* orig - original filenames
|
||||
* renamed - renamed filenames (sorted in the same order as `orig`)
|
||||
*/
|
||||
void renameFiles(const std::vector<std::pair<std::string, std::pair<std::string, std::string>>> &renamed) {
|
||||
for(auto renamed_it = renamed.begin(); renamed_it != renamed.end(); ++renamed_it) {
|
||||
std::cout << renamed_it->first << "/" << renamed_it->second.first << " --> "
|
||||
<< renamed_it->first << "/" << renamed_it->second.second << std::endl;
|
||||
FSLib::rename(renamed_it->first + "/" + renamed_it->second.first
|
||||
, renamed_it->first + "/" + renamed_it->second.second);
|
||||
void renameFiles(
|
||||
const std::vector<
|
||||
std::pair< std::string, std::pair< std::string, std::string > > >
|
||||
&renamed ) {
|
||||
for ( auto renamed_it = renamed.begin(); renamed_it != renamed.end();
|
||||
++renamed_it ) {
|
||||
std::cout << renamed_it->first << "/" << renamed_it->second.first
|
||||
<< " --> " << renamed_it->first << "/"
|
||||
<< renamed_it->second.second << std::endl;
|
||||
FSLib::rename( renamed_it->first + "/" + renamed_it->second.first,
|
||||
renamed_it->first + "/" + renamed_it->second.second );
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::finishedSelection() {
|
||||
// remove created SeasonWindow and delete it from memory
|
||||
app->remove_window(*sw);
|
||||
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 << ( *iter )[m_columns_url.m_col_show] << " " << language_code
|
||||
<< std::endl;
|
||||
|
||||
std::string url = static_cast<Glib::ustring>((*iter)[m_columns_url.m_col_url]);
|
||||
std::string url =
|
||||
static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_url] );
|
||||
|
||||
// shouldn't ever happen, but just to be sure
|
||||
if( url.empty() )
|
||||
if ( url.empty() )
|
||||
return;
|
||||
|
||||
std::cout << "https://www.thetvdb.com" << 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 ) {
|
||||
// store pattern to cache if it's different from default
|
||||
if ( input_pattern != default_pattern ) {
|
||||
std::ofstream file( userHome() + "/.cache/tv_rename_pattern" );
|
||||
if ( file ) {
|
||||
file << input_pattern;
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &x : selected ) {
|
||||
for ( auto &x : selected ) {
|
||||
// get renamed files for given season
|
||||
auto renamed_files = getRenamedFiles( static_cast<Glib::ustring>((*iter)[m_columns_url.m_col_show]), x,
|
||||
"https://www.thetvdb.com" + url,
|
||||
language_code,
|
||||
(input_pattern.empty() ? default_pattern : input_pattern),
|
||||
auto renamed_files = getRenamedFiles(
|
||||
static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_show] ),
|
||||
x, "https://www.thetvdb.com" + url, language_code,
|
||||
( input_pattern.empty() ? default_pattern : input_pattern ),
|
||||
!m_check_linux.get_active(), c, files[x] );
|
||||
|
||||
if( renamed_files.empty() )
|
||||
if ( renamed_files.empty() )
|
||||
continue;
|
||||
|
||||
// if trust checkbox is ticked, rename files
|
||||
if( m_check_trust.get_active() ) {
|
||||
renameFiles(renamed_files);
|
||||
if ( m_check_trust.get_active() ) {
|
||||
renameFiles( renamed_files );
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a custom dialog box with textview of new episode names
|
||||
Gtk::Dialog dialog("Rename confirmation", *this);
|
||||
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);
|
||||
content->pack_start( tx );
|
||||
tx.set_editable( false );
|
||||
dialog.add_button( "_No", Gtk::RESPONSE_CANCEL );
|
||||
dialog.add_button( "_Yes", Gtk::RESPONSE_OK );
|
||||
tx.show();
|
||||
|
||||
auto buff = tx.get_buffer();
|
||||
buff->place_cursor(buff->begin());
|
||||
buff->insert_at_cursor(renamed_files[0].second.first.c_str());
|
||||
buff->insert_at_cursor(" --> ");
|
||||
buff->insert_at_cursor(renamed_files[0].second.second.c_str());
|
||||
buff->place_cursor( buff->begin() );
|
||||
buff->insert_at_cursor( renamed_files[0].second.first.c_str() );
|
||||
buff->insert_at_cursor( " --> " );
|
||||
buff->insert_at_cursor( renamed_files[0].second.second.c_str() );
|
||||
|
||||
for( size_t i = 1; i < renamed_files.size(); i++ ) {
|
||||
buff->insert_at_cursor("\n");
|
||||
buff->insert_at_cursor(renamed_files[i].second.first.c_str());
|
||||
buff->insert_at_cursor(" --> ");
|
||||
buff->insert_at_cursor(renamed_files[i].second.second.c_str());
|
||||
for ( size_t i = 1; i < renamed_files.size(); i++ ) {
|
||||
buff->insert_at_cursor( "\n" );
|
||||
buff->insert_at_cursor( renamed_files[i].second.first.c_str() );
|
||||
buff->insert_at_cursor( " --> " );
|
||||
buff->insert_at_cursor( renamed_files[i].second.second.c_str() );
|
||||
}
|
||||
|
||||
auto response = dialog.run();
|
||||
|
||||
// if user clicked "Yes" in dialog, rename files
|
||||
switch(response) {
|
||||
switch ( response ) {
|
||||
case Gtk::RESPONSE_OK:
|
||||
renameFiles(renamed_files);
|
||||
renameFiles( renamed_files );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(const Glib::RefPtr<Gtk::Application> &ptr) : app(ptr) {
|
||||
set_title("TV Rename");
|
||||
MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr )
|
||||
: app( ptr ) {
|
||||
set_title( "TV Rename" );
|
||||
|
||||
set_default_size(400, 310);
|
||||
set_resizable(false);
|
||||
set_default_size( 400, 310 );
|
||||
set_resizable( false );
|
||||
|
||||
{
|
||||
std::ifstream file(userHome() + "/.cache/tv_rename_pattern");
|
||||
if( file ) {
|
||||
// if cached pattern exists, load that instead of default
|
||||
std::ifstream file( userHome() + "/.cache/tv_rename_pattern" );
|
||||
if ( file ) {
|
||||
std::getline( file, default_pattern );
|
||||
} else {
|
||||
default_pattern = "%filename - %epname";
|
||||
}
|
||||
}
|
||||
|
||||
add( m_layout );
|
||||
|
||||
add(m_layout);
|
||||
|
||||
// set widgets' location
|
||||
m_layout.put( m_label_show, 5, 5 );
|
||||
m_layout.put( m_label_language, 190, 5 );
|
||||
m_layout.put( m_entry_show, 5, 25 );
|
||||
@ -301,35 +320,45 @@ MainWindow::MainWindow(const Glib::RefPtr<Gtk::Application> &ptr) : app(ptr) {
|
||||
m_button_get_names.set_size_request( 80, 30 );
|
||||
|
||||
// set default pattern
|
||||
m_entry_pattern.set_text(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());
|
||||
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());
|
||||
|
||||
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];
|
||||
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);
|
||||
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));
|
||||
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();
|
||||
@ -342,11 +371,10 @@ MainWindow::MainWindow(const Glib::RefPtr<Gtk::Application> &ptr) : app(ptr) {
|
||||
m_button_quit.show();
|
||||
m_button_dir.show();
|
||||
m_check_linux.show();
|
||||
m_check_linux.set_active(true);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include <gtkmm/checkbutton.h>
|
||||
#include <gtkmm/combobox.h>
|
||||
#include <gtkmm/entry.h>
|
||||
#include <gtkmm/layout.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/layout.h>
|
||||
#include <gtkmm/window.h>
|
||||
#include <set>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
class MainWindow : public Gtk::Window {
|
||||
public:
|
||||
MainWindow(const Glib::RefPtr<Gtk::Application> &ptr);
|
||||
MainWindow( const Glib::RefPtr< Gtk::Application > &ptr );
|
||||
virtual ~MainWindow() = default;
|
||||
|
||||
private:
|
||||
@ -55,37 +55,36 @@ protected:
|
||||
|
||||
class LanguageColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
public:
|
||||
|
||||
LanguageColumns() {
|
||||
add(m_col_code);
|
||||
add(m_col_language);
|
||||
add( m_col_code );
|
||||
add( m_col_language );
|
||||
}
|
||||
|
||||
Gtk::TreeModelColumn<std::string> m_col_code;
|
||||
Gtk::TreeModelColumn<std::string> m_col_language;
|
||||
Gtk::TreeModelColumn< std::string > m_col_code;
|
||||
Gtk::TreeModelColumn< std::string > m_col_language;
|
||||
};
|
||||
|
||||
class UrlColumns: public Gtk::TreeModel::ColumnRecord {
|
||||
class UrlColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
public:
|
||||
UrlColumns() {
|
||||
add(m_col_url);
|
||||
add(m_col_show);
|
||||
add( m_col_url );
|
||||
add( m_col_show );
|
||||
}
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_url;
|
||||
Gtk::TreeModelColumn<Glib::ustring> 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;
|
||||
Glib::RefPtr< Gtk::Application > app;
|
||||
|
||||
SeasonWindow *sw;
|
||||
std::vector<int> selected;
|
||||
std::map<int, std::set<std::string>> files;
|
||||
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
|
||||
#endif // GTKMM_MAIN_WINDOW
|
||||
|
77
network.cpp
77
network.cpp
@ -1,32 +1,83 @@
|
||||
#include <iostream>
|
||||
#include "network.hpp"
|
||||
|
||||
size_t writeCallback( void *contents, size_t size, size_t nmemb, void *target ) {
|
||||
*static_cast<std::string*>(target) += std::string(static_cast<char*>(contents), size*nmemb);
|
||||
#ifdef _WIN32
|
||||
|
||||
// shamelessly stolen from http://www.cplusplus.com/forum/windows/109799/
|
||||
|
||||
Curl::Curl() {
|
||||
connect = InternetOpen( L"WinInet/1.0", INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
nullptr, nullptr, 0 );
|
||||
if ( !connect )
|
||||
std::wcerr << "Something went wrong while connecting" << std::endl;
|
||||
}
|
||||
|
||||
Curl::~Curl() {
|
||||
if ( connect )
|
||||
InternetCloseHandle( connect );
|
||||
}
|
||||
|
||||
std::string Curl::execute( const std::wstring &url ) {
|
||||
std::string source{};
|
||||
source.reserve( 10000 );
|
||||
|
||||
HINTERNET openAddress = InternetOpenUrl(
|
||||
connect, url.c_str(), nullptr, 0,
|
||||
INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION, 0 );
|
||||
|
||||
if ( !openAddress ) {
|
||||
unsigned long errorNum = GetLastError();
|
||||
std::wcout << "Failed to open URL" << std::endl
|
||||
<< "Error No: " << errorNum << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
char dataReceived[4096];
|
||||
unsigned long numberOfBytesRead = 0;
|
||||
while ( InternetReadFile( openAddress, dataReceived, 4096,
|
||||
&numberOfBytesRead ) &&
|
||||
numberOfBytesRead ) {
|
||||
source.append( dataReceived, numberOfBytesRead );
|
||||
}
|
||||
|
||||
InternetCloseHandle( openAddress );
|
||||
return source;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
size_t writeCallback( void *contents, size_t size, size_t nmemb,
|
||||
void *target ) {
|
||||
*static_cast< std::string * >( target ) +=
|
||||
std::string( static_cast< char * >( contents ), size * nmemb );
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
Curl::Curl() {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl_global_init( CURL_GLOBAL_ALL );
|
||||
curl_handle = curl_easy_init();
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeCallback);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
curl_easy_setopt( curl_handle, CURLOPT_WRITEFUNCTION, writeCallback );
|
||||
curl_easy_setopt( curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0" );
|
||||
}
|
||||
|
||||
Curl::~Curl() {
|
||||
curl_easy_cleanup(curl_handle);
|
||||
curl_easy_cleanup( curl_handle );
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
std::string Curl::execute(const std::string &url) {
|
||||
std::string Curl::execute( const std::string &url ) {
|
||||
std::string source;
|
||||
source.reserve(100000);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, static_cast<void*>(&source));
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
|
||||
auto res = curl_easy_perform(curl_handle);
|
||||
if(res != CURLE_OK) {
|
||||
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
|
||||
source.reserve( 100000 );
|
||||
curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA,
|
||||
static_cast< void * >( &source ) );
|
||||
curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() );
|
||||
auto res = curl_easy_perform( curl_handle );
|
||||
if ( res != CURLE_OK ) {
|
||||
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res )
|
||||
<< std::endl;
|
||||
return "";
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
20
network.hpp
20
network.hpp
@ -1,16 +1,34 @@
|
||||
#ifndef NETWORK_HPP
|
||||
#define NETWORK_HPP
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
#include <WinInet.h>
|
||||
|
||||
using string = std::wstring;
|
||||
|
||||
#else
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
|
||||
using string = std::string;
|
||||
|
||||
#endif
|
||||
|
||||
class Curl {
|
||||
public:
|
||||
Curl();
|
||||
~Curl();
|
||||
std::string execute( const std::string &url );
|
||||
std::string execute( const string &url );
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HINTERNET connect = nullptr;
|
||||
#else
|
||||
CURL *curl_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
50
resource.h
Normal file
50
resource.h
Normal file
@ -0,0 +1,50 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by tv_rename_gui.rc
|
||||
//
|
||||
#define IDC_MYICON 2
|
||||
#define IDD_TV_RENAME_GUI_DIALOG 102
|
||||
#define IDS_APP_TITLE 103
|
||||
#define IDI_TV_RENAME_GUI 107
|
||||
#define IDI_SMALL 108
|
||||
#define IDC_TV_RENAME_GUI 109
|
||||
#define IDR_MAINFRAME 128
|
||||
#define IDD_MAIN 129
|
||||
#define IDD_SEASONS 130
|
||||
#define IDD_HELP 131
|
||||
#define IDD_DIALOG1 132
|
||||
#define IDD_PREVIEW 132
|
||||
#define IDC_COMBO1 1000
|
||||
#define IDLANG 1000
|
||||
#define IDOK_MAIN 1001
|
||||
#define ID 1002
|
||||
#define IDEND 1002
|
||||
#define IDALL 1003
|
||||
#define IDC_BUTTON2 1004
|
||||
#define IDNONE 1004
|
||||
#define IDDIRB 1004
|
||||
#define IDSHOWIN 1006
|
||||
#define ID_STATIC 1007
|
||||
#define IDLANGUAGE 1009
|
||||
#define IDDIR 1010
|
||||
#define IDPATTERN 1011
|
||||
#define IDPROCESS 1013
|
||||
#define IDTRUST 1015
|
||||
#define IDSHOWS 1016
|
||||
#define IDSEASONS 1017
|
||||
#define IDC_STATIC6 1019
|
||||
#define ID_STATIC6 1019
|
||||
#define IDTEXT 1020
|
||||
#define IDC_STATIC -1
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NO_MFC 1
|
||||
#define _APS_NEXT_RESOURCE_VALUE 133
|
||||
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||
#define _APS_NEXT_CONTROL_VALUE 1021
|
||||
#define _APS_NEXT_SYMED_VALUE 110
|
||||
#endif
|
||||
#endif
|
@ -5,9 +5,9 @@ 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()));
|
||||
for ( auto &x : m_checks ) {
|
||||
if ( x.get_active() ) {
|
||||
returned.push_back( std::stoi( x.get_label() ) );
|
||||
}
|
||||
}
|
||||
hide();
|
||||
@ -15,34 +15,36 @@ void SeasonWindow::confirm() {
|
||||
|
||||
void SeasonWindow::select_all() {
|
||||
// set all check boxes to checked
|
||||
for( auto &x : m_checks ) {
|
||||
x.set_active(true);
|
||||
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);
|
||||
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");
|
||||
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);
|
||||
set_default_size( 250, 250 );
|
||||
set_resizable( false );
|
||||
|
||||
add(m_layout);
|
||||
add( m_layout );
|
||||
|
||||
size_t x{5}, y{25};
|
||||
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);
|
||||
for ( auto &s : seasons ) {
|
||||
m_checks.emplace_back( std::to_string( s ) );
|
||||
m_layout.put( m_checks.back(), x, y );
|
||||
|
||||
if( x == 185 ) {
|
||||
if ( x == 185 ) {
|
||||
x = 5;
|
||||
y += 20;
|
||||
} else {
|
||||
@ -50,24 +52,27 @@ SeasonWindow::SeasonWindow(const std::vector<int> &seasons, std::vector<int> &_r
|
||||
}
|
||||
}
|
||||
|
||||
m_layout.put(m_label, 5, 5);
|
||||
m_label.set_label("Select seasons:");
|
||||
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_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_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.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));
|
||||
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();
|
||||
}
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/checkbutton.h>
|
||||
#include <gtkmm/layout.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/layout.h>
|
||||
#include <gtkmm/window.h>
|
||||
|
||||
class SeasonWindow : public Gtk::Window {
|
||||
public:
|
||||
SeasonWindow(const std::vector<int> &seasons, std::vector<int> &_returned);
|
||||
SeasonWindow( const std::vector< int > &seasons,
|
||||
std::vector< int > &_returned );
|
||||
virtual ~SeasonWindow() = default;
|
||||
|
||||
private:
|
||||
@ -26,9 +27,9 @@ protected:
|
||||
|
||||
Gtk::Layout m_layout;
|
||||
|
||||
std::vector<Gtk::CheckButton> m_checks;
|
||||
std::vector< Gtk::CheckButton > m_checks;
|
||||
|
||||
std::vector<int> &returned;
|
||||
std::vector< int > &returned;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
278
tv_rename.cpp
278
tv_rename.cpp
@ -1,6 +1,15 @@
|
||||
#include <algorithm>
|
||||
#include "functions.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
#include <map>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <codecvt>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <locale>
|
||||
#include <windows.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
@ -11,24 +20,53 @@
|
||||
|
||||
#endif
|
||||
|
||||
std::vector<std::string> parseEpisodeNames( const std::string &season_code, const std::string &language) {
|
||||
std::vector<std::string> episodes;
|
||||
#include "functions.hpp"
|
||||
#include "tv_rename.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
constexpr const char_t *dir_divider = L"\\";
|
||||
|
||||
#define cout std::wcout
|
||||
#define cerr std::wcerr
|
||||
#define cin std::wcin
|
||||
|
||||
#else
|
||||
|
||||
constexpr const char_t *dir_divider = "/";
|
||||
|
||||
#define cout std::cout
|
||||
#define cerr std::cerr
|
||||
#define cin std::cin
|
||||
|
||||
#define TEXT( a ) a
|
||||
|
||||
#endif
|
||||
|
||||
// get names for all episodes for a given season
|
||||
std::vector< string > parseEpisodeNames( const string &season_code,
|
||||
const string &language ) {
|
||||
std::vector< string > episodes;
|
||||
size_t pos = 0;
|
||||
while( true ) {
|
||||
pos = season_code.find("ge=\"" + language + "\" ", pos);
|
||||
if( pos != std::string::npos ) {
|
||||
if( language == "en" )
|
||||
while ( true ) {
|
||||
pos =
|
||||
season_code.find( TEXT( "ge=\"" ) + language + TEXT( "\" " ), pos );
|
||||
if ( pos != string::npos ) {
|
||||
if ( language == TEXT( "en" ) )
|
||||
pos += 17;
|
||||
else
|
||||
pos += 29;
|
||||
while( isspace(season_code[pos]) )
|
||||
while ( iswspace( season_code[pos] ) )
|
||||
pos++;
|
||||
auto end = season_code.find("<", pos);
|
||||
|
||||
auto end = season_code.find( TEXT( "<" ), pos );
|
||||
end--;
|
||||
while( isspace(season_code[end]) )
|
||||
|
||||
while ( iswspace( season_code[end] ) )
|
||||
end--;
|
||||
|
||||
end++;
|
||||
episodes.push_back(season_code.substr(pos, end - pos));
|
||||
episodes.push_back( season_code.substr( pos, end - pos ) );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -36,96 +74,130 @@ std::vector<std::string> parseEpisodeNames( const std::string &season_code, cons
|
||||
return episodes;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> getRenamedFiles( 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 ) {
|
||||
url += "/seasons/" + std::to_string(season);
|
||||
//get source code of season's page
|
||||
auto season_code = c.execute(url);
|
||||
//remove newlines
|
||||
season_code.erase(std::remove_if(season_code.begin(), season_code.end(), [](char x) {return x == '\r' || x == '\n';}), season_code.end());
|
||||
//first 900 chars or so are useless to us
|
||||
season_code = season_code.substr(900);
|
||||
//get only the episode names
|
||||
auto pos = season_code.find("\"translations\"");
|
||||
if( pos != std::string::npos ) {
|
||||
season_code = season_code.substr(pos);
|
||||
pos = season_code.find("</table>");
|
||||
if( pos != std::string::npos )
|
||||
season_code = season_code.substr(0,pos);
|
||||
std::vector< std::pair< string, std::pair< string, string > > >
|
||||
getRenamedFiles( const string &show, int season, string url,
|
||||
const string &language, const string &pattern,
|
||||
const bool &linux, Curl &c, const std::set< string > &files ) {
|
||||
#ifdef _WIN32
|
||||
auto season_num = std::to_wstring( season );
|
||||
#else
|
||||
auto season_num = std::to_string( season );
|
||||
#endif
|
||||
|
||||
url += TEXT( "/seasons/" );
|
||||
url += season_num;
|
||||
|
||||
// get source code of season's page
|
||||
auto season_code = c.execute( url );
|
||||
|
||||
// remove newlines
|
||||
season_code.erase(
|
||||
std::remove_if( season_code.begin(), season_code.end(),
|
||||
[]( char x ) { return x == '\r' || x == '\n'; } ),
|
||||
season_code.end() );
|
||||
|
||||
// first 900 chars or so are useless to us
|
||||
season_code = season_code.substr( 900 );
|
||||
|
||||
// get only the episode names
|
||||
auto pos = season_code.find( "\"translations\"" );
|
||||
|
||||
if ( pos != string::npos ) {
|
||||
season_code = season_code.substr( pos );
|
||||
pos = season_code.find( "</table>" );
|
||||
if ( pos != string::npos )
|
||||
season_code = season_code.substr( 0, pos );
|
||||
else
|
||||
return {};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto episodes = parseEpisodeNames(season_code, language);
|
||||
#ifdef _WIN32
|
||||
auto episodes =
|
||||
parseEpisodeNames( utf8_to_wstring( season_code ), language );
|
||||
#else
|
||||
auto episodes = parseEpisodeNames( season_code, language );
|
||||
#endif
|
||||
|
||||
if( episodes.empty() )
|
||||
if ( episodes.empty() )
|
||||
return {};
|
||||
|
||||
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> renamed_files;
|
||||
|
||||
if( files.empty() )
|
||||
if ( files.empty() )
|
||||
return {};
|
||||
|
||||
for( const auto &x : files ) {
|
||||
auto last = x.find_last_of("/");
|
||||
std::string og_name;
|
||||
std::string dir;
|
||||
if( last == std::string::npos ) {
|
||||
std::vector< std::pair< string, std::pair< string, string > > >
|
||||
renamed_files;
|
||||
|
||||
for ( const auto &x : files ) {
|
||||
auto last = x.find_last_of( dir_divider );
|
||||
string og_name;
|
||||
string dir;
|
||||
if ( last == string::npos ) {
|
||||
og_name = x;
|
||||
dir = ".";
|
||||
dir = TEXT( "." );
|
||||
} else {
|
||||
og_name = x.substr(last+1);
|
||||
dir = x.substr(0, last);
|
||||
og_name = x.substr( last + 1 );
|
||||
dir = x.substr( 0, last );
|
||||
}
|
||||
unsigned long num;
|
||||
// get file's episode number
|
||||
if( searchSpecificSeason(x.c_str(), pos, std::to_string(season)) ) {
|
||||
num = atoi(x.c_str()+pos);
|
||||
if ( searchSpecificSeason( x.c_str(), pos, season_num ) ) {
|
||||
num = std::stoi( x.c_str() + pos );
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
num -= 1;
|
||||
if( num < episodes.size() ) {
|
||||
auto pos = og_name.find_last_of('.');
|
||||
if ( num < episodes.size() ) {
|
||||
auto pos = og_name.find_last_of( TEXT( "." ) );
|
||||
// get desired filename
|
||||
auto name = compilePattern(pattern, season, num+1, og_name.substr(0, pos), episodes[num], show) + og_name.substr(pos);
|
||||
auto name = compilePattern( pattern, season, num + 1,
|
||||
og_name.substr( 0, pos ), episodes[num],
|
||||
show ) +
|
||||
og_name.substr( pos );
|
||||
// replace '/' with '|'
|
||||
for( size_t i = 0; i < name.size(); i++ ) {
|
||||
if( name[i] == '/' ) {
|
||||
for ( size_t i = 0; i < name.size(); i++ ) {
|
||||
if ( name[i] == '/' ) {
|
||||
name[i] = '|';
|
||||
}
|
||||
}
|
||||
// replace characters illegal in windows if desired
|
||||
if( !linux ) {
|
||||
name.erase(std::remove_if(name.begin(), name.end(), [](char x) {return x == '?' || x == '"' || x == '\\' || x == '*';}), name.end());
|
||||
size_t max{name.size()};
|
||||
for( size_t i = 0; i < max ; i++ ) {
|
||||
if( name[i] == '|' ) {
|
||||
if ( !linux ) {
|
||||
name.erase( std::remove_if( name.begin(), name.end(),
|
||||
[]( char_t x ) {
|
||||
return x == '?' || x == '"' ||
|
||||
x == '\\' || x == '*';
|
||||
} ),
|
||||
name.end() );
|
||||
size_t max{ name.size() };
|
||||
for ( size_t i = 0; i < max; i++ ) {
|
||||
if ( name[i] == '|' ) {
|
||||
name[i] = '-';
|
||||
}else if( name[i] == '<' ) {
|
||||
} else if ( name[i] == '<' ) {
|
||||
name[i] = 'i';
|
||||
name.insert(i+1, "s less than");
|
||||
name.insert( i + 1, TEXT( "s less than" ) );
|
||||
max += 11;
|
||||
} else if ( name[i] == '>' ) {
|
||||
name[i] = 'i';
|
||||
name.insert(i+1, "s more than");
|
||||
name.insert( i + 1, TEXT( "s more than" ) );
|
||||
max += 11;
|
||||
} else if ( name[i] == ':' ) {
|
||||
name[i] = ' ';
|
||||
name.insert(i+1, 1, '-');
|
||||
name.insert( i + 1, 1, '-' );
|
||||
max++;
|
||||
}
|
||||
}
|
||||
for( size_t i = 0; i < max; i++ ) {
|
||||
if ( name[i] == ' ' && name[i+1] == ' ' ) {
|
||||
name.erase(i, 1);
|
||||
for ( size_t i = 0; i < max; i++ ) {
|
||||
if ( name[i] == ' ' && name[i + 1] == ' ' ) {
|
||||
name.erase( i, 1 );
|
||||
max--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
renamed_files.emplace_back(dir, std::pair<std::string, std::string>(og_name, name));
|
||||
renamed_files.emplace_back(
|
||||
dir, std::pair< string, string >( og_name, name ) );
|
||||
}
|
||||
}
|
||||
return renamed_files;
|
||||
@ -133,62 +205,80 @@ std::vector<std::pair<std::string, std::pair<std::string, std::string>>> getRena
|
||||
|
||||
#ifndef GUI
|
||||
|
||||
void singleSeason( const std::string &path, 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_ptr) {
|
||||
if( url.empty() )
|
||||
url = getDefUrl(show, language, c);
|
||||
void singleSeason( const string &path, string &show, int season, string url,
|
||||
const string &language, const string &pattern,
|
||||
const bool &linux, const bool &trust, Curl &c,
|
||||
std::set< string > const *files_ptr ) {
|
||||
if ( url.empty() )
|
||||
url = getDefUrl( show, language, c );
|
||||
|
||||
std::set<std::string> *found_files = nullptr;
|
||||
std::set< string > *found_files = nullptr;
|
||||
|
||||
if( files_ptr == nullptr ) {
|
||||
found_files = new std::set<std::string>;
|
||||
findSeason(*found_files, season, path);
|
||||
if ( files_ptr == nullptr ) {
|
||||
found_files = new std::set< string >;
|
||||
findSeason( *found_files, season, path );
|
||||
files_ptr = found_files;
|
||||
}
|
||||
|
||||
auto renamed_files = getRenamedFiles(show, season, std::move(url), language, pattern, linux, c, *files_ptr);
|
||||
auto renamed_files =
|
||||
getRenamedFiles( show, season, std::move( url ), language, pattern,
|
||||
linux, c, *files_ptr );
|
||||
|
||||
for(auto renamed = renamed_files.begin(); renamed != renamed_files.end(); ++renamed) {
|
||||
std::cout << renamed->second.first << " --> " << renamed->second.second << std::endl;
|
||||
for ( auto renamed = renamed_files.begin(); renamed != renamed_files.end();
|
||||
++renamed ) {
|
||||
cout << renamed->second.first << " --> " << renamed->second.second
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if( !trust ) {
|
||||
std::cout << "Does this seem ok? (y/n) ";
|
||||
std::string response;
|
||||
std::cin >> response;
|
||||
std::cin.clear();
|
||||
std::cin.ignore(1, '\n');
|
||||
if( response[0] != 'y' && response[0] != 'Y' )
|
||||
if ( !trust ) {
|
||||
cout << "Does this seem ok? (y/n) ";
|
||||
string response;
|
||||
cin >> response;
|
||||
cin.clear();
|
||||
cin.ignore( 1, '\n' );
|
||||
if ( response[0] != 'y' && response[0] != 'Y' )
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto renamed = renamed_files.begin(); renamed != renamed_files.end(); ++renamed) {
|
||||
FSLib::rename(renamed->first + "/" + renamed->second.first, renamed->first + "/" + renamed->second.second);
|
||||
for ( auto renamed = renamed_files.begin(); renamed != renamed_files.end();
|
||||
++renamed ) {
|
||||
FSLib::rename( renamed->first + dir_divider + renamed->second.first,
|
||||
renamed->first + dir_divider + renamed->second.second );
|
||||
}
|
||||
|
||||
if( found_files != nullptr ) {
|
||||
if ( found_files != nullptr ) {
|
||||
delete found_files;
|
||||
}
|
||||
}
|
||||
|
||||
void multipleSeasons( const std::string &path, 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 ) {
|
||||
singleSeason( path, show, x.first, url, language, pattern, linux, trust, c, &x.second);
|
||||
void multipleSeasons( const string &path, string &show,
|
||||
const std::map< int, std::set< string > > &seasons,
|
||||
const string &language, const string &pattern,
|
||||
const bool &linux, const bool &trust, Curl &c ) {
|
||||
auto url = getDefUrl( show, language, c );
|
||||
for ( const auto &x : seasons ) {
|
||||
singleSeason( path, show, x.first, url, language, pattern, linux, trust,
|
||||
c, &x.second );
|
||||
}
|
||||
}
|
||||
|
||||
void multipleSeasons( const std::string &path, std::string &show, const std::set<int> seasons, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c) {
|
||||
std::map<int, std::set<std::string>> season_map;
|
||||
findSeasons(season_map, path, seasons);
|
||||
multipleSeasons(path, show, season_map, language, pattern, linux, trust, c);
|
||||
void multipleSeasons( const string &path, string &show,
|
||||
const std::set< int > seasons, const string &language,
|
||||
const string &pattern, const bool &linux,
|
||||
const bool &trust, Curl &c ) {
|
||||
std::map< int, std::set< string > > season_map;
|
||||
findSeasons( season_map, path, seasons );
|
||||
multipleSeasons( path, show, season_map, language, pattern, linux, trust,
|
||||
c );
|
||||
}
|
||||
|
||||
void allSeasons( const std::string &path, std::string &show, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c) {
|
||||
std::map<int, std::set<std::string>> seasons;
|
||||
//get all season number from this directory and subdirectories
|
||||
iterateFS(seasons, path);
|
||||
multipleSeasons( path, show, seasons, language, pattern, linux, trust, c);
|
||||
void allSeasons( const string &path, string &show, const string &language,
|
||||
const string &pattern, const bool &linux, const bool &trust,
|
||||
Curl &c ) {
|
||||
std::map< int, std::set< string > > seasons;
|
||||
// get all season number from this directory and subdirectories
|
||||
iterateFS( seasons, path );
|
||||
multipleSeasons( path, show, seasons, language, pattern, linux, trust, c );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -2,19 +2,45 @@
|
||||
#define TV_RENAME_HPP
|
||||
|
||||
#include <set>
|
||||
#include "network.hpp"
|
||||
|
||||
#ifdef GUI
|
||||
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> getRenamedFiles( 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 );
|
||||
#include "network.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
using string = std::wstring;
|
||||
using char_t = wchar_t;
|
||||
|
||||
#else
|
||||
|
||||
void singleSeason( const std::string &path, 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, 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, std::string &show, const std::string &language, const std::string &pattern, const bool &linux, const bool &trust, Curl &c);
|
||||
using string = std::string;
|
||||
using char_t = char;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GUI
|
||||
|
||||
std::vector< std::pair< string, std::pair< string, string > > >
|
||||
getRenamedFiles( const string &show, int season, string url,
|
||||
const string &language, const string &pattern,
|
||||
const bool &linux, Curl &c, const std::set< string > &files );
|
||||
|
||||
#else
|
||||
|
||||
void singleSeason( const string &path, string &show, int season, string url,
|
||||
const string &language, const string &pattern,
|
||||
const bool &linux, const bool &trust, Curl &c,
|
||||
std::set< string > const *files = nullptr );
|
||||
void multipleSeasons( const string &path, string &show,
|
||||
const std::set< int > seasons, const string &language,
|
||||
const string &pattern, const bool &linux,
|
||||
const bool &trust, Curl &c );
|
||||
void allSeasons( const string &path, string &show, const string &language,
|
||||
const string &pattern, const bool &linux, const bool &trust,
|
||||
Curl &c );
|
||||
|
||||
#endif
|
||||
|
||||
|
360
tv_rename_gui.cpp
Normal file
360
tv_rename_gui.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
// many thanks to https://www.winprog.org/tutorial from where I got a lot of
|
||||
// code for this
|
||||
|
||||
#include <Windows.h>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <shlobj.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "resource.h"
|
||||
#include "tv_rename.hpp"
|
||||
|
||||
constexpr std::array< const wchar_t *, 46 > languages{
|
||||
L"en", L"English", L"sv", L"Svenska", L"no", L"Norsk",
|
||||
L"da", L"Dansk", L"fi", L"Suomeksi", L"nl", L"Nederlands",
|
||||
L"de", L"Deutsch", L"it", L"Italiano", L"es", L"Español",
|
||||
L"fr", L"Français", L"pl", L"Polski", L"hu", L"Magyar",
|
||||
L"el", L"Greek", L"tr", L"Turkish", L"ru", L"Russian",
|
||||
L"he", L"Hebrew", L"ja", L"Japanese", L"pt", L"Portuguese",
|
||||
L"zh", L"Chinese", L"cs", L"Czech", L"sl", L"Slovenian",
|
||||
L"hr", L"Croatian", L"ko", L"Korea"
|
||||
};
|
||||
|
||||
std::vector< int > options;
|
||||
std::vector< int > checkboxes;
|
||||
std::vector< int > checked;
|
||||
Curl c;
|
||||
|
||||
wchar_t lang_code[3] = L"en";
|
||||
std::wstring default_pattern = L"%filename - %epname";
|
||||
|
||||
std::map< int, std::set< string > > files;
|
||||
std::vector< std::pair< string, string > > possible_shows;
|
||||
std::vector< std::pair< string, std::pair< string, string > > >
|
||||
*current_renamed_files{ nullptr };
|
||||
|
||||
BOOL CALLBACK HelpBox( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) {
|
||||
switch ( message ) {
|
||||
case WM_COMMAND:
|
||||
switch ( LOWORD( wParam ) ) {
|
||||
case IDOK:
|
||||
EndDialog( hwnd, 0 );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
EndDialog( hwnd, 0 );
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CALLBACK SeasonsBox( HWND hwnd, UINT message, WPARAM wParam,
|
||||
LPARAM lParam ) {
|
||||
switch ( message ) {
|
||||
case WM_INITDIALOG: {
|
||||
checked.clear();
|
||||
checkboxes.clear();
|
||||
int left{ 15 }, top{ 30 };
|
||||
for ( int i = 0; i < options.size(); i++ ) {
|
||||
if ( checkboxes.empty() ) {
|
||||
checkboxes.push_back( 2000 );
|
||||
} else {
|
||||
checkboxes.push_back( checkboxes.back() + 1 );
|
||||
}
|
||||
auto hCheckBox = CreateWindowEx(
|
||||
0, L"Button", std::to_wstring( options[i] ).c_str(),
|
||||
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, left, top, 60, 20,
|
||||
hwnd, ( HMENU )checkboxes.back(), GetModuleHandle( NULL ),
|
||||
NULL );
|
||||
if ( hCheckBox == NULL ) {
|
||||
MessageBox( hwnd, L"Could not create checkbox", L"Error",
|
||||
MB_OK | MB_ICONERROR );
|
||||
return TRUE;
|
||||
}
|
||||
auto hfDefault = GetStockObject( DEFAULT_GUI_FONT );
|
||||
SendMessage( hCheckBox, WM_SETFONT, ( WPARAM )hfDefault,
|
||||
MAKELPARAM( FALSE, 0 ) );
|
||||
if ( left == 195 ) {
|
||||
left = 15;
|
||||
top += 20;
|
||||
} else {
|
||||
left += 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
case WM_COMMAND:
|
||||
switch ( LOWORD( wParam ) ) {
|
||||
case IDOK:
|
||||
for ( int i = 0; i < checkboxes.size(); i++ ) {
|
||||
if ( SendDlgItemMessage( hwnd, checkboxes[i], BM_GETCHECK, 0,
|
||||
0 ) )
|
||||
checked.push_back( options[i] );
|
||||
}
|
||||
EndDialog( hwnd, 0 );
|
||||
break;
|
||||
case IDALL:
|
||||
for ( auto &x : checkboxes ) {
|
||||
SendDlgItemMessage( hwnd, x, BM_SETCHECK, BST_CHECKED, 0 );
|
||||
}
|
||||
break;
|
||||
case IDNONE:
|
||||
for ( auto &x : checkboxes ) {
|
||||
SendDlgItemMessage( hwnd, x, BM_SETCHECK, BST_UNCHECKED, 0 );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
EndDialog( hwnd, 0 );
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// stolen from
|
||||
// https://www.codeproject.com/Articles/2604/Browse-Folder-dialog-search-folder-and-all-sub-fol
|
||||
std::wstring getDir() {
|
||||
wchar_t path[MAX_PATH];
|
||||
BROWSEINFO bi{};
|
||||
bi.lpszTitle = L"Choose a folder";
|
||||
LPITEMIDLIST pidl = SHBrowseForFolder( &bi );
|
||||
if ( pidl != 0 ) {
|
||||
SHGetPathFromIDList( pidl, path );
|
||||
IMalloc *imalloc = 0;
|
||||
if ( SUCCEEDED( SHGetMalloc( &imalloc ) ) ) {
|
||||
imalloc->Free( pidl );
|
||||
imalloc->Release();
|
||||
}
|
||||
} else {
|
||||
path[0] = '\0';
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void renameFiles(
|
||||
const std::vector< std::pair< string, std::pair< string, string > > >
|
||||
&renamed ) {
|
||||
for ( auto renamed_it = renamed.begin(); renamed_it != renamed.end();
|
||||
++renamed_it ) {
|
||||
FSLib::rename( renamed_it->first + L"\\" + renamed_it->second.first,
|
||||
renamed_it->first + L"\\" + renamed_it->second.second );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CALLBACK PreviewBox( HWND hwnd, UINT message, WPARAM wParam,
|
||||
LPARAM lParam ) {
|
||||
switch ( message ) {
|
||||
case WM_INITDIALOG: {
|
||||
std::wstring text{};
|
||||
for ( auto renamed_it = current_renamed_files->begin();
|
||||
renamed_it != current_renamed_files->end(); ++renamed_it ) {
|
||||
text += renamed_it->second.first + L" --> " +
|
||||
renamed_it->second.second + L"\r\n";
|
||||
}
|
||||
SetDlgItemText( hwnd, IDTEXT, text.c_str() );
|
||||
}
|
||||
return TRUE;
|
||||
case WM_COMMAND:
|
||||
switch ( LOWORD( wParam ) ) {
|
||||
case IDYES: {
|
||||
renameFiles( *current_renamed_files );
|
||||
EndDialog( hwnd, IDYES );
|
||||
} break;
|
||||
case IDNO:
|
||||
EndDialog( hwnd, IDNO );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
EndDialog( hwnd, 0 );
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void finishedSelection( HWND hwnd ) {
|
||||
auto index = SendDlgItemMessage( hwnd, IDSHOWS, CB_GETCURSEL, 0, 0 );
|
||||
auto url = possible_shows[index].second;
|
||||
auto show = possible_shows[index].first;
|
||||
wchar_t input_pattern[100];
|
||||
|
||||
GetDlgItemText( hwnd, IDPATTERN, input_pattern, 100 );
|
||||
|
||||
if ( input_pattern != default_pattern ) {
|
||||
std::wofstream file( userHome() + L"\\tv_rename\\pattern" );
|
||||
if ( file ) {
|
||||
file << input_pattern;
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto &x : checked ) {
|
||||
auto renamed_files = getRenamedFiles(
|
||||
show, x, L"https://www.thetvdb.com" + url, lang_code,
|
||||
( input_pattern[0] == '\0' ? default_pattern : input_pattern ),
|
||||
false, c, files[x] );
|
||||
if ( renamed_files.empty() )
|
||||
continue;
|
||||
if ( SendDlgItemMessage( hwnd, IDTRUST, BM_GETCHECK, 0, 0 ) ) {
|
||||
renameFiles( renamed_files );
|
||||
continue;
|
||||
}
|
||||
|
||||
current_renamed_files = &renamed_files;
|
||||
DialogBox( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDD_PREVIEW ),
|
||||
hwnd, PreviewBox );
|
||||
}
|
||||
if ( SendDlgItemMessage( hwnd, IDTRUST, BM_GETCHECK, 0, 0 ) ) {
|
||||
MessageBox( hwnd, L"Finished renaming files", L"Info",
|
||||
MB_OK | MB_ICONINFORMATION );
|
||||
}
|
||||
}
|
||||
|
||||
void getNames( HWND hwnd ) {
|
||||
wchar_t path[MAX_PATH];
|
||||
GetDlgItemText( hwnd, IDDIR, path, MAX_PATH );
|
||||
|
||||
if ( wcslen( path ) == 0 ) {
|
||||
MessageBox( hwnd, L"Folder field is empty", L"Error",
|
||||
MB_OK | MB_ICONERROR );
|
||||
return;
|
||||
}
|
||||
if ( !FSLib::isDirectory( path ) ) {
|
||||
MessageBox( hwnd, L"Folder doesn't exist", L"Error",
|
||||
MB_OK | MB_ICONERROR );
|
||||
return;
|
||||
}
|
||||
|
||||
checked.clear();
|
||||
checkboxes.clear();
|
||||
options.clear();
|
||||
files.clear();
|
||||
|
||||
iterateFS( files, path );
|
||||
for ( auto &x : files ) {
|
||||
options.push_back( x.first );
|
||||
}
|
||||
|
||||
DialogBox( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDD_SEASONS ), hwnd,
|
||||
SeasonsBox );
|
||||
finishedSelection( hwnd );
|
||||
}
|
||||
|
||||
void process( HWND hwnd ) {
|
||||
wchar_t show[100];
|
||||
GetDlgItemText( hwnd, IDSHOWIN, show, 100 );
|
||||
|
||||
if ( wcslen( show ) == 0 ) {
|
||||
MessageBox( hwnd, L"Show field is empty", L"Error",
|
||||
MB_OK | MB_ICONERROR );
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t language[20];
|
||||
GetDlgItemText( hwnd, IDLANG, language, 20 );
|
||||
|
||||
for ( int i = 1; i < languages.size(); i += 2 ) {
|
||||
if ( !wcscmp( languages[i], language ) ) {
|
||||
wcscpy_s( lang_code, languages[i - 1] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
possible_shows = getPossibleShows( show, lang_code, c );
|
||||
if ( possible_shows.size() == 0 ) {
|
||||
MessageBox( hwnd, L"No results found for given show name", L"Error",
|
||||
MB_OK | MB_ICONERROR );
|
||||
return;
|
||||
}
|
||||
|
||||
ShowWindow( GetDlgItem( hwnd, ID_STATIC6 ), SW_SHOW );
|
||||
ShowWindow( GetDlgItem( hwnd, IDSHOWS ), SW_SHOW );
|
||||
ShowWindow( GetDlgItem( hwnd, IDSEASONS ), SW_SHOW );
|
||||
|
||||
SendDlgItemMessage( hwnd, IDSHOWS, CB_RESETCONTENT, 0, 0 );
|
||||
for ( int i = 0; i < possible_shows.size(); i++ ) {
|
||||
SendDlgItemMessage( hwnd, IDSHOWS, CB_ADDSTRING, 0,
|
||||
( LPARAM )possible_shows[i].first.c_str() );
|
||||
}
|
||||
SendDlgItemMessage( hwnd, IDSHOWS, CB_SETCURSEL, 0, 0 );
|
||||
}
|
||||
|
||||
void readDefaultPattern( const string &base_dir ) {
|
||||
std::wifstream file( base_dir + L"\\pattern" );
|
||||
if ( file ) {
|
||||
std::getline( file, default_pattern );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CALLBACK MainBox( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) {
|
||||
switch ( message ) {
|
||||
case WM_INITDIALOG: {
|
||||
for ( int i = 1; i < languages.size(); i += 2 ) {
|
||||
SendDlgItemMessage( hwnd, IDLANG, CB_ADDSTRING, 0,
|
||||
( LPARAM )languages[i] );
|
||||
}
|
||||
SendDlgItemMessage( hwnd, IDLANG, CB_SETCURSEL, 5, 0 );
|
||||
|
||||
auto appdata = userHome() + L"\\tv_rename";
|
||||
if ( !FSLib::isDirectory( appdata ) ) {
|
||||
if ( CreateDirectory( appdata.c_str(), NULL ) ) {
|
||||
readDefaultPattern( appdata );
|
||||
}
|
||||
} else {
|
||||
readDefaultPattern( appdata );
|
||||
}
|
||||
|
||||
SetDlgItemText( hwnd, IDPATTERN, default_pattern.c_str() );
|
||||
}
|
||||
return TRUE;
|
||||
case WM_COMMAND:
|
||||
switch ( LOWORD( wParam ) ) {
|
||||
case IDPROCESS: {
|
||||
process( hwnd );
|
||||
} break;
|
||||
case IDEND:
|
||||
EndDialog( hwnd, IDOK_MAIN );
|
||||
break;
|
||||
case IDSEASONS:
|
||||
getNames( hwnd );
|
||||
break;
|
||||
case IDDIRB:
|
||||
SetDlgItemText( hwnd, IDDIR, getDir().c_str() );
|
||||
break;
|
||||
case IDHELP:
|
||||
DialogBox( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDD_HELP ),
|
||||
hwnd, HelpBox );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
EndDialog( hwnd, 0 );
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
LPSTR lpCmdLine, int nCmdShow ) {
|
||||
return DialogBox( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDD_MAIN ),
|
||||
NULL, MainBox );
|
||||
}
|
BIN
tv_rename_gui.ico
Normal file
BIN
tv_rename_gui.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
tv_rename_gui.rc
Normal file
BIN
tv_rename_gui.rc
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user