* Copyright 2009-2010 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "FilePlaylistItem.h"
#include <stdio.h>
#include <new>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <MediaFile.h>
#include <MediaTrack.h>
#include <Path.h>
#include <TranslationUtils.h>
#include "MediaFileTrackSupplier.h"
#include "SubTitlesSRT.h"
static const char* kPathKey = "path";
FilePlaylistItem::FilePlaylistItem(const entry_ref& ref)
{
fRefs.push_back(ref);
fNamesInTrash.push_back("");
}
FilePlaylistItem::FilePlaylistItem(const FilePlaylistItem& other)
:
fRefs(other.fRefs),
fNamesInTrash(other.fNamesInTrash),
fImageRefs(other.fImageRefs),
fImageNamesInTrash(other.fImageNamesInTrash)
{
}
FilePlaylistItem::FilePlaylistItem(const BMessage* archive)
{
const char* path;
entry_ref ref;
if (archive != NULL) {
int32 i = 0;
while (archive->FindString(kPathKey, i, &path) == B_OK) {
if (get_ref_for_path(path, &ref) == B_OK) {
fRefs.push_back(ref);
}
i++;
}
}
if (fRefs.empty()) {
fRefs.push_back(entry_ref());
}
for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
fNamesInTrash.push_back("");
}
}
FilePlaylistItem::~FilePlaylistItem()
{
}
PlaylistItem*
FilePlaylistItem::Clone() const
{
return new (std::nothrow) FilePlaylistItem(*this);
}
BArchivable*
FilePlaylistItem::Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "FilePlaylistItem"))
return new (std::nothrow) FilePlaylistItem(archive);
return NULL;
}
status_t
FilePlaylistItem::Archive(BMessage* into, bool deep) const
{
status_t ret = BArchivable::Archive(into, deep);
if (ret != B_OK)
return ret;
for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
BPath path(&fRefs[i]);
ret = path.InitCheck();
if (ret != B_OK)
return ret;
ret = into->AddString(kPathKey, path.Path());
if (ret != B_OK)
return ret;
}
return B_OK;
}
status_t
FilePlaylistItem::SetAttribute(const Attribute& attribute,
const BString& string)
{
switch (attribute) {
case ATTR_STRING_NAME:
{
BEntry entry(&fRefs[0], false);
return entry.Rename(string.String(), false);
}
case ATTR_STRING_KEYWORDS:
return _SetAttribute("Meta:Keywords", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_ARTIST:
return _SetAttribute("Audio:Artist", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_AUTHOR:
return _SetAttribute("Media:Author", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_ALBUM:
return _SetAttribute("Audio:Album", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_TITLE:
return _SetAttribute("Media:Title", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_AUDIO_BITRATE:
return _SetAttribute("Audio:Bitrate", B_STRING_TYPE,
string.String(), string.Length());
case ATTR_STRING_VIDEO_BITRATE:
return _SetAttribute("Video:Bitrate", B_STRING_TYPE,
string.String(), string.Length());
default:
return B_NOT_SUPPORTED;
}
}
status_t
FilePlaylistItem::GetAttribute(const Attribute& attribute,
BString& string) const
{
if (attribute == ATTR_STRING_NAME) {
if (fRefs[0].name == NULL)
return B_NAME_NOT_FOUND;
string = fRefs[0].name;
return B_OK;
}
return B_NOT_SUPPORTED;
}
status_t
FilePlaylistItem::SetAttribute(const Attribute& attribute,
const int32& value)
{
switch (attribute) {
case ATTR_INT32_TRACK:
return _SetAttribute("Audio:Track", B_INT32_TYPE, &value,
sizeof(int32));
case ATTR_INT32_YEAR:
return _SetAttribute("Media:Year", B_INT32_TYPE, &value,
sizeof(int32));
case ATTR_INT32_RATING:
return _SetAttribute("Media:Rating", B_INT32_TYPE, &value,
sizeof(int32));
default:
return B_NOT_SUPPORTED;
}
}
status_t
FilePlaylistItem::GetAttribute(const Attribute& attribute,
int32& value) const
{
switch (attribute) {
case ATTR_INT32_TRACK:
return _GetAttribute("Audio:Track", B_INT32_TYPE, &value,
sizeof(int32));
case ATTR_INT32_YEAR:
return _GetAttribute("Media:Year", B_INT32_TYPE, &value,
sizeof(int32));
case ATTR_INT32_RATING:
return _GetAttribute("Media:Rating", B_INT32_TYPE, &value,
sizeof(int32));
default:
return B_NOT_SUPPORTED;
}
}
status_t
FilePlaylistItem::SetAttribute(const Attribute& attribute,
const int64& value)
{
switch (attribute) {
case ATTR_INT64_FRAME:
return _SetAttribute("Media:Frame", B_INT64_TYPE, &value,
sizeof(int64));
case ATTR_INT64_DURATION:
return _SetAttribute("Media:Length", B_INT64_TYPE, &value,
sizeof(int64));
default:
return B_NOT_SUPPORTED;
}
}
status_t
FilePlaylistItem::GetAttribute(const Attribute& attribute,
int64& value) const
{
switch (attribute) {
case ATTR_INT64_FRAME:
return _GetAttribute("Media:Frame", B_INT64_TYPE, &value,
sizeof(int64));
case ATTR_INT64_DURATION:
return _GetAttribute("Media:Length", B_INT64_TYPE, &value,
sizeof(int64));
default:
return B_NOT_SUPPORTED;
}
}
status_t
FilePlaylistItem::SetAttribute(const Attribute& attribute,
const float& value)
{
if (attribute == ATTR_FLOAT_VOLUME) {
return _SetAttribute("Media:Volume", B_FLOAT_TYPE, &value,
sizeof(float));
}
return B_NOT_SUPPORTED;
}
status_t
FilePlaylistItem::GetAttribute(const Attribute& attribute,
float& value) const
{
if (attribute == ATTR_FLOAT_VOLUME) {
return _GetAttribute("Media:Volume", B_FLOAT_TYPE, &value,
sizeof(float));
}
return B_NOT_SUPPORTED;
}
BString
FilePlaylistItem::LocationURI() const
{
BPath path(&fRefs[0]);
BString locationURI("file://");
locationURI << path.Path();
return locationURI;
}
status_t
FilePlaylistItem::GetIcon(BBitmap* bitmap, icon_size iconSize) const
{
BNode node(&fRefs[0]);
BNodeInfo info(&node);
return info.GetTrackerIcon(bitmap, iconSize);
}
status_t
FilePlaylistItem::MoveIntoTrash()
{
if (fNamesInTrash[0].Length() != 0) {
return B_ERROR;
}
status_t err;
err = _MoveIntoTrash(&fRefs, &fNamesInTrash);
if (err != B_OK)
return err;
if (fImageRefs.empty())
return B_OK;
err = _MoveIntoTrash(&fImageRefs, &fImageNamesInTrash);
if (err != B_OK)
return err;
return B_OK;
}
status_t
FilePlaylistItem::RestoreFromTrash()
{
if (fNamesInTrash[0].Length() <= 0) {
return B_ERROR;
}
status_t err;
err = _RestoreFromTrash(&fRefs, &fNamesInTrash);
if (err != B_OK)
return err;
if (fImageRefs.empty())
return B_OK;
err = _RestoreFromTrash(&fImageRefs, &fImageNamesInTrash);
if (err != B_OK)
return err;
return B_OK;
}
TrackSupplier*
FilePlaylistItem::_CreateTrackSupplier() const
{
MediaFileTrackSupplier* supplier
= new(std::nothrow) MediaFileTrackSupplier();
if (supplier == NULL)
return NULL;
for (vector<entry_ref>::size_type i = 0; i < fRefs.size(); i++) {
BMediaFile* mediaFile = new(std::nothrow) BMediaFile(&fRefs[i]);
if (mediaFile == NULL) {
delete supplier;
return NULL;
}
if (supplier->AddMediaFile(mediaFile) != B_OK)
delete mediaFile;
}
for (vector<entry_ref>::size_type i = 0; i < fImageRefs.size(); i++) {
BBitmap* bitmap = BTranslationUtils::GetBitmap(&fImageRefs[i]);
if (bitmap == NULL)
continue;
if (supplier->AddBitmap(bitmap) != B_OK)
delete bitmap;
}
BEntry entry(&fRefs[0], true);
char originalName[B_FILE_NAME_LENGTH];
entry.GetName(originalName);
BString nameWithoutExtension(originalName);
int32 extension = nameWithoutExtension.FindLast('.');
if (extension > 0)
nameWithoutExtension.Truncate(extension);
BPath path;
entry.GetPath(&path);
path.GetParent(&path);
BDirectory directory(path.Path());
while (directory.GetNextEntry(&entry) == B_OK) {
char name[B_FILE_NAME_LENGTH];
if (entry.GetName(name) != B_OK)
continue;
BString nameString(name);
if (nameString == originalName)
continue;
if (nameString.IFindFirst(nameWithoutExtension) < 0)
continue;
BFile file(&entry, B_READ_ONLY);
if (file.InitCheck() != B_OK)
continue;
int32 pos = nameString.FindLast('.');
if (pos < 0)
continue;
BString extensionString(nameString.String() + pos + 1);
extensionString.ToLower();
BString language = "default";
if (pos > 1) {
int32 end = pos;
while (pos > 0 && *(nameString.String() + pos - 1) != '.')
pos--;
language.SetTo(nameString.String() + pos, end - pos);
}
if (extensionString == "srt") {
SubTitles* subTitles
= new(std::nothrow) SubTitlesSRT(&file, language.String());
if (subTitles != NULL && !supplier->AddSubTitles(subTitles))
delete subTitles;
}
}
return supplier;
}
status_t
FilePlaylistItem::AddRef(const entry_ref& ref)
{
fRefs.push_back(ref);
fNamesInTrash.push_back("");
return B_OK;
}
status_t
FilePlaylistItem::AddImageRef(const entry_ref& ref)
{
fImageRefs.push_back(ref);
fImageNamesInTrash.push_back("");
return B_OK;
}
const entry_ref&
FilePlaylistItem::ImageRef() const
{
static entry_ref ref;
if (fImageRefs.empty())
return ref;
return fImageRefs[0];
}
bigtime_t
FilePlaylistItem::_CalculateDuration()
{
BMediaFile mediaFile(&Ref());
if (mediaFile.InitCheck() != B_OK || mediaFile.CountTracks() < 1)
return 0;
return mediaFile.TrackAt(0)->Duration();
}
status_t
FilePlaylistItem::_SetAttribute(const char* attrName, type_code type,
const void* data, size_t size)
{
BEntry entry(&fRefs[0], true);
BNode node(&entry);
if (node.InitCheck() != B_OK)
return node.InitCheck();
ssize_t written = node.WriteAttr(attrName, type, 0, data, size);
if (written != (ssize_t)size) {
if (written < 0)
return (status_t)written;
return B_IO_ERROR;
}
return B_OK;
}
status_t
FilePlaylistItem::_GetAttribute(const char* attrName, type_code type,
void* data, size_t size) const
{
BEntry entry(&fRefs[0], true);
BNode node(&entry);
if (node.InitCheck() != B_OK)
return node.InitCheck();
ssize_t read = node.ReadAttr(attrName, type, 0, data, size);
if (read != (ssize_t)size) {
if (read < 0)
return (status_t)read;
return B_IO_ERROR;
}
return B_OK;
}
status_t
FilePlaylistItem::_MoveIntoTrash(vector<entry_ref>* refs,
vector<BString>* namesInTrash)
{
char trashPath[B_PATH_NAME_LENGTH];
status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device,
true , trashPath, B_PATH_NAME_LENGTH);
if (err != B_OK) {
fprintf(stderr, "failed to find Trash: %s\n", strerror(err));
return err;
}
BDirectory trashDir(trashPath);
err = trashDir.InitCheck();
if (err != B_OK) {
fprintf(stderr, "failed to init BDirectory for %s: %s\n",
trashPath, strerror(err));
return err;
}
for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
BEntry entry(&(*refs)[i]);
err = entry.InitCheck();
if (err != B_OK) {
fprintf(stderr, "failed to init BEntry for %s: %s\n",
(*refs)[i].name, strerror(err));
return err;
}
(*namesInTrash)[i] = (*refs)[i].name;
int32 uniqueNameIndex = 1;
while (true) {
BEntry test(&trashDir, (*namesInTrash)[i].String());
if (!test.Exists())
break;
(*namesInTrash)[i] = (*refs)[i].name;
(*namesInTrash)[i] << ' ' << uniqueNameIndex;
uniqueNameIndex++;
}
BPath originalPath;
entry.GetPath(&originalPath);
err = entry.MoveTo(&trashDir, (*namesInTrash)[i].String());
if (err != B_OK) {
fprintf(stderr, "failed to move entry into trash %s: %s\n",
trashPath, strerror(err));
return err;
}
BNode node(&entry);
BString originalPathString(originalPath.Path());
node.WriteAttrString("_trk/original_path", &originalPathString);
}
return B_OK;
}
status_t
FilePlaylistItem::_RestoreFromTrash(vector<entry_ref>* refs,
vector<BString>* namesInTrash)
{
char trashPath[B_PATH_NAME_LENGTH];
status_t err = find_directory(B_TRASH_DIRECTORY, (*refs)[0].device,
false , trashPath, B_PATH_NAME_LENGTH);
if (err != B_OK) {
fprintf(stderr, "failed to find Trash: %s\n", strerror(err));
return err;
}
for (vector<entry_ref>::size_type i = 0; i < refs->size(); i++) {
BPath path(trashPath, (*namesInTrash)[i].String());
BEntry entry(path.Path());
err = entry.InitCheck();
if (err != B_OK) {
fprintf(stderr, "failed to init BEntry for %s: %s\n",
(*namesInTrash)[i].String(), strerror(err));
return err;
}
node_ref nodeRef;
nodeRef.device = (*refs)[i].device;
nodeRef.node = (*refs)[i].directory;
BDirectory originalDir(&nodeRef);
err = originalDir.InitCheck();
if (err != B_OK) {
fprintf(stderr, "failed to init original BDirectory for "
"%s: %s\n", (*refs)[i].name, strerror(err));
return err;
}
(*namesInTrash)[i] = "";
err = entry.MoveTo(&originalDir, (*refs)[i].name);
if (err != B_OK) {
fprintf(stderr, "failed to restore entry from trash "
"%s: %s\n", (*refs)[i].name, strerror(err));
return err;
}
BNode node(&entry);
node.RemoveAttr("_trk/original_path");
}
return B_OK;
}