From 5a3b24d0f16b9d4c80a94795341a7d393314a8e7 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 1 Aug 2017 21:16:51 +0100 Subject: Store videos in the database --- p2pvr/daemon/daemon.cpp | 2 +- p2pvr/daemon/recorder.cpp | 32 +------- p2pvr/daemon/recorder.h | 13 ---- p2pvr/daemon/recordings.cpp | 2 +- p2pvr/daemon/sql/recordings/getAll.sql | 2 +- p2pvr/daemon/sql/storage/getSize.sql | 3 + p2pvr/daemon/sql/storage/writeBlock.sql | 2 + p2pvr/daemon/storage.cpp | 115 +++++++++++----------------- p2pvr/daemon/storage.h | 19 ++--- p2pvr/daemon/unittests/datasources/data.sql | 2 +- p2pvr/daemon/unittests/testMaint.cpp | 2 +- p2pvr/daemon/unittests/testRecordings.cpp | 5 +- p2pvr/daemon/unittests/testStorage.cpp | 45 +++++------ p2pvr/datasources/schema.sql | 67 ++++++++++++++-- p2pvr/ice/p2pvr.ice | 15 +--- 15 files changed, 151 insertions(+), 175 deletions(-) create mode 100644 p2pvr/daemon/sql/storage/getSize.sql create mode 100644 p2pvr/daemon/sql/storage/writeBlock.sql diff --git a/p2pvr/daemon/daemon.cpp b/p2pvr/daemon/daemon.cpp index 109086a..c19f5cf 100644 --- a/p2pvr/daemon/daemon.cpp +++ b/p2pvr/daemon/daemon.cpp @@ -42,7 +42,7 @@ class P2PvrDaemon : public IceTray::Service { auto maintenance = add(ic, adapter, new MaintenanceI(db, adapter, timer), "Maintenance"); auto si = add(ic, adapter, new SII(db), "SI"); auto schedules = add(ic, adapter, new SchedulesI(db), "Schedules"); - auto storage = add(ic, adapter, new StorageI(), "Storage"); + auto storage = add(ic, adapter, new StorageI(db), "Storage"); auto recorder = add(ic, adapter, new RecorderI(adapter, timer), "Recorder"); auto recordings = add(ic, adapter, new RecordingsI(db), "Recordings"); auto tmdb = add(ic, adapter, new TMDb::Proxy(tmdbOpts->baseUrl, tmdbOpts->apikey), "TMDb"); diff --git a/p2pvr/daemon/recorder.cpp b/p2pvr/daemon/recorder.cpp index 28e89ee..c797d04 100644 --- a/p2pvr/daemon/recorder.cpp +++ b/p2pvr/daemon/recorder.cpp @@ -7,25 +7,11 @@ #include "serviceStreamer.h" #include "storage.h" #include "muxer.h" -#include -#include #include namespace po = boost::program_options; namespace P2PVR { -RecorderI::Options::Options() : - IceTray::Options("P2PVR Recorder options") -{ -} - -ICETRAY_OPTIONS(RecorderI::Options, - ("p2pvr.recorder.extension", po::value(&extension)->default_value("mp4"), - "File extension to save with (default: mp4)") - ("p2pvr.recorder.muxercommand", po::value(&muxerCommand)->default_value("/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -"), - "Command to perform TS->PS muxing (default: '/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -')") -); - IceTray::Logging::LoggerPtr RecorderI::logger(LOGMANAGER()->getLogger()); RecorderI::RecorderI(Ice::ObjectAdapterPtr a, IceUtil::TimerPtr t) : @@ -73,16 +59,8 @@ RecorderI::StartRecording(SchedulePtr schedule, ::DVBSI::ServicePtr service, Eve auto devices = DevicesPrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("GlobalDevices"))); auto si = SIPrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("SI"))); - auto id = boost::lexical_cast(boost::uuids::random_generator()()); - auto store = storage->OpenForWrite(id); - AdHoc::ScopeExit _store(NULL, NULL, [this,&store,&storage,&id]() { storage->Close(store); storage->Delete(id); }); - auto target = store; - RawDataClientPrx muxer; - if (!options->muxerCommand.empty()) { - muxer = RawDataClientPrx::checkedCast(adapter->addWithUUID(new Muxer(store, options->muxerCommand))); - target = muxer; - } - AdHoc::ScopeExit _muxer(NULL, NULL, [this,&muxer]() { if (muxer) adapter->remove(muxer->ice_getIdentity()); }); + auto recordingId = recordings->NewRecording(new Recording(0, schedule->ScheduleId, event->EventUid)); + auto target = storage->OpenForWrite(recordingId); auto ss = ServiceStreamerPtr(new ServiceStreamer(service->ServiceId, target, devices, si, adapter)); ss->Start(); @@ -90,8 +68,7 @@ RecorderI::StartRecording(SchedulePtr schedule, ::DVBSI::ServicePtr service, Eve event->Title, event->StartTime, event->StopTime, service->Name ? *service->Name : "", service->ServiceId); - recordings->NewRecording(new Recording(0, storage->ice_toString(), id, schedule->ScheduleId, event->EventUid)); - auto newCurrent = CurrentPtr(new Current({muxer, store, ss, schedule, service, event, IceUtil::TimerTaskPtr()})); + auto newCurrent = CurrentPtr(new Current({target, ss, schedule, service, event, IceUtil::TimerTaskPtr()})); currentRecordings.insert(newCurrent); auto stopIn = (*event->StopTime + *schedule->Late - boost::posix_time::second_clock::universal_time()).total_seconds(); @@ -106,9 +83,6 @@ RecorderI::StopRecording(CurrentPtr c) std::lock_guard g(lock); logger->messagebf(LOG::DEBUG, "Stopping %s", c->event->Title); c->stream->Stop(); - if (c->muxer) { - adapter->remove(c->muxer->ice_getIdentity()); - } currentRecordings.erase(c); auto storage = StoragePrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("Storage"))); storage->Close(c->store); diff --git a/p2pvr/daemon/recorder.h b/p2pvr/daemon/recorder.h index 6effb07..215732f 100644 --- a/p2pvr/daemon/recorder.h +++ b/p2pvr/daemon/recorder.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include "serviceStreamer.h" @@ -13,21 +12,10 @@ namespace P2PVR { class DLL_PUBLIC RecorderI : public Recorder { public: - class Options : public IceTray::Options { - public: - Options(); - - ICETRAY_OPTIONS_DECLARE; - - std::string extension; - std::string muxerCommand; - }; - typedef std::vector Pendings; class Current { public: - RawDataClientPrx muxer; RawDataClientPrx store; ServiceStreamerPtr stream; SchedulePtr schedule; @@ -53,7 +41,6 @@ class DLL_PUBLIC RecorderI : public Recorder { Pendings pendingRecordings; std::mutex lock; - IceTray::OptionsResolver options; static IceTray::Logging::LoggerPtr logger; }; } diff --git a/p2pvr/daemon/recordings.cpp b/p2pvr/daemon/recordings.cpp index 6efa78c..0ab2e46 100644 --- a/p2pvr/daemon/recordings.cpp +++ b/p2pvr/daemon/recordings.cpp @@ -19,7 +19,7 @@ Ice::Int RecordingsI::NewRecording(const RecordingPtr & r, const Ice::Current &) { auto dbc = db->get(); - logger->messagebf(LOG::INFO, "%s: Creating new recording %s at %s", __PRETTY_FUNCTION__, r->Guid, r->StorageAddress); + logger->messagebf(LOG::INFO, "%s: Creating new recording for %d (EventUid %d)", __PRETTY_FUNCTION__, r->RecordingId, r->EventUid); DB::TransactionScope tx(dbc.get()); Slicer::SerializeAny(r, dbc.get(), "recordings"); logger->messagebf(LOG::INFO, "%s: Created recording Id: %d", __PRETTY_FUNCTION__, r->RecordingId); diff --git a/p2pvr/daemon/sql/recordings/getAll.sql b/p2pvr/daemon/sql/recordings/getAll.sql index 3c3ff51..ffc9613 100644 --- a/p2pvr/daemon/sql/recordings/getAll.sql +++ b/p2pvr/daemon/sql/recordings/getAll.sql @@ -1,3 +1,3 @@ -SELECT recordingId, storageAddress, guid, scheduleId, eventUid +SELECT recordingId, scheduleId, eventUid FROM recordings r ORDER BY recordingId diff --git a/p2pvr/daemon/sql/storage/getSize.sql b/p2pvr/daemon/sql/storage/getSize.sql new file mode 100644 index 0000000..1427d02 --- /dev/null +++ b/p2pvr/daemon/sql/storage/getSize.sql @@ -0,0 +1,3 @@ +SELECT SUM(LENGTH(videodata)) +FROM recordedvideo +WHERE recordingid = ? diff --git a/p2pvr/daemon/sql/storage/writeBlock.sql b/p2pvr/daemon/sql/storage/writeBlock.sql new file mode 100644 index 0000000..1b787e7 --- /dev/null +++ b/p2pvr/daemon/sql/storage/writeBlock.sql @@ -0,0 +1,2 @@ +INSERT INTO recordedvideo(recordingid, videodata) +VALUES(?, ?) diff --git a/p2pvr/daemon/storage.cpp b/p2pvr/daemon/storage.cpp index 51c1924..844623f 100644 --- a/p2pvr/daemon/storage.cpp +++ b/p2pvr/daemon/storage.cpp @@ -1,11 +1,9 @@ #include "storage.h" -#include "fileSink.h" -#include "muxedFileSink.h" -#include +#include "muxer.h" #include -#include +#include +#include -namespace fs = boost::filesystem; namespace po = boost::program_options; namespace P2PVR { @@ -17,89 +15,68 @@ StorageI::Options::Options() : ICETRAY_OPTIONS(StorageI::Options, ("p2pvr.storage.muxercommand", po::value(&muxerCommand)->default_value("/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", po::value(&root)->default_value("recordings"), - "Root folder in which to store recordings") ); IceTray::Logging::LoggerPtr StorageI::logger(LOGMANAGER()->getLogger()); -RawDataClientPrx -StorageI::OpenForWrite(const std::string & id, const Ice::Current & ice) -{ - fs::create_directories(options->root); - fs::path path = options->root / id; - RawDataClient * target; - if (options->muxerCommand.empty()) { - 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)); - throw StorageException(path.string(), strerror(errno)); +class RecordingSink : public RawDataClient { + public: + RecordingSink(AdHoc::ResourceHandle && d, Ice::Int recordingId) : + db(d), + ins(sql::storage::writeBlock.modify(db.get())) + { + ins->bindParamI(0, recordingId); } - target = new FileSink(fd); - } - else { - target = new MuxedFileSink(path, options->muxerCommand); - } - auto openFile = OpenFilePtr(new OpenFile(ice.adapter, target)); - openFiles.insert(openFile); - return *openFile; -} -void -StorageI::Close(const RawDataClientPrx & file, const Ice::Current &) + bool NewData(const Data & data, const Ice::Current &) override + { + if (!data.empty()) { + ins->bindParamBLOB(1, data); + ins->execute(); + return false; + } + return true; + } + + AdHoc::ResourceHandle db; + DB::ModifyCommandPtr ins; +}; + +StorageI::StorageI(IceTray::DatabasePoolPtr db) : + IceTray::AbstractDatabaseClient(db) { - openFiles.erase(std::find_if(openFiles.begin(), openFiles.end(), [&file](const OpenFilePtr & of) { return *of == file; })); } -void -StorageI::Delete(const std::string & id, const Ice::Current &) +RawDataClientPrx +StorageI::OpenForWrite(Ice::Int recordingId, const Ice::Current & ice) { - fs::path path = options->root / id; - logger->messagebf(LOG::INFO, "%s: Deleting %s", __PRETTY_FUNCTION__, path); - fs::remove(path); + auto target = RawDataClientPrx::uncheckedCast(ice.adapter->addWithUUID(new RecordingSink(db->get(), recordingId))); + if (!options->muxerCommand.empty()) { + auto storageId = target->ice_getIdentity(); + target = RawDataClientPrx::uncheckedCast(ice.adapter->addWithUUID(new Muxer(target, options->muxerCommand))); + muxerStorageLink.insert({ target->ice_getIdentity(), storageId }); + } + return target; } void -StorageI::Send(const std::string & id, const RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) +StorageI::Close(const RawDataClientPrx & file, const Ice::Current & ice) { - fs::path path = options->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__, - path, errno, strerror(errno)); - throw StorageException(path.string(), strerror(errno)); - } - lseek(fd, start, SEEK_SET); - auto end = std::min(fs::file_size(path), start + len); - while (start < end) { - Data buf(16 * 1024); - auto r = read(fd, &buf.front(), std::min(buf.size(), end - start)); - if (r < 0) { - logger->messagebf(LOG::ERR, "%s: Failed to read file %s (%d:%s)", __PRETTY_FUNCTION__, - path, errno, strerror(errno)); - close(fd); - throw StorageException(path.string(), strerror(errno)); - } - if (target->NewData(buf)) { - close(fd); - return; - } - start += r; + ice.adapter->remove(file->ice_getIdentity()); + auto storageIdItr = muxerStorageLink.find(file->ice_getIdentity()); + if (storageIdItr != muxerStorageLink.end()) { + ice.adapter->remove(storageIdItr->second); + muxerStorageLink.erase(storageIdItr); } - close(fd); } Ice::Long -StorageI::FileSize(const std::string & id, const Ice::Current &) +StorageI::GetSize(Ice::Int recordingId, const Ice::Current &) { - fs::path path = options->root / id; - try { - return fs::file_size(path); - } - catch (...) { - throw StorageException(path.string(), "Couldn't get file size"); - } + // Handles video not existing + auto size = fetch>(sql::storage::getSize, recordingId); + if (size) return *size; + return 0; } } diff --git a/p2pvr/daemon/storage.h b/p2pvr/daemon/storage.h index 4a2b389..48d3b57 100644 --- a/p2pvr/daemon/storage.h +++ b/p2pvr/daemon/storage.h @@ -9,9 +9,10 @@ #include "temporaryIceAdapterObject.h" #include #include +#include namespace P2PVR { -class DLL_PUBLIC StorageI : public Storage { +class DLL_PUBLIC StorageI : public Storage, IceTray::AbstractDatabaseClient { public: class Options : public IceTray::Options { public: @@ -20,20 +21,16 @@ class DLL_PUBLIC StorageI : public Storage { ICETRAY_OPTIONS_DECLARE; std::string muxerCommand; - boost::filesystem::path root; }; - RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &) override; - void Close(const 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 RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) override; + StorageI(IceTray::DatabasePoolPtr db); + + RawDataClientPrx OpenForWrite(Ice::Int recordingId, const Ice::Current &) override; + void Close(const RawDataClientPrx &, const Ice::Current &) override; + Ice::Long GetSize(Ice::Int recordingId, const Ice::Current &) override; protected: - typedef TemporaryIceAdapterObject OpenFile; - typedef boost::shared_ptr OpenFilePtr; - typedef std::set OpenFiles; - OpenFiles openFiles; + std::map muxerStorageLink; IceTray::OptionsResolver options; static IceTray::Logging::LoggerPtr logger; }; diff --git a/p2pvr/daemon/unittests/datasources/data.sql b/p2pvr/daemon/unittests/datasources/data.sql index 5c91ecd..cc5198a 100644 --- a/p2pvr/daemon/unittests/datasources/data.sql +++ b/p2pvr/daemon/unittests/datasources/data.sql @@ -99,7 +99,7 @@ SELECT pg_catalog.setval('recorded_recordedid_seq', 1, false); -- Data for Name: recordings; Type: TABLE DATA; Schema: public; Owner: p2pvr -- -COPY recordings (recordingid, storageaddress, guid, scheduleid, eventuid) FROM '$SCRIPTDIR/recordings'; +COPY recordings (recordingid, scheduleid, eventuid) FROM '$SCRIPTDIR/recordings'; -- diff --git a/p2pvr/daemon/unittests/testMaint.cpp b/p2pvr/daemon/unittests/testMaint.cpp index 10e0293..026ec27 100644 --- a/p2pvr/daemon/unittests/testMaint.cpp +++ b/p2pvr/daemon/unittests/testMaint.cpp @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE( update_events ) irecorded->bindParamI(1, keyEvent1->EventUid); irecorded->execute(); auto irecordings = boost::shared_ptr( - db->newModifyCommand("INSERT INTO recordings(storageAddress, guid, scheduleId, eventUid) VALUES('', '', ?, ?)")); + db->newModifyCommand("INSERT INTO recordings(scheduleId, eventUid) VALUES(?, ?)")); irecordings->bindParamI(0, 1); irecordings->bindParamI(1, keyEvent2->EventUid); irecordings->execute(); diff --git a/p2pvr/daemon/unittests/testRecordings.cpp b/p2pvr/daemon/unittests/testRecordings.cpp index b12c984..b68a2a3 100644 --- a/p2pvr/daemon/unittests/testRecordings.cpp +++ b/p2pvr/daemon/unittests/testRecordings.cpp @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include "mockDefs.h" @@ -33,8 +31,7 @@ BOOST_AUTO_TEST_CASE( recordings_addAndDelete ) BOOST_REQUIRE(event); BOOST_TEST_MESSAGE(event->Title); - auto guid = boost::lexical_cast(boost::uuids::random_generator()()); - auto rec = P2PVR::RecordingPtr(new P2PVR::Recording(0, "", guid, 0, event->EventUid)); + auto rec = P2PVR::RecordingPtr(new P2PVR::Recording(0, 0, event->EventUid)); rec->RecordingId = recordings->NewRecording(rec); BOOST_REQUIRE_EQUAL(218, rec->RecordingId); recordings->GetRecordings(); diff --git a/p2pvr/daemon/unittests/testStorage.cpp b/p2pvr/daemon/unittests/testStorage.cpp index ebbddcc..5083848 100644 --- a/p2pvr/daemon/unittests/testStorage.cpp +++ b/p2pvr/daemon/unittests/testStorage.cpp @@ -19,14 +19,11 @@ using namespace P2PVR::Testing; const boost::filesystem::path storageRootDir = "/tmp/ut/p2pvr/recordings"; namespace P2PVR { namespace Testing { -class TestService : public PQ::Mock, public IceTray::DryIce { +class TestService : public StandardMockDatabase { public: TestService() : - PQ::Mock("user=postgres dbname=postgres", "postgres", { - rootDir.parent_path().parent_path() / "datasources" / "schema.sql" }), - IceTray::DryIce({ - R"C(--p2pvr.storage.muxercommand="")C", - std::string("--p2pvr.storage.root=" + storageRootDir.string()), + StandardMockDatabase({ + R"C(--p2pvr.storage.muxercommand="cat")C" }) { } @@ -38,39 +35,39 @@ BOOST_GLOBAL_FIXTURE( TestService ); BOOST_FIXTURE_TEST_SUITE( StCore, TestClient ); -BOOST_AUTO_TEST_CASE( st_openWriteClose ) +static +void +runTest(RecordingsPrx recordings, StoragePrx storage) { - boost::filesystem::remove_all(storageRootDir); - std::string id = "made-up-storage-id"; - + auto id = recordings->NewRecording(new Recording(0, 8, 2556)); auto rdc = storage->OpenForWrite(id); BOOST_REQUIRE(rdc); - P2PVR::Data data; + Data data; data.resize(1024); rdc->NewData(data); storage->Close(rdc); - auto stSize = storage->FileSize(id); - BOOST_REQUIRE_EQUAL(1024, stSize); - BOOST_REQUIRE_EQUAL(1024, boost::filesystem::file_size(storageRootDir / id)); + BOOST_REQUIRE_EQUAL(1024, storage->GetSize(id)); - storage->Delete(id); - BOOST_REQUIRE(!boost::filesystem::exists(storageRootDir / id)); + recordings->DeleteRecording(id); + BOOST_REQUIRE_EQUAL(0, storage->GetSize(id)); } -BOOST_AUTO_TEST_CASE( st_notuniqueid ) +BOOST_AUTO_TEST_CASE( st_openWriteCloseMuxWithCat ) { - boost::filesystem::remove_all(storageRootDir); - std::string id = "made-up-storage-id"; - - auto rdc = storage->OpenForWrite(id); - storage->Close(rdc); + IceTray::OptionsResolver opts; + const_cast(opts->muxerCommand) = "/bin/cat"; + runTest(recordings, storage); +} - BOOST_REQUIRE_THROW(storage->OpenForWrite(id), P2PVR::StorageException); - storage->Delete(id); +BOOST_AUTO_TEST_CASE( st_openWriteCloseNoMuxer ) +{ + IceTray::OptionsResolver opts; + const_cast(opts->muxerCommand).clear(); + runTest(recordings, storage); } BOOST_AUTO_TEST_SUITE_END() diff --git a/p2pvr/datasources/schema.sql b/p2pvr/datasources/schema.sql index 1f9cab0..ee41bfc 100644 --- a/p2pvr/datasources/schema.sql +++ b/p2pvr/datasources/schema.sql @@ -299,14 +299,42 @@ CREATE SEQUENCE recorded_recordedid_seq ALTER SEQUENCE recorded_recordedid_seq OWNED BY recorded.recordedid; +-- +-- Name: recordedvideo; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE recordedvideo ( + recordingid integer NOT NULL, + blockid bigint NOT NULL, + videodata bytea NOT NULL +); + + +-- +-- Name: recordedvideo_blockid_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE recordedvideo_blockid_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: recordedvideo_blockid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE recordedvideo_blockid_seq OWNED BY recordedvideo.blockid; + + -- -- Name: recordings; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE recordings ( recordingid integer NOT NULL, - storageaddress text NOT NULL, - guid text NOT NULL, scheduleid integer NOT NULL, eventuid integer NOT NULL ); @@ -410,6 +438,13 @@ ALTER TABLE ONLY events ALTER COLUMN eventuid SET DEFAULT nextval('events_eventu ALTER TABLE ONLY recorded ALTER COLUMN recordedid SET DEFAULT nextval('recorded_recordedid_seq'::regclass); +-- +-- Name: recordedvideo blockid; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY recordedvideo ALTER COLUMN blockid SET DEFAULT nextval('recordedvideo_blockid_seq'::regclass); + + -- -- Name: recordings recordingid; Type: DEFAULT; Schema: public; Owner: - -- @@ -472,6 +507,14 @@ ALTER TABLE ONLY recorded ADD CONSTRAINT pk_recorded PRIMARY KEY (recordedid); +-- +-- Name: recordedvideo pk_recordedvideo; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY recordedvideo + ADD CONSTRAINT pk_recordedvideo PRIMARY KEY (blockid); + + -- -- Name: recordings pk_recordings; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -541,24 +584,24 @@ CREATE INDEX idx_recorded_schedule ON recorded USING btree (scheduleid); -- --- Name: idx_recordings_schedule; Type: INDEX; Schema: public; Owner: - +-- Name: idx_recordedvideo_recording; Type: INDEX; Schema: public; Owner: - -- -CREATE INDEX idx_recordings_schedule ON recordings USING btree (scheduleid); +CREATE INDEX idx_recordedvideo_recording ON recordedvideo USING btree (recordingid); -- --- Name: uni_events_serviceevent; Type: INDEX; Schema: public; Owner: - +-- Name: idx_recordings_schedule; Type: INDEX; Schema: public; Owner: - -- -CREATE UNIQUE INDEX uni_events_serviceevent ON events USING btree (serviceid, eventid) WHERE current; +CREATE INDEX idx_recordings_schedule ON recordings USING btree (scheduleid); -- --- Name: uni_recordings_storage; Type: INDEX; Schema: public; Owner: - +-- Name: uni_events_serviceevent; Type: INDEX; Schema: public; Owner: - -- -CREATE UNIQUE INDEX uni_recordings_storage ON recordings USING btree (storageaddress, guid); +CREATE UNIQUE INDEX uni_events_serviceevent ON events USING btree (serviceid, eventid) WHERE current; -- @@ -609,6 +652,14 @@ ALTER TABLE ONLY recorded ADD CONSTRAINT fk_recorded_schedule FOREIGN KEY (scheduleid) REFERENCES schedules(scheduleid) ON UPDATE CASCADE ON DELETE SET NULL; +-- +-- Name: recordedvideo fk_recordedvideo_recording; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY recordedvideo + ADD CONSTRAINT fk_recordedvideo_recording FOREIGN KEY (recordingid) REFERENCES recordings(recordingid) ON UPDATE CASCADE ON DELETE CASCADE; + + -- -- Name: recordings fk_recordings_eventuid; Type: FK CONSTRAINT; Schema: public; Owner: - -- diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice index 2d3a7a5..ae263a2 100644 --- a/p2pvr/ice/p2pvr.ice +++ b/p2pvr/ice/p2pvr.ice @@ -19,8 +19,6 @@ module P2PVR { class Recording { ["slicer:db:auto","slicer:db:pkey"] int RecordingId; - string StorageAddress; - string Guid; int ScheduleId; int EventUid; }; @@ -63,17 +61,10 @@ module P2PVR { idempotent void UpdateEvents(); }; - exception StorageException { - string path; - string message; - }; - interface Storage { - idempotent RawDataClient * OpenForWrite(string guid) throws StorageException; - idempotent void Close(RawDataClient * file) throws StorageException; - idempotent void Delete(string guid) throws StorageException; - idempotent long FileSize(string guid) throws StorageException; - void Send(string guid, RawDataClient * target, long start, long len) throws StorageException; + idempotent RawDataClient * OpenForWrite(int recordingId); + idempotent void Close(RawDataClient * rdc); + idempotent long GetSize(int recordingId); }; interface Recordings { -- cgit v1.2.3