From 9903511005c75142de203e27a0f8bd2b886e6910 Mon Sep 17 00:00:00 2001 From: SuperFashi Date: Sun, 11 Feb 2018 13:31:18 +0800 Subject: [PATCH] A wrapper from QFile to TagLib device, few fixes, and v1.1 --- bp_audio.pro | 3 +- file_stream.cpp | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 53 +++++++++++--------- 3 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 file_stream.cpp diff --git a/bp_audio.pro b/bp_audio.pro index 5223d59..bbc2f7c 100644 --- a/bp_audio.pro +++ b/bp_audio.pro @@ -14,6 +14,7 @@ DEFINES += QT_DEPRECATED_WARNINGS # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -SOURCES += main.cpp +SOURCES += main.cpp \ + file_stream.cpp LIBS += -ltag -lz -lgumbo_query_static -lgumbo QMAKE_CXXFLAGS += -DTAGLIB_STATIC diff --git a/file_stream.cpp b/file_stream.cpp new file mode 100644 index 0000000..f133c95 --- /dev/null +++ b/file_stream.cpp @@ -0,0 +1,130 @@ +#include +#include + +class FileStream: public TagLib::IOStream { + QFile *file; + unsigned int bufferSize() const { return 1024; } +public: + FileStream(QFile *f): file(f) {} + + bool readOnly() const { + return !file->isWritable(); + } + + bool isOpen() const { + return file->isOpen(); + } + + long tell() const { + return file->pos(); + } + + long length() { + return file->size(); + } + + void truncate(long length) { + file->resize(length); + } + + void seek(long offset, + TagLib::IOStream::Position p = TagLib::IOStream::Beginning) { + switch (p) { + case TagLib::IOStream::Beginning: + file->seek(offset); + break; + case TagLib::IOStream::Current: + file->seek(file->pos() + offset); + break; + case TagLib::IOStream::End: + file->seek(file->size() + offset); + } + } + + TagLib::FileName name() const { + return file->fileName().toStdWString().data(); + } + + TagLib::ByteVector readBlock(unsigned long length) { + TagLib::ByteVector bv(static_cast(length)); + const qint64 &l = file->read(bv.data(), length); + bv.resize(l); + return std::move(bv); + } + + void writeBlock(const TagLib::ByteVector &data) { + file->write(data.data(), data.size()); + } + + void insert(const TagLib::ByteVector &data, + unsigned long start, + unsigned long replace) { + if (data.size() == replace) { + seek(start); + writeBlock(data); + return; + } + if (data.size() < replace) { + seek(start); + writeBlock(data); + removeBlock(start + data.size(), replace - data.size()); + return; + } + unsigned long bufferLength = bufferSize(); + while (data.size() - replace > bufferLength) + bufferLength += bufferSize(); + + long readPosition = start + replace; + long writePosition = start; + + QByteArray buffer(data.data(), data.size()); + QByteArray aboutToOverwrite; + aboutToOverwrite.resize(bufferLength); + + while (true) { + seek(readPosition); + const qint64 &bytesRead = file->peek(aboutToOverwrite.data(), bufferLength); + aboutToOverwrite.resize(bytesRead); + readPosition += bufferLength; + + if (bytesRead < bufferLength) clear(); + + seek(writePosition); + file->write(buffer); + + if (bytesRead == 0) break; + + writePosition += buffer.size(); + buffer = aboutToOverwrite; + } + } + + void removeBlock(unsigned long start, unsigned long length) { + unsigned long bufferLength = bufferSize(); + + long readPosition = start + length; + long writePosition = start; + + QByteArray buffer; + buffer.resize(bufferLength); + + for (qint64 bytesRead = -1; bytesRead != 0;) + { + seek(readPosition); + bytesRead = file->peek(buffer.data(), bufferLength); + readPosition += bytesRead; + + if (bytesRead < buffer.size()) { + clear(); + buffer.resize(bytesRead); + } + + seek(writePosition); + file->write(buffer); + + writePosition += bytesRead; + } + + truncate(writePosition); + } +}; diff --git a/main.cpp b/main.cpp index 8512259..b9b616e 100644 --- a/main.cpp +++ b/main.cpp @@ -19,20 +19,13 @@ #include #include -#include +#include "file_stream.cpp" class PictureWrapper { TagLib::String mime_type; TagLib::FLAC::Picture::Type t; TagLib::ByteVector bv; public: - PictureWrapper(const TagLib::String &mime, - const TagLib::FLAC::Picture::Type &type, - const QByteArray &byte): - mime_type(mime), - t(type), - bv(byte.data(), byte.length()) {} - PictureWrapper(QNetworkReply *resp) { const QByteArray &bin = resp->readAll(); bv = TagLib::ByteVector(bin.data(), bin.length()); @@ -51,7 +44,7 @@ class PictureWrapper { const int bar_width = 50; const QString app_name = "bp_audio"; -const QString app_version = "1.0"; +const QString app_version = "1.1"; QCommandLineOption au_opt({"a", "au"}, "Audio AU number.", "au"); QCommandLineOption all_opt("menu", "Download whole menu."); @@ -61,11 +54,26 @@ QScopedPointer session; QDir output; QHash > assets; +#ifdef _WIN32 +#include + +QString santize_filename(const QString &name) { + std::array buf{}; + name.toWCharArray(buf.data()); + PathCleanupSpec(output.absolutePath().toStdWString().data(), buf.data()); + return std::move(QString::fromWCharArray(buf.data())); +} +#else +QString santize_filename(const QString &name) { + return name; // Yeah'em lazy. +} +#endif + QString find_audio(const QByteArray &data) { CDocument doc; doc.parse(data.toStdString()); CSelection audio = doc.find("audio"); - return QString::fromStdString(audio.nodeAt(0).attribute("src")); + return std::move(QString::fromStdString(audio.nodeAt(0).attribute("src"))); } void download_progress(const qint64 &bytes_received, const qint64 &bytes_total) { @@ -81,9 +89,9 @@ void download_progress(const qint64 &bytes_received, const qint64 &bytes_total) if (bytes_received >= bytes_total) std::cerr << '\n'; } -void write_metadata(const QString &file, const QJsonObject &data, +void write_metadata(QFile *file, const QJsonObject &data, const int &track, const int &total) { - TagLib::FLAC::File f(TagLib::FileName(file.toStdWString().data())); + TagLib::FLAC::File f(new FileStream(file), nullptr); if (!f.isValid()) { qCritical("Downloaded song not valid"); std::exit(EXIT_FAILURE); @@ -139,10 +147,7 @@ void write_metadata(const QString &file, const QJsonObject &data, void download_song(const QJsonObject &data, const int &track = -1, const int &total = -1) { QString file_name = QStringLiteral("%1 - %2.flac").arg(data["author"].toString(), data["title"].toString()); - std::array buf{}; - file_name.toWCharArray(buf.data()); - PathCleanupSpec(output.absolutePath().toStdWString().data(), buf.data()); - file_name = QString::fromWCharArray(buf.data()); + file_name = santize_filename(file_name); const QString &au = QString::number(data["id"].toInt()); qInfo("Getting audio preview for %s...", qUtf8Printable(au)); @@ -178,7 +183,7 @@ void download_song(const QJsonObject &data, const int &track = -1, const int &to file_name = output.filePath(file_name); qInfo("Writing to %s...", qUtf8Printable(file_name)); QFile f(file_name); - if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + if (f.open(QIODevice::ReadWrite | QIODevice::Truncate)) { do { loop.exec(); if (song_res->error() != QNetworkReply::NoError) { @@ -188,9 +193,9 @@ void download_song(const QJsonObject &data, const int &track = -1, const int &to } f.write(song_res->readAll()); } while (!song_res->isFinished()); - f.close(); } - write_metadata(file_name, data, track, total); + write_metadata(&f, data, track, total); + f.close(); qInfo("Finished for %s", qUtf8Printable(file_name)); } @@ -222,11 +227,11 @@ void get_menu_info(const QString &menuid) { const QJsonObject &menus_response = data["menusRespones"].toObject(); QString folder_name = QStringLiteral("%1 - %2").arg(menus_response["mbnames"].toString(), menus_response["title"].toString()); - std::array buf{}; - folder_name.toWCharArray(buf.data()); - PathCleanupSpec(output.absolutePath().toStdWString().data(), buf.data()); - folder_name = QString::fromWCharArray(buf.data()); - output.mkdir(folder_name); + folder_name = santize_filename(folder_name); + if (!output.exists(folder_name) && !output.mkdir(folder_name)) { + qCritical("Error creating folder -- check permission"); + std::exit(EXIT_FAILURE); + } output.cd(folder_name); const QJsonArray &songsList = data["songsList"].toArray();