Compare commits
5 Commits
copilot/ad
...
moodbar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29b5b889ab | ||
|
|
da2f28811a | ||
|
|
0bfa736081 | ||
|
|
1392bcbbe1 | ||
|
|
11705889f1 |
@@ -110,21 +110,32 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob &job, QString &error_te
|
|||||||
|
|
||||||
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) {
|
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) {
|
||||||
|
|
||||||
QString path = job.metadata_.url().toLocalFile();
|
const QString path = job.metadata_.url().toLocalFile();
|
||||||
QFileInfo fileInfo(path);
|
const QFileInfo fileInfo(path);
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||||
if (job.use_trash_ && QFile::supportsMoveToTrash()) {
|
if (job.use_trash_ && QFile::supportsMoveToTrash()) {
|
||||||
#else
|
#else
|
||||||
if (job.use_trash_) {
|
if (job.use_trash_) {
|
||||||
#endif
|
#endif
|
||||||
return QFile::moveToTrash(path);
|
if (QFile::moveToTrash(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
qLog(Warning) << "Moving file to trash failed for" << path << ", falling back to direct deletion";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
if (fileInfo.isDir()) {
|
if (fileInfo.isDir()) {
|
||||||
return Utilities::RemoveRecursive(path);
|
success = Utilities::RemoveRecursive(path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
success = QFile::remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QFile::remove(path);
|
if (!success) {
|
||||||
|
qLog(Error) << "Failed to delete file" << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ SongLoader::SongLoader(const SharedPtr<UrlHandlers> url_handlers,
|
|||||||
|
|
||||||
QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout);
|
QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout);
|
||||||
|
|
||||||
|
QObject::connect(playlist_parser_, &PlaylistParser::Error, this, &SongLoader::ParserError);
|
||||||
|
QObject::connect(cue_parser_, &CueParser::Error, this, &SongLoader::ParserError);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SongLoader::~SongLoader() {
|
SongLoader::~SongLoader() {
|
||||||
@@ -106,6 +109,10 @@ SongLoader::~SongLoader() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SongLoader::ParserError(const QString &error) {
|
||||||
|
errors_ << error;
|
||||||
|
}
|
||||||
|
|
||||||
SongLoader::Result SongLoader::Load(const QUrl &url) {
|
SongLoader::Result SongLoader::Load(const QUrl &url) {
|
||||||
|
|
||||||
if (url.isEmpty()) return Result::Error;
|
if (url.isEmpty()) return Result::Error;
|
||||||
@@ -287,6 +294,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parser) { // It's a playlist!
|
if (parser) { // It's a playlist!
|
||||||
|
QObject::connect(parser, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
|
||||||
qLog(Debug) << "Parsing using" << parser->name();
|
qLog(Debug) << "Parsing using" << parser->name();
|
||||||
LoadPlaylist(parser, filename);
|
LoadPlaylist(parser, filename);
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
@@ -706,6 +714,10 @@ void SongLoader::MagicReady() {
|
|||||||
StopTypefindAsync(true);
|
StopTypefindAsync(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser_) {
|
||||||
|
QObject::connect(parser_, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
|
||||||
|
}
|
||||||
|
|
||||||
state_ = State::WaitingForData;
|
state_ = State::WaitingForData;
|
||||||
|
|
||||||
if (!IsPipelinePlaying()) {
|
if (!IsPipelinePlaying()) {
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class SongLoader : public QObject {
|
|||||||
void ScheduleTimeout();
|
void ScheduleTimeout();
|
||||||
void Timeout();
|
void Timeout();
|
||||||
void StopTypefind();
|
void StopTypefind();
|
||||||
|
void ParserError(const QString &error);
|
||||||
|
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
void AudioCDTracksLoadErrorSlot(const QString &error);
|
void AudioCDTracksLoadErrorSlot(const QString &error);
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
|||||||
audiobin_(nullptr),
|
audiobin_(nullptr),
|
||||||
audiosink_(nullptr),
|
audiosink_(nullptr),
|
||||||
audioqueue_(nullptr),
|
audioqueue_(nullptr),
|
||||||
audioqueueconverter_(nullptr),
|
|
||||||
volume_(nullptr),
|
volume_(nullptr),
|
||||||
volume_sw_(nullptr),
|
volume_sw_(nullptr),
|
||||||
volume_fading_(nullptr),
|
volume_fading_(nullptr),
|
||||||
@@ -187,6 +186,7 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent)
|
|||||||
equalizer_(nullptr),
|
equalizer_(nullptr),
|
||||||
equalizer_preamp_(nullptr),
|
equalizer_preamp_(nullptr),
|
||||||
eventprobe_(nullptr),
|
eventprobe_(nullptr),
|
||||||
|
bufferprobe_(nullptr),
|
||||||
logged_unsupported_analyzer_format_(false),
|
logged_unsupported_analyzer_format_(false),
|
||||||
about_to_finish_(false),
|
about_to_finish_(false),
|
||||||
finish_requested_(false),
|
finish_requested_(false),
|
||||||
@@ -436,7 +436,7 @@ void GstEnginePipeline::Disconnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buffer_probe_cb_id_.has_value()) {
|
if (buffer_probe_cb_id_.has_value()) {
|
||||||
GstPad *pad = gst_element_get_static_pad(audioqueueconverter_, "src");
|
GstPad *pad = gst_element_get_static_pad(bufferprobe_, "src");
|
||||||
if (pad) {
|
if (pad) {
|
||||||
gst_pad_remove_probe(pad, buffer_probe_cb_id_.value());
|
gst_pad_remove_probe(pad, buffer_probe_cb_id_.value());
|
||||||
gst_object_unref(pad);
|
gst_object_unref(pad);
|
||||||
@@ -674,8 +674,13 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioqueueconverter_ = CreateElement(u"audioconvert"_s, u"audioqueueconverter"_s, audiobin_, error);
|
GstElement *audioqueueconverter = CreateElement(u"audioconvert"_s, u"audioqueueconverter"_s, audiobin_, error);
|
||||||
if (!audioqueueconverter_) {
|
if (!audioqueueconverter) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstElement *audioqueueresampler = CreateElement(u"audioresample"_s, u"audioqueueresampler"_s, audiobin_, error);
|
||||||
|
if (!audioqueueresampler) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +689,11 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstElement *audiosinkresampler = CreateElement(u"audioresample"_s, u"audiosinkresampler"_s, audiobin_, error);
|
||||||
|
if (!audiosinkresampler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the volume element if it's enabled.
|
// Create the volume element if it's enabled.
|
||||||
if (volume_enabled_ && !volume_) {
|
if (volume_enabled_ && !volume_) {
|
||||||
volume_sw_ = CreateElement(u"volume"_s, u"volume_sw"_s, audiobin_, error);
|
volume_sw_ = CreateElement(u"volume"_s, u"volume_sw"_s, audiobin_, error);
|
||||||
@@ -761,7 +771,8 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eventprobe_ = audioqueueconverter_;
|
eventprobe_ = audioqueueconverter;
|
||||||
|
bufferprobe_ = audioqueueconverter;
|
||||||
|
|
||||||
// Create the replaygain elements if it's enabled.
|
// Create the replaygain elements if it's enabled.
|
||||||
GstElement *rgvolume = nullptr;
|
GstElement *rgvolume = nullptr;
|
||||||
@@ -847,12 +858,17 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
|
|
||||||
// Link all elements
|
// Link all elements
|
||||||
|
|
||||||
if (!gst_element_link(audioqueue_, audioqueueconverter_)) {
|
if (!gst_element_link(audioqueue_, audioqueueconverter)) {
|
||||||
error = u"Failed to link audio queue to audio queue converter."_s;
|
error = u"Failed to link audio queue to audio queue converter."_s;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GstElement *element_link = audioqueueconverter_; // The next element to link from.
|
if (!gst_element_link(audioqueueconverter, audioqueueresampler)) {
|
||||||
|
error = u"Failed to link audio queue converter to audio queue resampler."_s;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstElement *element_link = audioqueueresampler; // The next element to link from.
|
||||||
|
|
||||||
// Link replaygain elements if enabled.
|
// Link replaygain elements if enabled.
|
||||||
if (rg_enabled_ && rgvolume && rglimiter && rgconverter) {
|
if (rg_enabled_ && rgvolume && rglimiter && rgconverter) {
|
||||||
@@ -928,6 +944,11 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gst_element_link(audiosinkconverter, audiosinkresampler)) {
|
||||||
|
error = "Failed to link audio sink converter to audio sink resampler."_L1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw");
|
GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw");
|
||||||
if (!caps) {
|
if (!caps) {
|
||||||
@@ -938,16 +959,16 @@ bool GstEnginePipeline::InitAudioBin(QString &error) {
|
|||||||
qLog(Debug) << "Setting channels to" << channels_;
|
qLog(Debug) << "Setting channels to" << channels_;
|
||||||
gst_caps_set_simple(caps, "channels", G_TYPE_INT, channels_, nullptr);
|
gst_caps_set_simple(caps, "channels", G_TYPE_INT, channels_, nullptr);
|
||||||
}
|
}
|
||||||
const bool link_filtered_result = gst_element_link_filtered(audiosinkconverter, audiosink_, caps);
|
const bool link_filtered_result = gst_element_link_filtered(audiosinkresampler, audiosink_, caps);
|
||||||
gst_caps_unref(caps);
|
gst_caps_unref(caps);
|
||||||
if (!link_filtered_result) {
|
if (!link_filtered_result) {
|
||||||
error = "Failed to link audio sink converter to audio sink with filter for "_L1 + output_;
|
error = "Failed to link audio sink resampler to audio sink with filter for "_L1 + output_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Add probes and handlers.
|
{ // Add probes and handlers.
|
||||||
GstPad *pad = gst_element_get_static_pad(audioqueueconverter_, "src");
|
GstPad *pad = gst_element_get_static_pad(bufferprobe_, "src");
|
||||||
if (pad) {
|
if (pad) {
|
||||||
buffer_probe_cb_id_ = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, BufferProbeCallback, this, nullptr);
|
buffer_probe_cb_id_ = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, BufferProbeCallback, this, nullptr);
|
||||||
gst_object_unref(pad);
|
gst_object_unref(pad);
|
||||||
|
|||||||
@@ -355,7 +355,6 @@ class GstEnginePipeline : public QObject {
|
|||||||
GstElement *audiobin_;
|
GstElement *audiobin_;
|
||||||
GstElement *audiosink_;
|
GstElement *audiosink_;
|
||||||
GstElement *audioqueue_;
|
GstElement *audioqueue_;
|
||||||
GstElement *audioqueueconverter_;
|
|
||||||
GstElement *volume_;
|
GstElement *volume_;
|
||||||
GstElement *volume_sw_;
|
GstElement *volume_sw_;
|
||||||
GstElement *volume_fading_;
|
GstElement *volume_fading_;
|
||||||
@@ -364,6 +363,7 @@ class GstEnginePipeline : public QObject {
|
|||||||
GstElement *equalizer_;
|
GstElement *equalizer_;
|
||||||
GstElement *equalizer_preamp_;
|
GstElement *equalizer_preamp_;
|
||||||
GstElement *eventprobe_;
|
GstElement *eventprobe_;
|
||||||
|
GstElement *bufferprobe_;
|
||||||
|
|
||||||
std::optional<gulong> upstream_events_probe_cb_id_;
|
std::optional<gulong> upstream_events_probe_cb_id_;
|
||||||
std::optional<gulong> buffer_probe_cb_id_;
|
std::optional<gulong> buffer_probe_cb_id_;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
@@ -61,6 +64,29 @@ void MoodbarBuilder::Init(const int bands, const int rate_hz) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MoodbarBuilder::AddFrame(const GValue *magnitudes, const int size) {
|
||||||
|
|
||||||
|
if (size > barkband_table_.length()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total magnitudes for different bark bands.
|
||||||
|
double bands[sBarkBandCount]{};
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
const GValue *magnitude = gst_value_list_get_value(magnitudes, i);
|
||||||
|
bands[barkband_table_[i]] += g_value_get_float(magnitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now divide the bark bands into thirds and compute their total amplitudes.
|
||||||
|
double rgb[] = { 0, 0, 0 };
|
||||||
|
for (int i = 0; i < sBarkBandCount; ++i) {
|
||||||
|
rgb[(i * 3) / sBarkBandCount] += bands[i] * bands[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
frames_.append(Rgb(sqrt(rgb[0]), sqrt(rgb[1]), sqrt(rgb[2])));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MoodbarBuilder::AddFrame(const double *magnitudes, const int size) {
|
void MoodbarBuilder::AddFrame(const double *magnitudes, const int size) {
|
||||||
|
|
||||||
if (size > barkband_table_.length()) {
|
if (size > barkband_table_.length()) {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
#ifndef MOODBARBUILDER_H
|
#ifndef MOODBARBUILDER_H
|
||||||
#define MOODBARBUILDER_H
|
#define MOODBARBUILDER_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@@ -31,6 +33,7 @@ class MoodbarBuilder {
|
|||||||
explicit MoodbarBuilder();
|
explicit MoodbarBuilder();
|
||||||
|
|
||||||
void Init(const int bands, const int rate_hz);
|
void Init(const int bands, const int rate_hz);
|
||||||
|
void AddFrame(const GValue *magnitudes, const int size);
|
||||||
void AddFrame(const double *magnitudes, const int size);
|
void AddFrame(const double *magnitudes, const int size);
|
||||||
QByteArray Finish(const int width);
|
QByteArray Finish(const int width);
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ void MoodbarPipeline::Start() {
|
|||||||
|
|
||||||
GstElement *decodebin = CreateElement("uridecodebin");
|
GstElement *decodebin = CreateElement("uridecodebin");
|
||||||
convert_element_ = CreateElement("audioconvert");
|
convert_element_ = CreateElement("audioconvert");
|
||||||
GstElement *spectrum = CreateElement("strawberry-fastspectrum");
|
GstElement *spectrum = CreateElement("spectrum");
|
||||||
GstElement *fakesink = CreateElement("fakesink");
|
GstElement *fakesink = CreateElement("fakesink");
|
||||||
|
|
||||||
if (!decodebin || !convert_element_ || !spectrum || !fakesink) {
|
if (!decodebin || !convert_element_ || !spectrum || !fakesink) {
|
||||||
@@ -122,7 +122,7 @@ void MoodbarPipeline::Start() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder_ = make_unique<MoodbarBuilder>();
|
//builder_ = make_unique<MoodbarBuilder>();
|
||||||
|
|
||||||
// Set properties
|
// Set properties
|
||||||
|
|
||||||
@@ -130,8 +130,16 @@ void MoodbarPipeline::Start() {
|
|||||||
g_object_set(decodebin, "uri", gst_url.constData(), nullptr);
|
g_object_set(decodebin, "uri", gst_url.constData(), nullptr);
|
||||||
g_object_set(spectrum, "bands", kBands, nullptr);
|
g_object_set(spectrum, "bands", kBands, nullptr);
|
||||||
|
|
||||||
GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast<GstStrawberryFastSpectrum*>(spectrum);
|
//GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast<GstStrawberryFastSpectrum*>(spectrum);
|
||||||
fastspectrum->output_callback = [this](double *magnitudes, const int size) { builder_->AddFrame(magnitudes, size); };
|
//fastspectrum->output_callback = [this](double *magnitudes, const int size) { builder_->AddFrame(magnitudes, size); };
|
||||||
|
|
||||||
|
{
|
||||||
|
GstPad *pad = gst_element_get_static_pad(fakesink, "src");
|
||||||
|
if (pad) {
|
||||||
|
buffer_probe_cb_id_ = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, BufferProbeCallback, this, nullptr);
|
||||||
|
gst_object_unref(pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Connect signals
|
// Connect signals
|
||||||
CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, this);
|
CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, this);
|
||||||
@@ -212,6 +220,20 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus *bus, GstMessage *messag
|
|||||||
if (!instance->running_) return GST_BUS_PASS;
|
if (!instance->running_) return GST_BUS_PASS;
|
||||||
|
|
||||||
switch (GST_MESSAGE_TYPE(message)) {
|
switch (GST_MESSAGE_TYPE(message)) {
|
||||||
|
#if 0
|
||||||
|
case GST_MESSAGE_ELEMENT:{
|
||||||
|
const GstStructure *structure = gst_message_get_structure(message);
|
||||||
|
const gchar *name = gst_structure_get_name(structure);
|
||||||
|
if (strcmp(name, "spectrum") == 0) {
|
||||||
|
const GValue *magnitudes = gst_structure_get_value(structure, "magnitude");
|
||||||
|
if (instance->builder_) {
|
||||||
|
instance->builder_->AddFrame(magnitudes, kBands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
case GST_MESSAGE_EOS:
|
case GST_MESSAGE_EOS:
|
||||||
instance->Stop(true);
|
instance->Stop(true);
|
||||||
break;
|
break;
|
||||||
@@ -229,6 +251,24 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus *bus, GstMessage *messag
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstPadProbeReturn MoodbarPipeline::BufferProbeCallback(GstPad *pad, GstPadProbeInfo *info, gpointer self) {
|
||||||
|
|
||||||
|
Q_UNUSED(pad)
|
||||||
|
|
||||||
|
MoodbarPipeline *instance = reinterpret_cast<MoodbarPipeline*>(self);
|
||||||
|
|
||||||
|
GstBuffer *buffer = gst_pad_probe_info_get_buffer(info);
|
||||||
|
|
||||||
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
||||||
|
instance->data_.append(reinterpret_cast<const char*>(map.data), map.size);
|
||||||
|
gst_buffer_unmap(buffer, &map);
|
||||||
|
gst_buffer_unref(buffer);
|
||||||
|
|
||||||
|
return GST_PAD_PROBE_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MoodbarPipeline::Stop(const bool success) {
|
void MoodbarPipeline::Stop(const bool success) {
|
||||||
|
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class MoodbarPipeline : public QObject {
|
|||||||
|
|
||||||
static void NewPadCallback(GstElement *element, GstPad *pad, gpointer self);
|
static void NewPadCallback(GstElement *element, GstPad *pad, gpointer self);
|
||||||
static GstBusSyncReply BusCallbackSync(GstBus *bus, GstMessage *message, gpointer self);
|
static GstBusSyncReply BusCallbackSync(GstBus *bus, GstMessage *message, gpointer self);
|
||||||
|
static GstPadProbeReturn BufferProbeCallback(GstPad *pad, GstPadProbeInfo *info, gpointer self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl url_;
|
QUrl url_;
|
||||||
@@ -74,6 +75,7 @@ class MoodbarPipeline : public QObject {
|
|||||||
bool success_;
|
bool success_;
|
||||||
bool running_;
|
bool running_;
|
||||||
QByteArray data_;
|
QByteArray data_;
|
||||||
|
gint buffer_probe_cb_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using MoodbarPipelinePtr = QSharedPointer<MoodbarPipeline>;
|
using MoodbarPipelinePtr = QSharedPointer<MoodbarPipeline>;
|
||||||
|
|||||||
@@ -80,12 +80,13 @@ void SongLoaderInserter::Load(Playlist *destination, const int row, const bool p
|
|||||||
songs_ << loader->songs();
|
songs_ << loader->songs();
|
||||||
playlist_name_ = loader->playlist_name();
|
playlist_name_ = loader->playlist_name();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
const QStringList errors = loader->errors();
|
// Always check for errors, even on success (e.g., playlist parsed but some songs failed to load)
|
||||||
for (const QString &error : errors) {
|
const QStringList errors = loader->errors();
|
||||||
Q_EMIT Error(error);
|
for (const QString &error : errors) {
|
||||||
}
|
Q_EMIT Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete loader;
|
delete loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,11 +193,13 @@ void SongLoaderInserter::AsyncLoad() {
|
|||||||
const SongLoader::Result result = loader->LoadFilenamesBlocking();
|
const SongLoader::Result result = loader->LoadFilenamesBlocking();
|
||||||
task_manager_->SetTaskProgress(async_load_id, static_cast<quint64>(++async_progress));
|
task_manager_->SetTaskProgress(async_load_id, static_cast<quint64>(++async_progress));
|
||||||
|
|
||||||
|
// Always check for errors, even on success (e.g., playlist parsed but some songs failed to load)
|
||||||
|
const QStringList errors = loader->errors();
|
||||||
|
for (const QString &error : errors) {
|
||||||
|
Q_EMIT Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
if (result == SongLoader::Result::Error) {
|
if (result == SongLoader::Result::Error) {
|
||||||
const QStringList errors = loader->errors();
|
|
||||||
for (const QString &error : errors) {
|
|
||||||
Q_EMIT Error(error);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,10 +112,18 @@ void ParserBase::LoadSong(const QString &filename_or_url, const qint64 beginning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the file exists before trying to read it
|
||||||
|
if (!QFile::exists(filename)) {
|
||||||
|
qLog(Error) << "File does not exist:" << filename;
|
||||||
|
Q_EMIT Error(tr("File %1 does not exist").arg(filename));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tagreader_client_) {
|
if (tagreader_client_) {
|
||||||
const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, song);
|
const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, song);
|
||||||
if (!result.success()) {
|
if (!result.success()) {
|
||||||
qLog(Error) << "Could not read file" << filename << result.error_string();
|
qLog(Error) << "Could not read file" << filename << result.error_string();
|
||||||
|
Q_EMIT Error(tr("Could not read file %1: %2").arg(filename, result.error_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5526,7 +5526,7 @@ Are you sure you want to continue?</source>
|
|||||||
<name>RadioParadiseService</name>
|
<name>RadioParadiseService</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Getting %1 channels</source>
|
<source>Getting %1 channels</source>
|
||||||
<translation>Получение %1 каналов</translation>
|
<translation>Получение каналов %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -6191,7 +6191,7 @@ Are you sure you want to continue?</source>
|
|||||||
<name>SomaFMService</name>
|
<name>SomaFMService</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Getting %1 channels</source>
|
<source>Getting %1 channels</source>
|
||||||
<translation>Получение %1 каналов</translation>
|
<translation>Получение каналов %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
|||||||
@@ -1187,7 +1187,7 @@ If there are no matches then it will use the largest image in the directory.</tr
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Queue to play next</source>
|
<source>Queue to play next</source>
|
||||||
<translation>Sıradaki Yap</translation>
|
<translation>Sıradaki yap</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Search for this</source>
|
<source>Search for this</source>
|
||||||
@@ -3496,7 +3496,7 @@ If there are no matches then it will use the largest image in the directory.</tr
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Queue to play next</source>
|
<source>Queue to play next</source>
|
||||||
<translation>Sıradaki Yap</translation>
|
<translation>Sıradaki yap</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unskip track</source>
|
<source>Unskip track</source>
|
||||||
@@ -4431,7 +4431,7 @@ If there are no matches then it will use the largest image in the directory.</tr
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>&Hide %1</source>
|
<source>&Hide %1</source>
|
||||||
<translation>&%1'i sakla</translation>
|
<translation>&%1 ögesini sakla</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -6407,7 +6407,7 @@ Devam etmek istediğinizden emin misiniz?</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Queue to play next</source>
|
<source>Queue to play next</source>
|
||||||
<translation>Sıradaki Yap</translation>
|
<translation>Sıradaki yap</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove from favorites</source>
|
<source>Remove from favorites</source>
|
||||||
|
|||||||
Reference in New Issue
Block a user