Refactor and improve coding style of renaming utility

This commit is contained in:
Martchus 2018-08-15 21:49:06 +02:00
parent adf78c6955
commit db2f178542
8 changed files with 389 additions and 389 deletions

View File

@ -34,7 +34,7 @@ FileSystemItem::FileSystemItem(ItemStatus status, ItemType type, const QString &
FileSystemItem::~FileSystemItem() FileSystemItem::~FileSystemItem()
{ {
for (FileSystemItem *child : m_children) { for (auto *const child : m_children) {
child->m_parent = nullptr; child->m_parent = nullptr;
delete child; delete child;
} }
@ -45,14 +45,15 @@ FileSystemItem::~FileSystemItem()
void FileSystemItem::setParent(FileSystemItem *parent) void FileSystemItem::setParent(FileSystemItem *parent)
{ {
if (parent != m_parent) { if (parent == m_parent) {
if (m_parent) { return;
m_parent->m_children.removeAll(this); }
} if (m_parent) {
m_parent = parent; m_parent->m_children.removeAll(this);
if (m_parent && !m_parent->m_children.contains(this)) { }
m_parent->m_children << this; m_parent = parent;
} if (m_parent && !m_parent->m_children.contains(this)) {
m_parent->m_children << this;
} }
} }
@ -100,19 +101,16 @@ bool FileSystemItem::setNewName(const QString &newName)
{ {
switch (m_status) { switch (m_status) {
case ItemStatus::Current: case ItemStatus::Current:
if (!m_counterpart) { if (m_counterpart) {
if (m_parent) {
m_counterpart = new FileSystemItem(ItemStatus::New, m_type, newName);
m_counterpart->m_counterpart = this;
m_counterpart->setParent(m_parent);
} else {
// parent required
return false;
}
} else {
m_counterpart->setName(newName); m_counterpart->setName(newName);
return true;
} }
return true; if (m_parent) {
m_counterpart = new FileSystemItem(ItemStatus::New, m_type, newName, m_parent);
m_counterpart->m_counterpart = this;
return true;
}
return false;
case ItemStatus::New: case ItemStatus::New:
setName(newName); setName(newName);
return true; return true;
@ -122,7 +120,7 @@ bool FileSystemItem::setNewName(const QString &newName)
FileSystemItem *FileSystemItem::findChild(const QString &name) const FileSystemItem *FileSystemItem::findChild(const QString &name) const
{ {
for (FileSystemItem *child : m_children) { for (auto *const child : m_children) {
if (child->name() == name) { if (child->name() == name) {
return child; return child;
} }
@ -132,7 +130,7 @@ FileSystemItem *FileSystemItem::findChild(const QString &name) const
FileSystemItem *FileSystemItem::findChild(const QString &name, const FileSystemItem *exclude) const FileSystemItem *FileSystemItem::findChild(const QString &name, const FileSystemItem *exclude) const
{ {
for (FileSystemItem *child : m_children) { for (auto *const child : m_children) {
if (child != exclude && child->name() == name) { if (child != exclude && child->name() == name) {
return child; return child;
} }
@ -142,23 +140,24 @@ FileSystemItem *FileSystemItem::findChild(const QString &name, const FileSystemI
FileSystemItem *FileSystemItem::makeChildAvailable(const QString &relativePath) FileSystemItem *FileSystemItem::makeChildAvailable(const QString &relativePath)
{ {
QStringList dirs = relativePath.split(QDir::separator(), QString::SkipEmptyParts); auto dirs = relativePath.split(QDir::separator(), QString::SkipEmptyParts);
FileSystemItem *parent = this; if (dirs.isEmpty()) {
if (!dirs.isEmpty()) { return this;
if (relativePath.startsWith(QChar('/'))) { }
// we actually just got an absolute path
// -> just leave the / there to handle absolute path as well auto *parent = this;
dirs.front().prepend(QChar('/')); if (relativePath.startsWith(QChar('/'))) {
} // we actually just got an absolute path
for (const QString &dir : dirs) { // -> just leave the / there to handle absolute path as well
FileSystemItem *child = parent->findChild(dir); dirs.front().prepend(QChar('/'));
if (!child) { }
child = new FileSystemItem(ItemStatus::New, ItemType::Dir, dir); for (const QString &dir : dirs) {
child->setParent(parent); auto *child = parent->findChild(dir);
child->setNote(QCoreApplication::translate("RenamingUtility::FileSystemItem", "will be created")); if (!child) {
} child = new FileSystemItem(ItemStatus::New, ItemType::Dir, dir, parent);
parent = child; child->setNote(QCoreApplication::translate("RenamingUtility::FileSystemItem", "will be created"));
} }
parent = child;
} }
return parent; return parent;
} }
@ -179,13 +178,14 @@ QString FileSystemItem::relativeDir() const
void FileSystemItem::relativePath(QString &res) const void FileSystemItem::relativePath(QString &res) const
{ {
if (m_parent) { if (!m_parent) {
m_parent->relativePath(res); return;
if (!res.isEmpty()) {
res.append(QLatin1Char('/'));
}
res.append(name());
} }
m_parent->relativePath(res);
if (!res.isEmpty()) {
res.append(QLatin1Char('/'));
}
res.append(name());
} }
QString FileSystemItem::relativePath() const QString FileSystemItem::relativePath() const
@ -197,17 +197,16 @@ QString FileSystemItem::relativePath() const
bool FileSystemItem::hasSibling(const QString &name) const bool FileSystemItem::hasSibling(const QString &name) const
{ {
if (m_parent) { if (!m_parent) {
const QList<FileSystemItem *> &siblings = m_parent->children(); return false;
for (const FileSystemItem *siblingItem : siblings) { }
if (siblingItem == this) { const auto &siblings = m_parent->children();
continue; for (const auto *const siblingItem : siblings) {
} if (siblingItem == this) {
if (!siblingItem->newName().isEmpty() && siblingItem->newName() == name) { continue;
return true; }
} else if (siblingItem->name() == name) { if ((!siblingItem->newName().isEmpty() && siblingItem->newName() == name) || siblingItem->name() == name) {
return true; return true;
}
} }
} }
return false; return false;

View File

@ -48,7 +48,7 @@ public:
void setChecked(bool checked); void setChecked(bool checked);
bool checkable() const; bool checkable() const;
void setCheckable(bool checkable); void setCheckable(bool checkable);
int row(); int row() const;
void relativeDir(QString &res) const; void relativeDir(QString &res) const;
QString relativeDir() const; QString relativeDir() const;
void relativePath(QString &res) const; void relativePath(QString &res) const;
@ -186,9 +186,9 @@ inline void FileSystemItem::setCheckable(bool checkable)
m_checkable = checkable; m_checkable = checkable;
} }
inline int FileSystemItem::row() inline int FileSystemItem::row() const
{ {
return m_parent ? m_parent->children().indexOf(this) : -1; return m_parent ? m_parent->children().indexOf(const_cast<FileSystemItem *>(this)) : -1;
} }
} // namespace RenamingUtility } // namespace RenamingUtility

View File

@ -23,119 +23,126 @@ FileSystemItemModel::FileSystemItemModel(FileSystemItem *rootItem, QObject *pare
{ {
} }
FileSystemItemModel::~FileSystemItemModel()
{
}
void FileSystemItemModel::setRootItem(FileSystemItem *rootItem) void FileSystemItemModel::setRootItem(FileSystemItem *rootItem)
{ {
if (m_rootItem != rootItem) { if (m_rootItem == rootItem) {
beginResetModel(); return;
m_rootItem = rootItem;
endResetModel();
} }
beginResetModel();
m_rootItem = rootItem;
endResetModel();
}
FileSystemItem *FileSystemItemModel::fileSystemItemFromIndex(const QModelIndex &index)
{
return index.isValid() ? reinterpret_cast<FileSystemItem *>(index.internalPointer()) : nullptr;
}
const FileSystemItem *FileSystemItemModel::fileSystemItemFromIndex(const QModelIndex &index) const
{
return const_cast<FileSystemItemModel *>(this)->fileSystemItemFromIndex(index);
} }
QVariant FileSystemItemModel::data(const QModelIndex &index, int role) const QVariant FileSystemItemModel::data(const QModelIndex &index, int role) const
{ {
if (index.isValid()) { const auto *const item = fileSystemItemFromIndex(index);
if (FileSystemItem *item = reinterpret_cast<FileSystemItem *>(index.internalPointer())) { if (!item) {
switch (role) { return QVariant();
case Qt::DisplayRole: }
switch (index.column()) { switch (role) {
case 0: case Qt::DisplayRole:
switch (item->status()) { switch (index.column()) {
case ItemStatus::Current: case 0:
return item->name(); switch (item->status()) {
case ItemStatus::New: case ItemStatus::Current:
return item->counterpart() ? item->counterpart()->name() : item->name(); return item->name();
} case ItemStatus::New:
break; return item->counterpart() ? item->counterpart()->name() : item->name();
case 1:
switch (item->status()) {
case ItemStatus::Current:
return item->counterpart() ? item->counterpart()->name() : item->name();
case ItemStatus::New:
return item->name();
}
break;
case 2:
return item->note();
default:;
}
break;
case Qt::DecorationRole:
switch (index.column()) {
case 0:
case 1:
switch (item->type()) {
case ItemType::Dir:
return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
default:;
}
break;
default:;
}
break;
case Qt::FontRole: {
QFont font;
if ((index.column() == 0 && item->status() == ItemStatus::New && !item->counterpart())
|| (index.column() == 1 && item->status() == ItemStatus::Current && !item->counterpart())) {
font.setItalic(true);
}
return font;
} }
case Qt::ForegroundRole: break;
if (item->errorOccured()) { case 1:
return QBrush(Qt::red); switch (item->status()) {
} else if (item->applied()) { case ItemStatus::Current:
return QBrush(Qt::darkGreen); return item->counterpart() ? item->counterpart()->name() : item->name();
} else if ((index.column() == 0 && item->status() == ItemStatus::New && !item->counterpart()) case ItemStatus::New:
|| (index.column() == 1 && item->status() == ItemStatus::Current && !item->counterpart())) { return item->name();
return QBrush(Qt::gray); }
} break;
break; case 2:
case ErrorStatusRole: return item->note();
return item->errorOccured(); default:;
}
break;
case Qt::DecorationRole:
switch (index.column()) {
case 0:
case 1:
switch (item->type()) {
case ItemType::Dir:
return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
default:; default:;
} }
break;
default:;
} }
break;
case Qt::FontRole: {
QFont font;
if ((index.column() == 0 && item->status() == ItemStatus::New && !item->counterpart())
|| (index.column() == 1 && item->status() == ItemStatus::Current && !item->counterpart())) {
font.setItalic(true);
}
return font;
}
case Qt::ForegroundRole:
if (item->errorOccured()) {
return QBrush(Qt::red);
} else if (item->applied()) {
return QBrush(Qt::darkGreen);
} else if ((index.column() == 0 && item->status() == ItemStatus::New && !item->counterpart())
|| (index.column() == 1 && item->status() == ItemStatus::Current && !item->counterpart())) {
return QBrush(Qt::gray);
}
break;
case ErrorStatusRole:
return item->errorOccured();
default:;
} }
return QVariant(); return QVariant();
} }
bool FileSystemItemModel::setData(const QModelIndex &index, const QVariant &value, int role) bool FileSystemItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
if (index.isValid()) { auto *const item = fileSystemItemFromIndex(index);
if (FileSystemItem *item = reinterpret_cast<FileSystemItem *>(index.internalPointer())) { if (!item) {
switch (role) { return false;
case Qt::DisplayRole: }
switch (index.column()) { switch (role) {
case 0: case Qt::DisplayRole:
if (item->setCurrentName(value.toString())) { switch (index.column()) {
emit dataChanged(index, index, QVector<int>() << role); case 0:
return true; if (item->setCurrentName(value.toString())) {
} else { emit dataChanged(index, index, QVector<int>() << role);
return false;
}
case 1:
item->setNewName(value.toString());
emit dataChanged(index, index, QVector<int>() << role);
return true;
case 2:
item->setNote(value.toString());
emit dataChanged(index, index, QVector<int>() << role);
return true;
default:;
}
break;
case ErrorStatusRole:
item->setErrorOccured(value.toBool());
emit dataChanged(index, index, QVector<int>() << role << Qt::DecorationRole);
return true; return true;
default:; } else {
return false;
} }
case 1:
item->setNewName(value.toString());
emit dataChanged(index, index, QVector<int>() << role);
return true;
case 2:
item->setNote(value.toString());
emit dataChanged(index, index, QVector<int>() << role);
return true;
default:;
} }
break;
case ErrorStatusRole:
item->setErrorOccured(value.toBool());
emit dataChanged(index, index, QVector<int>() << role << Qt::DecorationRole);
return true;
default:;
} }
return false; return false;
} }
@ -152,7 +159,7 @@ QVariant FileSystemItemModel::headerData(int section, Qt::Orientation orientatio
case 1: case 1:
return tr("New name"); return tr("New name");
case 2: case 2:
return tr("Notes"); return tr("Note");
default:; default:;
} }
break; break;
@ -166,32 +173,32 @@ QVariant FileSystemItemModel::headerData(int section, Qt::Orientation orientatio
QModelIndex FileSystemItemModel::index(int row, int column, const QModelIndex &parent) const QModelIndex FileSystemItemModel::index(int row, int column, const QModelIndex &parent) const
{ {
if (FileSystemItem *parentItem = parent.isValid() ? reinterpret_cast<FileSystemItem *>(parent.internalPointer()) : m_rootItem) { const auto *const parentItem = parent.isValid() ? reinterpret_cast<FileSystemItem *>(parent.internalPointer()) : m_rootItem;
const QList<FileSystemItem *> &children = parentItem->children(); if (!parentItem) {
if (row < children.length()) { return QModelIndex();
return createIndex(row, column, children.at(row));
}
} }
return QModelIndex(); const auto &children = parentItem->children();
return row < children.size() ? createIndex(row, column, children.at(row)) : QModelIndex();
} }
QModelIndex FileSystemItemModel::index(FileSystemItem *item, int column) const QModelIndex FileSystemItemModel::index(FileSystemItem *item, int column) const
{ {
forward_list<FileSystemItem *> path; forward_list<FileSystemItem *> path;
path.push_front(item); path.push_front(item);
FileSystemItem *parent = item->parent(); auto *parent = item->parent();
while (parent) { while (parent) {
path.push_front(parent); path.push_front(parent);
parent = parent->parent(); parent = parent->parent();
} }
if (path.front() == m_rootItem) { if (path.front() != m_rootItem) {
path.pop_front(); return QModelIndex();
QModelIndex index; }
for (FileSystemItem *pathItem : path) { path.pop_front();
index = this->index(pathItem->row(), column, index); QModelIndex index;
if (pathItem == item || !index.isValid()) { for (auto *const pathItem : path) {
return index; index = this->index(pathItem->row(), column, index);
} if (pathItem == item || !index.isValid()) {
return index;
} }
} }
return QModelIndex(); return QModelIndex();
@ -199,35 +206,32 @@ QModelIndex FileSystemItemModel::index(FileSystemItem *item, int column) const
QModelIndex FileSystemItemModel::parent(const QModelIndex &index) const QModelIndex FileSystemItemModel::parent(const QModelIndex &index) const
{ {
if (index.isValid()) { const auto *const item = fileSystemItemFromIndex(index);
if (FileSystemItem *item = reinterpret_cast<FileSystemItem *>(index.internalPointer())) { if (!item) {
FileSystemItem *parent = item->parent(); return QModelIndex();
if (parent && (index.row() < parent->children().length())) {
return createIndex(parent->row(), index.column(), parent);
}
}
} }
return QModelIndex(); auto *const parent = item->parent();
if (!parent || index.row() >= parent->children().size()) {
return QModelIndex();
}
return createIndex(parent->row(), index.column(), parent);
} }
QModelIndex FileSystemItemModel::counterpart(const QModelIndex &index, int column = -1) QModelIndex FileSystemItemModel::counterpart(const QModelIndex &index, int column = -1)
{ {
if (index.isValid()) { const auto *const item = fileSystemItemFromIndex(index);
if (column < 0) { if (!item) {
column = index.column(); return QModelIndex();
}
if (FileSystemItem *item = reinterpret_cast<FileSystemItem *>(index.internalPointer())) {
if (item->counterpart()) {
return this->index(item->counterpart(), column);
}
}
} }
return QModelIndex(); if (column < 0) {
column = index.column();
}
return item->counterpart() ? this->index(item->counterpart(), column) : QModelIndex();
} }
int FileSystemItemModel::rowCount(const QModelIndex &parent) const int FileSystemItemModel::rowCount(const QModelIndex &parent) const
{ {
if (const FileSystemItem *parentItem = (parent.isValid() ? reinterpret_cast<FileSystemItem *>(parent.internalPointer()) : m_rootItem)) { if (const auto *const parentItem = (parent.isValid() ? reinterpret_cast<FileSystemItem *>(parent.internalPointer()) : m_rootItem)) {
return parentItem->children().size(); return parentItem->children().size();
} else { } else {
return 0; return 0;
@ -236,7 +240,7 @@ int FileSystemItemModel::rowCount(const QModelIndex &parent) const
bool FileSystemItemModel::hasChildren(const QModelIndex &parent) const bool FileSystemItemModel::hasChildren(const QModelIndex &parent) const
{ {
if (const FileSystemItem *parentItem = (parent.isValid() ? reinterpret_cast<const FileSystemItem *>(parent.internalPointer()) : m_rootItem)) { if (const auto *const parentItem = (parent.isValid() ? reinterpret_cast<const FileSystemItem *>(parent.internalPointer()) : m_rootItem)) {
return parentItem->children().size() > 0; return parentItem->children().size() > 0;
} else { } else {
return false; return false;

View File

@ -14,9 +14,10 @@ class FileSystemItemModel : public QAbstractItemModel {
public: public:
explicit FileSystemItemModel(FileSystemItem *rootItem, QObject *parent = nullptr); explicit FileSystemItemModel(FileSystemItem *rootItem, QObject *parent = nullptr);
~FileSystemItemModel();
void setRootItem(FileSystemItem *rootItem); void setRootItem(FileSystemItem *rootItem);
FileSystemItem *fileSystemItemFromIndex(const QModelIndex &index);
const FileSystemItem *fileSystemItemFromIndex(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role); bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

View File

@ -15,23 +15,28 @@ FilteredFileSystemItemModel::FilteredFileSystemItemModel(ItemStatus statusFilter
bool FilteredFileSystemItemModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool FilteredFileSystemItemModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); const auto sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
if (sourceIndex.isValid()) { if (!sourceIndex.isValid()) {
if (FileSystemItem *item = reinterpret_cast<FileSystemItem *>(sourceIndex.internalPointer())) { return false;
if (item->status() == m_statusFilter) { }
return true; const auto *const item = reinterpret_cast<FileSystemItem *>(sourceIndex.internalPointer());
} else if (item->status() == ItemStatus::Current && !item->counterpart() && !item->note().isEmpty()) { if (!item) {
return true; return false;
} else if (item->type() == ItemType::Dir) { }
QModelIndex child = sourceIndex.child(0, 0);
while (child.isValid()) { if (item->status() == m_statusFilter || (item->status() == ItemStatus::Current && !item->counterpart() && !item->note().isEmpty())) {
if (filterAcceptsRow(child.row(), sourceIndex)) { return true;
return true; }
} if (item->type() != ItemType::Dir) {
child = sourceIndex.child(child.row() + 1, 0); return false;
} }
}
auto child = sourceIndex.child(0, 0);
while (child.isValid()) {
if (filterAcceptsRow(child.row(), sourceIndex)) {
return true;
} }
child = sourceIndex.child(child.row() + 1, 0);
} }
return false; return false;
} }
@ -43,9 +48,8 @@ bool FilteredFileSystemItemModel::filterAcceptsColumn(int sourceColumn, const QM
return sourceColumn == 0; return sourceColumn == 0;
case ItemStatus::New: case ItemStatus::New:
return sourceColumn == 1 || sourceColumn == 2; return sourceColumn == 1 || sourceColumn == 2;
default:
return false;
} }
return false;
} }
} // namespace RenamingUtility } // namespace RenamingUtility

View File

@ -204,7 +204,7 @@ unique_ptr<FileSystemItem> RenamingEngine::generatePreview(const QDir &dir, File
void RenamingEngine::applyChangings(FileSystemItem *parentItem) void RenamingEngine::applyChangings(FileSystemItem *parentItem)
{ {
for (FileSystemItem *item : parentItem->children()) { for (auto *const item : parentItem->children()) {
if (!item->applied() && !item->errorOccured()) { if (!item->applied() && !item->errorOccured()) {
switch (item->status()) { switch (item->status()) {
case ItemStatus::New: { case ItemStatus::New: {
@ -277,7 +277,7 @@ void RenamingEngine::applyChangings(FileSystemItem *parentItem)
void RenamingEngine::setError(const QList<FileSystemItem *> items) void RenamingEngine::setError(const QList<FileSystemItem *> items)
{ {
for (FileSystemItem *item : items) { for (auto *const item : items) {
item->setErrorOccured(true); item->setErrorOccured(true);
item->setNote(tr("skipped due to error of superior item")); item->setNote(tr("skipped due to error of superior item"));
} }

View File

@ -228,30 +228,29 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileName(const QString &fileName)
TAGEDITOR_JS_VALUE TagEditorObject::allFiles(const QString &dirName) TAGEDITOR_JS_VALUE TagEditorObject::allFiles(const QString &dirName)
{ {
const QDir dir(dirName); const QDir dir(dirName);
if (dir.exists()) { if (!dir.exists()) {
const auto files(dir.entryList(QDir::Files));
auto entriesObj = m_engine->newArray(static_cast<uint>(files.size()));
quint32 counter = 0;
for (const auto &file : files) {
entriesObj.setProperty(counter, file TAGEDITOR_JS_READONLY);
++counter;
}
return entriesObj;
} else {
return TAGEDITOR_JS_VALUE(); return TAGEDITOR_JS_VALUE();
} }
const auto files(dir.entryList(QDir::Files));
auto entriesObj = m_engine->newArray(static_cast<uint>(files.size()));
quint32 counter = 0;
for (const auto &file : files) {
entriesObj.setProperty(counter++, file TAGEDITOR_JS_READONLY);
}
return entriesObj;
} }
TAGEDITOR_JS_VALUE TagEditorObject::firstFile(const QString &dirName) TAGEDITOR_JS_VALUE TagEditorObject::firstFile(const QString &dirName)
{ {
const QDir dir(dirName); const QDir dir(dirName);
if (dir.exists()) { if (!dir.exists()) {
const auto files(dir.entryList(QDir::Files)); return TAGEDITOR_JS_VALUE();
if (!files.empty()) {
return TAGEDITOR_JS_VALUE(files.first());
}
} }
return TAGEDITOR_JS_VALUE(); const auto files(dir.entryList(QDir::Files));
if (files.empty()) {
return TAGEDITOR_JS_VALUE();
}
return TAGEDITOR_JS_VALUE(files.first());
} }
void TagEditorObject::writeLog(const QString &message) void TagEditorObject::writeLog(const QString &message)

View File

@ -2,49 +2,47 @@
// This is an example script demonstrating how the renaming tool can be used. // This is an example script demonstrating how the renaming tool can be used.
// //
//
// script configuration // script configuration
//
// specifies the separator between artist, album and track number // specifies the separator between artist, album and track number
var separator = ", "; var separator = ", ";
// specifies the separator between title and other fields // specifies the separator between title and other fields
var lastSeparator = " - "; var lastSeparator = " - ";
// specifies whether the artist name should be included // specifies whether the artist name should be included
var includeArtist = false; var includeArtist = true;
// specifies whether the album name should be included // specifies whether the album name should be included
var includeAlbum = false; var includeAlbum = true;
// specifies whether the title should be included // specifies whether the title should be included
var includeTitle = true; var includeTitle = true;
// specifies the distribution directory // specifies the "distribution directory"
// all files will be moved in an appropriate subdirectory in the //var distDir = false; // don't move files around
// distribution directory if one is specified var distDir = "/path/to/my/music-collection"; // move files to an appropriate subdirectory under this path
var distDir = false; // directory used to store collections which contain songs from multiple artists
// string used for "miscellaneous" category
var misc = "misc";
// directory used to store collections
var collectionsDir = "collections"; var collectionsDir = "collections";
// directory used to store miscellaneous songs by miscellaneous artists
var miscDir = "misc";
// directory used for miscellaneous songs by specific artist
var miscAlbumDir = "misc";
// condition to move files to miscDir
var isMiscFile = function (tag) { return tag.comment === "misc"; };
// condition to consider files part of a collection which contains songs from multiple artists
var isPartOfCollection = function (tag) { return tag.comment === "collection"; }
// define some helper functions //
// helper functions
//
/*! // returns whether the specified \a value is not undefined and not an empty string.
* Returns whether the specified \a value is not undefined
* and not an empty string.
*/
function notEmpty(value) { function notEmpty(value) {
return value !== undefined && value !== ""; return value !== undefined && value !== "";
} }
// returns whether the specified \a value is not undefined and not zero.
/*!
* Returns whether the specified \a value is not undefined
* and not zero.
*/
function notNull(value) { function notNull(value) {
return value !== undefined && value !== 0; return value !== undefined && value !== 0;
} }
// returns the string representation of \a pos using at least as many digits as \a total has
/*!
* Returns the string representation of \a pos using at least as
* many digits as \a total has.
*/
function appropriateDigitCount(pos, total) { function appropriateDigitCount(pos, total) {
var res = pos + ""; var res = pos + "";
var count = (total + "").length; var count = (total + "").length;
@ -53,152 +51,147 @@ function appropriateDigitCount(pos, total) {
} }
return res; return res;
} }
// returns a copy of the specified \a name with characters that might be avoided in file names striped out
/*!
* Returns a copy of the specified \a name with characters that might be
* avoided in file names striped out.
*/
function validFileName(name) { function validFileName(name) {
if(name !== undefined) { return name !== undefined ? name.replace(/[\/\\]/gi, " - ").replace(/[<>?!*|:\"\n\f\r]/gi, "") : "";
return name.replace(/[\/\\]/gi, " - ").replace(/[<>?!*|:\"\n\f\r]/gi, "");
} else {
return "";
}
} }
// returns a copy of the specified \a name with characters that might be avoided in directory names striped out.
/*!
* Returns a copy of the specified \a name with characters that might be
* avoided in directory names striped out.
*/
function validDirectoryName(name) { function validDirectoryName(name) {
if(name !== undefined) { return name !== undefined ? name.replace(/[\/\\]/gi, " - ").replace(/[<>?!*|:\".\n\f\r]/gi, "") : "";
return name.replace(/[\/\\]/gi, " - ").replace(/[<>?!*|:\".\n\f\r]/gi, "");
} else {
return "";
}
} }
// the actual script //
// actual script
//
// check whether we have to deal with a file or a directory // skip directories in this example script
if(tageditor.isFile) { if (!tageditor.isFile) {
// parse file using the built-in parseFileInfo function
var fileInfo = tageditor.parseFileInfo(tageditor.currentPath);
var tag = fileInfo.tag; // get the tag information
// read title and track number from the file name using the built-in parseFileName function
var infoFromFileName = tageditor.parseFileName(fileInfo.currentBaseName);
// read the suffix from the file info object to filter backup and temporary files
if(fileInfo.currentName === "desktop.ini") {
tageditor.skip(); // skip these files
} else if(fileInfo.currentSuffix === "bak") {
// filter backup by putting them in a separate directory
tageditor.move("backups");
} else if(fileInfo.currentSuffix === "tmp") {
// filter temporary files in the same way as backup files
tageditor.move("temp");
} else {
// define an array for the fields; will be joined later
var fields = [];
// get the artist and remove invalid characters
var artist = validFileName(tag.artist);
// add artist to the fields array
// (if configured and present and if it is no collection)
if(includeArtist && tag.comment !== "collection") {
if(notEmpty(artist)) {
fields.push(artist);
}
}
// get the album and remove invalid characters
var album = validFileName(tag.album);
// add album to the fields array (if configure and present)
if(includeAlbum) {
if(notEmpty(tag.album)) {
fields.push(album);
}
}
// get the track/disk position; use the value from the tag if possible
if(notNull(tag.trackPos)) {
// define an array for the track position; will be joined later
var pos = [];
// push the disk position
if(notNull(tag.diskPos)
&& notNull(tag.diskTotal)
&& tag.diskTotal >= 2) {
pos.push(appropriateDigitCount(tag.diskPos, tag.diskTotal));
}
// push the track count
if(notNull(tag.trackTotal)) {
pos.push(appropriateDigitCount(tag.trackPos, tag.trackTotal));
} else {
pos.push(appropriateDigitCount(tag.trackPos, 10));
}
fields.push(pos.join("-"));
} else if(notNull(infoFromFileName.trackPos)) {
// get the track position from the file name if the tag has no track position field
fields.push(appropriateDigitCount(infoFromFileName.trackPos, 10));
}
// join the first part of the new name
var newName = fields.join(separator);
// get the title
var title = validFileName(tag.title);
// append the title (if configured and present)
if(includeTitle) {
// use value from file name if the tag has no title information
if(!notEmpty(title)) {
title = validFileName(infoFromFileName.title);
}
if(newName.length > 0) {
newName = newName.concat(lastSeparator, title);
} else {
newName = newName.concat(title);
}
}
// get an appropriate suffix
var suffix = "";
if(notEmpty(fileInfo.suitableSuffix)) {
// get a suitable suffix from the file info object if available
suffix = fileInfo.suitableSuffix;
} else if(notEmpty(fileInfo.currentSuffix)) {
// or just use the current suffix otherwise
suffix = fileInfo.currentSuffix;
}
// append the suffix
if(notEmpty(suffix)) {
newName = newName.concat(".", suffix);
}
// apply new name
tageditor.rename(newName);
// set the distribution directory
if(distDir) {
var path = [distDir];
var artist = validDirectoryName(tag.artist);
if(tag.comment === "collection") {
path.push(collectionsDir);
} else {
if(notEmpty(artist)) {
path.push(artist);
} else {
path.push(misc);
}
}
var album = validDirectoryName(tag.album);
if(notEmpty(album)) {
if(notEmpty(tag.year)) {
path.push([tag.year.split("-")[0], album].join(" - "));
} else {
path.push(album);
}
} else if(notEmpty(artist)) {
path.push(misc);
}
if(tag.diskTotal >= 2) {
path.push("Disk " + appropriateDigitCount(tag.diskPos, tag.diskTotal));
}
// apply new relative directory
tageditor.move(path.join("/"));
}
}
} else if(tageditor.isDir) {
// skip directories in this example script
tageditor.skip(); tageditor.skip();
return;
} }
// parse file using the built-in parseFileInfo function
var fileInfo = tageditor.parseFileInfo(tageditor.currentPath);
var tag = fileInfo.tag;
// deduce title and track number from the file name using the built-in parseFileName function (as fallback if tags missing)
var infoFromFileName = tageditor.parseFileName(fileInfo.currentBaseName);
// skip hidden and "desktop.ini" files
if (fileInfo.currentName.indexOf(".") === 0 || fileInfo.currentName === "desktop.ini") {
tageditor.skip();
return;
}
// skip files which don't contain audio or video tracks
if (!fileInfo.hasAudioTracks && !fileInfo.hasVideoTracks) {
tageditor.skip();
return;
}
// filter backup and temporary files by putting them in a separate directory
if (fileInfo.currentSuffix === "bak") {
tageditor.move("backups");
return;
}
if (fileInfo.currentSuffix === "tmp") {
tageditor.move("temp");
return;
}
// define an array for the fields to be joined later
var fields = [];
// get the artist, remove invalid characters and add it to fields array
var artist = validFileName(tag.artist);
if (includeArtist && !isPartOfCollection(tag) && notEmpty(artist)) {
fields.push(artist);
}
// get the album and remove invalid characters and add it to fields array
var album = validFileName(tag.album);
if (includeAlbum && notEmpty(tag.album)) {
fields.push(album);
}
// get the track/disk position and add it to fields array
// use the value from the tag if possible; otherwise the value deduced from the filename
if (notNull(tag.trackPos)) {
var pos = [];
// push the disk position
if (notNull(tag.diskPos) && notNull(tag.diskTotal) && tag.diskTotal >= 2) {
pos.push(appropriateDigitCount(tag.diskPos, tag.diskTotal));
}
// push the track count
if (notNull(tag.trackTotal)) {
pos.push(appropriateDigitCount(tag.trackPos, tag.trackTotal));
} else {
pos.push(appropriateDigitCount(tag.trackPos, 10));
}
fields.push(pos.join("-"));
} else if (notNull(infoFromFileName.trackPos)) {
fields.push(appropriateDigitCount(infoFromFileName.trackPos, 10));
}
// join the first part of the new name
var newName = fields.join(separator);
// get the title and append it
var title = validFileName(tag.title);
if (includeTitle) {
// use value from file name if the tag has no title information
if (!notEmpty(title)) {
title = validFileName(infoFromFileName.title);
}
if (newName.length > 0) {
newName = newName.concat(lastSeparator, title);
} else {
newName = newName.concat(title);
}
}
// append an appropriate suffix
var suffix = "";
if (notEmpty(fileInfo.suitableSuffix)) {
// get a suitable suffix from the file info object if available
suffix = fileInfo.suitableSuffix;
} else if (notEmpty(fileInfo.currentSuffix)) {
// or just use the current suffix otherwise
suffix = fileInfo.currentSuffix;
}
if (notEmpty(suffix)) {
newName = newName.concat(".", suffix);
}
// apply new name
tageditor.rename(newName);
// set the distribution directory
if (!distDir) {
return;
}
var path = [distDir];
var artist = validDirectoryName(tag.artist);
if (isPartOfCollection(tag)) {
path.push(collectionsDir);
} else if (isMiscFile(tag)) {
path.push(miscDir);
} else if (notEmpty(artist)) {
path.push(artist);
} else {
path.push(misc);
}
var album = validDirectoryName(tag.album);
if (notEmpty(album)) {
if (notEmpty(tag.year)) {
path.push([tag.year.split("-")[0], album].join(" - "));
} else {
path.push(album);
}
} else if (notEmpty(artist) && !isMiscFile(tag)) {
path.push(miscAlbumDir);
}
if (tag.diskTotal >= 2) {
path.push("Disk " + appropriateDigitCount(tag.diskPos, tag.diskTotal));
}
// apply new relative directory
tageditor.move(path.join("/"));