mistserver/src/input/input_playlist.cpp
2021-12-24 15:09:36 +01:00

187 lines
6.8 KiB
C++

#include "input_playlist.h"
#include <algorithm>
#include <mist/procs.h>
#include <mist/stream.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace Mist{
inputPlaylist::inputPlaylist(Util::Config *cfg) : Input(cfg){
capa["name"] = "Playlist";
capa["desc"] = "Enables Playlist Input";
capa["source_match"] = "*.pls";
capa["always_match"] = "*.pls";
capa["variables_match"] = "*.pls";
capa["priority"] = 9;
capa["hardcoded"]["resume"] = 1;
playlistIndex = 0xFFFFFFFEull; // Not FFFFFFFF on purpose!
}
bool inputPlaylist::checkArguments(){
if (config->getString("input") == "-"){
std::cerr << "Input from stdin not supported" << std::endl;
return false;
}
if (!config->getString("streamname").size()){
if (config->getString("output") == "-"){
std::cerr << "Output to stdout not supported" << std::endl;
return false;
}
}else{
if (config->getString("output") != "-"){
std::cerr << "File output not supported" << std::endl;
return false;
}
}
return true;
}
void inputPlaylist::streamMainLoop(){
bool seenValidEntry = true;
Comms::Users killSwitch;
killSwitch.reload(streamName, (size_t)INVALID_TRACK_ID, (uint8_t)(COMM_STATUS_ACTIVE | COMM_STATUS_DONOTTRACK));
while (config->is_active){
if (killSwitch && killSwitch.getStatus() & COMM_STATUS_REQDISCONNECT){
Util::logExitReason("buffer requested shutdown");
config->is_active = false;
break;
}
struct tm *wTime;
time_t nowTime = time(0);
wTime = localtime(&nowTime);
wallTime = wTime->tm_hour * 60 + wTime->tm_min;
reloadPlaylist();
if (!playlist.size()){
Util::logExitReason("No entries in playlist");
return;
}
++playlistIndex;
if (playlistIndex >= playlist.size()){
if (!seenValidEntry){
HIGH_MSG("Parsed entire playlist without seeing a valid entry, waiting for any entry to "
"become available");
Util::sleep(1000);
}
playlistIndex = 0;
seenValidEntry = false;
}
if (minIndex != std::string::npos && playlistIndex < minIndex){
INFO_MSG("Clipping playlist index from %zu to %zu to stay within playback timing schedule",
playlistIndex, minIndex);
playlistIndex = minIndex;
}
if (maxIndex != std::string::npos && playlistIndex > maxIndex){
INFO_MSG("Clipping playlist index from %zu to %zu to stay within playback timing schedule",
playlistIndex, maxIndex);
playlistIndex = maxIndex;
}
currentSource = playlist.at(playlistIndex);
std::map<std::string, std::string> overrides;
overrides["realtime"] = "1";
overrides["alwaysStart"] = ""; // Just making this value "available" is enough
std::string srcPath = config->getString("input");
if ((currentSource.size() && currentSource[0] == '/') || srcPath.rfind('/') == std::string::npos){
srcPath = currentSource;
}else{
srcPath = srcPath.substr(0, srcPath.rfind("/") + 1) + currentSource;
}
char *workingDir = getcwd(NULL, 0);
if (srcPath[0] != '/'){srcPath = std::string(workingDir) + "/" + srcPath;}
free(workingDir);
Util::streamVariables(srcPath, streamName, "");
struct stat statRes;
if (stat(srcPath.c_str(), &statRes)){
FAIL_MSG("%s does not exist on the system, skipping it.", srcPath.c_str());
continue;
}
if ((statRes.st_mode & S_IFMT) != S_IFREG){
FAIL_MSG("%s is not a valid file, skipping it.", srcPath.c_str());
continue;
}
pid_t spawn_pid = 0;
// manually override stream url to start the correct input
if (!Util::startInput(streamName, srcPath, true, true, overrides, &spawn_pid)){
FAIL_MSG("Could not start input for source %s", srcPath.c_str());
continue;
}
seenValidEntry = true;
while (Util::Procs::isRunning(spawn_pid) && config->is_active){
if (killSwitch && killSwitch.getStatus() & COMM_STATUS_REQDISCONNECT){
Util::logExitReason("buffer requested shutdown");
config->is_active = false;
break;
}
Util::sleep(1000);
if (reloadOn != 0xFFFF){
time_t nowTime = time(0);
wTime = localtime(&nowTime);
wallTime = wTime->tm_hour * 60 + wTime->tm_min;
if (wallTime >= reloadOn){reloadPlaylist();}
if ((minIndex != std::string::npos && playlistIndex < minIndex) ||
(maxIndex != std::string::npos && playlistIndex > maxIndex)){
INFO_MSG("Killing current playback to stay within min/max playlist entry for current "
"time of day");
Util::Procs::Stop(spawn_pid);
}
}
}
if (!config->is_active && Util::Procs::isRunning(spawn_pid)){Util::Procs::Stop(spawn_pid);}
}
}
void inputPlaylist::reloadPlaylist(){
minIndex = std::string::npos;
maxIndex = std::string::npos;
std::string playlistFile;
char *origSource = getenv("MIST_ORIGINAL_SOURCE");
if (origSource){
playlistFile = origSource;
}else{
playlistFile = config->getString("input");
}
MEDIUM_MSG("Reloading playlist '%s'", playlistFile.c_str());
Util::streamVariables(playlistFile, streamName, playlistFile);
std::ifstream inFile(playlistFile.c_str());
if (!inFile.good()){
WARN_MSG("Unable to open playlist '%s', aborting reload!", playlistFile.c_str());
return;
}
std::string line;
uint16_t plsStartTime = 0xFFFF;
reloadOn = 0xFFFF;
playlist.clear();
playlist_startTime.clear();
while (inFile.good()){
std::getline(inFile, line);
if (inFile.good() && line.size() && line.at(0) != '#'){
playlist.push_back(line);
playlist_startTime.push_back(plsStartTime);
if (plsStartTime != 0xFFFF){
// If the newest entry has a time under the current time, we know we should never play earlier than this
if (plsStartTime <= wallTime){minIndex = playlist.size() - 1;}
// If the newest entry has a time above the current time, we know we should never play it
if (plsStartTime > wallTime && maxIndex == std::string::npos){
maxIndex = playlist.size() - 2;
reloadOn = plsStartTime;
}
HIGH_MSG("Start %s on %d (min: %zu, max: %zu)", line.c_str(), plsStartTime, minIndex, maxIndex);
}
plsStartTime = 0xFFFF;
}else{
if (line.size() > 13 && line.at(0) == '#' && line.substr(0, 13) == "#X-STARTTIME:"){
int hour, min;
if (sscanf(line.c_str() + 13, "%d:%d", &hour, &min) == 2){
plsStartTime = hour * 60 + min;
}
}
}
}
inFile.close();
}
}// namespace Mist