#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "torrentwidget.h" #include "filesorter.h" #include "torrentparser.h" #include "torrentdisplay.h" #include "globals.h" TorrentWidget::TorrentWidget(QWidget *parent) : QWidget(parent), mExt("*.torrent") { setupGui(); } TorrentWidget::~TorrentWidget(){ writeHeaderData(); writeSettings(); } void TorrentWidget::setupGui(){ mDir = new QLineEdit; QCompleter *torDirCompl = new QCompleter(this); torDirCompl->setModel(new QDirModel(torDirCompl)); mDir->setCompleter(torDirCompl); connect(mDir, &QLineEdit::returnPressed, this, &TorrentWidget::setDir); QPushButton *selDirB = new QPushButton(tr("Browse...")); connect(selDirB, &QPushButton::clicked, this, &TorrentWidget::selectDir); mSearchTorrents = new QLineEdit; connect(mSearchTorrents, &QLineEdit::returnPressed, this, &TorrentWidget::searchFile); QPushButton *doSearchTorrentsB = new QPushButton(tr("Search")); connect(doSearchTorrentsB, &QPushButton::clicked, this, &TorrentWidget::searchFile); QLabel *searchL = new QLabel(tr("&Search")); searchL->setBuddy(mSearchTorrents); QGridLayout *buttonL = new QGridLayout; buttonL->addWidget(new QLabel(tr("Directory")), 0, 0); buttonL->addWidget(mDir, 0, 1); buttonL->addWidget(selDirB, 0, 2); buttonL->addWidget(searchL, 1, 0); buttonL->addWidget(mSearchTorrents, 1, 1); buttonL->addWidget(doSearchTorrentsB, 1, 2); mFileView = new QTreeView; Globals::instance()->addView(mFileView); mFileView->setSortingEnabled(true); mFileView->setSelectionBehavior(QAbstractItemView::SelectRows); mFileView->setSelectionMode(QAbstractItemView::ExtendedSelection); mFileView->setUniformRowHeights(true); mModel = new QStandardItemModel; mProxy = new FileSorter; mProxy->setSourceModel(mModel); mFileView->setModel(mProxy); QLabel *fileL = new QLabel(tr("Found:")); readSettings(); mToolBar = new QToolBar; mToolBar->setIconSize(QSize(16, 16)); mMenuBar = new QMenuBar; QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mMenuBar); mainLayout->addWidget(mToolBar); mainLayout->addLayout(buttonL); mainLayout->addWidget(fileL); mainLayout->addWidget(mFileView); setLayout(mainLayout); mTorrentDisplay = new TorrentDisplay(this); connect(mFileView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TorrentWidget::fileSelectionChanged); createActions(); connect(mFileView, &QTreeView::doubleClicked, this, &TorrentWidget::torrentInfo); } void TorrentWidget::createActions(){ mRefreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); mRefreshA->setShortcut(tr("CTRL+R")); connect(mRefreshA, &QAction::triggered, this, &TorrentWidget::gatherData); mDeleteA = new QAction(QIcon(":/delete.png"), tr("Delete..."), this); mDeleteA->setShortcut(Qt::Key_Delete); connect(mDeleteA, &QAction::triggered, this, &TorrentWidget::deleteFiles); mMoveA = new QAction(QIcon(":/diaper.png"), tr("Move..."), this); mMoveA->setShortcut(tr("CTRL+M")); connect(mMoveA, &QAction::triggered, this, &TorrentWidget::moveFiles); mTorrentInfoA = new QAction(QIcon(":/huge_bra.png"), tr("Torrent Info..."), this); mTorrentInfoA->setShortcut(Qt::CTRL + Qt::Key_Return); connect(mTorrentInfoA, &QAction::triggered, this, &TorrentWidget::torrentInfo); mSelDirA = new QAction(QIcon(":/folder.png"), tr("Select folder..."), this); mSelDirA->setShortcut(tr("CTRL+O")); connect(mSelDirA, &QAction::triggered, this, &TorrentWidget::selectDir); mCopyFnToClipA = new QAction(QIcon(":/edit-copy.png"), tr("Filename to Clip"), this); mCopyFnToClipA->setShortcut(tr("CTRL+C")); connect(mCopyFnToClipA, &QAction::triggered, this, &TorrentWidget::copyToClipboard); mGuessSubtitleA = new QAction(QIcon(":/hourglass_figure.png"), tr("Guess subtitle"), this); mGuessSubtitleA->setShortcut(tr("CTRL+SHIFT+C")); connect(mGuessSubtitleA, &QAction::triggered, this, &TorrentWidget::guessSubtitle); QMenu *fileMenu = new QMenu(tr("&File")); fileMenu->addAction(mSelDirA); fileMenu->addAction(mRefreshA); fileMenu->addSeparator(); fileMenu->addAction(Globals::instance()->action(Globals::QuitAction)); QMenu *editMenu = new QMenu(tr("&Edit")); editMenu->addAction(mCopyFnToClipA); editMenu->addAction(mGuessSubtitleA); editMenu->addAction(mMoveA); editMenu->addAction(mDeleteA); editMenu->addSeparator(); editMenu->addAction(mTorrentInfoA); editMenu->addSeparator(); editMenu->addAction(Globals::instance()->action(Globals::ConfigAction)); mMenuBar->addMenu(fileMenu); mMenuBar->addMenu(editMenu); mToolBar->addAction(mRefreshA); mToolBar->addAction(mSelDirA); mToolBar->addAction(createSeparator()); mToolBar->addAction(mCopyFnToClipA); mToolBar->addAction(mGuessSubtitleA); mToolBar->addAction(mMoveA); mToolBar->addAction(mDeleteA); mToolBar->addAction(createSeparator()); mToolBar->addAction(mTorrentInfoA); mToolBar->addAction(createSeparator()); mToolBar->addAction(Globals::instance()->action(Globals::ConfigAction)); addActions(QList() << mRefreshA << mSelDirA << createSeparator() << mCopyFnToClipA << mGuessSubtitleA << mMoveA << mDeleteA << createSeparator() << mTorrentInfoA); } void TorrentWidget::gatherData(){ rememberSelection(); if(isVisible()){ writeHeaderData(); } QSqlDatabase db = QSqlDatabase::database("shemovdb"); if(!db.isOpen()){ db.open(); } QSqlQuery q(db); q.prepare("SELECT COUNT(*) FROM metadata WHERE tsubject = :fn"); QDir d(mDir->text()); QFileInfoList fl = d.entryInfoList(QStringList() << mExt, QDir::Files, QDir::Name); mProgressBar->reset(); mProgressBar->setMinimum(0); mProgressBar->setMaximum(fl.count() - 1); if(fl.count() > 100){ QString msg = QString(tr("

Looking at %1 files could take a while

Do you really want to continue

")).arg(QString::number(fl.count())); int retval = QMessageBox::question(this, tr("Continue"), msg); if(retval == QMessageBox::No){ return; } } int count = 0; mFileView->setSortingEnabled(false); mModel->clear(); QStandardItem *root = mModel->invisibleRootItem(); mModel->setHorizontalHeaderLabels(QStringList() << QChar(0x26A7) << tr("Name") << tr("Created")); QBrush redBrush(Qt::red); QBrush greenBrush(Qt::darkGreen); for(QFileInfo fi : fl){ QList fData; for(int i = 0; i < ColumnCount; ++i){ QStandardItem *item = new QStandardItem; item->setEditable(false); if(i == CreatedColumn){ item->setFont(QFont("courier new")); } fData << item; } int result = 0; q.bindValue(":fn", fi.fileName()); q.exec(); while(q.next()){ result = q.value(0).toInt(); } fData[NameColumn]->setText(fi.fileName()); fData[NameColumn]->setData(fi.absoluteFilePath(), FullPathRole); fData[CreatedColumn]->setText(fi.birthTime().toString(Qt::RFC2822Date)); fData[CreatedColumn]->setData(fi.birthTime(), CreatedRole); if(result){ fData[IconColumn]->setIcon(QIcon(":/huge_bra.png")); fData[IconColumn]->setData(Present, PresentRole); fData[NameColumn]->setForeground(greenBrush); }else{ fData[IconColumn]->setIcon(QIcon(":/gaping_ass.png")); fData[IconColumn]->setData(NotPresent, PresentRole); fData[NameColumn]->setForeground(redBrush); } root->appendRow(fData); mProgressBar->setValue(++count); } qApp->processEvents(); mFileView->setSortingEnabled(true); readHeaderData(); restoreSelection(); } void TorrentWidget::deleteFiles(){ QModelIndexList sel = mFileView->selectionModel()->selectedRows(NameColumn); if(sel.isEmpty()){ return; } QString q = QString(tr("Really delete %1 file(s)?")).arg(QString::number(sel.count())); int res = QMessageBox::question(this, tr("Delete files..."), q); if(res == QMessageBox::Yes){ int ctr = 0; QStringList removed; for(const QModelIndex &i : sel){ QString fp = i.data(FullPathRole).toString(); bool ok = QFile::remove(fp); if(ok){ ++ctr; }else{ QFileInfo fi(fp); QString msg = QString(tr("Shit: %1 vanished from under us!")).arg(fi.fileName()); QMessageBox::critical(this, tr("Error"), msg); } removed << i.data().toString(); } for(const QString& f : removed){ QList res = mModel->findItems(f, Qt::MatchExactly, NameColumn); if(!res.isEmpty()){ mModel->removeRow(res.at(0)->row()); } } QString msg = QString(tr("Successfully deleted %1 of %2 file(s)")).arg(QString::number(ctr)).arg(QString::number(sel.count())); emit statusMessage(msg); emit freeSpaceChanged(mDir->text()); } } void TorrentWidget::moveFiles(){ QModelIndexList sel = mFileView->selectionModel()->selectedRows(NameColumn); if(sel.isEmpty()){ return; } QSettings s; QString sDir = s.value("dirs/movedir", QDir::homePath()).toString(); QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), sDir); if(!dir.isEmpty()){ s.setValue("dirs/movedir", dir); int ctr = 0; QStringList removed; for(const QModelIndex &i : sel){ QFileInfo fp(i.data(FullPathRole).toString()); QString newfn = QString("%1/%2").arg(dir).arg(fp.fileName()); bool ok = QFile::rename(fp.absoluteFilePath(), newfn); if(ok){ ++ctr; }else{ QFileInfo nf(newfn); if(nf.exists() && nf.isFile()){ //don't care if it's the same file or not... QFile::remove(fp.absoluteFilePath()); ++ctr; } } removed << i.data().toString(); } for(const QString& f : removed){ QList res = mModel->findItems(f, Qt::MatchExactly, NameColumn); if(!res.isEmpty()){ mModel->removeRow(res.at(0)->row()); } } QString msg = QString(tr("Successfully moved %1 of %2 file(s)")).arg(QString::number(ctr)).arg(QString::number(sel.count())); emit statusMessage(msg); emit freeSpaceChanged(mDir->text()); } } void TorrentWidget::torrentInfo(){ QModelIndexList sel = mFileView->selectionModel()->selectedRows(NameColumn); if(sel.isEmpty()){ return; } QString fn = sel.first().data(FullPathRole).toString(); TorrentParser p(fn, this); QList tData = p.parse(); tData.removeAll(QVariant()); tData.removeAll(0); mTorrentDisplay->setData(tData, sel.first().data().toString()); mTorrentDisplay->show(); return; } void TorrentWidget::searchFile(){ mFileView->selectionModel()->clear(); int count = mProxy->rowCount(); QRegularExpression regex(mSearchTorrents->text(), QRegularExpression::CaseInsensitiveOption); int success = 0; for(int i = 0; i < count; ++i){ QModelIndex cur = mProxy->index(i, 1); QString fn = cur.data(FullPathRole).toString(); TorrentParser p(fn); emit statusMessage(QString(tr("parsing %1").arg(fn))); QList torrentData = p.parse(); QStringList files = p.files(torrentData); for(QString f : files){ QRegularExpressionMatch m = regex.match(f); if(m.hasMatch()){ ++success; mFileView->selectionModel()->select(cur, QItemSelectionModel::Select | QItemSelectionModel::Rows); if(success == 1){ mFileView->scrollTo(cur); } } } } emit statusMessage(QString(tr("Found %1 file(s)").arg(QString::number(success)))); } void TorrentWidget::keyPressEvent(QKeyEvent *e){ if(e->key() == Qt::Key_S && (e->modifiers() & Qt::ControlModifier)){ mSearchTorrents->setFocus(); mSearchTorrents->selectAll(); goto exit; } if(e->key() == Qt::Key_D && (e->modifiers() & Qt::ControlModifier)){ mDir->setFocus(); mDir->selectAll(); goto exit; } if(e->key() == Qt::Key_F && (e->modifiers() & Qt::ControlModifier)){ mFileView->setFocus(); goto exit; } if(mFileView->hasFocus()){ if(e->key() == Qt::Key_Return){ } } return QWidget::keyPressEvent(e); exit: e->accept(); } void TorrentWidget::contextMenuEvent(QContextMenuEvent *e){ QMenu contextMenu(this); contextMenu.addActions(actions()); contextMenu.exec(e->globalPos()); } void TorrentWidget::selectDir(){ QSettings s; QString sDir = s.value("dirs/torrentdir", QDir::homePath()).toString(); QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), sDir); if(!dir.isEmpty()){ mDir->setText(QDir::toNativeSeparators(dir)); setDir(); } } void TorrentWidget::setDir(){ QSettings s; s.setValue("dirs/torrentdir", mDir->text()); gatherData(); emit freeSpaceChanged(mDir->text()); } void TorrentWidget::copyToClipboard(){ QModelIndex cur = mFileView->selectionModel()->currentIndex(); QClipboard *clip = QApplication::clipboard(); clip->setText(cur.data().toString()); } void TorrentWidget::fileSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected){ Q_UNUSED(selected); Q_UNUSED(deselected); int count = mFileView->selectionModel()->selectedRows().count(); int total = mModel->rowCount(); QString msg = QString("%1/%2").arg(QString::number(count)).arg(QString::number(total)); emit selectionCountChanged(msg); } void TorrentWidget::selectFirst(){ if(mModel->rowCount() > 0){ QModelIndex idx = mProxy->index(0, NameColumn); mFileView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Rows); } } void TorrentWidget::guessSubtitle(){ QModelIndex cur = mFileView->selectionModel()->currentIndex(); QClipboard *clip = QApplication::clipboard(); QString curName = cur.data().toString().toLower(); QRegExp re1(" *\\[.*\\] *"); re1.setMinimal(true); curName.replace(re1, ""); QRegExp re2(" *\\(.*\\) *"); re2.setMinimal(true); curName.replace(re2, ""); QRegExp re3("\\s+"); curName.replace(re3, " "); curName.replace(".torrent", ""); curName = curName.trimmed(); QRegExp re4("^shemale\\s*-*"); curName.replace(re4, ""); QRegExp re5("(\\.mp\\d|rq|\\s*720p|\\s*1080p)"); curName.replace(re5, ""); curName = curName.trimmed(); clip->setText(curName); QString msg = QString(tr("Copied \"%1\" to Clipboard")).arg(curName); emit statusMessage(msg); } void TorrentWidget::readSettings(){ QSettings s; QString dir = s.value("searchdir", QDir::toNativeSeparators(QDir::homePath())).toString(); mDir->setText(dir); } void TorrentWidget::writeSettings(){ QSettings s; s.setValue("searchdir", mDir->text()); } void TorrentWidget::rememberSelection(){ QModelIndexList sel = mFileView->selectionModel()->selectedRows(NameColumn); mSelection.clear(); for(QModelIndex idx : sel){ mSelection << idx.data().toString(); } } void TorrentWidget::restoreSelection(){ QList modelItems; for(QString sel : mSelection){ modelItems << mModel->findItems(sel, Qt::MatchExactly, NameColumn); } for(QStandardItem *i : modelItems){ QModelIndex srcIdx = mModel->indexFromItem(i); QModelIndex idx = mProxy->mapFromSource(srcIdx); mFileView->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } QAction *TorrentWidget::createSeparator(){ QAction *retval = new QAction(this); retval->setSeparator(true); return retval; } void TorrentWidget::readHeaderData(){ QSettings s; QByteArray headers = s.value("fileheaders").toByteArray(); if(!headers.isEmpty()){ mFileView->header()->restoreState(headers); } } void TorrentWidget::writeHeaderData(){ QSettings s; s.setValue("fileheaders", mFileView->header()->saveState()); }