DCL 3.7.4
Loading...
Searching...
No Matches
TagReader Class Reference

#include <TagReader.h>

Classes

struct  FileInfo

Public Member Functions

 ~TagReader ()
 TagReader (const MainArguments &_args) __DCL_THROWS2(SQLDriverException *
SQLException *void readTag (const String &_path, FileInfo &_info)
void print (const FileInfo &_info) const
void read (const String &_dirname, const String &_filename)
void readDir (const String &_path)
const MainArgumentsargs () const

Protected Member Functions

void persist (const FileInfo &_info)

Protected Attributes

const MainArguments__args
SQLConnection__conn
String __dirname
int32_t __dirId
int64_t __fileId
String __doubleRule
String __singleRule

Detailed Description

Definition at line 28 of file TagReader.h.

Constructor & Destructor Documentation

◆ ~TagReader()

__DCL_BEGIN_NAMESPACE TagReader::~TagReader ( )

Definition at line 43 of file TagReader.cpp.

44{
45 if (__conn) {
46 //__DCL_TRACE1(L"Connection destroy [%p]\n", __conn);
47 delete __conn;
48 }
49}
SQLConnection * __conn
Definition TagReader.h:76

◆ TagReader()

TagReader::TagReader ( const MainArguments & _args)

Definition at line 51 of file TagReader.cpp.

52 : __args(_args) __DCL_THROWS2(SQLDriverException*, SQLException*)
53{
54 __conn = NULL;
55 __dirId = 0;
56 __fileId = 0;
57
58 size_t first = __args.database().search(L"DRIVER *=", true);
59 if (first != (size_t)-1) {
60 String driver = __args.database().substring(L"DRIVER *= *[^;]+", true);
61 if (driver.isEmpty()) {
62 driver = __args.database().substring(first);
63 }
64 if (!driver.isEmpty()) {
65 if (driver.indexOf(L'{') != (size_t)-1) {
66 driver = L"DCLODBC";
67 }
68 else {
69 driver = driver.substring(driver.indexOf(L'=') + 1).trim();
70 }
71 }
72 if (!driver.isEmpty()) {
73 __conn = new SQLConnection(driver);
74 __conn->open(__args.database());
75 __args.output() << L"Database connected. ["
76 << __conn->getServerInfo()
77 << L"] Using ["
78 << __args.database()
79 << L"]" << endl;
80 }
81 }
82
83 __doubleRule.assign(L'=', 80);
84 __singleRule.assign(L'-', 80);
85}
#define NULL
Definition Config.h:312
#define __DCL_THROWS2(e1, e2)
Definition Config.h:153
DCLCVAR const struct __endl endl
String __doubleRule
Definition TagReader.h:84
int32_t __dirId
Definition TagReader.h:79
String __singleRule
Definition TagReader.h:85
const MainArguments & __args
Definition TagReader.h:75
int64_t __fileId
Definition TagReader.h:80

Member Function Documentation

◆ args()

const MainArguments & TagReader::args ( ) const
inline

Definition at line 70 of file TagReader.h.

70 {
71 return __args;
72 }

◆ persist()

void TagReader::persist ( const FileInfo & _info)
protected

Definition at line 287 of file TagReader.cpp.

288{
289 Writer& out = args().output();
290 out << __singleRule << endl;
291
292 if (__dirId == 0) {
293 String sql = L""
294 "SELECT COALESCE(MAX(DIR_ID), 1000) FROM STOR_DIR"
295 ;
296 SQLQuery q(__conn);
297 q.execute(sql);
298 q.fetch();
299 __dirId = q.fields()[0].asInt32();
300 }
301
302 if (__fileId == 0) {
303 String sql = L""
304 "SELECT COALESCE(MAX(FILE_ID), 10000) FROM STOR_FILE"
305 ;
306 SQLQuery q(__conn);
307 q.execute(sql);
308 q.fetch();
309 __fileId = q.fields()[0].asInt64();
310 }
311
312 DateTime now = DateTime::getCurrentLocalTime();
313 if (__dirname != _info.dirname) {
314 __dirname = _info.dirname;
315 __dirId++;
316
317 String sql = L""
318 "INSERT INTO STOR_DIR ("
319 "DIR_ID, PATH, CREA_TS, MODI_TS"
320 ")"
321 "\nVALUES ("
322 ":DIR_ID, :PATH, :CREA_TS, :MODI_TS"
323 ")" ;
324 SQLQuery q(__conn);
325 q.prepare(sql);
326 q.params()[0].setValue(__dirId);
327 q.params()[1].setValue(__dirname);
328 q.params()[2].setValue(now);
329 q.params()[3].setValue(now);
330 q.execute();
331
332 out << L" INSERT STOR_DIR [" << __dirId << L"]["
333 << __dirname << L"]" << endl;
334 }
335
336 __fileId++;
337 String sql = L""
338 "INSERT INTO STOR_FILE ("
339 "FILE_ID, NAME, USER_ID, STOR_ID, TYPE_ID"
340 ", DIR_ID, FILE_NM, FILE_SZ, FILE_AT, FILE_MT, FILE_CT, FILE_ER"
341 ", MEDI_FM, MEDI_WI, MEDI_HE, MEDI_DU, MEDI_NF"
342 ", READ_CO, CREA_TS, MODI_TS"
343 ")"
344 "\nVALUES ("
345 ":FILE_ID, :NAME, 0, 0, 0"
346 ", :DIR_ID, :FILE_NM, :FILE_SZ, :FILE_AT, :FILE_MT, :FILE_CT, :FILE_ER"
347 ", :MEDI_FM, :MEDI_WI, :MEDI_HE, :MEDI_DU, :MEDI_NF"
348 ", 0, :CREA_TS, :MODI_TS"
349 ")";
350 SQLQuery q(__conn);
351 q.prepare(sql);
352 q.params().byName(L"FILE_ID").setValue(__fileId);
353 q.params().byName(L"NAME").setValue(_info.filename);
354 q.params().byName(L"DIR_ID").setValue(__dirId);
355 q.params().byName(L"FILE_NM").setValue(_info.filename);
356 q.params().byName(L"FILE_SZ").setValue(_info.size);
357 q.params().byName(L"FILE_AT").setValue(_info.atime);
358 q.params().byName(L"FILE_MT").setValue(_info.mtime);
359 q.params().byName(L"FILE_CT").setValue(_info.ctime);
360 q.params().byName(L"FILE_ER").setValue(_info.notes);
361 q.params().byName(L"MEDI_FM").setValue(_info.format);
362 q.params().byName(L"MEDI_WI").setValue(_info.width);
363 q.params().byName(L"MEDI_HE").setValue(_info.height);
364 q.params().byName(L"MEDI_DU").setValue(_info.duration);
365 q.params().byName(L"MEDI_NF").setValue(_info.inform);
366 q.params().byName(L"CREA_TS").setValue(now);
367 q.params().byName(L"MODI_TS").setValue(now);
368 q.execute();
369
370 out << L" INSERT STOR_FILE [" << __fileId << L"]["
371 << _info.filename << L"]" << endl;
372
373 if (_info.id3v1.version() > 0) {
374 if (_info.id3v1.title().length() > 0) {
375 String sql = L""
376 "INSERT INTO ID3V1 ("
377 "FILE_ID, TITLE, ARTIST, ALBUM, YEAR_"
378 ", COMMENT_, TRACK, GENRE"
379 ")"
380 "\nVALUES ("
381 ":FILE_ID, :TITLE, :ARTIST, :ALBUM, :YEAR_"
382 ", :COMMENT_, :TRACK, :GENRE"
383 ")";
384 SQLQuery q(__conn);
385 q.prepare(sql);
386 q.params().byName(L"FILE_ID").setValue(__fileId);
387 q.params().byName(L"TITLE").setValue(_info.id3v1.title());
388
389 if (_info.id3v1.artist().isEmpty())
390 q.params().byName(L"ARTIST").setNull();
391 else
392 q.params().byName(L"ARTIST").setValue(_info.id3v1.artist());
393
394 if (_info.id3v1.album().isEmpty())
395 q.params().byName(L"ALBUM").setNull();
396 else
397 q.params().byName(L"ALBUM").setValue(_info.id3v1.album());
398
399 if (_info.id3v1.year().isEmpty())
400 q.params().byName(L"YEAR_").setNull();
401 else
402 q.params().byName(L"YEAR_").setValue(_info.id3v1.year());
403
404 if (_info.id3v1.comment().isEmpty())
405 q.params().byName(L"COMMENT_").setNull();
406 else
407 q.params().byName(L"COMMENT_").setValue(_info.id3v1.comment());
408
409 q.params().byName(L"TRACK").setValue(_info.id3v1.track());
410 q.params().byName(L"GENRE").setValue(_info.id3v1.genre());
411 q.execute();
412
413 if (__args.verbose()) {
414 out << L" INSERT ID3V1" << endl;
415 }
416 }
417 else {
418 args().errout() << L"Title is NULL "
419 << _info.id3v1.toString()
420 << L" [" << _info.dirname << _info.filename << L"]" << endl;
421 }
422 }
423
424 if (_info.id3v2.version() > 0) {
425 String sql = L""
426 "INSERT INTO ID3V2 ("
427 "FILE_ID, VERSION, FLAGS, SIZE_, FSBITS"
428 ")"
429 "\nVALUES ("
430 ":FILE_ID, :VERSION, :FLAGS, :SIZE_, :FSBITS"
431 ")";
432 SQLQuery q(__conn);
433 q.prepare(sql);
434 q.params().byName(L"FILE_ID").setValue(__fileId);
435 q.params().byName(L"VERSION").setValue(_info.id3v2.version());
436 q.params().byName(L"FLAGS").setValue(_info.id3v2.flags());
437 q.params().byName(L"SIZE_").setValue(_info.id3v2.size());
438 q.params().byName(L"FSBITS").setValue(_info.id3v2.fsbits());
439 q.execute();
440
441 sql = L""
442 "INSERT INTO ID3V2_FRAME ("
443 "FILE_ID, NO_, FRAME_ID, SIZE_, FLAGS"
444 ", ENCODING, TYPE_, URL, DESCRIPTION, TEXT_, BINARY_"
445 ")"
446 "\nVALUES ("
447 ":FILE_ID, :NO_, :FRAME_ID, :SIZE_, :FLAGS"
448 ", :ENCODING, :TYPE_, :URL, :DESCRIPTION, :TEXT_, :BINARY_"
449 ")";
450 q.prepare(sql);
451 for (size_t i = 0; i < _info.id3v2.frames().size(); i++) {
452 ID3v2Frame& frame = *(ID3v2Frame*)_info.id3v2.frames()[i];
453 q.params().byName(L"FILE_ID").setValue(__fileId);
454 q.params().byName(L"NO_").setValue((int)(i + 1));
455 q.params().byName(L"FRAME_ID").setValue(frame.id());
456 q.params().byName(L"SIZE_").setValue(frame.size());
457 q.params().byName(L"FLAGS").setValue(frame.flags());
458 q.params().byName(L"ENCODING").setValue(frame.encoding());
459 q.params().byName(L"TYPE_").setValue(frame.type());
460
461 if (frame.url().isEmpty())
462 q.params().byName(L"URL").setNull();
463 else
464 q.params().byName(L"URL").setValue(frame.url());
465
466 if (frame.description().isEmpty())
467 q.params().byName(L"DESCRIPTION").setNull();
468 else
469 q.params().byName(L"DESCRIPTION").setValue(frame.description());
470
471 if (frame.text().isEmpty())
472 q.params().byName(L"TEXT_").setNull();
473 else
474 q.params().byName(L"TEXT_").setValue(frame.text());
475
476 if (frame.binary().length() == 0 || 100 < frame.binary().length())
477 // APIC, PIC 제외
478 q.params().byName(L"BINARY_").setNull();
479 else
480 q.params().byName(L"BINARY_").setValue(frame.binary());
481
482 q.execute();
483 }
484
485 if (__args.verbose()) {
486 out << L" INSERT ID3V2 ID3V2_FRAME["
487 << _info.id3v2.frames().size() << L"]" << endl;
488 }
489 }
490
491 if (_info.apev2.version() > 0) {
492 String sql = L""
493 "INSERT INTO APE ("
494 "FILE_ID, VERSION, SIZE_, COUNT_, FLAGS"
495 ")"
496 "\nVALUES ("
497 ":FILE_ID, :VERSION, :SIZE_, :COUNT_, :FLAGS"
498 ")";
499 SQLQuery q(__conn);
500 q.prepare(sql);
501 q.params().byName(L"FILE_ID").setValue(__fileId);
502 q.params().byName(L"VERSION").setValue(_info.apev2.version());
503 q.params().byName(L"SIZE_").setValue(_info.apev2.size());
504 q.params().byName(L"COUNT_").setValue(_info.apev2.count());
505 q.params().byName(L"FLAGS").setValue((int32_t)_info.apev2.flags());
506 q.execute();
507
508 sql = L""
509 "INSERT INTO APE_ITEM ("
510 "FILE_ID, NO_, SIZE_, FLAGS"
511 ", KEY_, VALUE_"
512 ")"
513 "\nVALUES ("
514 ":FILE_ID, :NO_, :SIZE_, :FLAGS"
515 ", :KEY_, :VALUE_"
516 ")";
517 q.prepare(sql);
518 for (size_t i = 0; i < _info.apev2.items().size(); i++) {
519 APEv2Item& item = *(APEv2Item*)_info.apev2.items()[i];
520 q.params().byName(L"FILE_ID").setValue(__fileId);
521 q.params().byName(L"NO_").setValue((int)(i + 1));
522 q.params().byName(L"SIZE_").setValue(item.size());
523 q.params().byName(L"FLAGS").setValue((int32_t)item.flags());
524 q.params().byName(L"KEY_").setValue(item.key());
525
526 if (item.value().isEmpty())
527 q.params().byName(L"VALUE_").setNull();
528 else
529 q.params().byName(L"VALUE_").setValue(item.value());
530
531 q.execute();
532 }
533
534 if (__args.verbose()) {
535 out << L" INSERT APE APE_ITEM["
536 << _info.apev2.items().size() << L"]" << endl;
537 }
538 }
539}
static DateTime getCurrentLocalTime()
Definition DateTime.cpp:954
uint32_t size() const
Definition ID3v2.h:140
char type() const
Definition ID3v2.h:152
short flags() const
Definition ID3v2.h:144
const String & url() const
Definition ID3v2.h:156
const String id() const
Definition ID3v2.h:136
char encoding() const
Definition ID3v2.h:148
const String & description() const
Definition ID3v2.h:160
const String & text() const
Definition ID3v2.h:164
const ByteString & binary() const
Definition ID3v2.h:168
virtual String toString() const
String __dirname
Definition TagReader.h:78
const MainArguments & args() const
Definition TagReader.h:70

◆ print()

void TagReader::print ( const FileInfo & _info) const

Definition at line 229 of file TagReader.cpp.

230{
231 Writer& out = args().output();
232
233 const ID3v2& id3v2 = _info.id3v2;
234 const APEv2& apev2 = _info.apev2;
235 const ID3v1& id3v1 = _info.id3v1;
236
237 out << __doubleRule << endl;
238 out << L"DIR [" << _info.dirname << L"]" << endl;
239 out << L"FILE[" << _info.filename << L"]" << endl;
240
241 out.printf(L" tags[%03d]",
242 id3v2.version() * 100 + apev2.version() / 100 + id3v1.version());
243 out << L" size[" << _info.size << L"]"
244 << L" mtime[" << _info.mtime.toString() << L"]"
245 << L" notes[" << _info.notes << L"]"
246 << endl;
247
248 out << L" format[" << _info.format << L"]"
249 << L" width[" << _info.width << L"]"
250 << L" height[" << _info.height << L"]"
251 << L" duration[" << _info.duration << L"]"
252 << endl;
253
254 if (!args().verbose()) {
255 return;
256 }
257
258 if (id3v2.version() > 0) {
259 out << __singleRule << endl;
260 out << id3v2.toString() << endl;
261
262 for (size_t i = 0; i < id3v2.frames().size(); i++) {
263 const ID3v2Frame& frame = *(ID3v2Frame*)id3v2.frames()[i];
264 out << frame.toString() << endl;
265 }
266 }
267
268 if (apev2.version() > 0) {
269 out << __singleRule << endl;
270 out << apev2.toString() << endl;
271
272 for (size_t i = 0; i < apev2.items().size(); i++) {
273 const APEv2Item& item = *(APEv2Item*)apev2.items()[i];
274 out << item.toString() << endl;
275 }
276 }
277
278 if (id3v1.version() > 0) {
279 out << __singleRule << endl;
280 out << id3v1.toString() << endl;
281 }
282
283 out << __singleRule << endl;
284 out << _info.inform << endl;
285}
virtual String toString() const
Definition ID3v2.cpp:596
virtual String toString() const
Definition Object.cpp:187

◆ read()

void TagReader::read ( const String & _dirname,
const String & _filename )

Definition at line 153 of file TagReader.cpp.

154{
155 FileInfo info(_dirname, _filename);
156 String path = _dirname + _filename;
157
158 info.size = Files::size(path);
159 time_t atime, mtime, ctime;
160 if (Files::time(path, &atime, &mtime, &ctime)) {
161 info.atime.assign(atime);
162 info.mtime.assign(mtime);
163 info.ctime.assign(ctime);
164 }
165
166 try {
167 if (_filename.toLowerCase().endsWith(L".mp3")) {
168 readTag(path, info);
169 }
170
171 MediaInfo mi(path);
172 info.inform = mi.inform_r();
173 info.format = mi.format();
174 if (mi.videoCount() > 0) {
175 info.width = mi.videoWidth(0);
176 info.height = mi.videoHeight(0);
177 info.duration = mi.videoDurationAsSeconds(0);
178 }
179 else if (mi.audioCount() > 0) {
180 info.duration = mi.audioDurationAsSeconds(0);
181 }
182 else if (mi.imageCount() > 0) {
183 info.width = mi.imageWidth(0);
184 info.height = mi.imageHeight(0);
185 }
186 }
187 catch (Exception* e) {
188 info.notes = e->toString();
189 args().errout() << e->toStringAll() << endl;
190 e->destroy();
191 }
192
193 print(info);
194
195 if (!__args.dryrun() && __conn && __conn->connected()) {
196 __conn->startTrans();
197 try {
198 persist(info);
199 __conn->commitTrans();
200 }
201 catch (Exception* e) {
202 __conn->rollbackTrans();
203 args().errout() << e->toStringAll() << endl;
204 e->destroy();
205 }
206 }
207}
virtual String toString() const
Definition Exception.cpp:40
virtual void destroy()
Definition Exception.cpp:74
String toStringAll() const
Definition Exception.cpp:45
static bool time(const String &_path, time_t *_atime, time_t *_mtime, time_t *_ctime)
Definition Files.cpp:140
static uint64_t size(const String &_path) __DCL_THROWS1(IOException *)
Definition Files.cpp:160
SQLException *void readTag(const String &_path, FileInfo &_info)
Definition TagReader.cpp:87
void persist(const FileInfo &_info)
void print(const FileInfo &_info) const

◆ readDir()

void TagReader::readDir ( const String & _path)

Definition at line 209 of file TagReader.cpp.

210{
211 Dir dir(_path);
212 Dir::Entry entry;
213
214 // __DCL_TRACE1_N(L"[%ls]\n", dir.path().data());
215 while (dir.read(entry)) {
216 // __DCL_TRACE1_N(L"[%ls]\n", entry.toString().data());
217 String name = entry.name();
218 if (entry.isDir()) {
219 if (name.compare(L"..", name.length()) != 0) {
220 readDir(dir.path() + entry.name());
221 }
222 }
223 else {
224 read(dir.path(), name);
225 }
226 }
227}
void read(const String &_dirname, const String &_filename)
void readDir(const String &_path)

◆ readTag()

void TagReader::readTag ( const String & _path,
FileInfo & _info )

Definition at line 87 of file TagReader.cpp.

88{
89 File file(_path);
90#if defined(__DCL_DEBUG) && __TRACE_THIS
91 {
92 File::off_t _n = file.size();
93 __DCL_TRACE2(L"[%ls] size[%lld]\n", file.path().data(), _n);
94 }
95#endif
96 char buf[160]; // 128 + 32
97
98 int __UNUSED__ id3v2Version = 0; // 0, 2, 3, 4
99 int apeVersion = 0; // 0, 1, 2
100 int __UNUSED__ id3v1Version = 0; // 0, 1
101
102 ID3v2& id3v2 = _info.id3v2;
103 APEv2& apev2 = _info.apev2;
104 ID3v1& id3v1 = _info.id3v1;
105
106 // 파일의 시작에서 ID3v2와 APEv2를 검사한다.
107 // "ID3" "APETAGEX"
108 size_t n = file.read(buf, 32);
109 if (n == 32) {
110 if (memcmp(buf, "ID3", 3) == 0) {
111 // ID3v2가 파일의 맨 앞에 있다.
112 if (_info.id3v2.read(file, buf)) {
113 id3v2Version = id3v2.version();
114 }
115 }
116 else if (memcmp(buf, "APETAGEX", 8) == 0) {
117 // APEv2가 파일의 맨 앞에 있다.
118 if (apev2.read(file, buf, 0)) {
119 apeVersion = apev2.version() / 1000;
120 }
121 }
122 }
123
124 // 파일의 끝에서 APEv2와 ID3v1을 검사한다.
125 file.seek(-160, File::END);
126 n = file.read(buf, 160);
127 if (n == 160) {
128 __DCL_TRACE2_N(L"read [%zd][%ls]\n",
129 n, String::tryString(buf, 160).data());
130 if (apeVersion == 0) {
131 if (memcmp(buf, "APETAGEX", 8) == 0) {
132 // APEv2 또는 1이 파일의 뒤쪽 ID3v1 앞에 있다.
133 if (apev2.read(file, buf, -160)) {
134 apeVersion = apev2.version() / 1000;
135 }
136 }
137 else if (memcmp(&buf[128], "APETAGEX", 8) == 0) {
138 // APEv2 또는 1이 파일의 뒤쪽에 있다.
139 if (apev2.read(file, buf, -32)) {
140 apeVersion = apev2.version() / 1000;
141 }
142 }
143 }
144
145 if (memcmp(&buf[32], "TAG", 3) == 0) {
146 if (id3v1.read(&buf[32])) {
147 id3v1Version = id3v1.version();
148 }
149 }
150 }
151}
#define __UNUSED__
Definition Config.h:341
#define __DCL_TRACE2_N(fmt, arg1, arg2)
#define __DCL_TRACE2(fmt, arg1, arg2)
Definition Object.h:400
@ END
Definition File.h:211

Member Data Documentation

◆ __args

const MainArguments& TagReader::__args
protected

Definition at line 75 of file TagReader.h.

◆ __conn

SQLConnection* TagReader::__conn
protected

Definition at line 76 of file TagReader.h.

◆ __dirId

int32_t TagReader::__dirId
protected

Definition at line 79 of file TagReader.h.

◆ __dirname

String TagReader::__dirname
protected

Definition at line 78 of file TagReader.h.

◆ __doubleRule

String TagReader::__doubleRule
protected

Definition at line 84 of file TagReader.h.

◆ __fileId

int64_t TagReader::__fileId
protected

Definition at line 80 of file TagReader.h.

◆ __singleRule

String TagReader::__singleRule
protected

Definition at line 85 of file TagReader.h.


The documentation for this class was generated from the following files: