aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/General/streambrowser/streamwindow.cpp
blob: a367fa9a95a1439ed24c70315595e45d37f4869d (plain) (tree)
1
2
3
                                                                            
                                                                            
                                                                            













                                                                            
                                                                            







                                                                             
                                


                      
                

                           
                    


                                   
                            

                         

                                                 
 
                        



                                            
             

                                                                      
                   

                                                                         


                                                        
                                                       


                                                                        
                   





                                                                                                






                                                                           
                                                         



                                                                          





                                                                                                  
                                             
 
                              
 

















                                                                                               


                                                                                                                  

                        
                                


                                    
                                                                 
                                      
                                       

                                      
                                                                    











                                                                                                       

 





                                                 
                                           

                                                 
                                                







                                                                       
                                       







                                                       
                                                                                              
                                          

                                                



                                             
                     
                                            
     
                                                                                              







                                                                                         
                                                                                            




                                                                                       




                                           

                                                                     
                                                     




                                                       
                                                                              



                                                       
                                                                                  



                                   
                                                                                        













                                                                              
                                            
               
                                                                                          




                                                                                
                                                                   









                                                            

 




                                                                 


                                                                                                      

                        
                             
                                                                 
                                    




                                          
                                                       

                                          




                                                                                            



                                 

                    
                                                                    


















                                                                                               

 
                                                                       
 
                                            












                                                                             


                                                                              
                                                          




                                                                             
                                                                          








                                                                  














                                                           
 

                                                                                    


                                                                                                          
                                                                                                       
      

     
/***************************************************************************
 *   Copyright (C) 2012-2018 by Ilya Kotov                                 *
 *   forkotov02@ya.ru                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkProxy>
#include <QUrl>
#include <QRegExp>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QSettings>
#include <QDir>
#include <QMessageBox>
#include <QMenu>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <algorithm>
#include <qmmp/qmmpsettings.h>
#include <qmmp/qmmp.h>
#include <qmmpui/playlistmanager.h>
#include "ui_streamwindow.h"
#include "streamwindow.h"

StreamWindow::StreamWindow(QWidget *parent)
    : QWidget(parent), m_ui(new Ui::StreamWindow)
{
    m_ui->setupUi(this);
    setWindowFlags(Qt::Window);
    setAttribute(Qt::WA_DeleteOnClose);
    setAttribute(Qt::WA_QuitOnClose, false);
    m_requestReply = 0;
    //buttons
    m_ui->addPushButton->setIcon(QIcon::fromTheme("list-add"));
    m_ui->updatePushButton->setIcon(QIcon::fromTheme("view-refresh"));
    //icecast model
    m_iceCastModel = new QStandardItemModel(this);
    m_iceCastModel->setHorizontalHeaderLabels(QStringList() << tr("Name")
                                       << tr("Genre")
                                       << tr("Bitrate")
                                       << tr("Format"));
    m_iceCastFilterModel = new StreamsProxyModel(this);
    m_iceCastFilterModel->setSourceModel(m_iceCastModel);
    m_iceCastFilterModel->setDynamicSortFilter(true);
    m_iceCastFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    //icecast table
    m_ui->icecastTableView->setModel(m_iceCastFilterModel);
    m_ui->icecastTableView->verticalHeader()->setDefaultSectionSize(fontMetrics().height() + 3);
    m_ui->icecastTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    m_ui->icecastTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    m_ui->icecastTableView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_ui->icecastTableView, SIGNAL(customContextMenuRequested(QPoint)),
            SLOT(execIceCastMenu(QPoint)));
    //favorites model
    m_favoritesModel = new QStandardItemModel(this);
    m_favoritesModel->setHorizontalHeaderLabels(QStringList() << tr("Name")
                                                     << tr("Genre")
                                                     << tr("Bitrate")
                                                     << tr("Format"));
    m_favoritesFilterModel = new StreamsProxyModel(this);
    m_favoritesFilterModel->setSourceModel(m_favoritesModel);
    m_favoritesFilterModel->setDynamicSortFilter(true);
    m_favoritesFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    //favorites table
    m_ui->favoritesTableView->setModel(m_favoritesFilterModel);
    m_ui->favoritesTableView->verticalHeader()->setDefaultSectionSize(fontMetrics().height() + 3);
    m_ui->favoritesTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    m_ui->favoritesTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    m_ui->favoritesTableView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_ui->favoritesTableView, SIGNAL(customContextMenuRequested(QPoint)),
            SLOT(execFavoritesMenu(QPoint)));

    m_ui->statusLabel->hide();

    m_http = new QNetworkAccessManager(this);
     //load global proxy settings
    QmmpSettings *gs = QmmpSettings::instance();
    if (gs->isProxyEnabled())
    {
        QNetworkProxy proxy(QNetworkProxy::HttpProxy, gs->proxy().host(),  gs->proxy().port());
        if(gs->useProxyAuth())
        {
            proxy.setUser(gs->proxy().userName());
            proxy.setPassword(gs->proxy().password());
        }
        m_http->setProxy(proxy);
    }
    connect(m_http, SIGNAL(finished (QNetworkReply *)), SLOT(showText(QNetworkReply *)));
    //read settings
    QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
    settings.beginGroup("StreamBrowser");
    restoreGeometry(settings.value("geometry").toByteArray());
    m_ui->icecastTableView->horizontalHeader()->restoreState(settings.value("icecast_headers").toByteArray());
    m_ui->favoritesTableView->horizontalHeader()->restoreState(settings.value("favorites_headers").toByteArray());
    m_ui->tabWidget->setCurrentIndex(settings.value("current_tab", 1).toInt());
    settings.endGroup();
    //create cache dir
    QDir dir(Qmmp::configDir());
    if(!dir.exists("streambrowser"))
        dir.mkdir("streambrowser");
    //read cache
    QFile file(Qmmp::configDir() + "/streambrowser/icecast.xml");
    if(file.open(QIODevice::ReadOnly))
        readXml(&file, m_iceCastModel);
    else
        on_updatePushButton_clicked();
    QFile file2(Qmmp::configDir() + "/streambrowser/favorites.xml");
    if(file2.open(QIODevice::ReadOnly))
        readXml(&file2, m_favoritesModel);
    //create menus
    m_iceCastMenu = new QMenu(this);
    m_iceCastMenu->addAction(tr("&Add to favorites"), this, SLOT(addToFavorites()));
    QAction *addAction = m_iceCastMenu->addAction(tr("&Add to playlist"), this,
                                                  SLOT(on_addPushButton_clicked()));
    m_favoritesMenu = new QMenu(this);
    m_favoritesMenu->addAction(addAction);
    m_favoritesMenu->addSeparator();
    m_favoritesMenu->addAction(tr("&Remove"), this, SLOT(removeFromFavorites()), QKeySequence::Delete);
    addActions(m_favoritesMenu->actions());
}

StreamWindow::~StreamWindow()
{
}

void StreamWindow::showText(QNetworkReply *reply)
{
    m_ui->statusLabel->setText(tr("Done"));
    if (reply->error() != QNetworkReply::NoError)
    {
        m_ui->statusLabel->setText(tr("Error"));
        QMessageBox::warning (this, tr("Error"), reply->errorString());
        m_requestReply = 0;
        reply->deleteLater();
        return;
    }
    if(m_requestReply == reply)
    {
        m_requestReply = 0;
        readXml(reply, m_iceCastModel);
    }
    reply->deleteLater();
}

void StreamWindow::on_updatePushButton_clicked()
{
    QNetworkRequest request;
    request.setUrl(QUrl("http://dir.xiph.org/yp.xml"));
    request.setRawHeader("User-Agent", QString("qmmp/%1").arg(Qmmp::strVersion()).toLatin1());
    m_requestReply = m_http->get(request);
    m_ui->statusLabel->setText(tr("Receiving"));
    m_ui->statusLabel->show();
}

void StreamWindow::on_addPushButton_clicked()
{
    QStringList urls;
    if(m_ui->tabWidget->currentIndex() == 0)
    {
        QModelIndexList indexes = m_ui->favoritesTableView->selectionModel()->selectedRows(0);
        foreach(QModelIndex index, indexes)
        {
            QModelIndex source_index = m_favoritesFilterModel->mapToSource(index);
            urls.append(m_favoritesModel->item(source_index.row(),0)->data().toString());
        }
    }
    else
    {
        QModelIndexList indexes = m_ui->icecastTableView->selectionModel()->selectedRows(0);
        foreach(QModelIndex index, indexes)
        {
            QModelIndex source_index = m_iceCastFilterModel->mapToSource(index);
            urls.append(m_iceCastModel->item(source_index.row(),0)->data().toString());
        }
    }
    urls.removeDuplicates();
    PlayListManager::instance()->add(urls);
}

void StreamWindow::on_filterLineEdit_textChanged(const QString &text)
{
    m_iceCastFilterModel->setFilterFixedString(text);
    m_favoritesFilterModel->setFilterFixedString(text);
}

void StreamWindow::execIceCastMenu(const QPoint &pos)
{
    m_iceCastMenu->exec(m_ui->icecastTableView->viewport()->mapToGlobal(pos));
}

void StreamWindow::execFavoritesMenu(const QPoint &pos)
{
    m_favoritesMenu->exec(m_ui->favoritesTableView->viewport()->mapToGlobal(pos));
}

void StreamWindow::addToFavorites()
{
    QModelIndexList indexes = m_ui->icecastTableView->selectionModel()->selectedRows(0);
    foreach(QModelIndex index, indexes)
    {
        QModelIndex source_index = m_iceCastFilterModel->mapToSource(index);
        int row = source_index.row();
        m_favoritesModel->appendRow(QList<QStandardItem *> ()
                                    << m_iceCastModel->item(row, 0)->clone()
                                    << m_iceCastModel->item(row, 1)->clone()
                                    << m_iceCastModel->item(row, 2)->clone()
                                    << m_iceCastModel->item(row, 3)->clone());
    }
}

void StreamWindow::removeFromFavorites()
{
    if(m_ui->tabWidget->currentIndex() != 0)
        return;
    QModelIndexList indexes = m_ui->favoritesTableView->selectionModel()->selectedRows(0);
    QList<int> rows_to_remove;
    foreach(QModelIndex index, indexes)
    {
        rows_to_remove.append(m_favoritesFilterModel->mapToSource(index).row());
    }
    std::stable_sort(rows_to_remove.begin(), rows_to_remove.end());
    int prev_row = -1;
    for(int i = rows_to_remove.count() - 1; i >= 0; i -= 1 )
    {
        int current = rows_to_remove[i];
        if(current != prev_row)
        {
            m_favoritesFilterModel->removeRows(current, 1);
            prev_row = current;
        }
    }
}

void StreamWindow::closeEvent(QCloseEvent *)
{
    QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
    settings.beginGroup("StreamBrowser");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("icecast_headers", m_ui->icecastTableView->horizontalHeader()->saveState());
    settings.setValue("favorites_headers", m_ui->favoritesTableView->horizontalHeader()->saveState());
    settings.setValue("current_tab", m_ui->tabWidget->currentIndex());
    settings.endGroup();

     //save icecast directory
    QFile file(Qmmp::configDir() + "/streambrowser/icecast.xml");
    file.open(QIODevice::WriteOnly);
    QXmlStreamWriter writer(&file);
    writer.setCodec("UTF-8");
    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement("directory");
    for(int i = 0; i < m_iceCastModel->rowCount(); ++i)
    {
        writer.writeStartElement("entry");
        writer.writeTextElement("server_name", m_iceCastModel->item(i,0)->text());
        writer.writeTextElement("listen_url", m_iceCastModel->item(i,0)->data().toString());
        writer.writeTextElement("genre", m_iceCastModel->item(i,1)->text());
        writer.writeTextElement("bitrate", m_iceCastModel->item(i,2)->text());
        writer.writeTextElement("server_type", m_iceCastModel->item(i,3)->text());
        writer.writeEndElement();
    }
    writer.writeEndElement();
    writer.writeEndDocument();
    file.close();
    //save favorites
    QFile file2(Qmmp::configDir() + "/streambrowser/favorites.xml");
    file2.open(QIODevice::WriteOnly);
    QXmlStreamWriter writer2(&file2);
    writer2.setCodec("UTF-8");
    writer2.setAutoFormatting(true);
    writer2.writeStartDocument();
    writer2.writeStartElement("directory");
    for(int i = 0; i < m_favoritesModel->rowCount(); ++i)
    {
        writer2.writeStartElement("entry");
        writer2.writeTextElement("server_name", m_favoritesModel->item(i,0)->text());
        writer2.writeTextElement("listen_url", m_favoritesModel->item(i,0)->data().toString());
        writer2.writeTextElement("genre", m_favoritesModel->item(i,1)->text());
        writer2.writeTextElement("bitrate", m_favoritesModel->item(i,2)->text());
        writer2.writeTextElement("server_type", m_favoritesModel->item(i,3)->text());
        writer2.writeEndElement();
    }
    writer2.writeEndElement();
    writer2.writeEndDocument();
    file2.close();
}

void StreamWindow::readXml(QIODevice *input, QStandardItemModel *model)
{
    model->removeRows(0, model->rowCount());
    QXmlStreamReader xml(input);
    QString currentTag, server_name, listen_url, genre, bitrate, server_type;
    while (!xml.atEnd())
    {
        xml.readNext();
        if (xml.isStartElement())
        {
            currentTag = xml.name().toString();
        }
        else if (xml.isEndElement())
        {
            if (xml.name() == "entry")
            {
                if(server_name == "Unspecified name" || server_name.isEmpty())
                    server_name = listen_url.section("/", -1);

                model->appendRow(QList<QStandardItem *> ()
                                          << new QStandardItem(server_name)
                                          << new QStandardItem(genre)
                                          << new QStandardItem(bitrate)
                                          << new QStandardItem(server_type));

                QStandardItem *item = model->item(model->rowCount()-1, 0);
                item->setToolTip(server_name + "\n" + listen_url);
                item->setData(listen_url);

                server_name.clear();
                listen_url.clear();
                genre.clear();
                bitrate.clear();
                server_type.clear();
            }
        }
        else if (xml.isCharacters() && !xml.isWhitespace())
        {
            if (currentTag == "server_name")
                server_name += xml.text().toString();
            else if (currentTag == "listen_url")
                listen_url += xml.text().toString();
            else if (currentTag == "genre")
                genre += xml.text().toString();
            else if (currentTag == "bitrate")
                bitrate += xml.text().toString();
            else if(currentTag == "server_type")
                server_type += xml.text().toString();
        }
    }

    if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError)
    {
#ifdef Q_OS_WIN
        qWarning("StreamWindow: xml error: %d: %s", (int)xml.lineNumber(), qPrintable(xml.errorString()));
#else
        qWarning("StreamWindow: xml error: %lld: %s", xml.lineNumber(), qPrintable(xml.errorString()));
#endif
    }
}