//=========================================== // Lumina-Desktop source code // Copyright (c) 2017, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "PianoBarProcess.h" #include #include #include #include #include #include PianoBarProcess::PianoBarProcess(QWidget *parent) : QObject(parent){ setupProcess(); saveTimer = new QTimer(this); saveTimer->setInterval(100); //1/10 second (just enough to change a few settings at once before dumping to disk) saveTimer->setSingleShot(true); makingStation = false; connect(saveTimer, SIGNAL(timeout()), this, SLOT(saveSettingsFile()) ); if( !loadSettings() ){ GenerateSettings(); } } PianoBarProcess::~PianoBarProcess(){ if(PROC->state()!=QProcess::NotRunning){ PROC->kill(); } } PianoBarProcess::State PianoBarProcess::currentState(){ return cState; } // ===== PUBLIC ====== //Interaction functions bool PianoBarProcess::isSetup(){ //email/password already saved for use or not return !(settingValue("user").isEmpty() || settingValue("password").isEmpty()); } void PianoBarProcess::setLogin(QString email, QString pass){ setSettingValue("user",email); setSettingValue("password",pass); } QString PianoBarProcess::email(){ return settingValue("user"); } QString PianoBarProcess::password(){ return settingValue("password"); } void PianoBarProcess::closePianoBar(){ //"q" sendToProcess("q"); } QString PianoBarProcess::currentStation(){ return cstation.simplified(); } QStringList PianoBarProcess::stations(){ return stationList; } void PianoBarProcess::setCurrentStation(QString station){ cstation = station; sendToProcess("s"); } void PianoBarProcess::answerQuestion(int selection){ QString sel; if(selection>=0){ sel = QString::number(selection); } if(makingStation && sel.isEmpty()){ makingStation = false; } //cancelled sendToProcess(sel, true); if(makingStation){ //Need to prompt to list all the available stations to switch over right away sendToProcess("s"); } } void PianoBarProcess::deleteCurrentStation(){ //"d" -> "y" if(cstation == "QuickMix" || cstation=="Thumbprint Radio"){ return; } //cannot delete these stations - provided by Pandora itself sendToProcess("d"); //delete current station sendToProcess("y",true); //yes, we want to delete it //Now need to automatically change to another station setCurrentStation("QuickMix"); //this is always a valid station } void PianoBarProcess::createNewStation(QString searchterm){ //"c" -> search term sendToProcess("c"); sendToProcess(searchterm,true); makingStation = true; } void PianoBarProcess::createStationFromCurrentSong(){ //"v" -> "s" sendToProcess("v"); sendToProcess("s",true); makingStation = true; //Need to prompt to list all the available stations to switch over right away sendToProcess("s"); } void PianoBarProcess::createStationFromCurrentArtist(){ //"v" -> "a" sendToProcess("v"); sendToProcess("a",true); makingStation = true; //Need to prompt to list all the available stations to switch over right away sendToProcess("s"); } //Settings Manipulation QString PianoBarProcess::audioQuality(){ // "audio_quality" = [low, medium, high] return settingValue("audio_quality"); } void PianoBarProcess::setAudioQuality(QString val){ // [low, medium, high] setSettingValue("audio_quality",val); } QString PianoBarProcess::autostartStation(){ //"autostart_station" = ID return settingValue("autostart_station"); } void PianoBarProcess::setAutostartStation(QString id){ setSettingValue("autostart_station", id); } QString PianoBarProcess::proxy(){ //"proxy" = URL (example: "http://USER:PASSWORD@HOST:PORT/" ) return settingValue("proxy"); } void PianoBarProcess::setProxy(QString url){ setSettingValue("proxy",url); } QString PianoBarProcess::controlProxy(){ //"control_proxy" = URL (example: "http://USER:PASSWORD@HOST:PORT/" ) return settingValue("control_proxy"); } void PianoBarProcess::setControlProxy(QString url){ setSettingValue("control_proxy", url); } QString PianoBarProcess::currentAudioDriver(){ QString driver = "auto"; QStringList info = LUtils::readFile(QDir::homePath()+"/.libao").filter("default_driver"); if(!info.isEmpty()){ driver = info.last().section("=",-1).simplified(); } return driver; } QStringList PianoBarProcess::availableAudioDrivers(){ QStringList known; known << "pulse" << "alsa" << "sndio" << "oss" << "sun" << "roar" << "esd" << "nas"; known.sort(); known.prepend("auto"); //make sure this is always first return known; } void PianoBarProcess::setAudioDriver(QString driver){ QStringList info = LUtils::readFile(QDir::homePath()+"/.libao"); bool found = false; QString line = "default_driver="+driver; if(driver=="auto"){ line.clear(); } //special bypass - nothing set for(int i=0; istate() == QProcess::NotRunning){ PROC->start(); }else{ sendToProcess("P"); cState = PianoBarProcess::Running; emit currentStateChanged(cState); } } void PianoBarProcess::pause(){ sendToProcess("S"); cState = PianoBarProcess::Paused; emit currentStateChanged(cState); } void PianoBarProcess::skipSong(){ sendToProcess("n"); } // ====== PRIVATE ====== void PianoBarProcess::GenerateSettings(){ currentSettings << "audio_quality = medium"; currentSettings << "autoselect = 1"; //automatically select the last item in a list (station selection only) currentSettings << "format_list_song = %r::::%t::::%a"; //[rating, title, artist] currentSettings << "format_nowplaying_song = %r::::%t::::%a::::%l::::%u::::%s"; // [rating, title, artist, album, details url, station (if not quickmix)] currentSettings << "format_nowplaying_station = %n::::%i"; //[name, id] saveSettingsFile(); //save this to disk *now* - needed before starting the pianobar process } bool PianoBarProcess::loadSettings(){ currentSettings.clear(); QFile file(settingsPath); if(!file.exists()){ return false; } if(file.open(QIODevice::ReadOnly)){ QTextStream in(&file); currentSettings = in.readAll().split("\n"); file.close(); return true; } return false; } QString PianoBarProcess::settingValue(QString var){ for(int i=0; isetProcessEnvironment(penv); //Now setup the rest of the process //PROC->setProcessChannelMode(QProcess::MergedChannels); QString bin = "pianobar"; LUtils::isValidBinary(bin); //will change "bin" to the full path PROC->setProgram(bin); connect(PROC, SIGNAL(readyReadStandardOutput()), this, SLOT(ProcUpdate()) ); connect(PROC, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(ProcStateChanged(QProcess::ProcessState)) ); cState = PianoBarProcess::Stopped; } void PianoBarProcess::sendToProcess(QString txt, bool withreturn){ if(PROC->state()==QProcess::Running){ if(withreturn){ PROC->write( QString(txt+"\r\n").toLocal8Bit() ); } else{ PROC->write( QString(txt).toLocal8Bit() ); } } } // ====== PRIVATE SLOTS ====== void PianoBarProcess::ProcUpdate(){ QString tmp = QString(PROC->readAllStandardOutput()).replace("\r","\n").remove("\u001B[2K"); QStringList info = tmp.split("\n",QString::SkipEmptyParts); //NOTE: Need to have a cache of info lines which can carry over between updates as needed (for questions, etc) //qDebug() << "Got Update:" << info; for(int i=0; i")){ //Now playing line (station, or song) QStringList data = info[i].section(">",1,-1).simplified().split("::::"); //Make sure to chop the line prefix off first if(data.length()==2){ //station cstation = data[0].simplified(); //save the name for later emit NowPlayingStation(data[0], data[1]); if(stationList.isEmpty()){ //Need to prompt to list all the available stations sendToProcess("s",true);//line return cancels the prompt //sendToProcess("",true); //empty line - cancels the prompt } //Automatically save this station for autostart next time (make toggle-able later) if(data[1]!=autostartStation()){ setAutostartStation(data[1]); } }else if(data.length()==6){ //song emit NowPlayingSong( data[0]=="<3", data[1], data[2], data[3], data[4], data[5] ); } //If a new song/station is detected, ensure that the state is set to "Running" if(cState!=PianoBarProcess::Running){ cState = PianoBarProcess::Running; emit currentStateChanged(cState); } }else if(info[i].startsWith("(i) ")){ //informational line emit NewInformation(info[i].section(" ",1,-1)); }else if(info[i].startsWith("[?] ")){ //waiting for reply to question //qDebug() << "Got Question:" << info[i] << infoList; if(info[i].contains("Select station:")){ //qDebug() << "Change to Station:" << cstation; //Clean up the station list a bit first (remove the quickmix-status) for(int j=0; j