xref: /haiku/src/apps/mediaplayer/playlist/FilePlaylistItem.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
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