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 <Path.h> 17 18 #include "MediaFileTrackSupplier.h" 19 #include "SubTitlesSRT.h" 20 21 22 static const char* kPathKey = "path"; 23 24 25 FilePlaylistItem::FilePlaylistItem(const entry_ref& ref) 26 : 27 fRef(ref), 28 fNameInTrash("") 29 { 30 } 31 32 33 FilePlaylistItem::FilePlaylistItem(const FilePlaylistItem& other) 34 : 35 fRef(other.fRef), 36 fNameInTrash(other.fNameInTrash) 37 { 38 } 39 40 41 FilePlaylistItem::FilePlaylistItem(const BMessage* archive) 42 : 43 fRef(), 44 fNameInTrash("") 45 { 46 const char* path; 47 if (archive != NULL && archive->FindString(kPathKey, &path) == B_OK) { 48 if (get_ref_for_path(path, &fRef) != B_OK) 49 fRef = entry_ref(); 50 } 51 } 52 53 54 FilePlaylistItem::~FilePlaylistItem() 55 { 56 } 57 58 59 PlaylistItem* 60 FilePlaylistItem::Clone() const 61 { 62 return new (std::nothrow) FilePlaylistItem(*this); 63 } 64 65 66 BArchivable* 67 FilePlaylistItem::Instantiate(BMessage* archive) 68 { 69 if (validate_instantiation(archive, "FilePlaylistItem")) 70 return new (std::nothrow) FilePlaylistItem(archive); 71 72 return NULL; 73 } 74 75 76 // #pragma mark - 77 78 79 status_t 80 FilePlaylistItem::Archive(BMessage* into, bool deep) const 81 { 82 status_t ret = BArchivable::Archive(into, deep); 83 if (ret != B_OK) 84 return ret; 85 BPath path(&fRef); 86 ret = path.InitCheck(); 87 if (ret == B_OK) 88 ret = into->AddString(kPathKey, path.Path()); 89 return ret; 90 } 91 92 93 status_t 94 FilePlaylistItem::SetAttribute(const Attribute& attribute, 95 const BString& string) 96 { 97 switch (attribute) { 98 case ATTR_STRING_NAME: 99 { 100 BEntry entry(&fRef, false); 101 return entry.Rename(string.String(), false); 102 } 103 104 case ATTR_STRING_KEYWORDS: 105 return _SetAttribute("Meta:Keywords", B_STRING_TYPE, 106 string.String(), string.Length()); 107 108 case ATTR_STRING_ARTIST: 109 return _SetAttribute("Audio:Artist", B_STRING_TYPE, 110 string.String(), string.Length()); 111 case ATTR_STRING_AUTHOR: 112 return _SetAttribute("Media:Author", B_STRING_TYPE, 113 string.String(), string.Length()); 114 case ATTR_STRING_ALBUM: 115 return _SetAttribute("Audio:Album", B_STRING_TYPE, 116 string.String(), string.Length()); 117 case ATTR_STRING_TITLE: 118 return _SetAttribute("Media:Title", B_STRING_TYPE, 119 string.String(), string.Length()); 120 case ATTR_STRING_AUDIO_BITRATE: 121 return _SetAttribute("Audio:Bitrate", B_STRING_TYPE, 122 string.String(), string.Length()); 123 case ATTR_STRING_VIDEO_BITRATE: 124 return _SetAttribute("Video:Bitrate", B_STRING_TYPE, 125 string.String(), string.Length()); 126 127 default: 128 return B_NOT_SUPPORTED; 129 } 130 } 131 132 133 status_t 134 FilePlaylistItem::GetAttribute(const Attribute& attribute, 135 BString& string) const 136 { 137 if (attribute == ATTR_STRING_NAME) { 138 string = fRef.name; 139 return B_OK; 140 } 141 142 return B_NOT_SUPPORTED; 143 } 144 145 146 status_t 147 FilePlaylistItem::SetAttribute(const Attribute& attribute, 148 const int32& value) 149 { 150 switch (attribute) { 151 case ATTR_INT32_TRACK: 152 return _SetAttribute("Audio:Track", B_INT32_TYPE, &value, 153 sizeof(int32)); 154 case ATTR_INT32_YEAR: 155 return _SetAttribute("Media:Year", B_INT32_TYPE, &value, 156 sizeof(int32)); 157 case ATTR_INT32_RATING: 158 return _SetAttribute("Media:Rating", B_INT32_TYPE, &value, 159 sizeof(int32)); 160 161 default: 162 return B_NOT_SUPPORTED; 163 } 164 } 165 166 167 status_t 168 FilePlaylistItem::GetAttribute(const Attribute& attribute, 169 int32& value) const 170 { 171 return B_NOT_SUPPORTED; 172 } 173 174 175 status_t 176 FilePlaylistItem::SetAttribute(const Attribute& attribute, 177 const int64& value) 178 { 179 return B_NOT_SUPPORTED; 180 } 181 182 183 status_t 184 FilePlaylistItem::GetAttribute(const Attribute& attribute, 185 int64& value) const 186 { 187 return B_NOT_SUPPORTED; 188 } 189 190 191 // #pragma mark - 192 193 194 BString 195 FilePlaylistItem::LocationURI() const 196 { 197 BPath path(&fRef); 198 BString locationURI("file://"); 199 locationURI << path.Path(); 200 return locationURI; 201 } 202 203 204 status_t 205 FilePlaylistItem::GetIcon(BBitmap* bitmap, icon_size iconSize) const 206 { 207 BNode node(&fRef); 208 BNodeInfo info(&node); 209 return info.GetTrackerIcon(bitmap, iconSize); 210 } 211 212 213 status_t 214 FilePlaylistItem::MoveIntoTrash() 215 { 216 if (fNameInTrash.Length() != 0) { 217 // Already in the trash! 218 return B_ERROR; 219 } 220 221 char trashPath[B_PATH_NAME_LENGTH]; 222 status_t err = find_directory(B_TRASH_DIRECTORY, fRef.device, 223 true /*create it*/, trashPath, B_PATH_NAME_LENGTH); 224 if (err != B_OK) { 225 fprintf(stderr, "failed to find Trash: %s\n", strerror(err)); 226 return err; 227 } 228 229 BEntry entry(&fRef); 230 err = entry.InitCheck(); 231 if (err != B_OK) { 232 fprintf(stderr, "failed to init BEntry for %s: %s\n", 233 fRef.name, strerror(err)); 234 return err; 235 } 236 BDirectory trashDir(trashPath); 237 if (err != B_OK) { 238 fprintf(stderr, "failed to init BDirectory for %s: %s\n", 239 trashPath, strerror(err)); 240 return err; 241 } 242 243 // Find a unique name for the entry in the trash 244 fNameInTrash = fRef.name; 245 int32 uniqueNameIndex = 1; 246 while (true) { 247 BEntry test(&trashDir, fNameInTrash.String()); 248 if (!test.Exists()) 249 break; 250 fNameInTrash = fRef.name; 251 fNameInTrash << ' ' << uniqueNameIndex; 252 uniqueNameIndex++; 253 } 254 255 // Remember the original path 256 BPath originalPath; 257 entry.GetPath(&originalPath); 258 259 // Finally, move the entry into the trash 260 err = entry.MoveTo(&trashDir, fNameInTrash.String()); 261 if (err != B_OK) { 262 fprintf(stderr, "failed to move entry into trash %s: %s\n", 263 trashPath, strerror(err)); 264 return err; 265 } 266 267 // Allow Tracker to restore this entry 268 BNode node(&entry); 269 BString originalPathString(originalPath.Path()); 270 node.WriteAttrString("_trk/original_path", &originalPathString); 271 272 return err; 273 } 274 275 276 277 status_t 278 FilePlaylistItem::RestoreFromTrash() 279 { 280 if (fNameInTrash.Length() <= 0) { 281 // Not in the trash! 282 return B_ERROR; 283 } 284 285 char trashPath[B_PATH_NAME_LENGTH]; 286 status_t err = find_directory(B_TRASH_DIRECTORY, fRef.device, 287 false /*create it*/, trashPath, B_PATH_NAME_LENGTH); 288 if (err != B_OK) { 289 fprintf(stderr, "failed to find Trash: %s\n", strerror(err)); 290 return err; 291 } 292 // construct the entry to the file in the trash 293 // TODO: BEntry(const BDirectory* directory, const char* path) is broken! 294 // BEntry entry(trashPath, fNamesInTrash[i].String()); 295 BPath path(trashPath, fNameInTrash.String()); 296 BEntry entry(path.Path()); 297 err = entry.InitCheck(); 298 if (err != B_OK) { 299 fprintf(stderr, "failed to init BEntry for %s: %s\n", 300 fNameInTrash.String(), strerror(err)); 301 return err; 302 } 303 //entry.GetPath(&path); 304 //printf("moving '%s'\n", path.Path()); 305 306 // construct the folder of the original entry_ref 307 node_ref nodeRef; 308 nodeRef.device = fRef.device; 309 nodeRef.node = fRef.directory; 310 BDirectory originalDir(&nodeRef); 311 err = originalDir.InitCheck(); 312 if (err != B_OK) { 313 fprintf(stderr, "failed to init original BDirectory for " 314 "%s: %s\n", fRef.name, strerror(err)); 315 return err; 316 } 317 318 //path.SetTo(&originalDir, fItems[i].name); 319 //printf("as '%s'\n", path.Path()); 320 321 // Reset the name here, the user may have already moved the entry 322 // out of the trash via Tracker for example. 323 fNameInTrash = ""; 324 325 // Finally, move the entry back into the original folder 326 err = entry.MoveTo(&originalDir, fRef.name); 327 if (err != B_OK) { 328 fprintf(stderr, "failed to restore entry from trash " 329 "%s: %s\n", fRef.name, strerror(err)); 330 return err; 331 } 332 333 // Remove the attribute that helps Tracker restore the entry. 334 BNode node(&entry); 335 node.RemoveAttr("_trk/original_path"); 336 337 return err; 338 } 339 340 341 // #pragma mark - 342 343 344 TrackSupplier* 345 FilePlaylistItem::CreateTrackSupplier() const 346 { 347 BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRef); 348 if (mediaFile == NULL) 349 return NULL; 350 MediaFileTrackSupplier* supplier 351 = new(std::nothrow) MediaFileTrackSupplier(mediaFile); 352 if (supplier == NULL) { 353 delete mediaFile; 354 return NULL; 355 } 356 357 // Search for subtitle files in the same folder 358 // TODO: Error checking 359 BEntry entry(&fRef, true); 360 361 char originalName[B_FILE_NAME_LENGTH]; 362 entry.GetName(originalName); 363 BString nameWithoutExtension(originalName); 364 int32 extension = nameWithoutExtension.FindLast('.'); 365 if (extension > 0) 366 nameWithoutExtension.Truncate(extension); 367 368 BPath path; 369 entry.GetPath(&path); 370 path.GetParent(&path); 371 BDirectory directory(path.Path()); 372 while (directory.GetNextEntry(&entry) == B_OK) { 373 char name[B_FILE_NAME_LENGTH]; 374 if (entry.GetName(name) != B_OK) 375 continue; 376 BString nameString(name); 377 if (nameString == originalName) 378 continue; 379 if (nameString.IFindFirst(nameWithoutExtension) < 0) 380 continue; 381 382 BFile file(&entry, B_READ_ONLY); 383 if (file.InitCheck() != B_OK) 384 continue; 385 386 int32 pos = nameString.FindLast('.'); 387 if (pos < 0) 388 continue; 389 390 BString extensionString(nameString.String() + pos + 1); 391 extensionString.ToLower(); 392 393 BString language = "default"; 394 if (pos > 1) { 395 int32 end = pos; 396 while (pos > 0 && *(nameString.String() + pos - 1) != '.') 397 pos--; 398 language.SetTo(nameString.String() + pos, end - pos); 399 } 400 401 if (extensionString == "srt") { 402 SubTitles* subTitles 403 = new(std::nothrow) SubTitlesSRT(&file, language.String()); 404 if (subTitles != NULL && !supplier->AddSubTitles(subTitles)) 405 delete subTitles; 406 } 407 } 408 409 return supplier; 410 } 411 412 413 status_t 414 FilePlaylistItem::_SetAttribute(const char* attrName, type_code type, 415 const void* data, size_t size) 416 { 417 BEntry entry(&fRef, true); 418 BNode node(&entry); 419 if (node.InitCheck() != B_OK) 420 return node.InitCheck(); 421 422 ssize_t written = node.WriteAttr(attrName, type, 0, data, size); 423 if (written != (ssize_t)size) { 424 if (written < 0) 425 return (status_t)written; 426 return B_IO_ERROR; 427 } 428 return B_OK; 429 } 430 431 432 status_t 433 FilePlaylistItem::_GetAttribute(const char* attrName, type_code type, 434 void* data, size_t size) 435 { 436 BEntry entry(&fRef, true); 437 BNode node(&entry); 438 if (node.InitCheck() != B_OK) 439 return node.InitCheck(); 440 441 ssize_t read = node.ReadAttr(attrName, type, 0, data, size); 442 if (read != (ssize_t)size) { 443 if (read < 0) 444 return (status_t)read; 445 return B_IO_ERROR; 446 } 447 return B_OK; 448 } 449 450