aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2012-08-20 02:53:24 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2012-08-20 02:53:24 +0200
commit43538809de8ebb49f5e6cea9a19bcbdd933da44e (patch)
tree4dac9081f8bcc27b45bb5132df554cc87caa0b97
parentSupport audio properties too. (diff)
downloadmusic-file-organizer-43538809de8ebb49f5e6cea9a19bcbdd933da44e.tar.xz
music-file-organizer-43538809de8ebb49f5e6cea9a19bcbdd933da44e.zip
Make fundumentals work decently.
-rw-r--r--AudioFile.cpp10
-rw-r--r--Makefile9
-rw-r--r--organizemusic.cpp189
-rw-r--r--readtags.cpp1
4 files changed, 205 insertions, 4 deletions
diff --git a/AudioFile.cpp b/AudioFile.cpp
index 9c3f612..ac4dc54 100644
--- a/AudioFile.cpp
+++ b/AudioFile.cpp
@@ -94,6 +94,16 @@ AudioFile::AudioFile(const std::string &filename) :
m_year = tag->year();
m_track = tag->track();
}
+ if (m_title.length() == 0) {
+ size_t pos;
+ m_title = filename;
+ pos = m_title.find_last_of('/');
+ if (pos != std::string::npos)
+ m_title.erase(0, pos + 1);
+ pos = m_title.find_last_of('.');
+ if (pos != std::string::npos)
+ m_title.erase(pos);
+ }
if (const TagLib::AudioProperties *audio = fileRef.audioProperties()) {
m_length = audio->length();
m_bitrate = audio->bitrate();
diff --git a/Makefile b/Makefile
index dcf72b2..5e51e2e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,11 @@
-LDFLAGS += $(shell pkg-config --libs taglib)
+LDFLAGS += $(shell pkg-config --libs taglib icu-i18n)
CXXFLAGS ?= -O3 -pipe -fomit-frame-pointer -march=native
-CXXFLAGS += $(shell pkg-config --cflags taglib)
-
+CXXFLAGS += $(shell pkg-config --cflags taglib icu-uc)
all: readtags organizemusic
readtags: AudioFile.cpp AudioFile.h readtags.cpp
-
organizemusic: AudioFile.cpp AudioFile.h organizemusic.cpp
+
+clean:
+ rm -vf readtags organizemusic
diff --git a/organizemusic.cpp b/organizemusic.cpp
new file mode 100644
index 0000000..b885ebe
--- /dev/null
+++ b/organizemusic.cpp
@@ -0,0 +1,189 @@
+#include "AudioFile.h"
+
+#include <unicode/translit.h>
+#include <unicode/unistr.h>
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <algorithm>
+
+#include <cstdio>
+#include <cstring>
+#include <cerrno>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+using namespace std;
+
+static string base_destination = "/home/zx2c4/Music/";
+static Transliterator *transliterator;
+
+void process_file(const char *filename, ino_t inode);
+void process_directory(const char *directory);
+void process_path(const char *path);
+
+string generate_path(const AudioFile &audio);
+string truncated(const string &str, const string &ext);
+string transliterated(const string &str);
+void rename_path(const string &source, const string &stem, ino_t inode);
+void strip_slash(string &name);
+void disc_track(unsigned int disc, unsigned int track, ostringstream &path);
+
+string strip_slash(const string &name)
+{
+ string ret(name);
+ replace(ret.begin(), ret.end(), '/', '-');
+ return ret;
+}
+void disc_track(unsigned int disc, unsigned int track, ostringstream &path)
+{
+ if (disc > 0) {
+ path << disc;
+ if (track > 0)
+ path << '-';
+ else
+ path << ' ';
+ }
+ if (track > 0)
+ path << setw(2) << setfill('0') << track << setw(0) << ' ';
+}
+string generate_path(const AudioFile &audio)
+{
+ ostringstream path;
+ if (audio.compilation()) {
+ path << "Various Artists/";
+ if (audio.album().length() == 0)
+ path << "Unknown Album/";
+ else
+ path << strip_slash(audio.album()) << '/';
+ disc_track(audio.disc(), audio.track(), path);
+ if (audio.artist().length() > 0)
+ path << strip_slash(audio.artist()) << " - ";
+ path << strip_slash(audio.title());
+ } else {
+ if (audio.artist().length() == 0)
+ path << "Unknown Artist/";
+ else
+ path << strip_slash(audio.artist()) << '/';
+ if (audio.album().length() > 0)
+ path << strip_slash(audio.album()) << '/';
+ disc_track(audio.disc(), audio.track(), path);
+ path << strip_slash(audio.title());
+ }
+ return transliterated(path.str());
+}
+string truncated(const string &str, const string &ext)
+{
+ stringstream stream;
+ size_t current, next = -1;
+ for (;;) {
+ current = next + 1;
+ next = str.find_first_of('/', current);
+ if (current)
+ stream << '/';
+ if (next == string::npos) {
+ stream << str.substr(current, static_cast<size_t>(255 - ext.length())) << ext;
+ break;
+ }
+ stream << str.substr(current, min(static_cast<size_t>(255), next - current));
+ }
+ return stream.str();
+}
+string transliterated(const string &str)
+{
+ UnicodeString in(str.c_str(), "UTF-8");
+ transliterator->transliterate(in);
+ char out[in.length() + 1];
+ out[in.extract(0, in.length(), out, in.length(), "ASCII")] = '\0';
+ return out;
+}
+void rename_path(const string &source, const string &stem, ino_t inode)
+{
+ size_t lastdot = source.find_last_of('.');
+ string ext;
+ if (lastdot != string::npos) {
+ ext = source.substr(lastdot);
+ transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+ }
+ string destination = truncated(base_destination + stem, ext);
+ unsigned long counter = 0;
+ for (;;) {
+ struct stat sbuf;
+ if (stat(destination.c_str(), &sbuf)) {
+ if (errno == ENOENT)
+ break;
+ }
+ if (sbuf.st_ino == inode)
+ return;
+
+ ostringstream stream;
+ stream << base_destination << stem << ' ' << ++counter;
+ destination = truncated(stream.str(), ext);
+
+ if (counter == 0)
+ return;
+ }
+ cout << source << endl << "\t-> " << destination << endl;
+}
+void process_file(const char *filename, ino_t inode)
+{
+ AudioFile audio(filename);
+ if (!audio.isValid()) {
+ cerr << filename << ": not a valid audio file" << endl;
+ return;
+ }
+ rename_path(filename, generate_path(audio), inode);
+}
+void process_directory(const char *directory)
+{
+ DIR *dir = opendir(directory);
+ if (!dir)
+ return;
+ while (struct dirent *entry = readdir(dir)) {
+ if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
+ (entry->d_name[1] == '.' && strlen(entry->d_name) == 2)))
+ continue;
+
+ char joined[strlen(directory) + strlen(entry->d_name) + 2];
+ sprintf(joined, "%s/%s", directory, entry->d_name);
+ process_path(joined);
+ }
+ closedir(dir);
+}
+void process_path(const char *path)
+{
+ struct stat sbuf;
+ if (stat(path, &sbuf)) {
+ perror(path);
+ return;
+ }
+ if (S_ISREG(sbuf.st_mode))
+ process_file(path, sbuf.st_ino);
+ else if (S_ISDIR(sbuf.st_mode))
+ process_directory(path);
+ else
+ cerr << path << ": not a file nor a directory" << endl;
+}
+
+int main(int argc, char *argv[])
+{
+ UErrorCode status = U_ZERO_ERROR;
+ transliterator = Transliterator::createInstance("Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:^ASCII:] Remove; [\\:;*?\"<>|\\\\] Remove", UTRANS_FORWARD, status);
+ if (!transliterator || status != U_ZERO_ERROR) {
+ cerr << "Fatal: Could not initialize transliterator." << endl;
+ return EXIT_FAILURE;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ size_t len = strlen(argv[i]);
+ if (argv[i][len - 1] == '/')
+ argv[i][len - 1] = '\0';
+ process_path(argv[i]);
+ }
+
+ return 0;
+}
diff --git a/readtags.cpp b/readtags.cpp
index 169645c..62312f6 100644
--- a/readtags.cpp
+++ b/readtags.cpp
@@ -31,4 +31,5 @@ int main(int argc, char *argv[])
cout << "Compilation: " << f.compilation() << endl;
cout << endl;
}
+ return 0;
}