summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--p2pvr/daemon/recorder.cpp4
-rw-r--r--p2pvr/daemon/storage.cpp247
-rw-r--r--p2pvr/daemon/storage.h21
-rw-r--r--p2pvr/daemon/unittests/Jamfile.jam19
-rw-r--r--p2pvr/daemon/unittests/testStorage.cpp83
-rw-r--r--p2pvr/ice/p2pvr.ice1
6 files changed, 118 insertions, 257 deletions
diff --git a/p2pvr/daemon/recorder.cpp b/p2pvr/daemon/recorder.cpp
index 35aeba8..2580e84 100644
--- a/p2pvr/daemon/recorder.cpp
+++ b/p2pvr/daemon/recorder.cpp
@@ -8,6 +8,8 @@
#include "serviceStreamer.h"
#include "storage.h"
#include "muxer.h"
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid_io.hpp>
std::string Recorder::extension;
std::string Recorder::muxerCommand;
@@ -62,7 +64,7 @@ Recorder::StartRecording(P2PVR::SchedulePtr schedule, DVBSI::ServicePtr service,
auto storage = P2PVR::StoragePrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("Storage")));
auto recordings = P2PVR::RecordingsPrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("Recordings")));
- auto id = storage->CreateForEventRecording(extension, schedule, service, event);
+ auto id = boost::lexical_cast<std::string>(boost::uuids::random_generator()());
auto store = storage->OpenForWrite(id);
ScopeObject _store(NULL, NULL, [this,&store,&storage,&id]() { storage->Close(store); storage->Delete(id); });
auto target = store;
diff --git a/p2pvr/daemon/storage.cpp b/p2pvr/daemon/storage.cpp
index 2465afe..d43bddc 100644
--- a/p2pvr/daemon/storage.cpp
+++ b/p2pvr/daemon/storage.cpp
@@ -2,234 +2,30 @@
#include "storage.h"
#include "fileSink.h"
#include "muxedFileSink.h"
-#include <commonHelpers.h>
#include <fcntl.h>
#include <logger.h>
#include <boost/filesystem/operations.hpp>
-#include <boost/uuid/uuid_generators.hpp>
-#include <boost/uuid/uuid_io.hpp>
-#include <boost/lexical_cast.hpp>
namespace fs = boost::filesystem;
std::string Storage::muxerCommand;
fs::path Storage::root;
-fs::path Storage::byAll;
-fs::path Storage::byTitle;
-fs::path Storage::byDate;
-fs::path Storage::byService;
-fs::path Storage::bySchedule;
DECLARE_OPTIONS(Storage, "P2PVR Storage options")
("p2pvr.storage.muxercommand", Options::value(&muxerCommand, "/usr/bin/ffmpeg -i - -f mp4 -y ${TARGET}"),
"Command to perform TS->PS muxing (default: '/usr/bin/ffmpeg -i - -f mp4 -y ${TARGET}')")
("p2pvr.storage.root", Options::value(&root, "recordings"),
"Root folder in which to store recordings")
-("p2pvr.storage.all", Options::value(&byAll, "all"),
- "Sub folder in which to store all recordings")
-("p2pvr.storage.bytitle", Options::value(&byTitle, "title"),
- "Sub folder in which to store by title")
-("p2pvr.storage.bydate", Options::value(&byDate, "date"),
- "Sub folder in which to store by date")
-("p2pvr.storage.byservice", Options::value(&byService, "service"),
- "Sub folder in which to store by service")
-("p2pvr.storage.byschedule", Options::value(&bySchedule, "schedule"),
- "Sub folder in which to store by schedule")
END_OPTIONS(Storage);
-inline static
-fs::path createPath(const fs::path & start)
-{
- return start;
-}
-
-template <typename Arg>
-inline static
-fs::path
-operator/(const fs::path & lhs, const IceUtil::Optional<Arg> & rhs)
-{
- if (rhs) {
- return lhs / *rhs;
- }
- return lhs;
-}
-
-template <typename Arg, typename ... Args>
-inline static
-fs::path createPath(const fs::path & start, const Arg & arg, const Args & ... args)
-{
- auto path = start / arg;
- return createPath(path, args...);
-}
-
-template <typename Arg, typename ... Args>
-inline static
-std::string firstNotNull(const Arg & arg, const Args & ...)
-{
- return boost::lexical_cast<std::string>(arg);
-}
-
-template <typename Arg, typename ... Args>
-inline static
-std::string firstNotNull(const IceUtil::Optional<Arg> & arg, const Args & ... args)
-{
- return arg ? firstNotNull(*arg, args...) : firstNotNull(args...);
-}
-
-template <typename ... Args>
-inline static
-std::string firstNotNull(const std::string & arg, const Args & ...)
-{
- return arg;
-}
-
-inline static
-std::string
-formatIf(const boost::format & f)
-{
- return f.str();
-}
-
-template <typename Arg, typename ... Args>
-inline static
-IceUtil::Optional<std::string>
-formatIf(boost::format & f, const IceUtil::Optional<Arg> & arg, const Args & ... args)
-{
- if (arg) {
- f % *arg;
- return formatIf(f, args...);
- }
- else {
- return IceUtil::Optional<std::string>();
- }
-}
-
-template <typename Arg, typename ... Args>
-inline static
-IceUtil::Optional<std::string>
-formatIf(boost::format & f, const Arg & arg, const Args & ... args)
-{
- f % arg;
- return formatIf(f, args...);
-}
-
-template <typename... Args>
-inline static
-IceUtil::Optional<std::string>
-formatIf(const std::string & msgfmt, const Args & ... args)
-{
- boost::format fmt(msgfmt);
- return formatIf(fmt, args...);
-}
-
-std::string
-Storage::CreateForEventRecording(const std::string & ext, const P2PVR::SchedulePtr & schedule, const DVBSI::ServicePtr & service, const DVBSI::EventPtr & event, const Ice::Current &)
-{
- fs::create_directories(root / byAll);
- auto id = boost::lexical_cast<std::string>(boost::uuids::random_generator()());
- fs::path path = root / byAll / id;
- path.replace_extension(ext);
- auto fd = open(path.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, 0664);
- if (fd < 0) {
- Logger()->messagebf(LOG_ERR, "%s: Failed to open file for writing at %s (%d:%s)", __PRETTY_FUNCTION__,
- path, errno, strerror(errno));
- throw P2PVR::StorageException(path.string(), strerror(errno));
- }
- createSymlinks(ext, id, schedule, service, event);
- Logger()->messagebf(LOG_INFO, "%s: Created new recording %s", __PRETTY_FUNCTION__, path);
-
- close(fd);
- return id;
-}
-
-void
-Storage::createSymlinks(const std::string & ext, const std::string & target, const P2PVR::SchedulePtr & schedule, const DVBSI::ServicePtr & service, const DVBSI::EventPtr & event)
-{
- // By title, with optional season and episode information
- createSymlink(ext, target, createPath(root, byTitle,
- event->Title,
- formatIf("Season %02d", event->Season),
- firstNotNull(
- formatIf("Part %02d of %02d - %s", event->Episode, event->Episodes, event->Subtitle),
- formatIf("Episode %02d - %s", event->Episode, event->Subtitle),
- formatIf("Part %02d of %02d - %s", event->Episode, event->Episodes, event->Description),
- formatIf("Part %02d of %02d", event->Episode, event->Episodes),
- formatIf("Episode %02d - %s", event->Episode, event->Description),
- event->Subtitle,
- event->Description,
- formatIf("Episode %02d", event->Episode),
- event->StartTime)));
- // By date
- createSymlink(ext, target, createPath(root, byDate,
- formatIf("%04d-%02d-%02d", event->StartTime.Year, event->StartTime.Month, event->StartTime.Day),
- firstNotNull(
- formatIf("%s - %s", event->Title, event->Subtitle),
- formatIf("%s - %s", event->Title, event->Description),
- event->Title)));
- // By service
- createSymlink(ext, target, createPath(root, byService,
- service->Name,
- firstNotNull(
- formatIf("%s - %s", event->Title, event->Subtitle),
- formatIf("%s - %s", event->Title, event->Description),
- event->Title)));
- // By schedule title
- createSymlink(ext, target, createPath(root, bySchedule,
- formatIf("Title: %s", schedule->Title),
- formatIf("%04d-%02d-%02d", event->StartTime.Year, event->StartTime.Month, event->StartTime.Day),
- firstNotNull(
- formatIf("%s - %s", event->Title, event->Subtitle),
- formatIf("%s - %s", event->Title, event->Description),
- event->Title)));
- // By schedule search
- createSymlink(ext, target, createPath(root, bySchedule,
- formatIf("Search: %s", schedule->Search),
- firstNotNull(
- formatIf("%s - %s", event->Title, event->Subtitle),
- formatIf("%s - %s", event->Title, event->Description),
- event->Title)));
-}
-void
-Storage::createSymlink(const std::string & ext, const std::string & target, const fs::path & link)
-{
- fs::path path = link;
- path.replace_extension(ext);
- Logger()->messagebf(LOG_DEBUG, "%s: link(%s) -> target(%s)", __PRETTY_FUNCTION__, path, target);
- if (fs::exists(path)) {
- Logger()->messagebf(LOG_WARNING, "%s: symlink already exists %s", __PRETTY_FUNCTION__, path);
- return;
- }
- fs::create_directories(path.parent_path());
- fs::path relativeTarget;
- for (fs::path tmp = path.parent_path(); tmp != root; tmp = tmp.parent_path()) {
- relativeTarget /= "..";
- }
- relativeTarget /= byAll;
- relativeTarget /= target;
- relativeTarget.replace_extension(ext);
- fs::create_symlink(relativeTarget, path);
-}
-
-std::string
-Storage::FindExtension(const std::string & id)
-{
- fs::directory_iterator end;
- for (fs::directory_iterator itr(root / byAll); itr != end; ++itr) {
- if (itr->path().stem() == id) {
- return itr->path().extension().string();
- }
- }
- return "";
-}
-
P2PVR::RawDataClientPrx
Storage::OpenForWrite(const std::string & id, const Ice::Current & ice)
{
- fs::path path = root / byAll / id;
- path.replace_extension(FindExtension(id));
+ fs::create_directories(root);
+ fs::path path = root / id;
P2PVR::RawDataClient * target;
if (muxerCommand.empty()) {
- auto fd = open(path.string().c_str(), O_WRONLY | O_APPEND | O_LARGEFILE);
+ auto fd = open(path.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, 0664);
if (fd < 0) {
Logger()->messagebf(LOG_ERR, "%s: Failed to open file for reading at %s (%d:%s)", __PRETTY_FUNCTION__,
path, errno, strerror(errno));
@@ -254,41 +50,15 @@ Storage::Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &)
void
Storage::Delete(const std::string & id, const Ice::Current &)
{
- fs::path path = root / byAll / id;
- path.replace_extension(FindExtension(id));
- Logger()->messagebf(LOG_INFO, "%s: Deleting links to %s", __PRETTY_FUNCTION__, path);
- DeleteFrom(path, fs::canonical(root));
-}
-
-void
-Storage::DeleteFrom(const fs::path & path, const fs::path & from)
-{
- Logger()->messagebf(LOG_DEBUG, "%s: Deleting links to %s to %s", __PRETTY_FUNCTION__, path, from);
- fs::directory_iterator end;
- for (fs::directory_iterator itr(from); itr != end; ++itr) {
- if (fs::is_directory(*itr)) {
- DeleteFrom(path, *itr);
- }
- else {
- boost::system::error_code err;
- auto link = fs::canonical(*itr, err);
- if (err || link == path) {
- Logger()->messagebf(LOG_DEBUG, "%s: deleting %s", __PRETTY_FUNCTION__, *itr);
- fs::remove(*itr);
- }
- }
- }
- if (from != root && fs::is_empty(from)) {
- Logger()->messagebf(LOG_DEBUG, "%s: deleting directory %s", __PRETTY_FUNCTION__, from);
- fs::remove(from);
- }
+ fs::path path = root / id;
+ Logger()->messagebf(LOG_INFO, "%s: Deleting %s", __PRETTY_FUNCTION__, path);
+ fs::remove(path);
}
void
Storage::Send(const std::string & id, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &)
{
- fs::path path = root / byAll / id;
- path.replace_extension(FindExtension(id));
+ fs::path path = root / id;
auto fd = open(path.string().c_str(), O_RDONLY | O_LARGEFILE);
if (fd < 0) {
Logger()->messagebf(LOG_ERR, "%s: Failed to open file for reading at %s (%d:%s)", __PRETTY_FUNCTION__,
@@ -318,9 +88,8 @@ Storage::Send(const std::string & id, const P2PVR::RawDataClientPrx & target, Ic
Ice::Long
Storage::FileSize(const std::string & id, const Ice::Current &)
{
- fs::path path = root / byAll / id;
+ fs::path path = root / id;
try {
- path.replace_extension(FindExtension(id));
return fs::file_size(path);
}
catch (...) {
diff --git a/p2pvr/daemon/storage.h b/p2pvr/daemon/storage.h
index 89a2836..0655bd2 100644
--- a/p2pvr/daemon/storage.h
+++ b/p2pvr/daemon/storage.h
@@ -9,21 +9,15 @@
class Storage : public P2PVR::Storage {
public:
- std::string CreateForEventRecording(const std::string & ext, const P2PVR::SchedulePtr &, const DVBSI::ServicePtr &, const DVBSI::EventPtr &, const Ice::Current &);
- P2PVR::RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &);
- void Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &);
- void Delete(const std::string &, const Ice::Current &);
- Ice::Long FileSize(const std::string &, const Ice::Current &);
- void Send(const std::string &, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &);
+ P2PVR::RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &) override;
+ void Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &) override;
+ void Delete(const std::string &, const Ice::Current &) override;
+ Ice::Long FileSize(const std::string &, const Ice::Current &) override;
+ void Send(const std::string &, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) override;
INITOPTIONS;
protected:
- static void createSymlinks(const std::string & ext, const std::string &, const P2PVR::SchedulePtr &, const DVBSI::ServicePtr &, const DVBSI::EventPtr &);
- static void createSymlink(const std::string & ext, const std::string & target, const boost::filesystem::path & link);
- static void DeleteFrom(const boost::filesystem::path &, const boost::filesystem::path &);
- static std::string FindExtension(const std::string & id);
-
typedef TemporarayIceAdapterObject<P2PVR::RawDataClient> OpenFile;
typedef boost::shared_ptr<OpenFile> OpenFilePtr;
typedef std::set<OpenFilePtr> OpenFiles;
@@ -31,11 +25,6 @@ class Storage : public P2PVR::Storage {
static std::string muxerCommand;
static boost::filesystem::path root;
- static boost::filesystem::path byAll;
- static boost::filesystem::path byTitle;
- static boost::filesystem::path byDate;
- static boost::filesystem::path byService;
- static boost::filesystem::path bySchedule;
};
#endif
diff --git a/p2pvr/daemon/unittests/Jamfile.jam b/p2pvr/daemon/unittests/Jamfile.jam
index e41b164..f07338f 100644
--- a/p2pvr/daemon/unittests/Jamfile.jam
+++ b/p2pvr/daemon/unittests/Jamfile.jam
@@ -102,3 +102,22 @@ unit-test testSched :
<define>ROOT=\"$(me)\"
;
+unit-test testStorage :
+ testStorage.cpp
+ :
+ <define>BOOST_TEST_DYN_LINK
+ <library>../..//p2common
+ <library>../..//p2basics
+ <library>../..//p2lib
+ <library>../..//p2ice
+ <library>../..//p2xml
+ <library>../..//p2ut
+ <library>..//p2pvrdaemon
+ <library>IceUtil
+ <library>Ice
+ <library>boost_system
+ <library>boost_filesystem
+ <library>../..//boost_utf
+ <define>ROOT=\"$(me)\"
+ ;
+
diff --git a/p2pvr/daemon/unittests/testStorage.cpp b/p2pvr/daemon/unittests/testStorage.cpp
new file mode 100644
index 0000000..5480566
--- /dev/null
+++ b/p2pvr/daemon/unittests/testStorage.cpp
@@ -0,0 +1,83 @@
+#define BOOST_TEST_MODULE Storage
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <testOptionsSource.h>
+#include <Ice/ObjectAdapter.h>
+#include <Ice/Service.h>
+#include <maintenance.h>
+#include <mockTuner.h>
+#include <si.h>
+#include <storage.h>
+#include <boost/lexical_cast.hpp>
+#include <commonHelpers.h>
+
+const boost::filesystem::path rootDir = "/tmp/ut/p2pvr/recordings";
+class Core {
+ public:
+ Core()
+ {
+ int paramCount = 0;
+ ic = Ice::initialize(paramCount, NULL);
+ auto adapter = ic->createObjectAdapterWithEndpoints("Adp", "tcp -p 12004");
+ TestOptionsSource::LoadTestOptions({
+ { "p2pvr.storage.muxercommand", std::string() },
+ { "p2pvr.storage.root", rootDir.string() },
+ });
+ adapter->add(new Storage(), ic->stringToIdentity("Storage"));
+ adapter->activate();
+
+ st = P2PVR::StoragePrx::checkedCast(ic->stringToProxy("Storage"));
+ BOOST_REQUIRE(st);
+ st->ice_ping();
+ }
+
+ ~Core()
+ {
+ ic->destroy();
+ }
+
+ P2PVR::StoragePrx st;
+
+ private:
+ Ice::CommunicatorPtr ic;
+};
+
+BOOST_FIXTURE_TEST_SUITE( StCore, Core )
+
+BOOST_AUTO_TEST_CASE( st_openWriteClose )
+{
+ boost::filesystem::remove_all(rootDir);
+ std::string id = "made-up-storage-id";
+
+ auto rdc = st->OpenForWrite(id);
+
+ P2PVR::Data data;
+ data.resize(1024);
+
+ rdc->NewData(data);
+
+ st->Close(rdc);
+
+ auto stSize = st->FileSize(id);
+ BOOST_REQUIRE_EQUAL(1024, stSize);
+ BOOST_REQUIRE_EQUAL(1024, boost::filesystem::file_size(rootDir / id));
+
+ st->Delete(id);
+ BOOST_REQUIRE(!boost::filesystem::exists(rootDir / id));
+}
+
+BOOST_AUTO_TEST_CASE( st_notuniqueid )
+{
+ boost::filesystem::remove_all(rootDir);
+ std::string id = "made-up-storage-id";
+
+ auto rdc = st->OpenForWrite(id);
+ st->Close(rdc);
+
+ BOOST_REQUIRE_THROW(st->OpenForWrite(id), P2PVR::StorageException);
+ st->Delete(id);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice
index 7f58539..0d287d5 100644
--- a/p2pvr/ice/p2pvr.ice
+++ b/p2pvr/ice/p2pvr.ice
@@ -64,7 +64,6 @@ module P2PVR {
};
interface Storage {
- idempotent string CreateForEventRecording(string ext, Schedule sc, DVBSI::Service se, DVBSI::Event ev) throws StorageException;
idempotent RawDataClient * OpenForWrite(string guid) throws StorageException;
idempotent void Close(RawDataClient * file) throws StorageException;
idempotent void Delete(string guid) throws StorageException;