Skip to content
minimediaplayer.cpp 6.56 KiB
Newer Older
/*
 * Copyright (C) 2019  Kevin Whitaker <eyecreate@eyecreate.org>
 *
 * 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 3 of the License, or
 * (at your option) any later version.
 *ByteArray(jsonTrack)
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "minimediaplayer.h"
#include <QProcess>
#include <QJsonDocument>
#include <QJsonObject>

MiniMediaPlayer::MiniMediaPlayer(QObject *parent) : QObject(parent)
{
    m_player = new QMediaPlayer(this,QMediaPlayer::StreamPlayback);
    connect(m_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),this,SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
    connect(m_player,SIGNAL(stateChanged(QMediaPlayer::State)),this,SLOT(mediaStateChanged(QMediaPlayer::State)));
    connect(m_player,SIGNAL(durationChanged(qint64)),this,SLOT(durationChanged(qint64)));
    connect(m_player,SIGNAL(positionChanged(qint64)),this,SLOT(positionChanged(qint64)));
}

bool MiniMediaPlayer::isPlaying()
{
    return m_player->state() == QMediaPlayer::State::PlayingState;
}

bool MiniMediaPlayer::nextTrack()
{
    if(playlist.size() > 1)
    {
        playlist.pop_front();
        m_player->stop();
        m_player->setMedia(getStreamUrlFromUrl(playlist.first().second));
        emit playlistChanged(getTrackNames());
        return true;
    }
    else if(playlist.size() == 1)
    {
        playlist.pop_front();
        m_player->stop();
        emit playlistChanged(getTrackNames());
    }
    return false;
}

bool MiniMediaPlayer::playPauseTrack(bool playing)
{
    if(m_player->state() == QMediaPlayer::State::PlayingState && !playing)
    {
        m_player->pause();
        return true;
    }
    else if(m_player->state() == QMediaPlayer::State::PausedState && playing)
    {
        m_player->play();
        return true;
    }
    return false;
}

QList<QString> MiniMediaPlayer::getTrackNames()
{
    QList<QString> playlistTitles;
    for(QPair<QString,QUrl> item: playlist)
    {
        playlistTitles.append(item.first);
    }
    return playlistTitles;
}

QMap<QString, qint64> MiniMediaPlayer::currentState()
{
    QMap<QString,qint64> state;
    state.insert("duration",m_player->duration());
    state.insert("position",m_player->position());
    state.insert("state",isPlaying());
    return state;
}

void MiniMediaPlayer::durationChanged(qint64 length)
{
    emit trackDurationChanged(length);
}

void MiniMediaPlayer::positionChanged(qint64 position)
{
    emit trackPositionChanged(position);
bool MiniMediaPlayer::addURLToPlaylist(QUrl mediaUrl)
    if(isValidMediaUrl(mediaUrl))
    {
        bool shouldStartPlaying = false;
        if(playlist.size() == 0) shouldStartPlaying = true;
        QList<QPair<QString,QUrl>> tracks = getStreamTitlesAndUrlsFromUrl(mediaUrl);
        playlist.append(tracks);
        emit playlistChanged(getTrackNames());
        if(shouldStartPlaying && playlist.size() > 0)
        {
            m_player->setMedia(getStreamUrlFromUrl(playlist.first().second));
        }
        if(tracks.size() > 0)
        {
            return true;
        }
    }
    return false;
QUrl MiniMediaPlayer::getStreamUrlFromUrl(QUrl mediaUrl)
    //Run youtube-dl on Url and return Url given back.
    QProcess youtubedl;
    youtubedl.start("youtube-dl -f bestaudio -g "+mediaUrl.toString());
    if(youtubedl.waitForFinished(-1))
    {
        QByteArray out = youtubedl.readAllStandardOutput();
        QString outStr = QString::fromStdString(out.toStdString());
        for(QString track:outStr.split('\n'))
        {
            qDebug() << "Found URL stream:" << track;
            return QUrl(track);
        }
    }
    return QUrl();

bool MiniMediaPlayer::isValidMediaUrl(QUrl mediaUrl)
{
    //Only support youtube and bandcamp for now.
    if(mediaUrl.host().endsWith("youtube.com") || mediaUrl.host().endsWith("bandcamp.com"))
    {
        //TODO: check more than host?
        return true;
    }
    //For now, allow all urls since worst case youtube-dl will just fail to add anything to playlist.
    return true;
}

void MiniMediaPlayer::mediaStateChanged(QMediaPlayer::State state)
{
    if(state == QMediaPlayer::State::PlayingState)
    {
        emit playStateChanged(true);
    } 
    else
    {
        emit playStateChanged(false);
    }
}

void MiniMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
{
    //Make sure to load next track once track has ended.
    if(status == QMediaPlayer::MediaStatus::EndOfMedia)
    {
        //Load next track
        nextTrack();
    }
    else if(status == QMediaPlayer::MediaStatus::InvalidMedia)
    {
        qWarning() << "Unable to play track.";
    }
    else if(status == QMediaPlayer::MediaStatus::LoadedMedia)
    {
        //Start playing media if more in playlsit
        if(playlist.size() > 0) m_player->play();
    }
}

QList<QPair<QString,QUrl>> MiniMediaPlayer::getStreamTitlesAndUrlsFromUrl(QUrl mediaUrl)
{
    QList<QPair<QString,QUrl>> tracks;
    //Run youtube-dl on Url and return json given back.
    QProcess youtubedl;
    youtubedl.start("youtube-dl -f bestaudio -j "+mediaUrl.toString());
    if(youtubedl.waitForFinished(-1))
    {
        //If there is anything in error output, error must have occured.
        if(youtubedl.readAllStandardError().size()== 0)
        {
            QByteArray out = youtubedl.readAllStandardOutput();
            QString outStr = QString::fromStdString(out.toStdString());
            for(QString jsonTrack:outStr.split('\n'))
            {
                //Parse urls and titles from json list
                QJsonDocument info = QJsonDocument::fromJson(jsonTrack.toUtf8());
                if(!info.isNull() && info.isObject())
                {
                    QPair<QString,QUrl> track;
                    QJsonObject jsonTrackInfo = info.object();
                    track.first = jsonTrackInfo["title"].toString();
                    track.second = QUrl(jsonTrackInfo["webpage_url"].toString());
                    tracks.append(track);
                }
            }
        }
        else
        {
Kevin Whitaker's avatar
Kevin Whitaker committed
            qDebug() << youtubedl.readAllStandardError();