/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Tizen.Multimedia
{
///
/// Provides a means to edit the metadata of the media file.
///
///
/// If you want to access only an internal storage,
/// you should add privilege http://tizen.org/privilege/mediastorage.
/// Or if you want to access only an external storage,
/// you should add privilege http://tizen.org/privilege/externalstorage.
///
/// 3
public class MetadataEditor : IDisposable
{
private bool _disposed = false;
private IntPtr _handle = IntPtr.Zero;
private bool _isFileReadOnly;
private IntPtr Handle
{
get
{
if (_handle == IntPtr.Zero)
{
throw new ObjectDisposedException(nameof(MetadataEditor));
}
return _handle;
}
}
///
/// Initializes a new instance of the class with the specified path.
///
/// 3
/// The path of the media file to edit the metadata.
/// is null.
/// is a zero-length string, contains only white space.
/// The file is not supported.
/// The file does not exist.
/// The caller does not have required privilege to access the file.
public MetadataEditor(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException($"{nameof(path)} is a zero-length string.", nameof(path));
}
Interop.MetadataEditor.Create(out _handle).ThrowIfError("Failed to create metadata");
try
{
Interop.MetadataEditor.SetPath(Handle, path).ThrowIfError("Failed to set path");
_isFileReadOnly = File.GetAttributes(path).HasFlag(FileAttributes.ReadOnly);
}
catch (Exception)
{
Interop.MetadataEditor.Destroy(_handle);
throw;
}
}
private string GetParam(MetadataEditorAttr attr)
{
IntPtr val = IntPtr.Zero;
try
{
Interop.MetadataEditor.GetMetadata(Handle, attr, out val)
.ThrowIfError("Failed to get metadata");
return Marshal.PtrToStringAnsi(val);
}
finally
{
Interop.Libc.Free(val);
}
}
private void SetParam(MetadataEditorAttr attr, string value)
{
if (_isFileReadOnly)
{
throw new InvalidOperationException("The media file is read-only.");
}
Interop.MetadataEditor.SetMetadata(Handle, attr, value).ThrowIfError("Failed to set value");
}
///
/// Gets or sets the artist of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Artist
{
get
{
return GetParam(MetadataEditorAttr.Artist);
}
set
{
SetParam(MetadataEditorAttr.Artist, value);
}
}
///
/// Gets or sets the title of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Title
{
get
{
return GetParam(MetadataEditorAttr.Title);
}
set
{
SetParam(MetadataEditorAttr.Title, value);
}
}
///
/// Gets or sets the album name of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Album
{
get
{
return GetParam(MetadataEditorAttr.Album);
}
set
{
SetParam(MetadataEditorAttr.Album, value);
}
}
///
/// Gets or sets the genre of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Genre
{
get
{
return GetParam(MetadataEditorAttr.Genre);
}
set
{
SetParam(MetadataEditorAttr.Genre, value);
}
}
///
/// Gets or sets the author of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Author
{
get
{
return GetParam(MetadataEditorAttr.Author);
}
set
{
SetParam(MetadataEditorAttr.Author, value);
}
}
///
/// Gets or sets the copyright of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Copyright
{
get
{
return GetParam(MetadataEditorAttr.Copyright);
}
set
{
SetParam(MetadataEditorAttr.Copyright, value);
}
}
///
/// Gets or sets the date of media.
///
/// 3
///
/// If the media contains the ID3 tag, this refers to the recorded date.
/// If the media is a mp4 format, this refers to the year, and the value to set will be converted into integer.
///
/// The file is read-only.
/// The has already been disposed of.
public string Date
{
get
{
return GetParam(MetadataEditorAttr.Date);
}
set
{
SetParam(MetadataEditorAttr.Date, value);
}
}
///
/// Gets or sets the description of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Description
{
get
{
return GetParam(MetadataEditorAttr.Description);
}
set
{
SetParam(MetadataEditorAttr.Description, value);
}
}
///
/// Gets or sets the comment of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Comment
{
get
{
return GetParam(MetadataEditorAttr.Comment);
}
set
{
SetParam(MetadataEditorAttr.Comment, value);
}
}
///
/// Gets or sets the track number of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string TrackNumber
{
get
{
return GetParam(MetadataEditorAttr.TrackNumber);
}
set
{
SetParam(MetadataEditorAttr.TrackNumber, value);
}
}
///
/// Gets the count of album arts of media.
///
/// 3
/// The has already been disposed of.
public int PictureCount
{
get => int.TryParse(GetParam(MetadataEditorAttr.PictureCount), out var value) ? value : 0;
}
///
/// Gets or sets the conductor of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string Conductor
{
get
{
return GetParam(MetadataEditorAttr.Conductor);
}
set
{
SetParam(MetadataEditorAttr.Conductor, value);
}
}
///
/// Gets or sets the unsynchronized lyrics of media.
///
/// 3
/// The file is read-only.
/// The has already been disposed of.
public string UnsyncLyrics
{
get
{
return GetParam(MetadataEditorAttr.UnsyncLyrics);
}
set
{
SetParam(MetadataEditorAttr.UnsyncLyrics, value);
}
}
///
/// Writes the modified metadata to the media file.
///
///
/// An internal error occurs.
/// -or-
/// The file is read-only.
///
/// The has already been disposed of.
/// 3
public void Commit()
{
if (_isFileReadOnly)
{
throw new InvalidOperationException("The media file is read-only.");
}
Interop.MetadataEditor.UpdateMetadata(Handle).ThrowIfError("Failed to update file");
}
///
/// Gets the artwork image in the media file.
///
/// 3
/// The index of the picture to import.
/// The artwork included in the media file.
/// An internal error occurs.
///
/// is less than zero.
/// -or-
/// is greater than or equal to .
///
/// The has already been disposed of.
public Artwork GetPicture(int index)
{
if (index < 0)
{
throw new ArgumentOutOfRangeException(nameof(index), index,
"Index should not be less than zero.");
}
if (index >= PictureCount)
{
throw new ArgumentOutOfRangeException(nameof(index), index,
"Index should not be greater thor or equal to PictureCount.");
}
IntPtr data = IntPtr.Zero;
IntPtr mimeType = IntPtr.Zero;
try
{
Interop.MetadataEditor.GetPicture(Handle, index, out data, out var size, out mimeType).
ThrowIfError("Failed to get the value");
if (size > 0)
{
byte[] tmpBuf = new byte[size];
Marshal.Copy(data, tmpBuf, 0, size);
return new Artwork(tmpBuf, Marshal.PtrToStringAnsi(mimeType));
}
return null;
}
finally
{
if (data != IntPtr.Zero)
{
Interop.Libc.Free(data);
}
if (mimeType != IntPtr.Zero)
{
Interop.Libc.Free(mimeType);
}
}
}
///
/// Appends the picture to the media file.
///
/// 3
/// The path of the picture for adding to the metadata.
///
/// An internal error occurs.
/// -or-
/// The media file is read-only.
///
/// is null.
/// The file does not exist.
/// The caller does not have required privilege to access the file.
/// The has already been disposed of.
/// The specified file is not supported.
public void AddPicture(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (File.Exists(path) == false)
{
throw new FileNotFoundException("File does not exist.", path);
}
if (_isFileReadOnly)
{
throw new InvalidOperationException("The media file is read-only.");
}
Interop.MetadataEditor.AddPicture(Handle, path).
ThrowIfError("Failed to append picture");
}
///
/// Removes the picture from the media file.
///
/// 3
/// The index of the picture to remove.
///
/// An internal error occurs.
/// -or-
/// The media file is read-only.
///
///
/// is less than zero.
/// -or-
/// is greater than or equal to .
///
/// The has already been disposed of.
public void RemovePicture(int index)
{
if (index < 0)
{
throw new ArgumentOutOfRangeException("Index should be larger than 0 [" + index + "]");
}
if (index >= PictureCount)
{
throw new ArgumentOutOfRangeException(nameof(index), index,
"Index should not be greater thor or equal to PictureCount.");
}
if (_isFileReadOnly)
{
throw new InvalidOperationException("The media file is read-only.");
}
Interop.MetadataEditor.RemovePicture(Handle, index).ThrowIfError("Failed to remove picture");
}
///
/// Finalizes an instance of the MetadataEditor class.
///
~MetadataEditor()
{
Dispose(false);
}
///
/// Releases the resources used by the object.
///
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
///
/// 3
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (_handle != IntPtr.Zero)
{
Interop.MetadataEditor.Destroy(_handle);
_handle = IntPtr.Zero;
}
_disposed = true;
}
}
///
/// Releases all resources used by the object.
///
/// 3
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}