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