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 23static const char* kPathKey = "path"; 24 25FilePlaylistItem::FilePlaylistItem(const entry_ref& ref) 26{ 27 fRefs.push_back(ref); 28 fNamesInTrash.push_back(""); 29} 30 31 32FilePlaylistItem::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 42FilePlaylistItem::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 64FilePlaylistItem::~FilePlaylistItem() 65{ 66} 67 68 69PlaylistItem* 70FilePlaylistItem::Clone() const 71{ 72 return new (std::nothrow) FilePlaylistItem(*this); 73} 74 75 76BArchivable* 77FilePlaylistItem::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 89status_t 90FilePlaylistItem::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 108status_t 109FilePlaylistItem::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 148status_t 149FilePlaylistItem::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 163status_t 164FilePlaylistItem::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 184status_t 185FilePlaylistItem::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 205status_t 206FilePlaylistItem::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 222status_t 223FilePlaylistItem::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 239status_t 240FilePlaylistItem::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 252status_t 253FilePlaylistItem::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 268BString 269FilePlaylistItem::LocationURI() const 270{ 271 BPath path(&fRefs[0]); 272 BString locationURI("file://"); 273 locationURI << path.Path(); 274 return locationURI; 275} 276 277 278status_t 279FilePlaylistItem::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 287status_t 288FilePlaylistItem::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 311status_t 312FilePlaylistItem::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 337TrackSupplier* 338FilePlaylistItem::_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 419status_t 420FilePlaylistItem::AddRef(const entry_ref& ref) 421{ 422 fRefs.push_back(ref); 423 fNamesInTrash.push_back(""); 424 return B_OK; 425} 426 427 428status_t 429FilePlaylistItem::AddImageRef(const entry_ref& ref) 430{ 431 fImageRefs.push_back(ref); 432 fImageNamesInTrash.push_back(""); 433 return B_OK; 434} 435 436 437const entry_ref& 438FilePlaylistItem::ImageRef() const 439{ 440 static entry_ref ref; 441 442 if (fImageRefs.empty()) 443 return ref; 444 445 return fImageRefs[0]; 446} 447 448 449bigtime_t 450FilePlaylistItem::_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 461status_t 462FilePlaylistItem::_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 480status_t 481FilePlaylistItem::_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 499status_t 500FilePlaylistItem::_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 562status_t 563FilePlaylistItem::_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