/* * Copyright (C) 2019 Kevin Whitaker * * 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 . */ #include "minimediaplayer.h" #include #include #include 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 MiniMediaPlayer::getTrackNames() { QList playlistTitles; for(QPair item: playlist) { playlistTitles.append(item.first); } return playlistTitles; } QMap MiniMediaPlayer::currentState() { QMap 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> 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> MiniMediaPlayer::getStreamTitlesAndUrlsFromUrl(QUrl mediaUrl) { QList> 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 track; QJsonObject jsonTrackInfo = info.object(); track.first = jsonTrackInfo["title"].toString(); track.second = QUrl(jsonTrackInfo["webpage_url"].toString()); tracks.append(track); } } } else { qDebug() << youtubedl.readAllStandardError(); qWarning() << "Url may be invalid."; } } return tracks; }