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 25 FilePlaylistItem::FilePlaylistItem(const entry_ref& ref) 26 { 27 fRefs.push_back(ref); 28 fNamesInTrash.push_back(""); 29 } 30 31 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 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 64 FilePlaylistItem::~FilePlaylistItem() 65 { 66 } 67 68 69 PlaylistItem* 70 FilePlaylistItem::Clone() const 71 { 72 return new (std::nothrow) FilePlaylistItem(*this); 73 } 74 75 76 BArchivable* 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 90 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 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 149 FilePlaylistItem::GetAttribute(const Attribute& attribute, 150 BString& string) const 151 { 152 if (attribute == ATTR_STRING_NAME) { 153 string = fRefs[0].name; 154 return B_OK; 155 } 156 157 return B_NOT_SUPPORTED; 158 } 159 160 161 status_t 162 FilePlaylistItem::SetAttribute(const Attribute& attribute, 163 const int32& value) 164 { 165 switch (attribute) { 166 case ATTR_INT32_TRACK: 167 return _SetAttribute("Audio:Track", B_INT32_TYPE, &value, 168 sizeof(int32)); 169 case ATTR_INT32_YEAR: 170 return _SetAttribute("Media:Year", B_INT32_TYPE, &value, 171 sizeof(int32)); 172 case ATTR_INT32_RATING: 173 return _SetAttribute("Media:Rating", B_INT32_TYPE, &value, 174 sizeof(int32)); 175 176 default: 177 return B_NOT_SUPPORTED; 178 } 179 } 180 181 182 status_t 183 FilePlaylistItem::GetAttribute(const Attribute& attribute, 184 int32& value) const 185 { 186 return B_NOT_SUPPORTED; 187 } 188 189 190 status_t 191 FilePlaylistItem::SetAttribute(const Attribute& attribute, 192 const int64& value) 193 { 194 switch (attribute) { 195 case ATTR_INT64_FRAME: 196 return _SetAttribute("Media:Frame", B_INT64_TYPE, &value, 197 sizeof(int64)); 198 case ATTR_INT64_DURATION: 199 return _SetAttribute("Media:Length", B_INT64_TYPE, &value, 200 sizeof(int64)); 201 default: 202 return B_NOT_SUPPORTED; 203 } 204 } 205 206 207 status_t 208 FilePlaylistItem::GetAttribute(const Attribute& attribute, 209 int64& value) const 210 { 211 switch (attribute) { 212 case ATTR_INT64_FRAME: 213 return _GetAttribute("Media:Frame", B_INT64_TYPE, &value, 214 sizeof(int64)); 215 case ATTR_INT64_DURATION: 216 return _GetAttribute("Media:Length", B_INT64_TYPE, &value, 217 sizeof(int64)); 218 default: 219 return B_NOT_SUPPORTED; 220 } 221 } 222 223 224 status_t 225 FilePlaylistItem::SetAttribute(const Attribute& attribute, 226 const float& value) 227 { 228 if (attribute == ATTR_FLOAT_VOLUME) { 229 return _SetAttribute("Media:Volume", B_FLOAT_TYPE, &value, 230 sizeof(float)); 231 } 232 233 return B_NOT_SUPPORTED; 234 } 235 236 237 status_t 238 FilePlaylistItem::GetAttribute(const Attribute& attribute, 239 float& value) const 240 { 241 if (attribute == ATTR_FLOAT_VOLUME) { 242 return _GetAttribute("Media:Volume", B_FLOAT_TYPE, &value, 243 sizeof(float)); 244 } 245 246 return B_NOT_SUPPORTED; 247 } 248 249 250 // #pragma mark - 251 252 253 BString 254 FilePlaylistItem::LocationURI() const 255 { 256 BPath path(&fRefs[0]); 257 BString locationURI("file://"); 258 locationURI << path.Path(); 259 return locationURI; 260 } 261 262 263 status_t 264 FilePlaylistItem::GetIcon(BBitmap* bitmap, icon_size iconSize) const 265 { 266 BNode node(&fRefs[0]); 267 BNodeInfo info(&node); 268 return info.GetTrackerIcon(bitmap, iconSize); 269 } 270 271 272 status_t 273 FilePlaylistItem::MoveIntoTrash() 274 { 275 if (fNamesInTrash[0].Length() != 0) { 276 // Already in the trash! 277 return B_ERROR; 278 } 279 280 status_t err; 281 err = _MoveIntoTrash(&fRefs, &fNamesInTrash); 282 if (err != B_OK) 283 return err; 284 285 if (fImageRefs.empty()) 286 return B_OK; 287 288 err = _MoveIntoTrash(&fImageRefs, &fImageNamesInTrash); 289 if (err != B_OK) 290 return err; 291 292 return B_OK; 293 } 294 295 296 status_t 297 FilePlaylistItem::RestoreFromTrash() 298 { 299 if (fNamesInTrash[0].Length() <= 0) { 300 // Not in the trash! 301 return B_ERROR; 302 } 303 304 status_t err; 305 err = _RestoreFromTrash(&fRefs, &fNamesInTrash); 306 if (err != B_OK) 307 return err; 308 309 if (fImageRefs.empty()) 310 return B_OK; 311 312 err = _RestoreFromTrash(&fImageRefs, &fImageNamesInTrash); 313 if (err != B_OK) 314 return err; 315 316 return B_OK; 317 } 318 319 320 // #pragma mark - 321 322 TrackSupplier* 323 FilePlaylistItem::_CreateTrackSupplier() const 324 { 325 MediaFileTrackSupplier* supplier 326 = new(std::nothrow) MediaFileTrackSupplier(); 327 if (supplier == NULL) 328 return NULL; 329 330 for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) { 331 BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRefs[i]); 332 if (mediaFile == NULL) { 333 delete supplier; 334 return NULL; 335 } 336 if (supplier->AddMediaFile(mediaFile) != B_OK) 337 delete mediaFile; 338 } 339 340 for (vector<entry_ref>::size_type i = 0; i < fImageRefs.size(); i++) { 341 BBitmap* bitmap = BTranslationUtils::GetBitmap(&fImageRefs[i]); 342 if (bitmap == NULL) 343 continue; 344 if (supplier->AddBitmap(bitmap) != B_OK) 345 delete bitmap; 346 } 347 348 // Search for subtitle files in the same folder 349 // TODO: Error checking 350 BEntry entry(&fRefs[0], true); 351 352 char originalName[B_FILE_NAME_LENGTH]; 353 entry.GetName(originalName); 354 BString nameWithoutExtension(originalName); 355 int32 extension = nameWithoutExtension.FindLast('.'); 356 if (extension > 0) 357 nameWithoutExtension.Truncate(extension); 358 359 BPath path; 360 entry.GetPath(&path); 361 path.GetParent(&path); 362 BDirectory directory(path.Path()); 363 while (directory.GetNextEntry(&entry) == B_OK) { 364 char name[B_FILE_NAME_LENGTH]; 365 if (entry.GetName(name) != B_OK) 366 continue; 367 BString nameString(name); 368 if (nameString == originalName) 369 continue; 370 if (nameString.IFindFirst(nameWithoutExtension) < 0) 371 continue; 372 373 BFile file(&entry, B_READ_ONLY); 374 if (file.InitCheck() != B_OK) 375 continue; 376 377 int32 pos = nameString.FindLast('.'); 378 if (pos < 0) 379 continue; 380 381 BString extensionString(nameString.String() + pos + 1); 382 extensionString.ToLower(); 383 384 BString language = "default"; 385 if (pos > 1) { 386 int32 end = pos; 387 while (pos > 0 && *(nameString.String() + pos - 1) != '.') 388 pos--; 389 language.SetTo(nameString.String() + pos, end - pos); 390 } 391 392 if (extensionString == "srt") { 393 SubTitles* subTitles 394 = new(std::nothrow) SubTitlesSRT(&file, language.String()); 395 if (subTitles != NULL && !supplier->AddSubTitles(subTitles)) 396 delete subTitles; 397 } 398 } 399 400 return supplier; 401 } 402 403 404 status_t 405 FilePlaylistItem::AddRef(const entry_ref& ref) 406 { 407 fRefs.push_back(ref); 408 fNamesInTrash.push_back(""); 409 return B_OK; 410 } 411 412 413 status_t 414 FilePlaylistItem::AddImageRef(const entry_ref& ref) 415 { 416 fImageRefs.push_back(ref); 417 fImageNamesInTrash.push_back(""); 418 return B_OK; 419 } 420 421 422 const entry_ref& 423 FilePlaylistItem::ImageRef() const 424 { 425 static entry_ref ref; 426 427 if (fImageRefs.empty()) 428 return ref; 429 430 return fImageRefs[0]; 431 } 432 433 434 bigtime_t 435 FilePlaylistItem::_CalculateDuration() 436 { 437 BMediaFile mediaFile(&Ref()); 438 439 if (mediaFile.InitCheck() != B_OK || mediaFile.CountTracks() < 1) 440 return 0; 441 442 return mediaFile.TrackAt(0)->Duration(); 443 } 444 445 446 status_t 447 FilePlaylistItem::_SetAttribute(const char* attrName, type_code type, 448 const void* data, size_t size) 449 { 450 BEntry entry(&fRefs[0], true); 451 BNode node(&entry); 452 if (node.InitCheck() != B_OK) 453 return node.InitCheck(); 454 455 ssize_t written = node.WriteAttr(attrName, type, 0, data, size); 456 if (written != (ssize_t)size) { 457 if (written < 0) 458 return (status_t)written; 459 return B_IO_ERROR; 460 } 461 return B_OK; 462 } 463 464 465 status_t 466 FilePlaylistItem::_GetAttribute(const char* attrName, type_code type, 467 void* data, size_t size) const 468 { 469 BEntry entry(&fRefs[0], true); 470 BNode node(&entry); 471 if (node.InitCheck() != B_OK) 472 return node.InitCheck(); 473 474 ssize_t read = node.ReadAttr(attrName, type, 0, data, size); 475 if (read != (ssize_t)size) { 476 if (read < 0) 477 return (status_t)read; 478 return B_IO_ERROR; 479 } 480 return B_OK; 481 } 482 483 484 status_t 485 FilePlaylistItem::_MoveIntoTrash(vector<entry_ref>* refs, 486 vector<BString>* namesInTrash) 487 { 488 char trashPath[B_PATH_NAME_LENGTH]; 489 status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device, 490 true /*create it*/, trashPath, B_PATH_NAME_LENGTH); 491 if (err != B_OK) { 492 fprintf(stderr, "failed to find Trash: %s\n", strerror(err)); 493 return err; 494 } 495 496 BDirectory trashDir(trashPath); 497 err = trashDir.InitCheck(); 498 if (err != B_OK) { 499 fprintf(stderr, "failed to init BDirectory for %s: %s\n", 500 trashPath, strerror(err)); 501 return err; 502 } 503 504 for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) { 505 BEntry entry(&(*refs)[i]); 506 err = entry.InitCheck(); 507 if (err != B_OK) { 508 fprintf(stderr, "failed to init BEntry for %s: %s\n", 509 (*refs)[i].name, strerror(err)); 510 return err; 511 } 512 513 // Find a unique name for the entry in the trash 514 (*namesInTrash)[i] = (*refs)[i].name; 515 int32 uniqueNameIndex = 1; 516 while (true) { 517 BEntry test(&trashDir, (*namesInTrash)[i].String()); 518 if (!test.Exists()) 519 break; 520 (*namesInTrash)[i] = (*refs)[i].name; 521 (*namesInTrash)[i] << ' ' << uniqueNameIndex; 522 uniqueNameIndex++; 523 } 524 525 // Remember the original path 526 BPath originalPath; 527 entry.GetPath(&originalPath); 528 529 // Finally, move the entry into the trash 530 err = entry.MoveTo(&trashDir, (*namesInTrash)[i].String()); 531 if (err != B_OK) { 532 fprintf(stderr, "failed to move entry into trash %s: %s\n", 533 trashPath, strerror(err)); 534 return err; 535 } 536 537 // Allow Tracker to restore this entry 538 BNode node(&entry); 539 BString originalPathString(originalPath.Path()); 540 node.WriteAttrString("_trk/original_path", &originalPathString); 541 } 542 543 return B_OK; 544 } 545 546 547 status_t 548 FilePlaylistItem::_RestoreFromTrash(vector<entry_ref>* refs, 549 vector<BString>* namesInTrash) 550 { 551 char trashPath[B_PATH_NAME_LENGTH]; 552 status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device, 553 false /*create it*/, trashPath, B_PATH_NAME_LENGTH); 554 if (err != B_OK) { 555 fprintf(stderr, "failed to find Trash: %s\n", strerror(err)); 556 return err; 557 } 558 559 for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) { 560 // construct the entry to the file in the trash 561 // TODO: BEntry(const BDirectory* directory, const char* path) is broken! 562 // BEntry entry(trashPath, (*namesInTrash)[i].String()); 563 BPath path(trashPath, (*namesInTrash)[i].String()); 564 BEntry entry(path.Path()); 565 err = entry.InitCheck(); 566 if (err != B_OK) { 567 fprintf(stderr, "failed to init BEntry for %s: %s\n", 568 (*namesInTrash)[i].String(), strerror(err)); 569 return err; 570 } 571 //entry.GetPath(&path); 572 //printf("moving '%s'\n", path.Path()); 573 574 // construct the folder of the original entry_ref 575 node_ref nodeRef; 576 nodeRef.device = (*refs)[i].device; 577 nodeRef.node = (*refs)[i].directory; 578 BDirectory originalDir(&nodeRef); 579 err = originalDir.InitCheck(); 580 if (err != B_OK) { 581 fprintf(stderr, "failed to init original BDirectory for " 582 "%s: %s\n", (*refs)[i].name, strerror(err)); 583 return err; 584 } 585 586 //path.SetTo(&originalDir, fItems[i].name); 587 //printf("as '%s'\n", path.Path()); 588 589 // Reset the name here, the user may have already moved the entry 590 // out of the trash via Tracker for example. 591 (*namesInTrash)[i] = ""; 592 593 // Finally, move the entry back into the original folder 594 err = entry.MoveTo(&originalDir, (*refs)[i].name); 595 if (err != B_OK) { 596 fprintf(stderr, "failed to restore entry from trash " 597 "%s: %s\n", (*refs)[i].name, strerror(err)); 598 return err; 599 } 600 601 // Remove the attribute that helps Tracker restore the entry. 602 BNode node(&entry); 603 node.RemoveAttr("_trk/original_path"); 604 } 605 606 return B_OK; 607 } 608 609