Toob Player UI
This commit is contained in:
@@ -33,6 +33,8 @@ JSON_MAP_REFERENCE(AudioFileMetadata, lastModified)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, title)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, track)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, album)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, albumArtist)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, artist)
|
||||
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, thumbnailType)
|
||||
JSON_MAP_REFERENCE(AudioFileMetadata, thumbnailFile)
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace pipedal {
|
||||
std::string title_;
|
||||
int32_t track_ = -1; // track number, 0-based, -1 if not set
|
||||
std::string album_;
|
||||
std::string artist_;
|
||||
std::string albumArtist_;
|
||||
float duration_ = 0;
|
||||
int32_t thumbnailType_ = 0; // 0 = unknown, 1 = embedded, 3 = folder, 4 = none
|
||||
std::string thumbnailFile_; // only when thumbnailType is 3= folder.
|
||||
@@ -80,6 +82,8 @@ namespace pipedal {
|
||||
GETTER_SETTER_REF(title)
|
||||
GETTER_SETTER_REF(track)
|
||||
GETTER_SETTER_REF(album)
|
||||
GETTER_SETTER_REF(albumArtist)
|
||||
GETTER_SETTER_REF(artist)
|
||||
GETTER_SETTER(duration)
|
||||
ThumbnailType thumbnailType() const { return (ThumbnailType)thumbnailType_;}
|
||||
void thumbnailType(ThumbnailType value) { thumbnailType_ = (int32_t)value; }
|
||||
|
||||
@@ -144,8 +144,12 @@ static int32_t MetadataTrackToInt(const std::string &track, const std::string &d
|
||||
int trackNumber = std::stoi(track);
|
||||
if (!disc.empty())
|
||||
{
|
||||
int discNumber = std::stoi(disc);
|
||||
trackNumber += discNumber + trackNumber; // e.g. disc 1 track 2 becomes 1002
|
||||
if (disc != "" && disc != "1/1" && disc != "1")
|
||||
{
|
||||
int discNumber = std::stoi(disc);
|
||||
trackNumber += discNumber*1000+ trackNumber; // e.g. disc 1 track 2 becomes 1002
|
||||
}
|
||||
|
||||
}
|
||||
return trackNumber;
|
||||
}
|
||||
@@ -178,8 +182,8 @@ AudioFileMetadata::AudioFileMetadata(const std::filesystem::path &file)
|
||||
{
|
||||
// not all of this data gets used (e.g. DATE, YEAR, ALBUM_ARTIST,TOTALTRACKS). But ... write once.
|
||||
this->album_ = MetadataString(tags, {"ALBUM", "album"});
|
||||
//this->artist_ = MetadataString(tags, {"ARTIST", "artist"});
|
||||
//this->albumArtist_ = MetadataString(tags, {"ALBUM ARTIST", "album_artist", "album artist"});
|
||||
this->artist_ = MetadataString(tags, {"ARTIST", "artist"});
|
||||
this->albumArtist_ = MetadataString(tags, {"ALBUM ARTIST", "album_artist", "album artist"});
|
||||
this->title_ = MetadataString(tags, {"TITLE", "title"});
|
||||
//this->date_ = MetadataString(tags, {"DATE", "date"});
|
||||
//this->year_ = MetadataString(tags, {"YEAR", "year"});
|
||||
|
||||
+149
-30
@@ -30,6 +30,9 @@
|
||||
#include "MimeTypes.hpp"
|
||||
#include <stdexcept>
|
||||
#include "AudioFilesDb.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
#include "ss.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#undef _GLIBCXX_DEBUG // Ensure we are not in debug mode, as this file is not compatible with it.
|
||||
#include "SQLiteCpp/SQLiteCpp.h"
|
||||
@@ -48,7 +51,7 @@ void AudioDirectoryInfo::SetResourceDirectory(const std::filesystem::path &path)
|
||||
{
|
||||
resourceDirectory = path;
|
||||
}
|
||||
std::filesystem::path AudioDirectoryInfo::GetTemporaryDirectory() const
|
||||
std::filesystem::path AudioDirectoryInfo::GetTemporaryDirectory()
|
||||
{
|
||||
if (temporaryDirectory.empty())
|
||||
{
|
||||
@@ -56,7 +59,7 @@ std::filesystem::path AudioDirectoryInfo::GetTemporaryDirectory() const
|
||||
}
|
||||
return temporaryDirectory;
|
||||
}
|
||||
std::filesystem::path AudioDirectoryInfo::GetResourceDirectory() const
|
||||
std::filesystem::path AudioDirectoryInfo::GetResourceDirectory()
|
||||
{
|
||||
if (resourceDirectory.empty())
|
||||
{
|
||||
@@ -74,7 +77,9 @@ namespace
|
||||
|
||||
static int64_t fileTimeToInt64(const fs::file_time_type &fileTime)
|
||||
{
|
||||
return fileTime.time_since_epoch().count();
|
||||
std::chrono::system_clock::time_point system_time = std::chrono::clock_cast<std::chrono::system_clock>(fileTime);
|
||||
auto result = std::chrono::duration_cast<std::chrono::milliseconds>(system_time.time_since_epoch()).count();
|
||||
return result;
|
||||
}
|
||||
static int64_t GetLastWriteTime(const fs::path &file)
|
||||
{
|
||||
@@ -91,9 +96,15 @@ namespace
|
||||
class AudioDirectoryInfoImpl : public AudioDirectoryInfo
|
||||
{
|
||||
public:
|
||||
AudioDirectoryInfoImpl(const std::string &path)
|
||||
: path(path)
|
||||
AudioDirectoryInfoImpl(const std::filesystem::path &path, const std::filesystem::path&indexPath)
|
||||
: path(path), indexPath(
|
||||
(indexPath.empty() ? path: indexPath) / ".index.pipedal"
|
||||
)
|
||||
{
|
||||
if (this->indexPath.parent_path() != path)
|
||||
{
|
||||
fs::create_directories(this->indexPath.parent_path());
|
||||
}
|
||||
}
|
||||
virtual ~AudioDirectoryInfoImpl() {}
|
||||
|
||||
@@ -110,7 +121,16 @@ namespace
|
||||
virtual std::string GetNextAudioFile(const std::string &fileNameOnly) override;
|
||||
virtual std::string GetPreviousAudioFile(const std::string &fileNameOnly) override;
|
||||
|
||||
virtual void MoveAudioFile(
|
||||
const std::string &directory,
|
||||
int32_t fromPosition,
|
||||
int32_t toPosition) override;
|
||||
|
||||
private:
|
||||
std::vector<DbFileInfo> QueryTracks();
|
||||
|
||||
|
||||
bool opened = false;
|
||||
std::filesystem::path indexPath;
|
||||
void OpenAudioDb();
|
||||
ThumbnailTemporaryFile GetUnindexedThunbnail(const std::string &fileNameOnly, int32_t width, int32_t height);
|
||||
@@ -119,7 +139,6 @@ namespace
|
||||
fs::path path;
|
||||
using id_t = int64_t;
|
||||
|
||||
std::vector<DbFileInfo> QueryTracks();
|
||||
|
||||
std::vector<DbFileInfo> UpdateDbFiles();
|
||||
void DbSetThumbnailType(
|
||||
@@ -137,14 +156,23 @@ namespace
|
||||
void UpdateMetadata(DbFileInfo *dbFile);
|
||||
static constexpr int DB_VERSION = 1;
|
||||
std::filesystem::path GetFolderFile() const;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
AudioDirectoryInfo::Ptr AudioDirectoryInfo::Create(const std::string &path)
|
||||
AudioDirectoryInfo::Ptr AudioDirectoryInfo::Create(const std::filesystem::path &path, const std::filesystem::path&indexPath)
|
||||
{
|
||||
return std::shared_ptr<AudioDirectoryInfo>(
|
||||
new AudioDirectoryInfoImpl(path));
|
||||
return std::make_shared<AudioDirectoryInfoImpl>(path,indexPath);
|
||||
}
|
||||
|
||||
|
||||
std::vector<DbFileInfo> AudioDirectoryInfoImpl::QueryTracks()
|
||||
{
|
||||
if (audioFilesDb)
|
||||
{
|
||||
return audioFilesDb->QueryTracks();
|
||||
}
|
||||
return std::vector<DbFileInfo>();
|
||||
}
|
||||
|
||||
std::vector<AudioFileMetadata> AudioDirectoryInfoImpl::GetFiles()
|
||||
@@ -160,14 +188,6 @@ std::vector<AudioFileMetadata> AudioDirectoryInfoImpl::GetFiles()
|
||||
return metadataResults;
|
||||
}
|
||||
|
||||
std::vector<DbFileInfo> AudioDirectoryInfoImpl::QueryTracks()
|
||||
{
|
||||
if (audioFilesDb)
|
||||
{
|
||||
return audioFilesDb->QueryTracks();
|
||||
}
|
||||
return std::vector<DbFileInfo>();
|
||||
}
|
||||
static bool isAudioExtension(const std::string &extension)
|
||||
{
|
||||
return MimeTypes::instance().AudioExtensions().contains(extension);
|
||||
@@ -340,12 +360,6 @@ std::vector<DbFileInfo> AudioDirectoryInfoImpl::UpdateDbFiles()
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateRequired && newFiles.empty())
|
||||
{
|
||||
// Nothing to do.
|
||||
return dbFiles;
|
||||
}
|
||||
|
||||
Locale::ptr locale = Locale::GetInstance();
|
||||
Collator::ptr collator = locale->GetCollator();
|
||||
if (!HasPositionInfo(dbFiles))
|
||||
@@ -415,7 +429,8 @@ static std::vector<std::filesystem::path> COVER_ART_FILES = {
|
||||
"AlbumArt.jpg",
|
||||
"albumArt.jpg",
|
||||
"Frontcover.jpg",
|
||||
"AlbumArtSmall.jpg"};
|
||||
"AlbumArtSmall.jpg" // windows media player.
|
||||
};
|
||||
|
||||
fs::path AudioDirectoryInfoImpl::GetFolderFile() const
|
||||
{
|
||||
@@ -446,7 +461,7 @@ void AudioDirectoryInfoImpl::DbSetThumbnailType(
|
||||
}
|
||||
}
|
||||
void AudioDirectoryInfoImpl::DbSetThumbnailType(
|
||||
const std::string&fileNameOnly,
|
||||
const std::string &fileNameOnly,
|
||||
ThumbnailType thumbnailType,
|
||||
const std::string &thumbnailFile,
|
||||
int64_t thumbnailLastModified)
|
||||
@@ -614,6 +629,8 @@ void AudioDirectoryInfoImpl::UpdateMetadata(DbFileInfo *dbFile)
|
||||
dbFile->track(metadata.track());
|
||||
dbFile->duration(metadata.duration());
|
||||
dbFile->album(metadata.album());
|
||||
dbFile->artist(metadata.artist());
|
||||
dbFile->albumArtist(metadata.albumArtist());
|
||||
dbFile->lastModified(GetLastWriteTime(file));
|
||||
dbFile->duration(metadata.duration());
|
||||
}
|
||||
@@ -656,8 +673,17 @@ size_t AudioDirectoryInfoImpl::TestGetNumberOfThumbnails()
|
||||
|
||||
void AudioDirectoryInfoImpl::OpenAudioDb()
|
||||
{
|
||||
if (!this->audioFilesDb) {
|
||||
this->audioFilesDb = std::make_shared<AudioFilesDb>(this->path, indexPath);
|
||||
|
||||
if (!this->audioFilesDb)
|
||||
{
|
||||
try
|
||||
{
|
||||
this->audioFilesDb = std::make_shared<AudioFilesDb>(this->path, indexPath);
|
||||
}
|
||||
catch (const SQLite::Exception &e)
|
||||
{
|
||||
Lv2Log::debug("Can't create .index.pipedal: %s - %s", e.what(), this->path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +695,7 @@ ThumbnailTemporaryFile AudioDirectoryInfoImpl::DefaultThumbnailTemporaryFile()
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
std::string AudioDirectoryInfoImpl::GetNextAudioFile(const std::string &fileNameOnly)
|
||||
std::string AudioDirectoryInfoImpl::GetNextAudioFile(const std::string &fileNameOnly)
|
||||
{
|
||||
auto files = this->GetFiles();
|
||||
if (files.empty())
|
||||
@@ -692,7 +718,7 @@ std::string AudioDirectoryInfoImpl::GetNextAudioFile(const std::string &fileName
|
||||
}
|
||||
return "";
|
||||
}
|
||||
std::string AudioDirectoryInfoImpl::GetPreviousAudioFile(const std::string &fileNameOnly)
|
||||
std::string AudioDirectoryInfoImpl::GetPreviousAudioFile(const std::string &fileNameOnly)
|
||||
{
|
||||
auto files = this->GetFiles();
|
||||
if (files.empty())
|
||||
@@ -715,3 +741,96 @@ std::string AudioDirectoryInfoImpl::GetPreviousAudioFile(const std::string &file
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void AudioDirectoryInfoImpl::MoveAudioFile(
|
||||
const std::string &directory,
|
||||
int32_t fromPosition,
|
||||
int32_t toPosition)
|
||||
{
|
||||
OpenAudioDb();
|
||||
if (!audioFilesDb)
|
||||
{
|
||||
throw std::runtime_error("Directory is not writable.");
|
||||
}
|
||||
auto files = this->UpdateDbFiles();
|
||||
if (directory.empty() || fromPosition < 0 || toPosition < 0 ||
|
||||
fromPosition >= static_cast<int32_t>(files.size()) ||
|
||||
toPosition > static_cast<int32_t>(files.size()))
|
||||
{
|
||||
throw std::invalid_argument("Invalid arguments for MoveAudioFile");
|
||||
}
|
||||
std::shared_ptr<SQLite::Transaction> transaction;
|
||||
if (audioFilesDb)
|
||||
{
|
||||
transaction = audioFilesDb->transaction();
|
||||
}
|
||||
|
||||
if (fromPosition == toPosition)
|
||||
{
|
||||
return; // No change needed.
|
||||
}
|
||||
if (fromPosition > toPosition)
|
||||
{
|
||||
auto t = files[fromPosition];
|
||||
files.erase(files.begin() + fromPosition);
|
||||
files.insert(files.begin() + toPosition, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fromPosition < toPosition
|
||||
auto t = files[fromPosition];
|
||||
files.erase(files.begin() + fromPosition);
|
||||
files.insert(files.begin() + toPosition, t); // insert before the toPosition.
|
||||
}
|
||||
for (size_t i = 0; i < files.size(); ++i)
|
||||
{
|
||||
auto &file = files[i];
|
||||
if (file.position() != static_cast<int32_t>(i))
|
||||
{
|
||||
file.position(static_cast<int32_t>(i));
|
||||
if (this->audioFilesDb)
|
||||
{
|
||||
audioFilesDb->UpdateFilePosition(file.idFile(), file.position());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Directory is not writable.");
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction->commit();
|
||||
}
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
std::filesystem::path IndexPathToShadowIndexPath(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path)
|
||||
{
|
||||
std::filesystem::path relativePath = MakeRelativePath(path, audioRootDirectory);
|
||||
if (relativePath.is_absolute())
|
||||
{
|
||||
throw std::runtime_error(SS("Can't write to path " << path << "."));
|
||||
};
|
||||
return audioRootDirectory / "shadow_indexes" / relativePath;
|
||||
}
|
||||
|
||||
// recover the original path fromthe shadow index path.
|
||||
std::filesystem::path ShadowIndexPathToIndexPath(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path)
|
||||
{
|
||||
auto shadowIndexesPath = audioRootDirectory / "shadow_indexes";
|
||||
std::filesystem::path relativePath = MakeRelativePath(path, shadowIndexesPath);
|
||||
if (relativePath.is_absolute())
|
||||
{
|
||||
throw std::runtime_error(SS("Path must start with " << shadowIndexesPath));
|
||||
}
|
||||
return audioRootDirectory / relativePath; // recover the original path.
|
||||
}
|
||||
|
||||
std::filesystem::path GetShadowIndexDirectory(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path)
|
||||
{
|
||||
if (HasWritePermissions(path))
|
||||
{
|
||||
return ""; // use default index path.
|
||||
}
|
||||
return IndexPathToShadowIndexPath(audioRootDirectory, path);
|
||||
}
|
||||
}
|
||||
|
||||
+59
-37
@@ -8,10 +8,10 @@
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@@ -21,7 +21,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -31,75 +31,97 @@
|
||||
#include "AudioFileMetadata.hpp"
|
||||
#include "TemporaryFile.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class ThumbnailTemporaryFile: public TemporaryFile {
|
||||
class ThumbnailTemporaryFile : public TemporaryFile
|
||||
{
|
||||
private:
|
||||
// Private constructor to enforce the use of CreateTemporaryFile or the other constructor.
|
||||
explicit ThumbnailTemporaryFile(const std::filesystem::path&temporaryDirectory);
|
||||
explicit ThumbnailTemporaryFile(const std::filesystem::path &temporaryDirectory);
|
||||
|
||||
public:
|
||||
using super = TemporaryFile;
|
||||
ThumbnailTemporaryFile() = default;
|
||||
ThumbnailTemporaryFile(const ThumbnailTemporaryFile&) = delete;
|
||||
ThumbnailTemporaryFile(ThumbnailTemporaryFile&&) = default;
|
||||
ThumbnailTemporaryFile&operator=(const ThumbnailTemporaryFile&) = delete;
|
||||
ThumbnailTemporaryFile&operator=(ThumbnailTemporaryFile&&) = default;
|
||||
public:
|
||||
static ThumbnailTemporaryFile CreateTemporaryFile(const std::filesystem::path&tempDirectory,const std::string &mimeType);
|
||||
static ThumbnailTemporaryFile FromFile(const std::filesystem::path&filePath,const std::string &mimeType);
|
||||
public:
|
||||
ThumbnailTemporaryFile(const ThumbnailTemporaryFile &) = delete;
|
||||
ThumbnailTemporaryFile(ThumbnailTemporaryFile &&) = default;
|
||||
ThumbnailTemporaryFile &operator=(const ThumbnailTemporaryFile &) = delete;
|
||||
ThumbnailTemporaryFile &operator=(ThumbnailTemporaryFile &&) = default;
|
||||
|
||||
void SetNonDeletedPath(const std::filesystem::path&path, const std::string&mimeType = "audio/mpeg") {
|
||||
public:
|
||||
static ThumbnailTemporaryFile CreateTemporaryFile(const std::filesystem::path &tempDirectory, const std::string &mimeType);
|
||||
static ThumbnailTemporaryFile FromFile(const std::filesystem::path &filePath, const std::string &mimeType);
|
||||
|
||||
public:
|
||||
void SetNonDeletedPath(const std::filesystem::path &path, const std::string &mimeType = "audio/mpeg")
|
||||
{
|
||||
TemporaryFile::SetNonDeletedPath(path);
|
||||
// Set the MIME type for audio files.
|
||||
this->mimeType = mimeType; // Default MIME type, can be set later.
|
||||
}
|
||||
void Attach(const std::filesystem::path&path, const std::string&mimeType);
|
||||
void Attach(const std::filesystem::path &path, const std::string &mimeType);
|
||||
|
||||
std::string GetMimeType() {
|
||||
std::string GetMimeType()
|
||||
{
|
||||
return mimeType;
|
||||
}
|
||||
void SetMimeType(const std::string&mimeType) {
|
||||
void SetMimeType(const std::string &mimeType)
|
||||
{
|
||||
this->mimeType = mimeType;
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
std::string mimeType = "image/jpeg"; // Default MIME type, can be set later.
|
||||
|
||||
};
|
||||
|
||||
class AudioDirectoryInfo {
|
||||
class AudioDirectoryInfo
|
||||
{
|
||||
protected:
|
||||
AudioDirectoryInfo() { }
|
||||
virtual ~AudioDirectoryInfo() { }
|
||||
AudioDirectoryInfo() {}
|
||||
virtual ~AudioDirectoryInfo() {}
|
||||
|
||||
public:
|
||||
using self = AudioDirectoryInfo;
|
||||
using Ptr = std::shared_ptr<self>;
|
||||
|
||||
static Ptr Create(const std::string&path);
|
||||
static Ptr Create(
|
||||
const std::filesystem::path &path,
|
||||
const std::filesystem::path &indexPath = "");
|
||||
|
||||
virtual std::vector<AudioFileMetadata> GetFiles() = 0;
|
||||
|
||||
virtual ThumbnailTemporaryFile GetThumbnail(const std::string&fileNameOnly, int32_t width, int32_t height) = 0;
|
||||
virtual ThumbnailTemporaryFile GetThumbnail(const std::string &fileNameOnly, int32_t width, int32_t height) = 0;
|
||||
|
||||
virtual std::string GetNextAudioFile(const std::string&fileNameOnly) = 0;
|
||||
virtual std::string GetPreviousAudioFile(const std::string&fileNameOnly) = 0;
|
||||
virtual std::string GetNextAudioFile(const std::string &fileNameOnly) = 0;
|
||||
virtual std::string GetPreviousAudioFile(const std::string &fileNameOnly) = 0;
|
||||
|
||||
virtual void MoveAudioFile(
|
||||
const std::string &directory,
|
||||
int32_t fromPosition,
|
||||
int32_t toPosition) = 0;
|
||||
virtual ThumbnailTemporaryFile DefaultThumbnailTemporaryFile() = 0;
|
||||
|
||||
static void SetTemporaryDirectory(const std::filesystem::path&path);
|
||||
static void SetResourceDirectory(const std::filesystem::path&path);
|
||||
|
||||
static void SetTemporaryDirectory(const std::filesystem::path &path);
|
||||
static void SetResourceDirectory(const std::filesystem::path &path);
|
||||
|
||||
virtual size_t TestGetNumberOfThumbnails() = 0; // test use only.
|
||||
virtual void TestSetIndexPath(const std::filesystem::path&path) = 0;
|
||||
virtual void TestSetIndexPath(const std::filesystem::path &path) = 0;
|
||||
|
||||
protected:
|
||||
std::filesystem::path GetTemporaryDirectory() const;
|
||||
std::filesystem::path GetResourceDirectory() const;
|
||||
public:
|
||||
static std::filesystem::path GetTemporaryDirectory();
|
||||
static std::filesystem::path GetResourceDirectory();
|
||||
|
||||
private:
|
||||
static std::filesystem::path temporaryDirectory;
|
||||
static std::filesystem::path resourceDirectory;;
|
||||
|
||||
static std::filesystem::path resourceDirectory;
|
||||
;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::filesystem::path IndexPathToShadowIndexPath(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path);
|
||||
// recover the original path fromthe shadow index path.
|
||||
std::filesystem::path ShadowIndexPathToIndexPath(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path);
|
||||
std::filesystem::path GetShadowIndexDirectory(const std::filesystem::path &audioRootDirectory, const std::filesystem::path &path);
|
||||
|
||||
}
|
||||
+44
-23
@@ -78,7 +78,6 @@ namespace pipedal::impl
|
||||
}
|
||||
}
|
||||
lockEntry->mutex.lock(); // we now have exclusive access to the database.
|
||||
|
||||
}
|
||||
|
||||
~DatabaseLock()
|
||||
@@ -93,9 +92,7 @@ namespace pipedal::impl
|
||||
// Remove the entry from the map if no other locks are held.
|
||||
databaseLocks.erase(indexFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -178,6 +175,8 @@ void AudioFilesDb::CreateDb(const std::filesystem::path &dbPathName)
|
||||
"title TEXT NOT NULL,"
|
||||
"track NUMBER NOT NULL,"
|
||||
"album TEXT NOT NULL, "
|
||||
"artist TEXT NOT NULL, "
|
||||
"albumArtist TEXT NOT NULL, "
|
||||
"duration REAL NOT NULL DEFAULT 0.0,"
|
||||
|
||||
"thumbnailType INTEGER NOT NULL DEFAULT 0,"
|
||||
@@ -292,7 +291,7 @@ std::vector<DbFileInfo> AudioFilesDb::QueryTracks()
|
||||
SQLite::Statement query(
|
||||
*db,
|
||||
"SELECT idFile, fileName, "
|
||||
"lastModified, title, track, album,duration, "
|
||||
"lastModified, title, track, album, artist,albumArtist,duration, "
|
||||
"thumbnailType, position, "
|
||||
"thumbnailFile, thumbnailLastModified "
|
||||
"FROM files ");
|
||||
@@ -306,11 +305,13 @@ std::vector<DbFileInfo> AudioFilesDb::QueryTracks()
|
||||
row.title(query.getColumn(3).getText());
|
||||
row.track(query.getColumn(4).getInt());
|
||||
row.album(query.getColumn(5).getText());
|
||||
row.duration(query.getColumn(6).getDouble());
|
||||
row.thumbnailType((ThumbnailType)query.getColumn(7).getInt());
|
||||
row.position(query.getColumn(8).getInt());
|
||||
row.thumbnailFile(query.getColumn(9).getText());
|
||||
row.thumbnailLastModified(query.getColumn(10).getInt64());
|
||||
row.artist(query.getColumn(6).getText());
|
||||
row.albumArtist(query.getColumn(7).getText());
|
||||
row.duration(query.getColumn(8).getDouble());
|
||||
row.thumbnailType((ThumbnailType)query.getColumn(9).getInt());
|
||||
row.position(query.getColumn(10).getInt());
|
||||
row.thumbnailFile(query.getColumn(11).getText());
|
||||
row.thumbnailLastModified(query.getColumn(12).getInt64());
|
||||
result.push_back(std::move(row));
|
||||
}
|
||||
return result;
|
||||
@@ -326,9 +327,9 @@ void AudioFilesDb::WriteFile(DbFileInfo *dbFile)
|
||||
*db,
|
||||
"INSERT INTO files ("
|
||||
"fileName, lastModified,"
|
||||
"title,track,album, "
|
||||
"title,track,album,artist, albumArtist, "
|
||||
"duration, thumbnailType,thumbnailFile, thumbnailLastModified, position "
|
||||
") VALUES (?, ?, ?, ?, ?,?,?,?,?,?)");
|
||||
") VALUES (?, ?, ?, ?, ?,?,?,?,?,?,?,?)");
|
||||
}
|
||||
insertFileQuery->tryReset();
|
||||
insertFileQuery->bind(1, dbFile->fileName());
|
||||
@@ -336,11 +337,13 @@ void AudioFilesDb::WriteFile(DbFileInfo *dbFile)
|
||||
insertFileQuery->bind(3, dbFile->title());
|
||||
insertFileQuery->bind(4, dbFile->track());
|
||||
insertFileQuery->bind(5, dbFile->album());
|
||||
insertFileQuery->bind(6, dbFile->duration());
|
||||
insertFileQuery->bind(7, (int32_t)dbFile->thumbnailType());
|
||||
insertFileQuery->bind(8, dbFile->thumbnailFile());
|
||||
insertFileQuery->bind(9, dbFile->thumbnailLastModified());
|
||||
insertFileQuery->bind(10, dbFile->position());
|
||||
insertFileQuery->bind(6, dbFile->artist());
|
||||
insertFileQuery->bind(7, dbFile->albumArtist());
|
||||
insertFileQuery->bind(8, dbFile->duration());
|
||||
insertFileQuery->bind(9, (int32_t)dbFile->thumbnailType());
|
||||
insertFileQuery->bind(10, dbFile->thumbnailFile());
|
||||
insertFileQuery->bind(11, dbFile->thumbnailLastModified());
|
||||
insertFileQuery->bind(12, dbFile->position());
|
||||
insertFileQuery->exec();
|
||||
dbFile->idFile(db->getLastInsertRowid());
|
||||
}
|
||||
@@ -352,7 +355,7 @@ void AudioFilesDb::WriteFile(DbFileInfo *dbFile)
|
||||
*db,
|
||||
"UPDATE files SET "
|
||||
"fileName = ?, lastModified = ?, "
|
||||
"title = ?, track = ?, album = ?, "
|
||||
"title = ?, track = ?, album = ?, artist = ? , albumArtist = ?, "
|
||||
"duration = ?, thumbnailType = ?, "
|
||||
"thumbnailFile = ?, thumbnailLastModified = ?, position = ? "
|
||||
" WHERE idFile = ?");
|
||||
@@ -363,13 +366,15 @@ void AudioFilesDb::WriteFile(DbFileInfo *dbFile)
|
||||
updateFileQuery->bind(3, dbFile->title());
|
||||
updateFileQuery->bind(4, dbFile->track());
|
||||
updateFileQuery->bind(5, dbFile->album());
|
||||
updateFileQuery->bind(6, dbFile->duration());
|
||||
updateFileQuery->bind(7, (int32_t)dbFile->thumbnailType());
|
||||
updateFileQuery->bind(8, dbFile->thumbnailFile());
|
||||
updateFileQuery->bind(9, dbFile->thumbnailLastModified());
|
||||
updateFileQuery->bind(10, dbFile->position());
|
||||
updateFileQuery->bind(6, dbFile->artist());
|
||||
updateFileQuery->bind(7, dbFile->albumArtist());
|
||||
updateFileQuery->bind(8, dbFile->duration());
|
||||
updateFileQuery->bind(9, (int32_t)dbFile->thumbnailType());
|
||||
updateFileQuery->bind(10, dbFile->thumbnailFile());
|
||||
updateFileQuery->bind(11, dbFile->thumbnailLastModified());
|
||||
updateFileQuery->bind(12, dbFile->position());
|
||||
|
||||
updateFileQuery->bind(11, dbFile->idFile());
|
||||
updateFileQuery->bind(13, dbFile->idFile());
|
||||
|
||||
updateFileQuery->exec();
|
||||
}
|
||||
@@ -480,3 +485,19 @@ void AudioFilesDb::AddThumbnail(
|
||||
throw std::runtime_error("Failed to add thumbnail: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioFilesDb::UpdateFilePosition(
|
||||
int64_t idFile,
|
||||
int32_t position)
|
||||
{
|
||||
if (!updateFileQuery)
|
||||
{
|
||||
updateFileQuery = std::make_unique<SQLite::Statement>(
|
||||
*db,
|
||||
"UPDATE files SET position = ? WHERE idFile = ?");
|
||||
}
|
||||
updateFileQuery->tryReset();
|
||||
updateFileQuery->bind(1, position);
|
||||
updateFileQuery->bind(2, idFile);
|
||||
updateFileQuery->exec();
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ namespace pipedal::impl {
|
||||
int64_t width,
|
||||
int64_t height,
|
||||
const std::vector<uint8_t> &thumbnailData);
|
||||
void UpdateFilePosition(
|
||||
int64_t idFile,
|
||||
int32_t position);
|
||||
|
||||
private:
|
||||
|
||||
@@ -124,6 +127,7 @@ namespace pipedal::impl {
|
||||
std::unique_ptr<SQLite::Statement> deleteFileQuery;
|
||||
std::unique_ptr<SQLite::Statement> updateThumbnailInfoQueryByName;
|
||||
std::unique_ptr<SQLite::Statement> updateThumbnailInfoQueryById;
|
||||
std::unique_ptr<SQLite::Statement> updatePositionQuery;
|
||||
std::filesystem::path path;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "DBusLog.hpp"
|
||||
#include "AvahiService.hpp"
|
||||
#include "DummyAudioDriver.hpp"
|
||||
#include "AudioFiles.hpp"
|
||||
|
||||
#ifndef NO_MLOCK
|
||||
#include <sys/mman.h>
|
||||
@@ -2921,4 +2922,17 @@ void PiPedalModel::OnAlsaDriverTerminatedAbnormally() {
|
||||
bool PiPedalModel::IsInUploadsDirectory(const std::string &path)
|
||||
{
|
||||
return storage.IsInUploadsDirectory(path);
|
||||
}
|
||||
|
||||
void PiPedalModel::MoveAudioFile(
|
||||
const std::string &directory,
|
||||
int32_t fromPosition,
|
||||
int32_t toPosition
|
||||
) {
|
||||
if (directory.empty())
|
||||
{
|
||||
throw std::runtime_error("Directory is empty.");
|
||||
}
|
||||
AudioDirectoryInfo::Ptr dir = AudioDirectoryInfo::Create(directory);
|
||||
dir->MoveAudioFile(directory, fromPosition, toPosition);
|
||||
}
|
||||
@@ -462,6 +462,11 @@ namespace pipedal
|
||||
uint64_t CreateNewPreset();
|
||||
|
||||
bool LoadCurrentPedalboard();
|
||||
|
||||
void MoveAudioFile(
|
||||
const std::string & path,
|
||||
int32_t from,
|
||||
int32_t to);
|
||||
};
|
||||
|
||||
} // namespace pipedal.
|
||||
+45
-22
@@ -60,7 +60,6 @@ JSON_MAP_REFERENCE(PathPatchPropertyChangedBody, propertyUri)
|
||||
JSON_MAP_REFERENCE(PathPatchPropertyChangedBody, atomJson)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
class GetPatchPropertyBody
|
||||
{
|
||||
public:
|
||||
@@ -74,6 +73,20 @@ JSON_MAP_REFERENCE(GetPatchPropertyBody, instanceId)
|
||||
JSON_MAP_REFERENCE(GetPatchPropertyBody, propertyUri)
|
||||
JSON_MAP_END()
|
||||
|
||||
class MoveAudioFileArgs
|
||||
{
|
||||
public:
|
||||
std::string path_;
|
||||
int32_t from_;
|
||||
int32_t to_;
|
||||
DECLARE_JSON_MAP(MoveAudioFileArgs);
|
||||
};
|
||||
JSON_MAP_BEGIN(MoveAudioFileArgs)
|
||||
JSON_MAP_REFERENCE(MoveAudioFileArgs, path)
|
||||
JSON_MAP_REFERENCE(MoveAudioFileArgs, from)
|
||||
JSON_MAP_REFERENCE(MoveAudioFileArgs, to)
|
||||
JSON_MAP_END()
|
||||
|
||||
class CreateNewSampleDirectoryArgs
|
||||
{
|
||||
public:
|
||||
@@ -111,14 +124,11 @@ public:
|
||||
DECLARE_JSON_MAP(GetFilePropertyDirectoryTreeArgs);
|
||||
};
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(GetFilePropertyDirectoryTreeArgs)
|
||||
JSON_MAP_REFERENCE(GetFilePropertyDirectoryTreeArgs, fileProperty)
|
||||
JSON_MAP_REFERENCE(GetFilePropertyDirectoryTreeArgs, selectedPath)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
|
||||
class Lv2StateChangedBody
|
||||
{
|
||||
public:
|
||||
@@ -413,7 +423,8 @@ JSON_MAP_REFERENCE(SetSnapshotsBody, snapshots)
|
||||
JSON_MAP_REFERENCE(SetSnapshotsBody, selectedSnapshot)
|
||||
JSON_MAP_END()
|
||||
|
||||
class SnapshotModifiedBody {
|
||||
class SnapshotModifiedBody
|
||||
{
|
||||
public:
|
||||
int64_t snapshotIndex_;
|
||||
bool modified_;
|
||||
@@ -582,7 +593,6 @@ public:
|
||||
{
|
||||
FinalCleanup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool finalCleanup = false;
|
||||
@@ -1100,7 +1110,7 @@ public:
|
||||
else if (message == "getHasWifi")
|
||||
{
|
||||
bool result = model.GetHasWifi();
|
||||
this->Reply(replyTo, "getHasWifi",result);
|
||||
this->Reply(replyTo, "getHasWifi", result);
|
||||
}
|
||||
else if (message == "updateNow")
|
||||
{
|
||||
@@ -1473,13 +1483,8 @@ public:
|
||||
SetPatchPropertyBody body;
|
||||
pReader->read(&body);
|
||||
model.SendSetPatchProperty(clientId, body.instanceId_, body.propertyUri_, body.value_, [this, replyTo]()
|
||||
{
|
||||
this->JsonReply(replyTo, "setPatchProperty", "true");
|
||||
},
|
||||
[this, replyTo](const std::string &error)
|
||||
{
|
||||
this->SendError(replyTo, error.c_str());
|
||||
});
|
||||
{ this->JsonReply(replyTo, "setPatchProperty", "true"); }, [this, replyTo](const std::string &error)
|
||||
{ this->SendError(replyTo, error.c_str()); });
|
||||
}
|
||||
|
||||
else if (message == "getPatchProperty")
|
||||
@@ -1644,13 +1649,31 @@ public:
|
||||
{
|
||||
GetFilePropertyDirectoryTreeArgs args;
|
||||
pReader->read(&args);
|
||||
FilePropertyDirectoryTree::ptr result =
|
||||
FilePropertyDirectoryTree::ptr result =
|
||||
model.GetFilePropertydirectoryTree(
|
||||
args.fileProperty_,
|
||||
args.selectedPath_);
|
||||
this->Reply(replyTo, "GetFilePropertydirectoryTree", result);
|
||||
}
|
||||
else if (message == "getFilePropertyDirectoryTree")
|
||||
{
|
||||
GetFilePropertyDirectoryTreeArgs args;
|
||||
pReader->read(&args);
|
||||
FilePropertyDirectoryTree::ptr result =
|
||||
model.GetFilePropertydirectoryTree(
|
||||
args.fileProperty_,
|
||||
args.selectedPath_);
|
||||
this->Reply(replyTo, "GetFilePropertydirectoryTree", result);
|
||||
}
|
||||
|
||||
else if (message == "moveAudioFile")
|
||||
{
|
||||
MoveAudioFileArgs args;
|
||||
pReader->read(&args);
|
||||
this->model.MoveAudioFile(args.path_, args.from_, args.to_);
|
||||
bool result = true;
|
||||
this->Reply(replyTo,"moveAudioFile", result);
|
||||
}
|
||||
else if (message == "setOnboarding")
|
||||
{
|
||||
bool value;
|
||||
@@ -1659,7 +1682,7 @@ public:
|
||||
}
|
||||
else if (message == "getWifiRegulatoryDomains")
|
||||
{
|
||||
auto regulatoryDomains = this->model.GetWifiRegulatoryDomains();
|
||||
auto regulatoryDomains = this->model.GetWifiRegulatoryDomains();
|
||||
this->Reply(replyTo, "getWifiRegulatoryDomains", regulatoryDomains);
|
||||
}
|
||||
else
|
||||
@@ -1670,7 +1693,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onSocketClosed() override {
|
||||
virtual void
|
||||
onSocketClosed() override
|
||||
{
|
||||
SocketHandler::OnSocketClosed();
|
||||
this->Close();
|
||||
}
|
||||
@@ -1754,10 +1779,10 @@ private:
|
||||
Send("onLv2PluginsChanging", true);
|
||||
Flush();
|
||||
}
|
||||
virtual void OnHasWifiChanged(bool hasWifi){
|
||||
virtual void OnHasWifiChanged(bool hasWifi)
|
||||
{
|
||||
Send("onHasWifiChanged", hasWifi);
|
||||
Flush();
|
||||
|
||||
}
|
||||
|
||||
virtual void OnNetworkChanging(bool hotspotConnected) override
|
||||
@@ -1809,7 +1834,7 @@ private:
|
||||
Send("onChannelSelectionChanged", body);
|
||||
}
|
||||
|
||||
virtual void OnSnapshotModified(int64_t snapshotIndex, bool modified)
|
||||
virtual void OnSnapshotModified(int64_t snapshotIndex, bool modified)
|
||||
{
|
||||
SnapshotModifiedBody body;
|
||||
body.snapshotIndex_ = snapshotIndex;
|
||||
@@ -2124,7 +2149,6 @@ private:
|
||||
|
||||
std::atomic<uint64_t> PiPedalSocketHandler::nextClientId = 0;
|
||||
|
||||
|
||||
class PiPedalSocketFactory : public ISocketFactory
|
||||
{
|
||||
private:
|
||||
@@ -2152,7 +2176,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<ISocketFactory> pipedal::MakePiPedalSocketFactory(PiPedalModel &model)
|
||||
{
|
||||
return std::make_shared<PiPedalSocketFactory>(model);
|
||||
|
||||
+19
-3
@@ -1689,6 +1689,7 @@ static void AddFilesToResult(
|
||||
}
|
||||
|
||||
static void AddTracksToResult(
|
||||
const fs::path &audioRootDirectory,
|
||||
FileRequestResult &result,
|
||||
const ModFileTypes::ModDirectory *modDirectoryInfo, // yyx
|
||||
const UiFileProperty &fileProperty,
|
||||
@@ -1716,7 +1717,22 @@ static void AddTracksToResult(
|
||||
resultFiles.push_back(FileEntry{path, name, true, fs::is_symlink(path)});
|
||||
}
|
||||
}
|
||||
auto audioFiles = AudioDirectoryInfo::Create(rootPath);
|
||||
auto collator = Locale::GetInstance()->GetCollator();
|
||||
std::sort(
|
||||
resultFiles.begin(),
|
||||
resultFiles.end(),
|
||||
[collator](const FileEntry &l, const FileEntry &r)
|
||||
{
|
||||
if (l.isDirectory_ != r.isDirectory_)
|
||||
{
|
||||
return l.isDirectory_ > r.isDirectory_;
|
||||
}
|
||||
return collator->Compare(l.displayName_ ,r.displayName_) < 0;
|
||||
});
|
||||
// Add audio files.
|
||||
auto audioFiles = AudioDirectoryInfo::Create(rootPath,
|
||||
GetShadowIndexDirectory(audioRootDirectory,rootPath)
|
||||
);
|
||||
for (const auto &audioFile : audioFiles->GetFiles())
|
||||
{
|
||||
fs::path audioFilePath = rootPath / audioFile.fileName();
|
||||
@@ -1821,7 +1837,7 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath, cons
|
||||
|
||||
if (IsInAudioTracksDirectory(relativePath))
|
||||
{
|
||||
AddTracksToResult(result, rootModDirectory, fileProperty, relativePath);
|
||||
AddTracksToResult(this->GetPluginUploadDirectory(),result, rootModDirectory, fileProperty, relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1921,7 +1937,7 @@ FileRequestResult Storage::GetFileList2(const std::string &relativePath_, const
|
||||
|
||||
if (IsInAudioTracksDirectory(absolutePath))
|
||||
{
|
||||
AddTracksToResult(result, pModDirectory, fileProperty, absolutePath);
|
||||
AddTracksToResult(GetPluginUploadDirectory(), result, pModDirectory, fileProperty, absolutePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ public:
|
||||
{
|
||||
set("");
|
||||
}
|
||||
const std::string& str() {
|
||||
const std::string& str() const {
|
||||
return text;
|
||||
}
|
||||
uri(const char*text)
|
||||
|
||||
+48
-9
@@ -37,6 +37,7 @@
|
||||
#include "MimeTypes.hpp"
|
||||
#include "AudioFileMetadataReader.hpp"
|
||||
#include "AudioFiles.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#define OLD_PRESET_EXTENSION ".piPreset"
|
||||
#define PRESET_EXTENSION ".piPreset"
|
||||
@@ -50,6 +51,10 @@ static const std::string PLUGIN_PRESETS_MIME_TYPE = "application/vnd.pipedal.plu
|
||||
static const std::string PRESET_MIME_TYPE = "application/vnd.pipedal.preset";
|
||||
static const std::string BANK_MIME_TYPE = "application/vnd.pipedal.bank";
|
||||
|
||||
static const std::string CACHE_CONTROL_INDEFINITELY = "max-age=31536000,public,immutable"; // 1 year
|
||||
static const std::string CACHE_CONTROL_SHORT = "max-age=300,public"; // 5 minutes
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace boost::system;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -137,6 +142,7 @@ private:
|
||||
std::vector<std::string> extensions;
|
||||
};
|
||||
|
||||
|
||||
class DownloadIntercept : public RequestHandler
|
||||
{
|
||||
PiPedalModel *model;
|
||||
@@ -396,6 +402,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void setLastModifiedFromFile(HttpResponse &res, const std::filesystem::path &path)
|
||||
{
|
||||
auto lastModified = std::filesystem::last_write_time(path);
|
||||
res.set(HttpField::LastModified, HtmlHelper::timeToHttpDate(lastModified));
|
||||
}
|
||||
AudioDirectoryInfo::Ptr CreateDirectoryInfo(const fs::path &path) {
|
||||
return AudioDirectoryInfo::Create(path,
|
||||
GetShadowIndexDirectory(
|
||||
this->model->GetPluginUploadDirectory(),
|
||||
path));
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual void get_response(
|
||||
const uri &request_uri,
|
||||
HttpRequest &req,
|
||||
@@ -493,8 +513,7 @@ public:
|
||||
{
|
||||
throw PiPedalException("File not found.");
|
||||
}
|
||||
AudioDirectoryInfo::Ptr audioDirectory = AudioDirectoryInfo::Create(path.parent_path());
|
||||
|
||||
AudioDirectoryInfo::Ptr audioDirectory = CreateDirectoryInfo(path.parent_path());
|
||||
auto files = audioDirectory->GetFiles();
|
||||
for (const auto &file : files)
|
||||
{
|
||||
@@ -516,7 +535,26 @@ public:
|
||||
try
|
||||
{
|
||||
fs::path path = request_uri.query("path");
|
||||
if (path.empty())
|
||||
{
|
||||
// path for folder thumbnails.
|
||||
path = request_uri.query("ffile");
|
||||
if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path))
|
||||
{
|
||||
throw PiPedalException("File not found.");
|
||||
}
|
||||
std::shared_ptr<TemporaryFile> thumbnail = std::make_shared<TemporaryFile>();
|
||||
thumbnail->SetNonDeletedPath(path);
|
||||
|
||||
res.set(HttpField::content_type, MimeTypes::instance().MimeTypeFromExtension(path.extension()));
|
||||
res.set(HttpField::cache_control, CACHE_CONTROL_INDEFINITELY); // URL is cache-busted, and will change if the file ismodified.
|
||||
setLastModifiedFromFile(res,path);
|
||||
res.set(HttpField::content_length, std::to_string(fs::file_size(path)));
|
||||
res.setBodyFile(thumbnail);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path))
|
||||
{
|
||||
throw PiPedalException("File not found.");
|
||||
@@ -525,8 +563,10 @@ public:
|
||||
int32_t width = ConvertThumbnailSize(request_uri.query("w"));
|
||||
int32_t height = ConvertThumbnailSize(request_uri.query("h"));
|
||||
|
||||
AudioDirectoryInfo::Ptr audioDirectory = AudioDirectoryInfo::Create(path.parent_path());
|
||||
AudioDirectoryInfo::Ptr audioDirectory = CreateDirectoryInfo(
|
||||
path.parent_path());
|
||||
audioDirectory->GetFiles(); // ensure that the .index file is up to date.
|
||||
|
||||
ThumbnailTemporaryFile thumbnail;
|
||||
try {
|
||||
thumbnail = audioDirectory->GetThumbnail(path.filename(), width, height);
|
||||
@@ -535,7 +575,8 @@ public:
|
||||
}
|
||||
res.set(HttpField::content_type, thumbnail.GetMimeType());
|
||||
|
||||
res.set(HttpField::cache_control, "max-age=300");
|
||||
res.set(HttpField::cache_control, CACHE_CONTROL_INDEFINITELY); // URL is cache-busted with time-stamp.
|
||||
setLastModifiedFromFile(res, path);
|
||||
res.set(HttpField::content_length, std::to_string(fs::file_size(thumbnail.Path())));
|
||||
|
||||
std::filesystem::path t = thumbnail.Path();
|
||||
@@ -544,9 +585,7 @@ public:
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error("Error getting thumbnail: %s", e.what());
|
||||
res.set(HttpField::location, "/img/missing_thumbnail.jpg");
|
||||
// response = 307
|
||||
Lv2Log::error("Error getting thumbnail: %s - (%s)", request_uri.str().c_str(), e.what());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -563,7 +602,7 @@ public:
|
||||
throw PiPedalException("File not found.");
|
||||
}
|
||||
auto directoryPath = path.parent_path();
|
||||
auto directoryInfo = AudioDirectoryInfo::Create(directoryPath);
|
||||
auto directoryInfo = CreateDirectoryInfo(directoryPath);
|
||||
std::string result = directoryInfo->GetNextAudioFile(path.filename());
|
||||
if (!result.empty())
|
||||
{
|
||||
@@ -595,7 +634,7 @@ public:
|
||||
throw PiPedalException("File not found.");
|
||||
}
|
||||
auto directoryPath = path.parent_path();
|
||||
auto directoryInfo = AudioDirectoryInfo::Create(directoryPath);
|
||||
auto directoryInfo = CreateDirectoryInfo(directoryPath);
|
||||
std::string result = directoryInfo->GetPreviousAudioFile(path.filename());
|
||||
if (!result.empty())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user