diff options
| -rw-r--r-- | p2pvr/daemon/recorder.cpp | 4 | ||||
| -rw-r--r-- | p2pvr/daemon/storage.cpp | 247 | ||||
| -rw-r--r-- | p2pvr/daemon/storage.h | 21 | ||||
| -rw-r--r-- | p2pvr/daemon/unittests/Jamfile.jam | 19 | ||||
| -rw-r--r-- | p2pvr/daemon/unittests/testStorage.cpp | 83 | ||||
| -rw-r--r-- | p2pvr/ice/p2pvr.ice | 1 | 
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; | 
