xref: /haiku/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 		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
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
185 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
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
223 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
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
253 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
269 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
279 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
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
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*
338 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
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
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&
438 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
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
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
481 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
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
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