xref: /haiku/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp (revision a6e73cb9e8addfe832c064bfcb68067f1c2fa3eb)
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 	if (err != B_OK) {
441 		fprintf(stderr, "failed to init BDirectory for %s: %s\n",
442 			trashPath, strerror(err));
443 		return err;
444 	}
445 
446 	for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
447 		BEntry entry(&(*refs)[i]);
448 		err = entry.InitCheck();
449 		if (err != B_OK) {
450 			fprintf(stderr, "failed to init BEntry for %s: %s\n",
451 				(*refs)[i].name, strerror(err));
452 			return err;
453 		}
454 
455 		// Find a unique name for the entry in the trash
456 		(*namesInTrash)[i] = (*refs)[i].name;
457 		int32 uniqueNameIndex = 1;
458 		while (true) {
459 			BEntry test(&trashDir, (*namesInTrash)[i].String());
460 			if (!test.Exists())
461 				break;
462 			(*namesInTrash)[i] = (*refs)[i].name;
463 			(*namesInTrash)[i] << ' ' << uniqueNameIndex;
464 			uniqueNameIndex++;
465 		}
466 
467 		// Remember the original path
468 		BPath originalPath;
469 		entry.GetPath(&originalPath);
470 
471 		// Finally, move the entry into the trash
472 		err = entry.MoveTo(&trashDir, (*namesInTrash)[i].String());
473 		if (err != B_OK) {
474 			fprintf(stderr, "failed to move entry into trash %s: %s\n",
475 				trashPath, strerror(err));
476 			return err;
477 		}
478 
479 		// Allow Tracker to restore this entry
480 		BNode node(&entry);
481 		BString originalPathString(originalPath.Path());
482 		node.WriteAttrString("_trk/original_path", &originalPathString);
483 	}
484 
485 	return B_OK;
486 }
487 
488 
489 status_t
490 FilePlaylistItem::_RestoreFromTrash(vector<entry_ref>* refs,
491 	vector<BString>* namesInTrash)
492 {
493 	char trashPath[B_PATH_NAME_LENGTH];
494 	status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device,
495 		false /*create it*/, trashPath, B_PATH_NAME_LENGTH);
496 	if (err != B_OK) {
497 		fprintf(stderr, "failed to find Trash: %s\n", strerror(err));
498 		return err;
499 	}
500 
501 	for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
502 		// construct the entry to the file in the trash
503 		// TODO: BEntry(const BDirectory* directory, const char* path) is broken!
504 		//	BEntry entry(trashPath, (*namesInTrash)[i].String());
505 		BPath path(trashPath, (*namesInTrash)[i].String());
506 		BEntry entry(path.Path());
507 		err = entry.InitCheck();
508 		if (err != B_OK) {
509 			fprintf(stderr, "failed to init BEntry for %s: %s\n",
510 				(*namesInTrash)[i].String(), strerror(err));
511 			return err;
512 		}
513 		//entry.GetPath(&path);
514 		//printf("moving '%s'\n", path.Path());
515 
516 		// construct the folder of the original entry_ref
517 		node_ref nodeRef;
518 		nodeRef.device = (*refs)[i].device;
519 		nodeRef.node = (*refs)[i].directory;
520 		BDirectory originalDir(&nodeRef);
521 		err = originalDir.InitCheck();
522 		if (err != B_OK) {
523 			fprintf(stderr, "failed to init original BDirectory for "
524 				"%s: %s\n", (*refs)[i].name, strerror(err));
525 			return err;
526 		}
527 
528 		//path.SetTo(&originalDir, fItems[i].name);
529 		//printf("as '%s'\n", path.Path());
530 
531 		// Reset the name here, the user may have already moved the entry
532 		// out of the trash via Tracker for example.
533 		(*namesInTrash)[i] = "";
534 
535 		// Finally, move the entry back into the original folder
536 		err = entry.MoveTo(&originalDir, (*refs)[i].name);
537 		if (err != B_OK) {
538 			fprintf(stderr, "failed to restore entry from trash "
539 				"%s: %s\n", (*refs)[i].name, strerror(err));
540 			return err;
541 		}
542 
543 		// Remove the attribute that helps Tracker restore the entry.
544 		BNode node(&entry);
545 		node.RemoveAttr("_trk/original_path");
546 	}
547 
548 	return B_OK;
549 }
550 
551