JustMotion/src/common.cpp
Maurice ONeal b0dbfa0852 v2.0.t2
Logs are not rotating correctly. Changed up the code to append the logs
in memory and then dump them to permanent storage on every loop of
upkeep(). Hopefully this fixes the issue.
2023-03-10 19:05:54 -05:00

397 lines
9.1 KiB
C++

// This file is part of Motion Watch.
// Motion Watch 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.
// Motion Watch 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.
#include "common.h"
string cleanDir(const string &path)
{
if (path[path.size() - 1] == '/')
{
return path.substr(0, path.size() - 1);
}
else
{
return path;
}
}
bool createDir(const string &dir)
{
auto ret = mkdir(dir.c_str(), 0777);
if (ret == -1)
{
return errno == EEXIST;
}
else
{
return true;
}
}
bool createDirTree(const string &full_path)
{
size_t pos = 0;
auto ret = true;
while (ret == true && pos != string::npos)
{
pos = full_path.find('/', pos + 1);
ret = createDir(full_path.substr(0, pos));
}
return ret;
}
void cleanupEmptyDirs(const string &path)
{
if (exists(path))
{
for (auto &entry : directory_iterator(path))
{
if (entry.is_directory())
{
try
{
remove(entry.path());
}
catch (filesystem_error const &ex)
{
// non-empty dir assumed when filesystem_error is raised.
cleanupEmptyDirs(path + "/" + entry.path().filename().string());
}
}
}
}
}
vector<string> lsFilesInDir(const string &path, const string &ext)
{
vector<string> names;
if (exists(path))
{
for (auto &entry : directory_iterator(path))
{
if (entry.is_regular_file())
{
auto name = entry.path().filename().string();
if (ext.empty() || name.ends_with(ext))
{
names.push_back(name);
}
}
}
}
sort(names.begin(), names.end());
return names;
}
vector<string> lsDirsInDir(const string &path)
{
vector<string> names;
if (exists(path))
{
for (auto &entry : directory_iterator(path))
{
if (entry.is_directory())
{
names.push_back(entry.path().filename().string());
}
}
}
sort(names.begin(), names.end());
return names;
}
void cleanupStream(const string &plsPath)
{
ifstream fileIn(plsPath);
for (string line; getline(fileIn, line); )
{
if (line.starts_with("VIDEO_TS/"))
{
remove(line);
}
}
}
void enforceMaxEvents(shared_t *share)
{
auto names = lsFilesInDir(".", ".m3u8");
while (names.size() > share->maxEvents)
{
if (names[0] != "stream.m3u8")
{
// removes the playlist file extension.
auto nameOnly = names[0].substr(0, names[0].size() - 5);
auto imgFile = nameOnly + ".jpg";
auto webFile = nameOnly + ".html";
cleanupStream(names[0]);
remove(names[0]);
if (exists(imgFile)) remove(imgFile);
if (exists(webFile)) remove(webFile);
}
names.erase(names.begin());
}
}
string genTimeStr(const char *fmt)
{
time_t rawtime;
time(&rawtime);
auto timeinfo = localtime(&rawtime);
char ret[50];
strftime(ret, 50, fmt, timeinfo);
return string(ret);
}
string genDstFile(const string &dirOut, const char *fmt, const string &ext)
{
createDirTree(cleanDir(dirOut));
return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext;
}
void rdLine(const string &param, const string &line, string *value)
{
if (line.rfind(param.c_str(), 0) == 0)
{
*value = line.substr(param.size());
}
}
void rdLine(const string &param, const string &line, int *value)
{
if (line.rfind(param.c_str(), 0) == 0)
{
*value = strtol(line.substr(param.size()).c_str(), NULL, 10);
}
}
bool rdConf(const string &filePath, shared_t *share)
{
ifstream varFile(filePath.c_str());
if (!varFile.is_open())
{
share->retCode = ENOENT;
cerr << "err: config file: " << filePath << " does not exists or lack read permissions." << endl;
}
else
{
string line;
do
{
getline(varFile, line);
if (line.rfind("#", 0) != 0)
{
rdLine("cam_name = ", line, &share->camName);
rdLine("recording_stream = ", line, &share->recordUrl);
rdLine("web_root = ", line, &share->webRoot);
rdLine("web_text = ", line, &share->webTxt);
rdLine("web_bg = ", line, &share->webBg);
rdLine("web_font = ", line, &share->webFont);
rdLine("sch_sec = ", line, &share->schSec);
rdLine("post_cmd = ", line, &share->postCmd);
rdLine("pix_thresh = ", line, &share->pixThresh);
rdLine("img_thresh = ", line, &share->imgThresh);
rdLine("max_days = ", line, &share->maxDays);
rdLine("max_events = ", line, &share->maxEvents);
rdLine("max_log_size = ", line, &share->maxLogSize);
}
} while(!line.empty());
}
return share->retCode == 0;
}
bool rdConf(shared_t *share)
{
share->recordUrl.clear();
share->postCmd.clear();
share->camName.clear();
share->retCode = 0;
share->pixThresh = 50;
share->imgThresh = 800;
share->maxDays = 15;
share->maxEvents = 20;
share->maxLogSize = 50000;
share->skipCmd = false;
share->schSec = 60;
share->webRoot = "/var/www/html";
share->webBg = "#485564";
share->webTxt = "#dee5ee";
share->webFont = "courier";
if (rdConf(share->conf, share))
{
if (share->camName.empty())
{
share->camName = path(share->conf).filename();
}
share->outDir = cleanDir(share->webRoot) + "/" + share->camName;
error_code ec;
createDirTree(share->outDir);
current_path(share->outDir, ec);
share->retCode = ec.value();
if (share->retCode != 0)
{
cerr << "err: " << ec.message() << endl;
}
}
return share->retCode == 0;
}
string parseForParam(const string &arg, int argc, char** argv, bool argOnly, int &offs)
{
auto ret = string();
for (; offs < argc; ++offs)
{
auto argInParams = string(argv[offs]);
if (arg.compare(argInParams) == 0)
{
if (!argOnly)
{
offs++;
// check ahead, make sure offs + 1 won't cause out-of-range exception
if (offs <= (argc - 1))
{
ret = string(argv[offs]);
}
}
else
{
ret = string("true");
}
}
}
return ret;
}
string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
{
auto notUsed = 0;
return parseForParam(arg, argc, argv, argOnly, notUsed);
}
string genEventPath(const string &tsPath)
{
if (tsPath.size() > 14)
{
// removes 'VIDEO_TS/live/' from the front of the string.
auto ret = tsPath.substr(14);
return "VIDEO_TS/events/" + ret;
}
else
{
return string();
}
}
string genVidNameFromLive(const string &tsPath)
{
if (tsPath.size() > 17)
{
// removes 'VIDEO_TS/live/' from the front of the string.
auto ret = tsPath.substr(14);
auto ind = tsPath.find('/');
// removes '.ts' from the end of the string.
ret = ret.substr(0, ret.size() - 3);
while (ind != string::npos)
{
// remove all '/'
ret.erase(ind, 1);
ind = ret.find('/');
}
return ret;
}
else
{
return string();
}
}
uint64_t genEpoch()
{
return duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
}
void wrOutm3u8(const pls_t &pls)
{
createDirTree(path(pls.dstPath).parent_path().string());
copy_file(pls.clipPath, pls.dstPath);
auto plsFile = pls.fileName + ".m3u8";
if (exists(plsFile))
{
ofstream file(plsFile.c_str(), ofstream::app);
file << endl;
file << pls.extINF << endl;
file << pls.dstPath;
file.close();
}
else
{
ofstream file(plsFile.c_str());
file << "#EXTM3U" << endl;
file << "#EXT-X-VERSION:3" << endl;
file << "#EXT-X-TARGETDURATION:10" << endl;
file << "#EXT-X-MEDIA-SEQUENCE:0" << endl;
file << "#EXT-X-DISCONTINUITY" << endl;
file << pls.extINF << endl;
file << pls.dstPath;
file.close();
}
}