diff options
| -rw-r--r-- | CMakeLists.txt | 173 | ||||
| -rw-r--r-- | archivebrowser.cpp | 44 | ||||
| -rw-r--r-- | archivebrowsermodel.cpp | 99 | ||||
| -rw-r--r-- | archivebrowsermodel.h | 16 | ||||
| -rw-r--r-- | configurationdialog.cpp | 57 | ||||
| -rw-r--r-- | configurationdialog.h | 3 | ||||
| -rw-r--r-- | delegates.cpp | 16 | ||||
| -rw-r--r-- | delegates.h | 12 | ||||
| -rw-r--r-- | helper.cpp | 12 | ||||
| -rw-r--r-- | movieinfopage.cpp | 33 | ||||
| -rw-r--r-- | movieinfopage.h | 2 | ||||
| -rw-r--r-- | moviemappingpage.cpp | 7 | ||||
| -rw-r--r-- | newmoviewizard.h | 3 | ||||
| -rw-r--r-- | randomtab.cpp | 34 | ||||
| -rw-r--r-- | randomtab.h | 2 | ||||
| -rw-r--r-- | shemov.pro | 2 | ||||
| -rw-r--r-- | smglobals.cpp | 2 |
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: @@ -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); @@ -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); } |
