summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt173
-rw-r--r--archivebrowser.cpp44
-rw-r--r--archivebrowsermodel.cpp99
-rw-r--r--archivebrowsermodel.h16
-rw-r--r--configurationdialog.cpp57
-rw-r--r--configurationdialog.h3
-rw-r--r--delegates.cpp16
-rw-r--r--delegates.h12
-rw-r--r--helper.cpp12
-rw-r--r--movieinfopage.cpp33
-rw-r--r--movieinfopage.h2
-rw-r--r--moviemappingpage.cpp7
-rw-r--r--newmoviewizard.h3
-rw-r--r--randomtab.cpp34
-rw-r--r--randomtab.h2
-rw-r--r--shemov.pro2
-rw-r--r--smglobals.cpp2
17 files changed, 460 insertions, 57 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..329b5ce
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,173 @@
+cmake_minimum_required(VERSION 3.16)
+project(shemov VERSION 1.0 LANGUAGES C CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent Gui MultimediaWidgets Sql Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(shemov WIN32 MACOSX_BUNDLE
+ archivebrowser.cpp archivebrowser.h
+ archivebrowsermodel.cpp archivebrowsermodel.h
+ archivemodel.cpp archivemodel.h
+ archiveview.cpp archiveview.h
+ configurationdialog.cpp configurationdialog.h
+ consistencycheck.cpp consistencycheck.h
+ copyworker.cpp copyworker.h
+ dbanalyzer.cpp dbanalyzer.h
+ delegates.cpp delegates.h
+ editfiledialog.cpp editfiledialog.h
+ filepropertiesdialog.cpp filepropertiesdialog.h
+ fsproxy.cpp fsproxy.h
+ fswidget.cpp fswidget.h
+ helper.cpp helper.h
+ main.cpp
+ mappingdata.cpp mappingdata.h
+ mappingeditdialog.cpp mappingeditdialog.h
+ mappingeditwidget.cpp mappingeditwidget.h
+ mappinginputdialog.cpp mappinginputdialog.h
+ mappingtableeditor.cpp mappingtableeditor.h
+ mappingtablemodel.cpp mappingtablemodel.h
+ mappingtablewidget.cpp mappingtablewidget.h
+ mappingtreemodel.cpp mappingtreemodel.h
+ mappingtreeproxy.cpp mappingtreeproxy.h
+ mappingtreeresultmodel.cpp mappingtreeresultmodel.h
+ mappingtreeresultview.cpp mappingtreeresultview.h
+ mappingtreeview.cpp mappingtreeview.h
+ mappingtreewidget.cpp mappingtreewidget.h
+ movieinfopage.cpp movieinfopage.h
+ moviemappingpage.cpp moviemappingpage.h
+ moviemetadatapage.cpp moviemetadatapage.h
+ moviepropertiesdialog.cpp moviepropertiesdialog.h
+ moviewidget.cpp moviewidget.h
+ newmoviewizard.cpp newmoviewizard.h
+ newpicsdialog.cpp newpicsdialog.h
+ picfilesmodel.cpp picfilesmodel.h
+ picturelistview.cpp picturelistview.h
+ pictureswidget.cpp pictureswidget.h
+ pictureviewer2.cpp pictureviewer2.h
+ programconfigurator.cpp programconfigurator.h
+ randomtab.cpp randomtab.h
+ searchdialog.cpp searchdialog.h
+ shemov.cpp shemov.h
+ shemoviconprovider.cpp shemoviconprovider.h
+ smdialog.cpp smdialog.h
+ smglobals.cpp smglobals.h
+ sminputdialog.cpp sminputdialog.h
+ smtreeitem.cpp smtreeitem.h
+ smtreemodel.cpp smtreemodel.h
+ smtreeview.cpp smtreeview.h
+ smview.cpp smview.h
+ statisticsdialog.cpp statisticsdialog.h
+ unpacker.cpp unpacker.h
+ viewer.cpp viewer.h
+ wizardtreemodel.cpp wizardtreemodel.h
+)
+target_compile_definitions(shemov PRIVATE
+ QT_DEPRECATED_WARNINGS
+)
+
+target_link_libraries(shemov PRIVATE
+ Magick++-7.Q16HDRI
+ Qt::Concurrent
+ Qt::Core
+ Qt::Gui
+ Qt::MultimediaWidgets
+ Qt::Sql
+ Qt::Widgets
+)
+
+
+# Resources:
+set(shemov_resource_files
+ "analstretcher.png"
+ "archive.svg"
+ "back_dick.png"
+ "bald_pussy.png"
+ "ball_gag.png"
+ "big_ass.png"
+ "big_balls.png"
+ "big_tit.png"
+ "bizarre_amputee.png"
+ "blue_syringe.png"
+ "butt_plug.png"
+ "butt_plug_light.png"
+ "catheter_with_bag.png"
+ "chastity_belt.png"
+ "chastity_belt_light.png"
+ "chastity_belt_with_cuffs.png"
+ "clean_tampon.png"
+ "clitoris.png"
+ "compare.png"
+ "delete.png"
+ "diaper.png"
+ "dick_in_cage.png"
+ "dildo.png"
+ "dog_hood.png"
+ "dog_hood_light.png"
+ "fire.png"
+ "folder.png"
+ "french_maid_dress.png"
+ "gaping_ass.png"
+ "higheels.png"
+ "hourglass_figure.png"
+ "huge_balls_pierced.png"
+ "huge_bra.png"
+ "huge_bra_light.png"
+ "letter_d.png"
+ "letter_n.png"
+ "letter_q.png"
+ "letter_t.png"
+ "male_chastity_belt.png"
+ "movie.svg"
+ "nipple_up.png"
+ "picgone.png"
+ "picture.svg"
+ "prince_albert.png"
+ "refresh.png"
+ "rename.png"
+ "shackles.png"
+ "shemov.png"
+ "shemov_splash2.png"
+ "snapshot.png"
+ "spreadingpants.png"
+ "squirting_nipple.png"
+ "squirting_nipple_light.png"
+ "steel_collar.png"
+ "up_dick.png"
+ "usb-32.png"
+ "used_tampon.png"
+)
+
+qt_add_resources(shemov "shemov"
+ PREFIX
+ "/"
+ FILES
+ ${shemov_resource_files}
+)
+
+if(WIN32)
+ target_include_directories(shemov PRIVATE
+ ..\..\..\mingw64\include\ImageMagick-7
+ )
+endif()
+
+if(UNIX)
+ target_include_directories(shemov PRIVATE
+ /usr/include/ImageMagick-7
+ )
+endif()
+
+install(TARGETS shemov
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+qt_generate_deploy_app_script(
+ TARGET shemov
+ FILENAME_VARIABLE deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/archivebrowser.cpp b/archivebrowser.cpp
index d3d64a5..45f3618 100644
--- a/archivebrowser.cpp
+++ b/archivebrowser.cpp
@@ -38,12 +38,14 @@ ArchiveBrowser::ArchiveBrowser(QWidget *parent) : QWidget(parent), mSelectedSize
mModel = static_cast<ArchiveBrowserModel*>(SmGlobals::instance()->model("BrowserModel"));
mProxy = new ArchiveBrowserModelProxy;
mProxy->setSourceModel(mModel);
+ mProxy->readSettings();
mTree = new SmTreeView("ui/archivebrowserheaders");
mTree->setModel(mProxy);
mTree->setColumnHidden(ArchiveBrowserModel::GenericId, true);
mTree->setColumnHidden(ArchiveBrowserModel::NodeType, true);
mTree->setItemDelegateForColumn(ArchiveBrowserModel::TotalSize, new SizeDelegate(this));
mTree->setItemDelegateForColumn(ArchiveBrowserModel::FileType, new FileTypeDelegate(this));
+ mTree->setItemDelegateForColumn(ArchiveBrowserModel::Genres, new GenreDelegate(this, mProxy));
mTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
QToolBar *toolBar = new QToolBar;
@@ -55,7 +57,45 @@ ArchiveBrowser::ArchiveBrowser(QWidget *parent) : QWidget(parent), mSelectedSize
mQualityFilter = new QComboBox;
toolBar->addWidget(mQualityFilter);
setupQualityFilter();
- connect(mQualityFilter, &QComboBox::currentTextChanged, mProxy, &ArchiveBrowserModelProxy::setQualityFilter);
+ toolBar->addSeparator();
+ QStringList genres = mModel->availableGenres();
+ std::sort(genres.begin(), genres.end());
+ QActionGroup *gDataAG = new QActionGroup(this);
+ gDataAG->setExclusive(false);
+ const QStringList &includedGenres = mProxy->genreFilters();
+ for(const QString &g : std::as_const(genres)){
+ QAction *a = new QAction(g, this);
+ a->setCheckable(true);
+ gDataAG->addAction(a);
+ connect(a, &QAction::triggered, a, [=] { mProxy->toggleGenre(a); });
+ if(includedGenres.indexOf(g) != -1){
+ a->setChecked(true);
+ }
+ }
+ QIcon genreIcon = Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), 'G', true, false);
+ QAction *genreA = new QAction(genreIcon, tr("Filter genres"), this);
+ QMenu *genreMenu = new QMenu;
+ genreMenu->addActions(gDataAG->actions());
+ genreA->setMenu(genreMenu);
+ toolBar->addAction(genreA);
+ QActionGroup *gDataExcludedAG = new QActionGroup(this);
+ gDataExcludedAG->setExclusive(false);
+ const QStringList &excludedGenres = mProxy->exclucedGenreFilters();
+ for(const QString &g : std::as_const(genres)){
+ QAction *a = new QAction(g, this);
+ a->setCheckable(true);
+ gDataExcludedAG->addAction(a);
+ connect(a, &QAction::triggered, a, [=] { mProxy->toggleExcludedGenre(a); });
+ if(excludedGenres.indexOf(g) != -1){
+ a->setChecked(true);
+ }
+ }
+ QIcon excludedGenreIcon = Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), 'E', true, false);
+ QAction *excludedGenreA = new QAction(excludedGenreIcon, tr("Filter genres"), this);
+ QMenu *excludedGenreMenu = new QMenu;
+ excludedGenreMenu->addActions(gDataExcludedAG->actions());
+ excludedGenreA->setMenu(excludedGenreMenu);
+ toolBar->addAction(excludedGenreA);
toolBar->addSeparator();
mSizeFilter = new QCheckBox(tr("Filter by size"));
connect(mSizeFilter, &QCheckBox::checkStateChanged, mProxy, &ArchiveBrowserModelProxy::setSizeFilter);
@@ -166,10 +206,12 @@ void ArchiveBrowser::readConfig(){
QSettings s;
QString qualFilter = s.value("ui/browserquality", tr("(none)")).toString();
mQualityFilter->setCurrentText(qualFilter);
+ mProxy->readSettings();
}
void ArchiveBrowser::writeSettings(){
mTree->writeHeaderConfig();
+ mProxy->writeSettings();
QSettings s;
s.setValue("ui/browserquality", mQualityFilter->currentText());
}
diff --git a/archivebrowsermodel.cpp b/archivebrowsermodel.cpp
index c6e7d08..2c17fd2 100644
--- a/archivebrowsermodel.cpp
+++ b/archivebrowsermodel.cpp
@@ -7,13 +7,15 @@
#include <QSqlDatabase>
#include <QSqlQuery>
+#include <QAction>
+#include <QSettings>
#include "archivebrowsermodel.h"
#include "smtreeitem.h"
#include "smglobals.h"
#include "helper.h"
-ArchiveBrowserModel::ArchiveBrowserModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent), mNumFields(9) {
+ArchiveBrowserModel::ArchiveBrowserModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent), mNumFields(10) {
mDb = QSqlDatabase::database("treedb");
readConfig();
populate();
@@ -52,6 +54,9 @@ QVariant ArchiveBrowserModel::data(const QModelIndex &index, int role) const {
if(role == SelectedRole){
return item->data(Selected);
}
+ if(role == GenreRole){
+ return item->data(Genres);
+ }
if(role == Qt::ForegroundRole){
if(col == TotalSize){
qint64 s = item->data(TotalSize).toLongLong();
@@ -162,6 +167,8 @@ void ArchiveBrowserModel::populate(){
mAvailableQualities.clear();
QSqlQuery localFilesQ(mDb);
localFilesQ.prepare("SELECT tfilename, bisize, sifiletype, siquality, ifiles_id, cmd5sum FROM files WHERE iseriespart_id = :sid ORDER BY sifiletype");
+ QSqlQuery localGenresQ(mDb);
+ localGenresQ.prepare("SELECT seriesparts_genremap.igenres_id, genres.tgenrename FROM seriesparts_genremap, genres WHERE iseriesparts_id = :sid AND seriesparts_genremap.igenres_id = genres.igenres_id ORDER BY genres.tgenrename ASC");
QSqlQuery localQ = QSqlQuery("SELECT DISTINCT(series.iseries_id), tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle, seriesparts.iseriesparts_id FROM series LEFT JOIN seriesparts ON series.iseries_id = seriesparts.iseries_id LEFT JOIN files ON seriesparts.iseriesparts_id = files.iseriespart_id WHERE files.sifiletype = 1 AND files.idvd < 1 AND seriesparts.bfavorite = false ORDER BY tseries_name ASC", mDb);
while(localQ.next()){
QList<QVariant> serPartData;
@@ -171,16 +178,17 @@ void ArchiveBrowserModel::populate(){
}else{
name = QString("%1 - %2").arg(localQ.value(1).toString(), localQ.value(3).toString());
}
- serPartData << QChar(0x26A4) << name << localQ.value(4) << SeriesPartNode << QVariant() << QVariant() << QVariant() << QString() << false;
+ serPartData << QChar(0x26A4) << name << localQ.value(4) << SeriesPartNode << QVariant() << QVariant() << QVariant() << QString() << false << QVariant();
SmTreeItem *seriesItem = new SmTreeItem(serPartData, rootItem);
rootItem->appendChild(seriesItem);
localFilesQ.bindValue(":sid", localQ.value(4));
localFilesQ.exec();
qint64 totalSize = 0;
int quality = -1;
+ QStringList genres;
while(localFilesQ.next()){
QList<QVariant> fileData;
- fileData << QVariant() << localFilesQ.value(0) << localFilesQ.value(4) << FileNode << localFilesQ.value(1) << localFilesQ.value(3) << localFilesQ.value(2) << Helper::createArchivePath(localFilesQ.value(0).toString(), localFilesQ.value(5).toString()) << false;
+ fileData << QVariant() << localFilesQ.value(0) << localFilesQ.value(4) << FileNode << localFilesQ.value(1) << localFilesQ.value(3) << localFilesQ.value(2) << Helper::createArchivePath(localFilesQ.value(0).toString(), localFilesQ.value(5).toString()) << false << QVariant();
totalSize += localFilesQ.value(1).toDouble();
if(localFilesQ.value(2) == FT_MOVIE){
int q = localFilesQ.value(3).toInt();
@@ -192,8 +200,18 @@ void ArchiveBrowserModel::populate(){
SmTreeItem *fileItem = new SmTreeItem(fileData, seriesItem);
seriesItem->appendChild(fileItem);
}
+ localGenresQ.bindValue(":sid", localQ.value(4));
+ localGenresQ.exec();
+ while(localGenresQ.next()){
+ QString genre = localGenresQ.value(1).toString();
+ genres << genre;
+ if(!mAvailableGenres.contains(genre)){
+ mAvailableGenres << genre;
+ }
+ }
seriesItem->setData(Quality, quality);
seriesItem->setData(TotalSize, totalSize);
+ seriesItem->setData(Genres, QVariant(genres));
}
setRoot(rootItem);
emit populated();
@@ -216,6 +234,38 @@ void ArchiveBrowserModelProxy::setQualityFilter(QString quality){
invalidateFilter();
}
+void ArchiveBrowserModelProxy::readSettings(){
+ QSettings s;
+ mGenreFilters = s.value("archivebrowser/includedgenres").toStringList();
+ mExcludedGenreFilters = s.value("archivebrowser/archivebrowser/excludedgenres").toStringList();
+}
+
+void ArchiveBrowserModelProxy::writeSettings(){
+ QSettings s;
+ s.setValue("archivebrowser/includedgenres", mGenreFilters);
+ s.setValue("archivebrowser/archivebrowser/excludedgenres", mExcludedGenreFilters);
+}
+
+void ArchiveBrowserModelProxy::toggleGenre(QAction *a){
+ QString text = a->text();
+ if(a->isChecked()){
+ mGenreFilters << text;
+ }else{
+ mGenreFilters.removeAll(text);
+ }
+ invalidateFilter();
+}
+
+void ArchiveBrowserModelProxy::toggleExcludedGenre(QAction *a){
+ QString text = a->text();
+ if(a->isChecked()){
+ mExcludedGenreFilters << text;
+ }else{
+ mExcludedGenreFilters.removeAll(text);
+ }
+ invalidateFilter();
+}
+
void ArchiveBrowserModelProxy::setSizeFilter(int activate){
mSizeFilter = activate;
invalidateFilter();
@@ -228,7 +278,7 @@ void ArchiveBrowserModelProxy::setBytesRemaining(qint64 bytes){
bool ArchiveBrowserModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
- if(mQuality == -1 && !mSizeFilter){
+ if(mQuality == -1 && !mSizeFilter && mGenreFilters.isEmpty() && mExcludedGenreFilters.isEmpty()){
return true;
}
QModelIndex selIdx = sourceModel()->index(sourceRow, ArchiveBrowserModel::Selected, sourceParent);
@@ -240,27 +290,36 @@ bool ArchiveBrowserModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex
if(nodeType == ArchiveBrowserModel::FileNode){
return true;
}
+ QModelIndex genreIdx = sourceModel()->index(sourceRow, ArchiveBrowserModel::Genres, sourceParent);
+ QStringList genres = genreIdx.data().toStringList();
+ if(!mExcludedGenreFilters.isEmpty()){
+ for(const QString &exg : std::as_const(mExcludedGenreFilters)){
+ if(genres.contains(exg)){
+ return false;
+ }
+ }
+ }
+ bool retval = true;
+ if(!mGenreFilters.isEmpty()){
+ for(const QString &fg : std::as_const(mGenreFilters)){
+ if(!genres.contains(fg)){
+ retval = false;
+ }
+ }
+ }
QModelIndex qualIdx = sourceModel()->index(sourceRow, ArchiveBrowserModel::Quality, sourceParent);
int quality = qualIdx.data().toInt();
QModelIndex sizeIdx = sourceModel()->index(sourceRow, ArchiveBrowserModel::TotalSize, sourceParent);
qint64 size = sizeIdx.data().toLongLong();
- if(mQuality > -1 && mSizeFilter){
- if(quality <= mQuality && size <= mBytesRemaining){
- return true;
- }
- return false;
- }
- if(mQuality > -1){
- if(quality <= mQuality){
- return true;
+ if(retval){
+ if(quality < mQuality){
+ retval = false;
}
- return false;
- }
- if(mSizeFilter){
- if(size <= mBytesRemaining){
- return true;
+ if(mSizeFilter){
+ if(size > mBytesRemaining){
+ retval = false;
+ }
}
- return false;
}
- return false;
+ return retval;
}
diff --git a/archivebrowsermodel.h b/archivebrowsermodel.h
index c72fe9e..b40258c 100644
--- a/archivebrowsermodel.h
+++ b/archivebrowsermodel.h
@@ -12,13 +12,15 @@
#include <QSortFilterProxyModel>
#include <QList>
+class QAction;
+
#include "smtreemodel.h"
class ArchiveBrowserModel : public SmTreeModel {
Q_OBJECT
public:
- enum CustomRoles { ExpansionRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 2, GenericIdRole = Qt::UserRole + 3, NodeTypeRole = Qt::UserRole + 4, TotalSizeRole = Qt::UserRole + 5, QualityRole = 6, FileTypeRole = Qt::UserRole + 7, FullPathRole = Qt::UserRole + 8, SelectedRole = Qt::UserRole + 9 };
- enum Fields { Expansion = 0, Name = 1, GenericId = 2, NodeType = 3, TotalSize = 4, Quality = 5, FileType = 6, FullPath = 7, Selected = 8 };
+ enum CustomRoles { ExpansionRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 2, GenericIdRole = Qt::UserRole + 3, NodeTypeRole = Qt::UserRole + 4, TotalSizeRole = Qt::UserRole + 5, QualityRole = 6, FileTypeRole = Qt::UserRole + 7, FullPathRole = Qt::UserRole + 8, SelectedRole = Qt::UserRole + 9, GenreRole = Qt::UserRole + 10 };
+ enum Fields { Expansion = 0, Name = 1, GenericId = 2, NodeType = 3, TotalSize = 4, Quality = 5, FileType = 6, FullPath = 7, Selected = 8, Genres = 9 };
enum NodeTypes { SeriesPartNode = 1, FileNode = 2 };
explicit ArchiveBrowserModel(const QStringList &headers, QObject *parent = nullptr);
virtual QVariant data(const QModelIndex &index, int role) const;
@@ -27,6 +29,7 @@ class ArchiveBrowserModel : public SmTreeModel {
int nextDVDNo() const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
QList<int> availableQualities() { return mAvailableQualities; }
+ QStringList availableGenres() { return mAvailableGenres; }
QModelIndexList children(const QModelIndex &idx);
public slots:
@@ -40,6 +43,7 @@ class ArchiveBrowserModel : public SmTreeModel {
void readConfig();
int mNumFields;
QList<int> mAvailableQualities;
+ QStringList mAvailableGenres;
QSqlDatabase mDb;
};
@@ -47,11 +51,17 @@ class ArchiveBrowserModelProxy : public QSortFilterProxyModel {
Q_OBJECT
public:
explicit ArchiveBrowserModelProxy(QObject *parent = nullptr);
+ const QStringList &genreFilters() const { return mGenreFilters; };
+ const QStringList &exclucedGenreFilters() const { return mExcludedGenreFilters; };
+ void readSettings();
+ void writeSettings();
public slots:
void setQualityFilter(QString quality);
void setSizeFilter(int activate);
void setBytesRemaining(qint64 bytes);
+ void toggleGenre(QAction *a);
+ void toggleExcludedGenre(QAction *a);
protected:
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
@@ -60,6 +70,8 @@ class ArchiveBrowserModelProxy : public QSortFilterProxyModel {
int mQuality;
bool mSizeFilter;
qint64 mBytesRemaining;
+ QStringList mGenreFilters;
+ QStringList mExcludedGenreFilters;
};
#endif // ARCHIVEBROWSERMODEL_H
diff --git a/configurationdialog.cpp b/configurationdialog.cpp
index 9f49e8e..1131293 100644
--- a/configurationdialog.cpp
+++ b/configurationdialog.cpp
@@ -30,6 +30,7 @@
#include <QStandardItem>
#include <QScreen>
#include <QInputDialog>
+#include <QFileDialog>
#include "configurationdialog.h"
#include "programconfigurator.h"
@@ -56,10 +57,7 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : S
mBurnDir = new QLineEdit;
mBurnDir->setCompleter(fsCompleter);
pathL->addRow(tr("Burn directory"), mBurnDir);
- mUSBDir = new QLineEdit;
- mUSBDir->setCompleter(fsCompleter);
- pathL->addRow(tr("USB directory"), mUSBDir);
- mFfProbePath = new QLineEdit;
+ mFfProbePath = new QLineEdit;
mFfProbePath->setCompleter(fsCompleter);
pathL->addRow(tr("Path to ffprobe"), mFfProbePath);
mFfMpegPath = new QLineEdit;
@@ -74,9 +72,28 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : S
mUnpackPath->setCompleter(fsCompleter);
pathL->addRow(tr("Unpack directory"), mUnpackPath);
pathBox->setLayout(pathL);
- QVBoxLayout *pathLayout = new QVBoxLayout;
- pathLayout->addWidget(pathBox);
- pathWidget->setLayout(pathLayout);
+ QVBoxLayout *pathLayout = new QVBoxLayout;
+ pathLayout->addWidget(pathBox);
+ pathWidget->setLayout(pathLayout);
+
+ // usb paths
+ QHBoxLayout *usbSelectorL = new QHBoxLayout;
+ usbSelectorL->addWidget(new QLabel(tr("USB directory")));
+ mUSBDir = new QComboBox;
+ usbSelectorL->addWidget(mUSBDir);
+ QHBoxLayout *usbButtonL = new QHBoxLayout;
+ QPushButton *removeUSB = new QPushButton("Remove USB path");
+ connect(removeUSB, &QPushButton::clicked, mUSBDir, [=]() { mUSBDir->removeItem(mUSBDir->currentIndex()); });
+ usbButtonL->addWidget(removeUSB);
+ QPushButton *addUSB = new QPushButton(tr("Add USB path"));
+ connect(addUSB, &QPushButton::clicked, this, &ConfigurationDialog::addUSBPath);
+ usbButtonL->addWidget(addUSB);
+ QVBoxLayout *usbBoxL = new QVBoxLayout;
+ usbBoxL->addLayout(usbSelectorL);
+ usbBoxL->addLayout(usbButtonL);
+ QGroupBox *usbBox = new QGroupBox(tr("USB paths"));
+ usbBox->setLayout(usbBoxL);
+ pathLayout->addWidget(usbBox);
//expensive option
QGroupBox *expensiveBox = new QGroupBox(tr("Expensive file operations"));
@@ -352,7 +369,7 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : S
colorGrid->addWidget(favoriteColorBtn, 7, 2);
mAlternateColors = new QCheckBox(tr("Use alternating row colors"));
mAlternateColors->setTristate(false);
- connect(mAlternateColors, &QCheckBox::stateChanged, this, &ConfigurationDialog::alternateColorsChanged);
+ connect(mAlternateColors, &QCheckBox::checkStateChanged, this, &ConfigurationDialog::alternateColorsChanged);
colorGrid->addWidget(mAlternateColors, 8, 1, 1, 2, Qt::AlignLeft);
colorsWidget->setLayout(colorGrid);
@@ -368,7 +385,7 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : S
pvGrid->addWidget(mPVBgButton, 0, 2);
mPVGradient = new QCheckBox(tr("Use random gradient as background"));
mPVGradient->setTristate(false);
- connect(mPVGradient, &QCheckBox::stateChanged, this, &ConfigurationDialog::randomGradientChanged);
+ connect(mPVGradient, &QCheckBox::checkStateChanged, this, &ConfigurationDialog::randomGradientChanged);
pvGrid->addWidget(mPVGradient, 1, 1, 1, 2, Qt::AlignLeft);
QGroupBox *treeGB = new QGroupBox(tr("Treeview colors"));
@@ -443,7 +460,8 @@ void ConfigurationDialog::readSettings(){
//read paths
mArchiveDir->setText(s.value("paths/archivedir").toString());
mBurnDir->setText(s.value("paths/burn").toString());
- mUSBDir->setText(s.value("paths/usb").toString());
+ mUSBDir->addItems(s.value("paths/usball").toStringList());
+ mUSBDir->setCurrentText(s.value("paths/usb").toString());
mFfProbePath->setText(s.value("paths/ffprobe").toString());
mFfMpegPath->setText(s.value("paths/ffmpeg").toString());
mDvdMountPath->setText(s.value("paths/dvdmount").toString());
@@ -498,7 +516,12 @@ void ConfigurationDialog::writeSettings(){
//write paths
s.setValue("paths/archivedir", mArchiveDir->text());
s.setValue("paths/burn", mBurnDir->text());
- s.setValue("paths/usb", mUSBDir->text());
+ s.setValue("paths/usb", mUSBDir->currentText());
+ QStringList allPaths;
+ for(int i = 0; i < mUSBDir->count(); ++i){
+ allPaths << mUSBDir->itemText(i);
+ }
+ s.setValue("paths/usball", allPaths);
QString ffprobe = mFfProbePath->text();
QFileInfo ffProbeInfo(ffprobe);
if(ffProbeInfo.exists() && ffProbeInfo.isExecutable()){
@@ -588,7 +611,7 @@ bool ConfigurationDialog::checkDvdPath(){
continue;
}
QStringList options = fsParts.at(3).split(',');
- for(const QString &opt : options){
+ for(const QString &opt : std::as_const(options)){
if(opt.toLower().trimmed() == "user"){
ok = true;
break;
@@ -680,6 +703,16 @@ void ConfigurationDialog::delReason(){
}
}
+void ConfigurationDialog::addUSBPath(){
+ const QString newDir = QFileDialog::getExistingDirectory(this, tr("Select USB directory"));
+ if(newDir.isEmpty()){
+ return;
+ }
+ if(mUSBDir->findText(newDir) == -1){
+ mUSBDir->addItem(newDir);
+ }
+}
+
void ConfigurationDialog::setColor(QWidget *label){
QLabel *curLabel = qobject_cast<QLabel*>(label);
if(!curLabel){
diff --git a/configurationdialog.h b/configurationdialog.h
index 704074f..d64a73b 100644
--- a/configurationdialog.h
+++ b/configurationdialog.h
@@ -40,6 +40,7 @@ class ConfigurationDialog : public SmDialog {
void editReason();
void addReason();
void delReason();
+ void addUSBPath();
private:
void readSettings();
@@ -53,7 +54,7 @@ class ConfigurationDialog : public SmDialog {
ProgramConfigurator *mMovieConfig;
QLineEdit *mArchiveDir;
QLineEdit *mBurnDir;
- QLineEdit *mUSBDir;
+ QComboBox *mUSBDir;
QLineEdit *mFfProbePath;
QLineEdit *mFfMpegPath;
QLineEdit *mDvdMountPath;
diff --git a/delegates.cpp b/delegates.cpp
index 7a14bb4..f542014 100644
--- a/delegates.cpp
+++ b/delegates.cpp
@@ -12,6 +12,7 @@
#include "delegates.h"
#include "smglobals.h"
#include "helper.h"
+#include "archivebrowsermodel.h"
/* Delegate for File no. */
@@ -56,6 +57,21 @@ QWidget *FileTypeDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
return retval;
}
+/* Delegate for Genres */
+QString GenreDelegate::displayText(const QVariant &value, const QLocale &) const {
+ const QStringList &filtered = mProxy->genreFilters();
+ QStringList genres = value.toStringList();
+ QStringList retval;
+ for(const QString &g : std::as_const(genres)){
+ if(filtered.contains(g)){
+ retval << QString("%1 %2").arg(QChar(0x2705)).arg(g);
+ }else{
+ retval << QString("%1 %2").arg(QChar(0x274c)).arg(g);
+ }
+ }
+ return retval.join(' ');
+}
+
/* Delegate for Dvd no. */
QString DvdNoDelegate::displayText(const QVariant &value, const QLocale &locale) const{
diff --git a/delegates.h b/delegates.h
index 72b3b13..d24095a 100644
--- a/delegates.h
+++ b/delegates.h
@@ -13,6 +13,8 @@
#include <QStyleOptionViewItem>
#include <QStyledItemDelegate>
+class ArchiveBrowserModelProxy;
+
class FileNoDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
@@ -32,6 +34,16 @@ class FileTypeDelegate : public QStyledItemDelegate {
QHash<int, QString> mFiletypeMap;
};
+class GenreDelegate : public QStyledItemDelegate {
+ Q_OBJECT
+ public:
+ explicit GenreDelegate(QObject *parent = nullptr, ArchiveBrowserModelProxy *proxy = nullptr) : QStyledItemDelegate(parent), mProxy(proxy) {};
+ virtual QString displayText(const QVariant &value, const QLocale &locale) const;
+
+ private:
+ ArchiveBrowserModelProxy *mProxy;
+};
+
class DvdNoDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
diff --git a/helper.cpp b/helper.cpp
index a32d137..2f8e3b2 100644
--- a/helper.cpp
+++ b/helper.cpp
@@ -142,7 +142,7 @@ namespace Helper {
const QString createUSBPath(const QString &filename, const QString &seriesName, const QString &subtitle, int dvdNo, int seriesNo){
QSettings s;
- QString usbPath = s.value("paths/usb").toString();
+ QStringList allUsbPaths = s.value("paths/usball").toStringList();
QString seriesDir = seriesName;
if(seriesNo > 0){
@@ -151,7 +151,15 @@ namespace Helper {
seriesDir.append(QString(" - %1").arg(subtitle));
}
seriesDir.replace(' ', '.');
- QString retval = QString("%1/DVD_%2/%3/%4").arg(usbPath, QString::number(dvdNo), seriesDir, filename);
+ QString templ = QString("%1/DVD_%2/%3/%4");
+ QString retval;
+ for( const QString &up : std::as_const(allUsbPaths)){
+ retval = templ.arg(up, QString::number(dvdNo), seriesDir, filename);
+ QFileInfo retInfo(retval);
+ if(retInfo.exists()){
+ return retval;
+ }
+ }
return retval;
}
diff --git a/movieinfopage.cpp b/movieinfopage.cpp
index ae77d00..6051e08 100644
--- a/movieinfopage.cpp
+++ b/movieinfopage.cpp
@@ -28,6 +28,7 @@
#include <QApplication>
#include "movieinfopage.h"
+#include "newmoviewizard.h"
#include "wizardtreemodel.h"
#include "smtreeview.h"
#include "delegates.h"
@@ -232,10 +233,40 @@ void MovieInfoPage::extractMetadata(){
fn.replace(".mkv", "");
fn.replace(".mp4", "");
fn.replace(".", " ");
- mSubtitle->setText(fn);
+ if(!extractFromTitle(fn)){
+ mSubtitle->setText(fn);
+ }
+ }
+ }
+}
+bool MovieInfoPage::extractFromTitle(const QString &title){
+ QString curTitle = title;
+ static QRegularExpression removals("\\(4[kK]\\)");
+ static QRegularExpression has_date("\\s*\\((\\d{4}-\\d{2}-\\d{2}\\))");
+ curTitle = curTitle.replace(removals, "");
+ QDate created = QDate::currentDate();
+ QRegularExpressionMatch dateMatch = has_date.match(curTitle);
+ if(dateMatch.hasMatch()){
+ created = QDate::fromString(dateMatch.captured(1), Qt::ISODate);
+ curTitle.replace(has_date, "");
+ }
+ static QRegularExpression titleRe("^(.*?)\\s*-\\s*(.*)");
+ QRegularExpressionMatch titleMatch = titleRe.match(curTitle);
+ if(titleMatch.hasMatch()){
+ QString subtitle = titleMatch.captured(2).simplified().toLower();
+ QStringList actors = titleMatch.captured(1).split('&');
+ QStringList aRes;
+ for(const auto &a : std::as_const(actors)){
+ aRes.append(a.simplified().toLower());
}
+ auto wiz = static_cast<NewMovieWizard*>(wizard());
+ wiz->setPossibleActors(aRes);
+ mSubtitle->setText(subtitle);
+ mCreationDate->setDate(created);
+ return true;
}
+ return false;
}
void MovieInfoPage::addOld(){
diff --git a/movieinfopage.h b/movieinfopage.h
index 0407c33..479194e 100644
--- a/movieinfopage.h
+++ b/movieinfopage.h
@@ -8,7 +8,6 @@
#ifndef MOVIEINFOPAGE_H
#define MOVIEINFOPAGE_H
-#include <QDateTime>
#include <QWizardPage>
class WizardTreeModel;
@@ -48,6 +47,7 @@ class MovieInfoPage : public QWizardPage {
private:
void setupGui();
+ bool extractFromTitle(const QString &title);
SmTreeView *mFileView;
QLineEdit *mTitle;
QLineEdit *mSubtitle;
diff --git a/moviemappingpage.cpp b/moviemappingpage.cpp
index 8b7c328..e7d8f45 100644
--- a/moviemappingpage.cpp
+++ b/moviemappingpage.cpp
@@ -10,6 +10,7 @@
#include <QSettings>
#include "moviemappingpage.h"
+#include "newmoviewizard.h"
#include "smglobals.h"
MovieMappingPage::MovieMappingPage(const QString &table, QWidget *parent) : QWizardPage(parent), mTable(table){
@@ -35,6 +36,12 @@ void MovieMappingPage::initializePage(){
}
mWidget->fillCompleter(actors);
mWidget->setDecorationItem(SmGlobals::instance()->iconFor("actor"));
+ auto wiz = static_cast<NewMovieWizard*>(wizard());
+ QStringList possibleActors = wiz->getPossibleActors();
+ if(!possibleActors.empty()){
+ mWidget->clear();
+ mWidget->setCurrentItems(possibleActors);
+ }
}else if(mTable.toLower() == "genres"){
QStringList genres;
QSqlQuery genresQ("SELECT tgenrename FROM genres", db);
diff --git a/newmoviewizard.h b/newmoviewizard.h
index aef50f5..a8f4908 100644
--- a/newmoviewizard.h
+++ b/newmoviewizard.h
@@ -20,6 +20,8 @@ class NewMovieWizard : public QWizard {
explicit NewMovieWizard(QWidget *parent = nullptr);
virtual void accept();
virtual void reject();
+ void setPossibleActors(const QStringList &list) { mPossibleActors = list; };
+ const QStringList &getPossibleActors() { return mPossibleActors; };
MovieInfoPage *infoPage() { return mInfoPage; }
MovieMappingPage *actorPage() { return mActorPage; }
MovieMappingPage *genrePage() { return mGenrePage; }
@@ -30,6 +32,7 @@ class NewMovieWizard : public QWizard {
MovieMappingPage *mActorPage;
MovieMappingPage *mGenrePage;
MovieMetadataPage *mMetadataPage;
+ QStringList mPossibleActors;
};
#endif
diff --git a/randomtab.cpp b/randomtab.cpp
index b80fd9b..03881ea 100644
--- a/randomtab.cpp
+++ b/randomtab.cpp
@@ -154,7 +154,7 @@ void RandomTab::setupGui(){
mFileView->setRootIsDecorated(false);
mFileView->setSelectionMode(QAbstractItemView::ExtendedSelection);
mFileView->setSelectionBehavior(QAbstractItemView::SelectRows);
- connect(mFileView, &RandomFileView::doubleClicked, this, &RandomTab::playDoubleclicked);
+ connect(mFileView, &RandomFileView::activated, this, &RandomTab::playDoubleclicked);
mFileModel = new QStandardItemModel;
mFileProxy = new QSortFilterProxyModel;
mFileProxy->setSourceModel(mFileModel);
@@ -211,15 +211,17 @@ void RandomTab::readSettings(){
QStringList RandomTab::validDvdNos(){
QStringList retval;
QSettings s;
- QString usbDir = s.value("paths/usb").toString();
- logMessage(QString(tr("Traversing %1")).arg(usbDir));
- QDirIterator it(usbDir);
- while(it.hasNext()){
- it.next();
- QString next = it.fileName();
- if(next.startsWith("DVD_")){
- QString no = QString(next.right(3));
- retval << no;
+ QStringList allUsbPaths = s.value("paths/usball").toStringList();
+ for(const QString &up : std::as_const(allUsbPaths)){
+ logMessage(QString(tr("Traversing %1")).arg(up));
+ QDirIterator it(up);
+ while(it.hasNext()){
+ it.next();
+ QString next = it.fileName();
+ if(next.startsWith("DVD_")){
+ QString no = QString(next.right(3));
+ retval << no;
+ }
}
}
logMessage(QString(tr("Found %1 valid Dirs: (%2)")).arg(QString::number(retval.count()), retval.join(',')));
@@ -414,6 +416,7 @@ void RandomTab::select(){
}
}
fData[5]->setText(fullPath);
+ fData[5]->setData(fullPath, RandomTab::FullPathRole);
}
fRootItem->appendRow(fData);
}
@@ -448,11 +451,12 @@ void RandomTab::playSelected(){
}
void RandomTab::playDoubleclicked(QModelIndex idx){
- QString fp = idx.sibling(0, FullPath).data().toString();
- logMessage(QString(tr("Doubleclick on %1")).arg(fp));
- QStringList f = QStringList() << fp;
- play(f);
-
+ if(idx.isValid()){
+ QModelIndex fpidx = idx.siblingAtColumn(FullPath);
+ QString fp = fpidx.data(RandomTab::FullPathRole).toString();
+ logMessage(QString(tr("Doubleclick on %1")).arg(fp));
+ play(QStringList() << fp);
+ }
}
void RandomTab::play(const QStringList &files){
diff --git a/randomtab.h b/randomtab.h
index c479ac3..10d25bd 100644
--- a/randomtab.h
+++ b/randomtab.h
@@ -28,7 +28,7 @@ class RandomFileView;
class RandomTab : public QWidget {
Q_OBJECT
public:
- enum CustomRoles { IdRole = Qt::UserRole + 1, SizeRole = Qt::UserRole + 2, DurationRole = Qt::UserRole + 3, DvdNoRole = Qt::UserRole + 4 };
+ enum CustomRoles { IdRole = Qt::UserRole + 1, SizeRole = Qt::UserRole + 2, DurationRole = Qt::UserRole + 3, DvdNoRole = Qt::UserRole + 4, FullPathRole = Qt::UserRole + 5 };
enum Columns { FileName = 0, Size = 1, Duration = 2, Md5 = 3, Location = 4, FullPath = 5 };
enum { ColumnCount = 6 };
explicit RandomTab(QWidget *parent = nullptr);
diff --git a/shemov.pro b/shemov.pro
index af4ca38..8cb4237 100644
--- a/shemov.pro
+++ b/shemov.pro
@@ -124,3 +124,5 @@ unix {
INCLUDEPATH += /usr/include/ImageMagick-7/
}
RESOURCES = shemov.qrc
+
+DISTFILES +=
diff --git a/smglobals.cpp b/smglobals.cpp
index 0612b13..8b287e9 100644
--- a/smglobals.cpp
+++ b/smglobals.cpp
@@ -83,7 +83,7 @@ QAbstractItemModel *SmGlobals::model(const QString &which){
}
}else if(which == "BrowserModel"){
if(!mModels.contains("BrowserModel")){
- QStringList headers = QStringList() << QChar(0x26A7) << tr("Name") << tr("Generic Id") << tr("Node Type") << tr("Size") << tr("Quality") << tr("File Type") << tr("Full Path") << tr("Selected");
+ QStringList headers = QStringList() << QChar(0x26A7) << tr("Name") << tr("Generic Id") << tr("Node Type") << tr("Size") << tr("Quality") << tr("File Type") << tr("Full Path") << tr("Selected") << tr("Genres");
ArchiveBrowserModel *model = new ArchiveBrowserModel(headers);
mModels.insert(which, model);
}