Add dialog to display streams' audio details (#5547)
* Add Stream Details window * Fix capitalization in StreamDiscoverer::Discover() * StreamDiscoverer::Discover(): get URL by const reference * Refactor StreamDiscoverer::Discover * Rename StreamDiscoverer callbacks * StreamDiscoverer::OnDiscovered: fix nullptr comparison * StreamDiscoverer: rename DiscoverFinished signal * StreamDiscoverer::DataReady: receive const reference * StreamDiscoverer: Remove unsigned types * StreamDetailsDialog: rename Close slot * StreamDetailsDialog: rename ui pointer to ui_ * MainWindow::ShowStreamDetails: receive a const reference * StreamDetailsDialog: use unique_ptr, remove unsigned types
This commit is contained in:
parent
589d641955
commit
d3898d2f47
|
@ -68,6 +68,7 @@ pkg_check_modules(GSTREAMER_APP REQUIRED gstreamer-app-1.0)
|
||||||
pkg_check_modules(GSTREAMER_AUDIO REQUIRED gstreamer-audio-1.0)
|
pkg_check_modules(GSTREAMER_AUDIO REQUIRED gstreamer-audio-1.0)
|
||||||
pkg_check_modules(GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
|
pkg_check_modules(GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
|
||||||
pkg_check_modules(GSTREAMER_TAG REQUIRED gstreamer-tag-1.0)
|
pkg_check_modules(GSTREAMER_TAG REQUIRED gstreamer-tag-1.0)
|
||||||
|
pkg_check_modules(GSTREAMER_PBUTILS REQUIRED gstreamer-pbutils-1.0)
|
||||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.9)
|
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.9)
|
||||||
|
@ -155,6 +156,7 @@ include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
|
||||||
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
|
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
|
||||||
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
|
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
|
||||||
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
|
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
|
||||||
|
include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS})
|
||||||
include_directories(${GLIB_INCLUDE_DIRS})
|
include_directories(${GLIB_INCLUDE_DIRS})
|
||||||
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
|
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
|
||||||
include_directories(${LIBXML_INCLUDE_DIRS})
|
include_directories(${LIBXML_INCLUDE_DIRS})
|
||||||
|
|
|
@ -311,6 +311,7 @@ set(SOURCES
|
||||||
songinfo/songkickconcertwidget.cpp
|
songinfo/songkickconcertwidget.cpp
|
||||||
songinfo/songplaystats.cpp
|
songinfo/songplaystats.cpp
|
||||||
songinfo/spotifyimages.cpp
|
songinfo/spotifyimages.cpp
|
||||||
|
songinfo/streamdiscoverer.cpp
|
||||||
songinfo/taglyricsinfoprovider.cpp
|
songinfo/taglyricsinfoprovider.cpp
|
||||||
songinfo/ultimatelyricslyric.cpp
|
songinfo/ultimatelyricslyric.cpp
|
||||||
songinfo/ultimatelyricsprovider.cpp
|
songinfo/ultimatelyricsprovider.cpp
|
||||||
|
@ -358,6 +359,7 @@ set(SOURCES
|
||||||
ui/settingsdialog.cpp
|
ui/settingsdialog.cpp
|
||||||
ui/settingspage.cpp
|
ui/settingspage.cpp
|
||||||
ui/standarditemiconloader.cpp
|
ui/standarditemiconloader.cpp
|
||||||
|
ui/streamdetailsdialog.cpp
|
||||||
ui/systemtrayicon.cpp
|
ui/systemtrayicon.cpp
|
||||||
ui/trackselectiondialog.cpp
|
ui/trackselectiondialog.cpp
|
||||||
ui/windows7thumbbar.cpp
|
ui/windows7thumbbar.cpp
|
||||||
|
@ -603,6 +605,7 @@ set(HEADERS
|
||||||
songinfo/songkickconcertwidget.h
|
songinfo/songkickconcertwidget.h
|
||||||
songinfo/songplaystats.h
|
songinfo/songplaystats.h
|
||||||
songinfo/spotifyimages.h
|
songinfo/spotifyimages.h
|
||||||
|
songinfo/streamdiscoverer.h
|
||||||
songinfo/taglyricsinfoprovider.h
|
songinfo/taglyricsinfoprovider.h
|
||||||
songinfo/ultimatelyricslyric.h
|
songinfo/ultimatelyricslyric.h
|
||||||
songinfo/ultimatelyricsprovider.h
|
songinfo/ultimatelyricsprovider.h
|
||||||
|
@ -641,6 +644,7 @@ set(HEADERS
|
||||||
ui/settingsdialog.h
|
ui/settingsdialog.h
|
||||||
ui/settingspage.h
|
ui/settingspage.h
|
||||||
ui/standarditemiconloader.h
|
ui/standarditemiconloader.h
|
||||||
|
ui/streamdetailsdialog.h
|
||||||
ui/systemtrayicon.h
|
ui/systemtrayicon.h
|
||||||
ui/trackselectiondialog.h
|
ui/trackselectiondialog.h
|
||||||
ui/windows7thumbbar.h
|
ui/windows7thumbbar.h
|
||||||
|
@ -764,6 +768,7 @@ set(UI
|
||||||
ui/organiseerrordialog.ui
|
ui/organiseerrordialog.ui
|
||||||
ui/playbacksettingspage.ui
|
ui/playbacksettingspage.ui
|
||||||
ui/settingsdialog.ui
|
ui/settingsdialog.ui
|
||||||
|
ui/streamdetailsdialog.ui
|
||||||
ui/trackselectiondialog.ui
|
ui/trackselectiondialog.ui
|
||||||
|
|
||||||
widgets/equalizerslider.ui
|
widgets/equalizerslider.ui
|
||||||
|
@ -1265,6 +1270,7 @@ target_link_libraries(clementine_lib
|
||||||
${GSTREAMER_LIBRARIES}
|
${GSTREAMER_LIBRARIES}
|
||||||
${GSTREAMER_APP_LIBRARIES}
|
${GSTREAMER_APP_LIBRARIES}
|
||||||
${GSTREAMER_TAG_LIBRARIES}
|
${GSTREAMER_TAG_LIBRARIES}
|
||||||
|
${GSTREAMER_PBUTILS_LIBRARIES}
|
||||||
${QTSINGLEAPPLICATION_LIBRARIES}
|
${QTSINGLEAPPLICATION_LIBRARIES}
|
||||||
${QTSINGLECOREAPPLICATION_LIBRARIES}
|
${QTSINGLECOREAPPLICATION_LIBRARIES}
|
||||||
${QTIOCOMPRESSOR_LIBRARIES}
|
${QTIOCOMPRESSOR_LIBRARIES}
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/pbutils/pbutils.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "devicefinder.h"
|
#include "devicefinder.h"
|
||||||
|
@ -146,6 +147,8 @@ bool GstEngine::Init() {
|
||||||
void GstEngine::InitialiseGstreamer() {
|
void GstEngine::InitialiseGstreamer() {
|
||||||
gst_init(nullptr, nullptr);
|
gst_init(nullptr, nullptr);
|
||||||
|
|
||||||
|
gst_pb_utils_init();
|
||||||
|
|
||||||
#ifdef HAVE_MOODBAR
|
#ifdef HAVE_MOODBAR
|
||||||
gstfastspectrum_register_static();
|
gstfastspectrum_register_static();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "streamdiscoverer.h"
|
||||||
|
|
||||||
|
#include <gst/pbutils/pbutils.h>
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/signalchecker.h"
|
||||||
|
#include "core/waitforsignal.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
|
||||||
|
const int StreamDiscoverer::kDiscoveryTimeoutS = 10;
|
||||||
|
|
||||||
|
StreamDiscoverer::StreamDiscoverer() : QObject(nullptr) {
|
||||||
|
// Setting up a discoverer:
|
||||||
|
discoverer_ = gst_discoverer_new(kDiscoveryTimeoutS * GST_SECOND, NULL);
|
||||||
|
if (discoverer_ == NULL) {
|
||||||
|
qLog(Error) << "Error creating discoverer" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connecting its signals:
|
||||||
|
CHECKED_GCONNECT(discoverer_, "discovered", &OnDiscovered, this);
|
||||||
|
CHECKED_GCONNECT(discoverer_, "finished", &OnFinished, this);
|
||||||
|
|
||||||
|
// Starting the discoverer process:
|
||||||
|
gst_discoverer_start(discoverer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamDiscoverer::~StreamDiscoverer() {
|
||||||
|
gst_discoverer_stop(discoverer_);
|
||||||
|
g_object_unref(discoverer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamDiscoverer::Discover(const QString& url) {
|
||||||
|
// Adding the request to discover the url given as a parameter:
|
||||||
|
qLog(Debug) << "Discover" << url;
|
||||||
|
if (!gst_discoverer_discover_uri_async(discoverer_,
|
||||||
|
url.toStdString().c_str())) {
|
||||||
|
qLog(Error) << "Failed to start discovering" << url << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WaitForSignal(this, SIGNAL(DiscoverFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamDiscoverer::OnDiscovered(GstDiscoverer* discoverer,
|
||||||
|
GstDiscovererInfo* info, GError* err,
|
||||||
|
gpointer self) {
|
||||||
|
StreamDiscoverer* instance = reinterpret_cast<StreamDiscoverer*>(self);
|
||||||
|
|
||||||
|
QString discovered_url(gst_discoverer_info_get_uri(info));
|
||||||
|
|
||||||
|
GstDiscovererResult result = gst_discoverer_info_get_result(info);
|
||||||
|
if (result != GST_DISCOVERER_OK) {
|
||||||
|
QString error_message = GSTdiscovererErrorMessage(result);
|
||||||
|
qLog(Error) << "Discovery failed:" << error_message << endl;
|
||||||
|
emit instance->Error(
|
||||||
|
tr("Error discovering %1: %2").arg(discovered_url).arg(error_message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get audio streams (we will only care about the first one, which should be
|
||||||
|
// the only one).
|
||||||
|
GList* audio_streams = gst_discoverer_info_get_audio_streams(info);
|
||||||
|
|
||||||
|
if (audio_streams != nullptr) {
|
||||||
|
qLog(Debug) << "Discovery successful" << endl;
|
||||||
|
// We found a valid audio stream, extracting and saving its info:
|
||||||
|
GstDiscovererStreamInfo* stream_audio_info =
|
||||||
|
(GstDiscovererStreamInfo*)g_list_first(audio_streams)->data;
|
||||||
|
|
||||||
|
StreamDetails stream_details;
|
||||||
|
stream_details.url = discovered_url;
|
||||||
|
stream_details.bitrate = gst_discoverer_audio_info_get_bitrate(
|
||||||
|
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
|
||||||
|
stream_details.channels = gst_discoverer_audio_info_get_channels(
|
||||||
|
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
|
||||||
|
stream_details.depth = gst_discoverer_audio_info_get_depth(
|
||||||
|
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
|
||||||
|
stream_details.sample_rate = gst_discoverer_audio_info_get_sample_rate(
|
||||||
|
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
|
||||||
|
|
||||||
|
// Human-readable codec name:
|
||||||
|
GstCaps* stream_caps =
|
||||||
|
gst_discoverer_stream_info_get_caps(stream_audio_info);
|
||||||
|
gchar* decoder_description =
|
||||||
|
gst_pb_utils_get_codec_description(stream_caps);
|
||||||
|
stream_details.format = (decoder_description == NULL)
|
||||||
|
? QString(tr("Unknown"))
|
||||||
|
: QString(decoder_description);
|
||||||
|
|
||||||
|
gst_caps_unref(stream_caps);
|
||||||
|
g_free(decoder_description);
|
||||||
|
|
||||||
|
emit instance->DataReady(stream_details);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
emit instance->Error(
|
||||||
|
tr("Could not detect an audio stream in %1").arg(discovered_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_discoverer_stream_info_list_free(audio_streams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamDiscoverer::OnFinished(GstDiscoverer* discoverer, gpointer self) {
|
||||||
|
// The discoverer doesn't have any more urls in its queue. Let the loop know
|
||||||
|
// it can exit.
|
||||||
|
StreamDiscoverer* instance = reinterpret_cast<StreamDiscoverer*>(self);
|
||||||
|
emit instance->DiscoverFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StreamDiscoverer::GSTdiscovererErrorMessage(
|
||||||
|
GstDiscovererResult result) {
|
||||||
|
switch (result) {
|
||||||
|
case (GST_DISCOVERER_URI_INVALID):
|
||||||
|
return tr("Invalid URL");
|
||||||
|
case (GST_DISCOVERER_TIMEOUT):
|
||||||
|
return tr("Connection timed out");
|
||||||
|
case (GST_DISCOVERER_BUSY):
|
||||||
|
return tr("The discoverer is busy");
|
||||||
|
case (GST_DISCOVERER_MISSING_PLUGINS):
|
||||||
|
return tr("Missing plugins");
|
||||||
|
case (GST_DISCOVERER_ERROR):
|
||||||
|
default:
|
||||||
|
return tr("Could not get details");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef STREAMDISCOVERER_H
|
||||||
|
#define STREAMDISCOVERER_H
|
||||||
|
|
||||||
|
#include <gst/pbutils/pbutils.h>
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
struct StreamDetails {
|
||||||
|
QString url;
|
||||||
|
QString format;
|
||||||
|
int bitrate;
|
||||||
|
int depth;
|
||||||
|
int channels;
|
||||||
|
int sample_rate;
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(StreamDetails)
|
||||||
|
|
||||||
|
class StreamDiscoverer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
StreamDiscoverer();
|
||||||
|
~StreamDiscoverer();
|
||||||
|
|
||||||
|
void Discover(const QString& url);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void DiscoverFinished();
|
||||||
|
void DataReady(const StreamDetails& data);
|
||||||
|
void Error(const QString& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
GstDiscoverer* discoverer_;
|
||||||
|
|
||||||
|
static const int kDiscoveryTimeoutS;
|
||||||
|
|
||||||
|
// GstDiscoverer callbacks:
|
||||||
|
static void OnDiscovered(GstDiscoverer* discoverer, GstDiscovererInfo* info,
|
||||||
|
GError* err, gpointer instance);
|
||||||
|
static void OnFinished(GstDiscoverer* discoverer, gpointer instance);
|
||||||
|
|
||||||
|
// Helper to return descriptive error messages:
|
||||||
|
static QString GSTdiscovererErrorMessage(GstDiscovererResult result);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STREAMDISCOVERER_H
|
|
@ -101,6 +101,7 @@
|
||||||
#include "smartplaylists/generatormimedata.h"
|
#include "smartplaylists/generatormimedata.h"
|
||||||
#include "songinfo/artistinfoview.h"
|
#include "songinfo/artistinfoview.h"
|
||||||
#include "songinfo/songinfoview.h"
|
#include "songinfo/songinfoview.h"
|
||||||
|
#include "songinfo/streamdiscoverer.h"
|
||||||
#include "transcoder/transcodedialog.h"
|
#include "transcoder/transcodedialog.h"
|
||||||
#include "ui/about.h"
|
#include "ui/about.h"
|
||||||
#include "ui/addstreamdialog.h"
|
#include "ui/addstreamdialog.h"
|
||||||
|
@ -113,6 +114,7 @@
|
||||||
#include "ui/organiseerrordialog.h"
|
#include "ui/organiseerrordialog.h"
|
||||||
#include "ui/qtsystemtrayicon.h"
|
#include "ui/qtsystemtrayicon.h"
|
||||||
#include "ui/settingsdialog.h"
|
#include "ui/settingsdialog.h"
|
||||||
|
#include "ui/streamdetailsdialog.h"
|
||||||
#include "ui/systemtrayicon.h"
|
#include "ui/systemtrayicon.h"
|
||||||
#include "ui/trackselectiondialog.h"
|
#include "ui/trackselectiondialog.h"
|
||||||
#include "ui/windows7thumbbar.h"
|
#include "ui/windows7thumbbar.h"
|
||||||
|
@ -173,6 +175,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||||
tray_icon_(tray_icon),
|
tray_icon_(tray_icon),
|
||||||
osd_(osd),
|
osd_(osd),
|
||||||
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
|
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
|
||||||
|
stream_discoverer_(std::bind(&MainWindow::CreateStreamDiscoverer, this)),
|
||||||
global_shortcuts_(new GlobalShortcuts(this)),
|
global_shortcuts_(new GlobalShortcuts(this)),
|
||||||
global_search_view_(new GlobalSearchView(app_, this)),
|
global_search_view_(new GlobalSearchView(app_, this)),
|
||||||
library_view_(new LibraryViewContainer(this)),
|
library_view_(new LibraryViewContainer(this)),
|
||||||
|
@ -477,6 +480,8 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||||
SLOT(ShowQueueManager()));
|
SLOT(ShowQueueManager()));
|
||||||
connect(ui_->action_add_files_to_transcoder, SIGNAL(triggered()),
|
connect(ui_->action_add_files_to_transcoder, SIGNAL(triggered()),
|
||||||
SLOT(AddFilesToTranscoder()));
|
SLOT(AddFilesToTranscoder()));
|
||||||
|
connect(ui_->action_view_stream_details, SIGNAL(triggered()),
|
||||||
|
SLOT(DiscoverStreamDetails()));
|
||||||
|
|
||||||
background_streams_->AddAction("Rain", ui_->action_rain);
|
background_streams_->AddAction("Rain", ui_->action_rain);
|
||||||
background_streams_->AddAction("Hypnotoad", ui_->action_hypnotoad);
|
background_streams_->AddAction("Hypnotoad", ui_->action_hypnotoad);
|
||||||
|
@ -684,6 +689,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||||
playlist_menu_->addAction(ui_->action_remove_from_playlist);
|
playlist_menu_->addAction(ui_->action_remove_from_playlist);
|
||||||
playlist_undoredo_ = playlist_menu_->addSeparator();
|
playlist_undoredo_ = playlist_menu_->addSeparator();
|
||||||
playlist_menu_->addAction(ui_->action_edit_track);
|
playlist_menu_->addAction(ui_->action_edit_track);
|
||||||
|
playlist_menu_->addAction(ui_->action_view_stream_details);
|
||||||
playlist_menu_->addAction(ui_->action_edit_value);
|
playlist_menu_->addAction(ui_->action_edit_value);
|
||||||
playlist_menu_->addAction(ui_->action_renumber_tracks);
|
playlist_menu_->addAction(ui_->action_renumber_tracks);
|
||||||
playlist_menu_->addAction(ui_->action_selection_set_value);
|
playlist_menu_->addAction(ui_->action_selection_set_value);
|
||||||
|
@ -1712,6 +1718,10 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos,
|
||||||
// no 'show in browser' action if only streams are selected
|
// no 'show in browser' action if only streams are selected
|
||||||
playlist_open_in_browser_->setVisible(streams != all);
|
playlist_open_in_browser_->setVisible(streams != all);
|
||||||
|
|
||||||
|
// If exactly one stream is selected, enable the 'show details' action.
|
||||||
|
ui_->action_view_stream_details->setEnabled(all == 1 && streams == 1);
|
||||||
|
ui_->action_view_stream_details->setVisible(all == 1 && streams == 1);
|
||||||
|
|
||||||
bool track_column = (index.column() == Playlist::Column_Track);
|
bool track_column = (index.column() == Playlist::Column_Track);
|
||||||
ui_->action_renumber_tracks->setVisible(editable >= 2 && track_column);
|
ui_->action_renumber_tracks->setVisible(editable >= 2 && track_column);
|
||||||
ui_->action_selection_set_value->setVisible(editable >= 2 && !track_column);
|
ui_->action_selection_set_value->setVisible(editable >= 2 && !track_column);
|
||||||
|
@ -1882,6 +1892,27 @@ void MainWindow::EditTagDialogAccepted() {
|
||||||
app_->playlist_manager()->current()->Save();
|
app_->playlist_manager()->current()->Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::DiscoverStreamDetails() {
|
||||||
|
int row = playlist_menu_index_.row();
|
||||||
|
Song song = app_->playlist_manager()->current()->item_at(row)->Metadata();
|
||||||
|
|
||||||
|
QString url = song.url().toString();
|
||||||
|
stream_discoverer_->Discover(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::ShowStreamDetails(const StreamDetails& details) {
|
||||||
|
StreamDetailsDialog stream_details_dialog(this);
|
||||||
|
|
||||||
|
stream_details_dialog.setUrl(details.url);
|
||||||
|
stream_details_dialog.setFormat(details.format);
|
||||||
|
stream_details_dialog.setBitrate(details.bitrate);
|
||||||
|
stream_details_dialog.setChannels(details.channels);
|
||||||
|
stream_details_dialog.setDepth(details.depth);
|
||||||
|
stream_details_dialog.setSampleRate(details.sample_rate);
|
||||||
|
|
||||||
|
stream_details_dialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::RenumberTracks() {
|
void MainWindow::RenumberTracks() {
|
||||||
QModelIndexList indexes =
|
QModelIndexList indexes =
|
||||||
ui_->playlist->view()->selectionModel()->selection().indexes();
|
ui_->playlist->view()->selectionModel()->selection().indexes();
|
||||||
|
@ -2490,6 +2521,14 @@ EditTagDialog* MainWindow::CreateEditTagDialog() {
|
||||||
return edit_tag_dialog;
|
return edit_tag_dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StreamDiscoverer* MainWindow::CreateStreamDiscoverer() {
|
||||||
|
StreamDiscoverer* discoverer = new StreamDiscoverer();
|
||||||
|
connect(discoverer, SIGNAL(DataReady(StreamDetails)),
|
||||||
|
SLOT(ShowStreamDetails(StreamDetails)));
|
||||||
|
connect(discoverer, SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString)));
|
||||||
|
return discoverer;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::ShowAboutDialog() { about_dialog_->show(); }
|
void MainWindow::ShowAboutDialog() { about_dialog_->show(); }
|
||||||
|
|
||||||
void MainWindow::ShowTranscodeDialog() { transcode_dialog_->show(); }
|
void MainWindow::ShowTranscodeDialog() { transcode_dialog_->show(); }
|
||||||
|
|
|
@ -31,8 +31,10 @@
|
||||||
#include "engines/engine_fwd.h"
|
#include "engines/engine_fwd.h"
|
||||||
#include "library/librarymodel.h"
|
#include "library/librarymodel.h"
|
||||||
#include "playlist/playlistitem.h"
|
#include "playlist/playlistitem.h"
|
||||||
|
#include "songinfo/streamdiscoverer.h"
|
||||||
#include "ui/organisedialog.h"
|
#include "ui/organisedialog.h"
|
||||||
#include "ui/settingsdialog.h"
|
#include "ui/settingsdialog.h"
|
||||||
|
#include "ui/streamdetailsdialog.h"
|
||||||
|
|
||||||
class About;
|
class About;
|
||||||
class AddStreamDialog;
|
class AddStreamDialog;
|
||||||
|
@ -72,6 +74,7 @@ class RipCDDialog;
|
||||||
class Song;
|
class Song;
|
||||||
class SongInfoBase;
|
class SongInfoBase;
|
||||||
class SongInfoView;
|
class SongInfoView;
|
||||||
|
class StreamDetailsDialog;
|
||||||
class SystemTrayIcon;
|
class SystemTrayIcon;
|
||||||
class TagFetcher;
|
class TagFetcher;
|
||||||
class TaskManager;
|
class TaskManager;
|
||||||
|
@ -165,6 +168,8 @@ signals:
|
||||||
void PlaylistEditFinished(const QModelIndex& index);
|
void PlaylistEditFinished(const QModelIndex& index);
|
||||||
void EditTracks();
|
void EditTracks();
|
||||||
void EditTagDialogAccepted();
|
void EditTagDialogAccepted();
|
||||||
|
void DiscoverStreamDetails();
|
||||||
|
void ShowStreamDetails(const StreamDetails& details);
|
||||||
void RenumberTracks();
|
void RenumberTracks();
|
||||||
void SelectionSetValue();
|
void SelectionSetValue();
|
||||||
void EditValue();
|
void EditValue();
|
||||||
|
@ -252,6 +257,7 @@ signals:
|
||||||
void ShowVisualisations();
|
void ShowVisualisations();
|
||||||
SettingsDialog* CreateSettingsDialog();
|
SettingsDialog* CreateSettingsDialog();
|
||||||
EditTagDialog* CreateEditTagDialog();
|
EditTagDialog* CreateEditTagDialog();
|
||||||
|
StreamDiscoverer* CreateStreamDiscoverer();
|
||||||
void OpenSettingsDialog();
|
void OpenSettingsDialog();
|
||||||
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
|
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
|
||||||
void ShowSongInfoConfig();
|
void ShowSongInfoConfig();
|
||||||
|
@ -299,6 +305,7 @@ signals:
|
||||||
OSD* osd_;
|
OSD* osd_;
|
||||||
Lazy<EditTagDialog> edit_tag_dialog_;
|
Lazy<EditTagDialog> edit_tag_dialog_;
|
||||||
Lazy<About> about_dialog_;
|
Lazy<About> about_dialog_;
|
||||||
|
Lazy<StreamDiscoverer> stream_discoverer_;
|
||||||
|
|
||||||
GlobalShortcuts* global_shortcuts_;
|
GlobalShortcuts* global_shortcuts_;
|
||||||
|
|
||||||
|
|
|
@ -928,6 +928,11 @@
|
||||||
<string>Remove unavailable tracks from playlist</string>
|
<string>Remove unavailable tracks from playlist</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_view_stream_details">
|
||||||
|
<property name="text">
|
||||||
|
<string>View Stream Details</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_toggle_show_sidebar">
|
<action name="action_toggle_show_sidebar">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "streamdetailsdialog.h"
|
||||||
|
#include "ui_streamdetailsdialog.h"
|
||||||
|
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
|
StreamDetailsDialog::StreamDetailsDialog(QWidget* parent)
|
||||||
|
: QDialog(parent), ui_(new Ui::StreamDetailsDialog) {
|
||||||
|
ui_->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamDetailsDialog::~StreamDetailsDialog() {}
|
||||||
|
|
||||||
|
void StreamDetailsDialog::setUrl(const QString& url) {
|
||||||
|
ui_->url->setText(url);
|
||||||
|
ui_->url->setCursorPosition(0);
|
||||||
|
}
|
||||||
|
void StreamDetailsDialog::setFormat(const QString& format) {
|
||||||
|
ui_->format->setText(format);
|
||||||
|
}
|
||||||
|
void StreamDetailsDialog::setBitrate(int bitrate) {
|
||||||
|
ui_->bitrate->setText(QString("%1 kbps").arg(bitrate / 1000));
|
||||||
|
|
||||||
|
// Some bitrates aren't properly reported by GStreamer.
|
||||||
|
// In that case do not display bitrate information.
|
||||||
|
ui_->bitrate->setVisible(bitrate != 0);
|
||||||
|
ui_->bitrate_label->setVisible(bitrate != 0);
|
||||||
|
}
|
||||||
|
void StreamDetailsDialog::setChannels(int channels) {
|
||||||
|
ui_->channels->setText(QString::number(channels));
|
||||||
|
}
|
||||||
|
void StreamDetailsDialog::setDepth(int depth) {
|
||||||
|
// Right now GStreamer seems to be reporting incorrect numbers for MP3 and AAC
|
||||||
|
// streams, so we leave that value hidden in the UI.
|
||||||
|
// ui_->depth->setText(QString("%1 bits").arg(depth));
|
||||||
|
ui_->depth->setVisible(false);
|
||||||
|
ui_->depth_label->setVisible(false);
|
||||||
|
}
|
||||||
|
void StreamDetailsDialog::setSampleRate(int sample_rate) {
|
||||||
|
ui_->sample_rate->setText(QString("%1 Hz").arg(sample_rate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamDetailsDialog::Close() { this->close(); }
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef STREAMDETAILSDIALOG_H
|
||||||
|
#define STREAMDETAILSDIALOG_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class StreamDetailsDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamDetailsDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit StreamDetailsDialog(QWidget* parent = 0);
|
||||||
|
~StreamDetailsDialog();
|
||||||
|
|
||||||
|
void setUrl(const QString& url);
|
||||||
|
void setFormat(const QString& codec); // This is localized, so only for human
|
||||||
|
// consumption.
|
||||||
|
void setBitrate(int);
|
||||||
|
void setDepth(int);
|
||||||
|
void setChannels(int);
|
||||||
|
void setSampleRate(int);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::StreamDetailsDialog> ui_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STREAMDETAILSDIALOG_H
|
|
@ -0,0 +1,148 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>StreamDetailsDialog</class>
|
||||||
|
<widget class="QDialog" name="StreamDetailsDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>500</width>
|
||||||
|
<height>210</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>210</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Stream Details</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="url_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>URL</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="url">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="format_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Format</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="format">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="channels_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Channels</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="channels">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="bitrate_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bit rate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="bitrate">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="sample_rate_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sample rate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLabel" name="sample_rate">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="depth_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Depth</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLabel" name="depth">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>StreamDetailsDialog</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>299</x>
|
||||||
|
<y>186</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>249</x>
|
||||||
|
<y>104</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
Loading…
Reference in New Issue