-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathrecman.h
480 lines (421 loc) · 19.7 KB
/
recman.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
#ifndef VDR_LIVE_RECORDINGS_H
#define VDR_LIVE_RECORDINGS_H
// STL headers need to be before VDR tools.h (included by <vdr/recording.h>)
#include <map>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include <list>
#if TNTVERSION >= 30000
#include <cxxtools/log.h> // must be loaded before any VDR include because of duplicate macros (LOG_ERROR, LOG_DEBUG, LOG_INFO)
#endif
#include <iostream>
#include "stdext.h"
#include "setup.h"
#include <vdr/recording.h>
#include <vdr/channels.h>
#include "stringhelpers.h"
#include "tools.h"
#include "services.h"
namespace vdrlive {
/**
* Some forward declarations
*/
class RecordingsTree;
class RecordingsItemDir;
class RecordingsItemRec;
typedef std::shared_ptr<RecordingsTree> RecordingsTreePtr;
typedef std::shared_ptr<RecordingsItemDir> RecordingsItemDirPtr;
typedef std::shared_ptr<RecordingsItemRec> RecordingsItemRecPtr;
bool operator< (const RecordingsItemDirPtr &a, const RecordingsItemDirPtr &b);
bool operator< (cSv a, const RecordingsItemDirPtr &b);
bool operator< (const RecordingsItemDirPtr &a, cSv b);
bool operator< (int a, const RecordingsItemDirPtr &b);
bool operator< (const RecordingsItemDirPtr &a, int b);
int GetNumberOfTsFiles(int recId);
/**
* Class for managing recordings inside the live plugin. It
* provides some convenience methods and provides automatic
* locking during requests on the recordings or during the
* traversion of the recordings tree or lists, which can only be
* obtained through methods of the RecordingsManager.
*/
class RecordingsManager
{
public:
/**
* Returns a shared pointer to a fully populated
* recordings tree.
*/
static RecordingsTreePtr GetRecordingsTree();
/**
* fetches a cRecording from VDR's Recordings collection. Returns
* NULL if recording was not found
*/
static const cRecording *GetByHash(cSv hash, const cRecordings* Recordings);
/**
* fetches a cRecording from the RecordingsTree collection. Returns
* NULL if recording was not found
*/
static RecordingsItemRecPtr const GetByIdHash(cSv hash);
/**
* Move a recording with the given hash according to
* VDRs recording mechanisms.
* @param directory new name of the sub folder this recording is stored in.
* @param name new recording folder name.
* @param copy create a copy of the original recording rather than moving it.
* @param title new title of the recording.
* @param shorttext new short text of the recording.
* @param description new description of the recording.
*/
static bool UpdateRecording(cSv hash, cSv directory, cSv name, bool copy, cSv title, cSv shorttext, cSv description);
/**
* Delete recording resume with the given hash according to
* VDRs recording mechanisms.
*/
static void DeleteResume(cRecording const * recording);
/**
* Delete recording marks with the given hash according to
* VDRs recording mechanisms.
*/
static void DeleteMarks(cRecording const * recording);
/**
* Delete a recording with the given hash according to
* VDRs recording deletion mechanisms.
* If name is provided, it is set to recording->Name() (name for use in Menues)
* return:
* 0 success
* 1 no recording with recording_hash exists (name will not be provided ...)
* 2 recoring is in use
* 3 other error (recording->Delete() returned false)
*/
static int DeleteRecording(cSv recording_hash, std::string *name = nullptr);
/**
* Determine whether the recording has been archived on
* removable media (e.g. DVD-ROM)
*/
static int GetArchiveType(cRecording const * recording);
/**
* Provide an identification of the removable media
* (e.g. DVD-ROM Number or Name) where the recording has
* been archived.
*/
static std::string const GetArchiveId(cRecording const * recording, int archiveType);
static std::string const GetArchiveDescr(cRecording const * recording);
/**
* Is this recording currently played?
* Return codes:
* 0: this recording is currently played
* 1: this recording does not exist
* 2: no recording is played
* 3: another recording is played
* Also return *fileName=recording->FileName() if requested
*/
static int CheckReplay(cSv recording_hash, std::string *fileName = nullptr);
private:
static bool StateChanged();
static void EnsureValidData();
static std::shared_ptr<RecordingsTree> m_recTree;
static cStateKey m_recordingsStateKey;
static time_t m_last_recordings_update;
};
class ShortTextDescription
{
public:
ShortTextDescription(const char * ShortText, const char * Description);
wint_t getNextNonPunctChar();
private:
const char * m_short_text;
const char * m_description;
};
/**
* Class containing possible recordings compare functions
*/
enum class eSortOrder { name, date, errors, durationDeviation, duplicatesLanguage };
typedef bool (*tCompRec)(const RecordingsItemRecPtr &a, const RecordingsItemRecPtr &b);
typedef bool (*tCompDir)(const RecordingsItemDirPtr &a, const RecordingsItemDirPtr &b);
class RecordingsItemPtrCompare
{
public:
// recs
static bool ByAscendingDate(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByDuplicatesName(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByDuplicates(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByDuplicatesLanguage(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByAscendingNameDescSort(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByDescendingRecordingErrors(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByDescendingDurationDeviation(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByEpisode(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
static bool ByReleaseDate(const RecordingsItemRecPtr & first, const RecordingsItemRecPtr & second);
// dirs
static bool ByAscendingNameSort(const RecordingsItemDirPtr & first, const RecordingsItemDirPtr & second);
static bool BySeason(const RecordingsItemDirPtr & first, const RecordingsItemDirPtr & second);
// helpers
static int FindBestMatch(RecordingsItemRecPtr &BestMatch, const std::vector<RecordingsItemRecPtr>::const_iterator & First, const std::vector<RecordingsItemRecPtr>::const_iterator & Last, const RecordingsItemRecPtr & EPG_Entry);
static tCompRec getComp(eSortOrder sortOrder);
};
// search a recording matching an EPG entry. The EPG entry is given with Name, ShortText, Description, Duration, scraperOverview
bool searchNameDesc(RecordingsItemRecPtr &RecItem, const std::vector<RecordingsItemRecPtr> *RecItems, const cEvent *event, cScraperVideo *scraperVideo);
/**
* A recordings item that resembles a directory with other
* subdirectories and/or real recordings.
*/
class RecordingsItemDir
{
friend class RecordingsTree;
public:
virtual ~RecordingsItemDir();
RecordingsItemDir(cSv name, int level);
cSv Name() const { return m_name; }
int Level() const { return m_level; }
void finishRecordingsTree(); // sort recursively, Order: m_cmp_rec (if defined. Otherwise: no sort)
// dirs: Order: m_cmp_dir (if defined. Otherwise: m_name_for_sort)
virtual bool operator< (const RecordingsItemDirPtr &sec) const { return m_name < sec->m_name; }
virtual bool operator< (cSv sec) const { return m_name < sec; }
virtual bool operator> (cSv sec) const { return m_name > sec; }
virtual bool operator< (int sec) const { return false; }
virtual bool operator> (int sec) const { return false; }
int numberOfRecordings() const;
RecordingsItemDirPtr addDirIfNotExists(cSv dirName);
RecordingsItemDirPtr addDirCollectionIfNotExists(int collectionId, const RecordingsItemRecPtr &rPtr);
RecordingsItemDirPtr addDirSeasonIfNotExists(int seasonNumber, const RecordingsItemRecPtr &rPtr);
const std::vector<RecordingsItemRecPtr> *getRecordings(eSortOrder sortOrder);
const std::vector<RecordingsItemDirPtr> *getDirs() { return &m_subdirs; }
bool checkNew() const;
bool matchesFilter(cSv filter) const;
void addDirList(std::vector<std::string> &dirs, cSv basePath) const;
void setTvShow(const RecordingsItemRecPtr &rPtr);
int scraperCollectionId() const { return m_s_collection_id; }
int scraperSeasonNumber() const { return m_s_season_number; }
const cTvMedia &scraperImage() const;
bool recEntriesSorted() const { return m_cmp_rec != NULL; }
bool dirEntriesSorted() const { return m_cmp_dir != NULL; }
protected:
std::string m_name;
int m_level;
std::vector<RecordingsItemDirPtr> m_subdirs;
std::vector<RecordingsItemRecPtr> m_entries;
bool m_entriesSorted = false;
std::vector<RecordingsItemRecPtr> m_entries_other_sort;
eSortOrder m_sortOrder = (eSortOrder)-1;
bool (*m_cmp_dir)(const RecordingsItemDirPtr &itemPtr1, const RecordingsItemDirPtr &itemPtr2) = NULL;
bool (*m_cmp_rec)(const RecordingsItemRecPtr &itemPtr1, const RecordingsItemRecPtr &itemPtr2) = NULL;
// scraper data
RecordingsItemRecPtr m_rec_item; // in this rec item (if available), there are the relevant scraper data
// for dirs (collection), it points to a rec item with relevant data for the collection
// similar for dirs (TV show, season).
// for others, it is just nullptr -> no data available
mutable cTvMedia m_s_image;
mutable bool m_s_image_requested = false;
cImageLevels m_imageLevels;
int m_s_collection_id = 0;
int m_s_season_number = 0;
};
class RecordingsItemDirSeason : public RecordingsItemDir
{
public:
RecordingsItemDirSeason(int level, const RecordingsItemRecPtr &rPtr);
virtual ~RecordingsItemDirSeason();
virtual bool operator< (const RecordingsItemDirPtr &sec) const { return m_s_season_number < sec->scraperSeasonNumber(); }
virtual bool operator< (cSv sec) const { return false; }
virtual bool operator> (cSv sec) const { return false; }
virtual bool operator< (int sec) const { return m_s_season_number < sec; }
virtual bool operator> (int sec) const { return m_s_season_number > sec; }
};
class RecordingsItemDirCollection : public RecordingsItemDir
{
public:
RecordingsItemDirCollection(int level, const RecordingsItemRecPtr &rPtr);
virtual ~RecordingsItemDirCollection();
virtual bool operator< (const RecordingsItemDirPtr &sec) const { return m_s_collection_id < sec->scraperCollectionId(); }
virtual bool operator< (cSv sec) const { return false; }
virtual bool operator> (cSv sec) const { return false; }
virtual bool operator< (int sec) const { return m_s_collection_id < sec; }
virtual bool operator> (int sec) const { return m_s_collection_id > sec; }
};
/**
* A recordings item that represents a real recording. This is
* the leaf item in the recordings tree or one of the items in
* the recordings list.
*/
class RecordingsItemRec
{
friend class RecordingsTree;
friend class RecordingsItemDir;
friend class RecordingsItemDirSeason;
friend class RecordingsItemDirCollection;
friend class RecordingsItemPtrCompare;
public:
RecordingsItemRec(cSv name, const cRecording* recording, cMeasureTime *timeIdentify, cMeasureTime *timeOverview, cMeasureTime *timeImage, cMeasureTime *timeDurationDeviation, cMeasureTime *timeNumTsFiles, cMeasureTime *timeItemRec);
virtual ~RecordingsItemRec();
cSv Name() const { return m_name; }
cSv NameForSearch() const { return m_name_for_search; }
const char *ShortText() const { return m_shortText.c_str(); }
const char *Description() const { return m_description.c_str(); }
cSv ChannelName() const { return m_channelName; }
const char *Folder() const { return *m_folder; }
int FileSizeMB() const { return m_fileSizeMB; }
time_t StartTime() const { return m_startTime; }
int Duration() const { return m_duration; } // duration in seconds
int DurationDeviation() const { return m_duration_deviation; } // duration deviation in seconds
int IdI() const { return m_idI;}
XXH128_hash_t IdHash() const { return m_hash; }
// To display the recording on the UI
int IsArchived() const { return m_isArchived; }
bool checkNew() const { return m_checkNew; }
const char *NewR() const { return LiveSetup().GetMarkNewRec() && checkNew() ? "_new" : "" ; }
int RecordingErrors() const { return m_recordingErrors; }
int NumberTsFiles() const {
if (m_number_ts_files == -2) m_number_ts_files = GetNumberOfTsFiles(m_idI);
return m_number_ts_files;
}
bool scraperDataAvailable() const { return m_s_videoType == tMovie || m_s_videoType == tSeries; }
tvType scraperVideoType() const { return m_s_videoType; }
int scraperCollectionId() const { return m_s_collection_id; }
int scraperEpisodeNumber() const { return m_s_episode_number; }
int scraperSeasonNumber() const { return m_s_season_number; }
cSv scraperName() const { return m_s_title; }
cSv scraperReleaseDate() const { return m_s_release_date; }
int language() const { return m_language; }
int SD_HD() const { return m_video_SD_HD; }
double FramesPerSecond(void) const { return m_framesPerSecond; }
uint16_t FrameWidth(void) const { return m_frameWidth; }
uint16_t FrameHeight(void) const { return m_frameHeight; }
#if VDRVERSNUM >= 20605
eScanType ScanType(void) const { return m_scanType; }
char ScanTypeChar(void) const { return ScanTypeChars[m_scanType]; }
eAspectRatio AspectRatio(void) const { return m_aspectRatio; }
#endif
int CompareStD(const RecordingsItemRecPtr &second, int *numEqualChars=NULL) const;
bool matchesFilter(cSv filter) const;
private:
void getScraperData(const cRecording *recording);
int get_SD_HD(const cRecordingInfo *info);
const cTvMedia &scraperImage() const;
int CompareTexts(const RecordingsItemRecPtr &second, int *numEqualChars=NULL) const;
bool orderDuplicates(const RecordingsItemRecPtr &second, bool alwaysShortText, bool lang = false) const;
public:
virtual void AppendAsJSArray(cToSvConcat<0> &target) const;
static void AppendAsJSArray(cToSvConcat<0> &target, std::vector<RecordingsItemRecPtr>::const_iterator recIterFirst, std::vector<RecordingsItemRecPtr>::const_iterator recIterLast, bool &first, cSv filter, bool reverse);
mutable cMeasureTime *m_timeIdentify = nullptr;
mutable cMeasureTime *m_timeOverview = nullptr;
mutable cMeasureTime *m_timeImage = nullptr;
mutable cMeasureTime *m_timeDurationDeviation = nullptr;
protected:
const std::string m_name;
std::string GetNameForSearch(cSv name);
const std::string m_name_for_search;
const cString m_folder;
const int m_idI = -1;
const XXH128_hash_t m_hash;
const int m_isArchived = 0;
mutable int m_number_ts_files = -2;
std::unique_ptr<cScraperVideo> m_scraperVideo;
tvType m_s_videoType = tNone;
int m_s_dbid = 0;
std::string m_s_title;
std::string m_s_episode_name;
std::string m_s_IMDB_ID;
std::string m_s_release_date;
mutable cTvMedia m_s_image;
mutable bool m_s_image_requested = false;
cImageLevels m_imageLevels;
int m_s_runtime = 0;
int m_s_collection_id = 0;
int m_s_episode_number = 0;
int m_s_season_number = 0;
int m_language = 0;
int m_video_SD_HD = -2; // < -2: not checked. -1: Radio. 0 is SD, 1 is HD, >1 is UHD or better
int m_duration_deviation = 0;
// to remove m_recording
std::string m_shortText;
std::string m_description;
std::string m_channelName;
int m_fileSizeMB = -1;
time_t m_startTime = 0;
int m_duration = -1;
bool m_checkNew = false;
int m_recordingErrors = 0;
double m_framesPerSecond = 0.;
uint16_t m_frameWidth = 0;
uint16_t m_frameHeight = 0;
#if VDRVERSNUM >= 20605
eScanType m_scanType = stUnknown;
eAspectRatio m_aspectRatio = arUnknown;
#endif
protected:
RecordingsItemRec(cSv name):
m_name(name),
m_name_for_search(GetNameForSearch(name)),
m_hash(XXH3_128bits("abc", 3)) {}
};
/**
* Class containing recordings to compare or "dummy" recordings, i.e. data from EPG which can be compared with a recording
*/
class RecordingsItemDummy: public RecordingsItemRec
{
friend class RecordingsItemPtrCompare;
public:
RecordingsItemDummy(const cEvent *event, cScraperVideo *scraperVideo);
};
/**
* The recordings tree contains all recordings in a file system
* tree like fashion.
*/
class RecordingsTree
{
friend class RecordingsManager;
private:
RecordingsTree();
public:
~RecordingsTree();
RecordingsItemDirPtr getRoot() const { return m_root; }
std::vector<std::string> getAllDirs() { std::vector<std::string> result; m_rootFileSystem->addDirList(result, ""); return result; }
const std::vector<RecordingsItemRecPtr> *allRecordings() { return &m_allRecordings;}
const std::vector<RecordingsItemRecPtr> *allRecordings(eSortOrder sortOrder);
int MaxLevel() const { return m_maxLevel; }
time_t getCreationTimestamp() { return m_creation_timestamp; }
private:
int m_maxLevel;
RecordingsItemDirPtr m_root;
RecordingsItemDirPtr m_rootFileSystem;
std::vector<RecordingsItemRecPtr> m_allRecordings;
bool m_allRecordingsSorted = false;
std::vector<RecordingsItemRecPtr> m_allRecordings_other_sort;
eSortOrder m_sortOrder = (eSortOrder)-1;
time_t m_creation_timestamp = 0;
};
inline void print(const char *t, const RecordingsItemDirPtr &a) {
std::cout << t << (a ? a->Name() : "nullptr");
}
inline void swap(RecordingsItemDirPtr &a, RecordingsItemDirPtr &b) {
a.swap(b);
}
inline void swap(RecordingsItemRecPtr &a, RecordingsItemRecPtr &b) {
a.swap(b);
}
void AppendScraperData(cToSvConcat<0> &target, cSv s_IMDB_ID, const cTvMedia &s_image, tvType s_videoType, cSv s_title, int s_season_number, int s_episode_number, cSv s_episode_name, int s_runtime, cSv s_release_date);
std::string recordingErrorsHtml(int recordingErrors);
void addDuplicateRecordingsNoSd(std::vector<RecordingsItemRecPtr> &DuplicateRecItems, RecordingsTreePtr &RecordingsTree);
void addDuplicateRecordingsLang(std::vector<RecordingsItemRecPtr> &DuplicateRecItems, RecordingsTreePtr &RecordingsTree);
void addDuplicateRecordingsSd(std::vector<RecordingsItemRecPtr> &DuplicateRecItems, RecordingsTreePtr &RecordingsTree);
template<std::size_t N>
cToSvConcat<N> & StringAppendFrameParams(cToSvConcat<N> &s, const RecordingsItemRec *itemRec) {
#if VDRVERSNUM >= 20605
if (!itemRec) return s;
if (itemRec->FrameWidth() && itemRec->FrameHeight() ) {
s << itemRec->FrameWidth() << 'x' << itemRec->FrameHeight();
if (itemRec->FramesPerSecond() > 0) {
s.appendFormated("/%.2g", itemRec->FramesPerSecond() );
if (itemRec->ScanType() != stUnknown)
s.append(1, itemRec->ScanTypeChar());
}
if (itemRec->AspectRatio() != arUnknown)
s << ' ' << AspectRatioTexts[itemRec->AspectRatio()];
}
#endif
return s;
}
} // namespace vdrlive
#endif // VDR_LIVE_RECORDINGS_H