1 /*
2 * Copyright 2009-2010 Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5
6 #include "FilePlaylistItem.h"
7
8 #include <stdio.h>
9
10 #include <new>
11
12 #include <Directory.h>
13 #include <File.h>
14 #include <FindDirectory.h>
15 #include <MediaFile.h>
16 #include <MediaTrack.h>
17 #include <Path.h>
18 #include <TranslationUtils.h>
19
20 #include "MediaFileTrackSupplier.h"
21 #include "SubTitlesSRT.h"
22
23 static const char* kPathKey = "path";
24
FilePlaylistItem(const entry_ref & ref)25 FilePlaylistItem::FilePlaylistItem(const entry_ref& ref)
26 {
27 fRefs.push_back(ref);
28 fNamesInTrash.push_back("");
29 }
30
31
FilePlaylistItem(const FilePlaylistItem & other)32 FilePlaylistItem::FilePlaylistItem(const FilePlaylistItem& other)
33 :
34 fRefs(other.fRefs),
35 fNamesInTrash(other.fNamesInTrash),
36 fImageRefs(other.fImageRefs),
37 fImageNamesInTrash(other.fImageNamesInTrash)
38 {
39 }
40
41
FilePlaylistItem(const BMessage * archive)42 FilePlaylistItem::FilePlaylistItem(const BMessage* archive)
43 {
44 const char* path;
45 entry_ref ref;
46 if (archive != NULL) {
47 int32 i = 0;
48 while (archive->FindString(kPathKey, i, &path) == B_OK) {
49 if (get_ref_for_path(path, &ref) == B_OK) {
50 fRefs.push_back(ref);
51 }
52 i++;
53 }
54 }
55 if (fRefs.empty()) {
56 fRefs.push_back(entry_ref());
57 }
58 for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
59 fNamesInTrash.push_back("");
60 }
61 }
62
63
~FilePlaylistItem()64 FilePlaylistItem::~FilePlaylistItem()
65 {
66 }
67
68
69 PlaylistItem*
Clone() const70 FilePlaylistItem::Clone() const
71 {
72 return new (std::nothrow) FilePlaylistItem(*this);
73 }
74
75
76 BArchivable*
Instantiate(BMessage * archive)77 FilePlaylistItem::Instantiate(BMessage* archive)
78 {
79 if (validate_instantiation(archive, "FilePlaylistItem"))
80 return new (std::nothrow) FilePlaylistItem(archive);
81
82 return NULL;
83 }
84
85
86 // #pragma mark -
87
88
89 status_t
Archive(BMessage * into,bool deep) const90 FilePlaylistItem::Archive(BMessage* into, bool deep) const
91 {
92 status_t ret = BArchivable::Archive(into, deep);
93 if (ret != B_OK)
94 return ret;
95 for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
96 BPath path(&fRefs[i]);
97 ret = path.InitCheck();
98 if (ret != B_OK)
99 return ret;
100 ret = into->AddString(kPathKey, path.Path());
101 if (ret != B_OK)
102 return ret;
103 }
104 return B_OK;
105 }
106
107
108 status_t
SetAttribute(const Attribute & attribute,const BString & string)109 FilePlaylistItem::SetAttribute(const Attribute& attribute,
110 const BString& string)
111 {
112 switch (attribute) {
113 case ATTR_STRING_NAME:
114 {
115 BEntry entry(&fRefs[0], false);
116 return entry.Rename(string.String(), false);
117 }
118
119 case ATTR_STRING_KEYWORDS:
120 return _SetAttribute("Meta:Keywords", B_STRING_TYPE,
121 string.String(), string.Length());
122
123 case ATTR_STRING_ARTIST:
124 return _SetAttribute("Audio:Artist", B_STRING_TYPE,
125 string.String(), string.Length());
126 case ATTR_STRING_AUTHOR:
127 return _SetAttribute("Media:Author", B_STRING_TYPE,
128 string.String(), string.Length());
129 case ATTR_STRING_ALBUM:
130 return _SetAttribute("Audio:Album", B_STRING_TYPE,
131 string.String(), string.Length());
132 case ATTR_STRING_TITLE:
133 return _SetAttribute("Media:Title", B_STRING_TYPE,
134 string.String(), string.Length());
135 case ATTR_STRING_AUDIO_BITRATE:
136 return _SetAttribute("Audio:Bitrate", B_STRING_TYPE,
137 string.String(), string.Length());
138 case ATTR_STRING_VIDEO_BITRATE:
139 return _SetAttribute("Video:Bitrate", B_STRING_TYPE,
140 string.String(), string.Length());
141
142 default:
143 return B_NOT_SUPPORTED;
144 }
145 }
146
147
148 status_t
GetAttribute(const Attribute & attribute,BString & string) const149 FilePlaylistItem::GetAttribute(const Attribute& attribute,
150 BString& string) const
151 {
152 if (attribute == ATTR_STRING_NAME) {
153 if (fRefs[0].name == NULL)
154 return B_NAME_NOT_FOUND;
155 string = fRefs[0].name;
156 return B_OK;
157 }
158
159 return B_NOT_SUPPORTED;
160 }
161
162
163 status_t
SetAttribute(const Attribute & attribute,const int32 & value)164 FilePlaylistItem::SetAttribute(const Attribute& attribute,
165 const int32& value)
166 {
167 switch (attribute) {
168 case ATTR_INT32_TRACK:
169 return _SetAttribute("Audio:Track", B_INT32_TYPE, &value,
170 sizeof(int32));
171 case ATTR_INT32_YEAR:
172 return _SetAttribute("Media:Year", B_INT32_TYPE, &value,
173 sizeof(int32));
174 case ATTR_INT32_RATING:
175 return _SetAttribute("Media:Rating", B_INT32_TYPE, &value,
176 sizeof(int32));
177
178 default:
179 return B_NOT_SUPPORTED;
180 }
181 }
182
183
184 status_t
GetAttribute(const Attribute & attribute,int32 & value) const185 FilePlaylistItem::GetAttribute(const Attribute& attribute,
186 int32& value) const
187 {
188 switch (attribute) {
189 case ATTR_INT32_TRACK:
190 return _GetAttribute("Audio:Track", B_INT32_TYPE, &value,
191 sizeof(int32));
192 case ATTR_INT32_YEAR:
193 return _GetAttribute("Media:Year", B_INT32_TYPE, &value,
194 sizeof(int32));
195 case ATTR_INT32_RATING:
196 return _GetAttribute("Media:Rating", B_INT32_TYPE, &value,
197 sizeof(int32));
198
199 default:
200 return B_NOT_SUPPORTED;
201 }
202 }
203
204
205 status_t
SetAttribute(const Attribute & attribute,const int64 & value)206 FilePlaylistItem::SetAttribute(const Attribute& attribute,
207 const int64& value)
208 {
209 switch (attribute) {
210 case ATTR_INT64_FRAME:
211 return _SetAttribute("Media:Frame", B_INT64_TYPE, &value,
212 sizeof(int64));
213 case ATTR_INT64_DURATION:
214 return _SetAttribute("Media:Length", B_INT64_TYPE, &value,
215 sizeof(int64));
216 default:
217 return B_NOT_SUPPORTED;
218 }
219 }
220
221
222 status_t
GetAttribute(const Attribute & attribute,int64 & value) const223 FilePlaylistItem::GetAttribute(const Attribute& attribute,
224 int64& value) const
225 {
226 switch (attribute) {
227 case ATTR_INT64_FRAME:
228 return _GetAttribute("Media:Frame", B_INT64_TYPE, &value,
229 sizeof(int64));
230 case ATTR_INT64_DURATION:
231 return _GetAttribute("Media:Length", B_INT64_TYPE, &value,
232 sizeof(int64));
233 default:
234 return B_NOT_SUPPORTED;
235 }
236 }
237
238
239 status_t
SetAttribute(const Attribute & attribute,const float & value)240 FilePlaylistItem::SetAttribute(const Attribute& attribute,
241 const float& value)
242 {
243 if (attribute == ATTR_FLOAT_VOLUME) {
244 return _SetAttribute("Media:Volume", B_FLOAT_TYPE, &value,
245 sizeof(float));
246 }
247
248 return B_NOT_SUPPORTED;
249 }
250
251
252 status_t
GetAttribute(const Attribute & attribute,float & value) const253 FilePlaylistItem::GetAttribute(const Attribute& attribute,
254 float& value) const
255 {
256 if (attribute == ATTR_FLOAT_VOLUME) {
257 return _GetAttribute("Media:Volume", B_FLOAT_TYPE, &value,
258 sizeof(float));
259 }
260
261 return B_NOT_SUPPORTED;
262 }
263
264
265 // #pragma mark -
266
267
268 BString
LocationURI() const269 FilePlaylistItem::LocationURI() const
270 {
271 BPath path(&fRefs[0]);
272 BString locationURI("file://");
273 locationURI << path.Path();
274 return locationURI;
275 }
276
277
278 status_t
GetIcon(BBitmap * bitmap,icon_size iconSize) const279 FilePlaylistItem::GetIcon(BBitmap* bitmap, icon_size iconSize) const
280 {
281 BNode node(&fRefs[0]);
282 BNodeInfo info(&node);
283 return info.GetTrackerIcon(bitmap, iconSize);
284 }
285
286
287 status_t
MoveIntoTrash()288 FilePlaylistItem::MoveIntoTrash()
289 {
290 if (fNamesInTrash[0].Length() != 0) {
291 // Already in the trash!
292 return B_ERROR;
293 }
294
295 status_t err;
296 err = _MoveIntoTrash(&fRefs, &fNamesInTrash);
297 if (err != B_OK)
298 return err;
299
300 if (fImageRefs.empty())
301 return B_OK;
302
303 err = _MoveIntoTrash(&fImageRefs, &fImageNamesInTrash);
304 if (err != B_OK)
305 return err;
306
307 return B_OK;
308 }
309
310
311 status_t
RestoreFromTrash()312 FilePlaylistItem::RestoreFromTrash()
313 {
314 if (fNamesInTrash[0].Length() <= 0) {
315 // Not in the trash!
316 return B_ERROR;
317 }
318
319 status_t err;
320 err = _RestoreFromTrash(&fRefs, &fNamesInTrash);
321 if (err != B_OK)
322 return err;
323
324 if (fImageRefs.empty())
325 return B_OK;
326
327 err = _RestoreFromTrash(&fImageRefs, &fImageNamesInTrash);
328 if (err != B_OK)
329 return err;
330
331 return B_OK;
332 }
333
334
335 // #pragma mark -
336
337 TrackSupplier*
_CreateTrackSupplier() const338 FilePlaylistItem::_CreateTrackSupplier() const
339 {
340 MediaFileTrackSupplier* supplier
341 = new(std::nothrow) MediaFileTrackSupplier();
342 if (supplier == NULL)
343 return NULL;
344
345 for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
346 BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRefs[i]);
347 if (mediaFile == NULL) {
348 delete supplier;
349 return NULL;
350 }
351 if (supplier->AddMediaFile(mediaFile) != B_OK)
352 delete mediaFile;
353 }
354
355 for (vector<entry_ref>::size_type i = 0; i < fImageRefs.size(); i++) {
356 BBitmap* bitmap = BTranslationUtils::GetBitmap(&fImageRefs[i]);
357 if (bitmap == NULL)
358 continue;
359 if (supplier->AddBitmap(bitmap) != B_OK)
360 delete bitmap;
361 }
362
363 // Search for subtitle files in the same folder
364 // TODO: Error checking
365 BEntry entry(&fRefs[0], true);
366
367 char originalName[B_FILE_NAME_LENGTH];
368 entry.GetName(originalName);
369 BString nameWithoutExtension(originalName);
370 int32 extension = nameWithoutExtension.FindLast('.');
371 if (extension > 0)
372 nameWithoutExtension.Truncate(extension);
373
374 BPath path;
375 entry.GetPath(&path);
376 path.GetParent(&path);
377 BDirectory directory(path.Path());
378 while (directory.GetNextEntry(&entry) == B_OK) {
379 char name[B_FILE_NAME_LENGTH];
380 if (entry.GetName(name) != B_OK)
381 continue;
382 BString nameString(name);
383 if (nameString == originalName)
384 continue;
385 if (nameString.IFindFirst(nameWithoutExtension) < 0)
386 continue;
387
388 BFile file(&entry, B_READ_ONLY);
389 if (file.InitCheck() != B_OK)
390 continue;
391
392 int32 pos = nameString.FindLast('.');
393 if (pos < 0)
394 continue;
395
396 BString extensionString(nameString.String() + pos + 1);
397 extensionString.ToLower();
398
399 BString language = "default";
400 if (pos > 1) {
401 int32 end = pos;
402 while (pos > 0 && *(nameString.String() + pos - 1) != '.')
403 pos--;
404 language.SetTo(nameString.String() + pos, end - pos);
405 }
406
407 if (extensionString == "srt") {
408 SubTitles* subTitles
409 = new(std::nothrow) SubTitlesSRT(&file, language.String());
410 if (subTitles != NULL && !supplier->AddSubTitles(subTitles))
411 delete subTitles;
412 }
413 }
414
415 return supplier;
416 }
417
418
419 status_t
AddRef(const entry_ref & ref)420 FilePlaylistItem::AddRef(const entry_ref& ref)
421 {
422 fRefs.push_back(ref);
423 fNamesInTrash.push_back("");
424 return B_OK;
425 }
426
427
428 status_t
AddImageRef(const entry_ref & ref)429 FilePlaylistItem::AddImageRef(const entry_ref& ref)
430 {
431 fImageRefs.push_back(ref);
432 fImageNamesInTrash.push_back("");
433 return B_OK;
434 }
435
436
437 const entry_ref&
ImageRef() const438 FilePlaylistItem::ImageRef() const
439 {
440 static entry_ref ref;
441
442 if (fImageRefs.empty())
443 return ref;
444
445 return fImageRefs[0];
446 }
447
448
449 bigtime_t
_CalculateDuration()450 FilePlaylistItem::_CalculateDuration()
451 {
452 BMediaFile mediaFile(&Ref());
453
454 if (mediaFile.InitCheck() != B_OK || mediaFile.CountTracks() < 1)
455 return 0;
456
457 return mediaFile.TrackAt(0)->Duration();
458 }
459
460
461 status_t
_SetAttribute(const char * attrName,type_code type,const void * data,size_t size)462 FilePlaylistItem::_SetAttribute(const char* attrName, type_code type,
463 const void* data, size_t size)
464 {
465 BEntry entry(&fRefs[0], true);
466 BNode node(&entry);
467 if (node.InitCheck() != B_OK)
468 return node.InitCheck();
469
470 ssize_t written = node.WriteAttr(attrName, type, 0, data, size);
471 if (written != (ssize_t)size) {
472 if (written < 0)
473 return (status_t)written;
474 return B_IO_ERROR;
475 }
476 return B_OK;
477 }
478
479
480 status_t
_GetAttribute(const char * attrName,type_code type,void * data,size_t size) const481 FilePlaylistItem::_GetAttribute(const char* attrName, type_code type,
482 void* data, size_t size) const
483 {
484 BEntry entry(&fRefs[0], true);
485 BNode node(&entry);
486 if (node.InitCheck() != B_OK)
487 return node.InitCheck();
488
489 ssize_t read = node.ReadAttr(attrName, type, 0, data, size);
490 if (read != (ssize_t)size) {
491 if (read < 0)
492 return (status_t)read;
493 return B_IO_ERROR;
494 }
495 return B_OK;
496 }
497
498
499 status_t
_MoveIntoTrash(vector<entry_ref> * refs,vector<BString> * namesInTrash)500 FilePlaylistItem::_MoveIntoTrash(vector<entry_ref>* refs,
501 vector<BString>* namesInTrash)
502 {
503 char trashPath[B_PATH_NAME_LENGTH];
504 status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device,
505 true /*create it*/, trashPath, B_PATH_NAME_LENGTH);
506 if (err != B_OK) {
507 fprintf(stderr, "failed to find Trash: %s\n", strerror(err));
508 return err;
509 }
510
511 BDirectory trashDir(trashPath);
512 err = trashDir.InitCheck();
513 if (err != B_OK) {
514 fprintf(stderr, "failed to init BDirectory for %s: %s\n",
515 trashPath, strerror(err));
516 return err;
517 }
518
519 for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
520 BEntry entry(&(*refs)[i]);
521 err = entry.InitCheck();
522 if (err != B_OK) {
523 fprintf(stderr, "failed to init BEntry for %s: %s\n",
524 (*refs)[i].name, strerror(err));
525 return err;
526 }
527
528 // Find a unique name for the entry in the trash
529 (*namesInTrash)[i] = (*refs)[i].name;
530 int32 uniqueNameIndex = 1;
531 while (true) {
532 BEntry test(&trashDir, (*namesInTrash)[i].String());
533 if (!test.Exists())
534 break;
535 (*namesInTrash)[i] = (*refs)[i].name;
536 (*namesInTrash)[i] << ' ' << uniqueNameIndex;
537 uniqueNameIndex++;
538 }
539
540 // Remember the original path
541 BPath originalPath;
542 entry.GetPath(&originalPath);
543
544 // Finally, move the entry into the trash
545 err = entry.MoveTo(&trashDir, (*namesInTrash)[i].String());
546 if (err != B_OK) {
547 fprintf(stderr, "failed to move entry into trash %s: %s\n",
548 trashPath, strerror(err));
549 return err;
550 }
551
552 // Allow Tracker to restore this entry
553 BNode node(&entry);
554 BString originalPathString(originalPath.Path());
555 node.WriteAttrString("_trk/original_path", &originalPathString);
556 }
557
558 return B_OK;
559 }
560
561
562 status_t
_RestoreFromTrash(vector<entry_ref> * refs,vector<BString> * namesInTrash)563 FilePlaylistItem::_RestoreFromTrash(vector<entry_ref>* refs,
564 vector<BString>* namesInTrash)
565 {
566 char trashPath[B_PATH_NAME_LENGTH];
567 status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device,
568 false /*create it*/, trashPath, B_PATH_NAME_LENGTH);
569 if (err != B_OK) {
570 fprintf(stderr, "failed to find Trash: %s\n", strerror(err));
571 return err;
572 }
573
574 for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
575 // construct the entry to the file in the trash
576 // TODO: BEntry(const BDirectory* directory, const char* path) is broken!
577 // BEntry entry(trashPath, (*namesInTrash)[i].String());
578 BPath path(trashPath, (*namesInTrash)[i].String());
579 BEntry entry(path.Path());
580 err = entry.InitCheck();
581 if (err != B_OK) {
582 fprintf(stderr, "failed to init BEntry for %s: %s\n",
583 (*namesInTrash)[i].String(), strerror(err));
584 return err;
585 }
586 //entry.GetPath(&path);
587 //printf("moving '%s'\n", path.Path());
588
589 // construct the folder of the original entry_ref
590 node_ref nodeRef;
591 nodeRef.device = (*refs)[i].device;
592 nodeRef.node = (*refs)[i].directory;
593 BDirectory originalDir(&nodeRef);
594 err = originalDir.InitCheck();
595 if (err != B_OK) {
596 fprintf(stderr, "failed to init original BDirectory for "
597 "%s: %s\n", (*refs)[i].name, strerror(err));
598 return err;
599 }
600
601 //path.SetTo(&originalDir, fItems[i].name);
602 //printf("as '%s'\n", path.Path());
603
604 // Reset the name here, the user may have already moved the entry
605 // out of the trash via Tracker for example.
606 (*namesInTrash)[i] = "";
607
608 // Finally, move the entry back into the original folder
609 err = entry.MoveTo(&originalDir, (*refs)[i].name);
610 if (err != B_OK) {
611 fprintf(stderr, "failed to restore entry from trash "
612 "%s: %s\n", (*refs)[i].name, strerror(err));
613 return err;
614 }
615
616 // Remove the attribute that helps Tracker restore the entry.
617 BNode node(&entry);
618 node.RemoveAttr("_trk/original_path");
619 }
620
621 return B_OK;
622 }
623
624