2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Runtime.InteropServices;
21 namespace Tizen.Multimedia
24 /// Provides a means to edit the metadata of the media file.
27 /// If you want to access only internal storage,
28 /// you should add privilege http://tizen.org/privilege/mediastorage. \n
29 /// Or if you want to access only external storage,
30 /// you should add privilege http://tizen.org/privilege/externalstorage. \n
32 public class MetadataEditor : IDisposable
34 private bool _disposed = false;
35 private IntPtr _handle = IntPtr.Zero;
36 private bool _isFileReadOnly;
42 if (_handle == IntPtr.Zero)
44 throw new ObjectDisposedException(nameof(MetadataEditor));
52 /// Initializes a new instance of the <see cref="MetadataEditor"/> class with the specified path.
54 /// <since_tizen> 3 </since_tizen>
55 /// <param name="path">The path of the media file to edit metadata.</param>
56 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
57 /// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string, contains only white space.</exception>
58 /// <exception cref="FileFormatException">The file is not supported.</exception>
59 /// <exception cref="FileNotFoundException">File does not exist.</exception>
60 /// <exception cref="UnauthorizedAccessException">Caller does not have required privilege to access the file.</exception>
61 public MetadataEditor(string path)
65 throw new ArgumentNullException(nameof(path));
68 if (string.IsNullOrWhiteSpace(path))
70 throw new ArgumentException($"{nameof(path)} is a zero-length string.", nameof(path));
73 Interop.MetadataEditor.Create(out _handle).ThrowIfError("Failed to create metadata");
77 Interop.MetadataEditor.SetPath(Handle, path).ThrowIfError("Failed to set path");
79 _isFileReadOnly = File.GetAttributes(path).HasFlag(FileAttributes.ReadOnly);
83 Interop.MetadataEditor.Destroy(_handle);
88 private string GetParam(MetadataEditorAttr attr)
90 IntPtr val = IntPtr.Zero;
94 Interop.MetadataEditor.GetMetadata(Handle, attr, out val)
95 .ThrowIfError("Failed to get metadata");
97 return Marshal.PtrToStringAnsi(val);
101 Interop.Libc.Free(val);
105 private void SetParam(MetadataEditorAttr attr, string value)
109 throw new InvalidOperationException("The media file is read-only.");
112 Interop.MetadataEditor.SetMetadata(Handle, attr, value).ThrowIfError("Failed to set value");
116 /// Gets or sets the artist of media.
118 /// <since_tizen> 3 </since_tizen>
119 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
120 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
125 return GetParam(MetadataEditorAttr.Artist);
130 SetParam(MetadataEditorAttr.Artist, value);
135 /// Gets or sets the title of media.
137 /// <since_tizen> 3 </since_tizen>
138 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
139 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
144 return GetParam(MetadataEditorAttr.Title);
149 SetParam(MetadataEditorAttr.Title, value);
154 /// Gets or sets the album name of media.
156 /// <since_tizen> 3 </since_tizen>
157 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
158 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
163 return GetParam(MetadataEditorAttr.Album);
168 SetParam(MetadataEditorAttr.Album, value);
173 /// Gets or sets the genre of media.
175 /// <since_tizen> 3 </since_tizen>
176 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
177 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
182 return GetParam(MetadataEditorAttr.Genre);
187 SetParam(MetadataEditorAttr.Genre, value);
192 /// Gets or sets the author of media.
194 /// <since_tizen> 3 </since_tizen>
195 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
196 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
201 return GetParam(MetadataEditorAttr.Author);
206 SetParam(MetadataEditorAttr.Author, value);
211 /// Gets or sets the copyright of media.
213 /// <since_tizen> 3 </since_tizen>
214 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
215 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
216 public string Copyright
220 return GetParam(MetadataEditorAttr.Copyright);
225 SetParam(MetadataEditorAttr.Copyright, value);
230 /// Gets or sets the date of media.
232 /// <since_tizen> 3 </since_tizen>
234 /// If the media contains ID3 tag, this refers to the recorded date.
235 /// If the media is a mp4 format, this refers to the year and the value to set will be converted into integer.
237 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
238 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
243 return GetParam(MetadataEditorAttr.Date);
248 SetParam(MetadataEditorAttr.Date, value);
253 /// Gets or sets the description of media.
255 /// <since_tizen> 3 </since_tizen>
256 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
257 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
258 public string Description
262 return GetParam(MetadataEditorAttr.Description);
267 SetParam(MetadataEditorAttr.Description, value);
272 /// Gets or sets the comment of media.
274 /// <since_tizen> 3 </since_tizen>
275 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
276 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
277 public string Comment
281 return GetParam(MetadataEditorAttr.Comment);
286 SetParam(MetadataEditorAttr.Comment, value);
291 /// Gets or sets the track number of media.
293 /// <since_tizen> 3 </since_tizen>
294 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
295 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
296 public string TrackNumber
300 return GetParam(MetadataEditorAttr.TrackNumber);
305 SetParam(MetadataEditorAttr.TrackNumber, value);
310 /// Gets the count of album arts of media.
312 /// <since_tizen> 3 </since_tizen>
313 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
314 public int PictureCount
316 get => int.TryParse(GetParam(MetadataEditorAttr.PictureCount), out var value) ? value : 0;
320 /// Gets or sets the conductor of media.
322 /// <since_tizen> 3 </since_tizen>
323 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
324 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
325 public string Conductor
329 return GetParam(MetadataEditorAttr.Conductor);
334 SetParam(MetadataEditorAttr.Conductor, value);
339 /// Gets or sets the unsynchronized lyrics of media.
341 /// <since_tizen> 3 </since_tizen>
342 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
343 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
344 public string UnsyncLyrics
348 return GetParam(MetadataEditorAttr.UnsyncLyrics);
353 SetParam(MetadataEditorAttr.UnsyncLyrics, value);
358 /// Writes the modified metadata to the media file.
360 /// <exception cref="InvalidOperationException">
361 /// An internal error occurs.\n
363 /// The file is read-only.
365 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
370 throw new InvalidOperationException("The media file is read-only.");
373 Interop.MetadataEditor.UpdateMetadata(Handle).ThrowIfError("Failed to update file");
377 /// Gets the artwork image in the media file.
379 /// <since_tizen> 3 </since_tizen>
380 /// <param name="index">The index of picture to import.</param>
381 /// <returns> Artwork included in the media file.</returns>
382 /// <returns>Artwork included in the media file.</returns>
383 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
384 /// <exception cref="ArgumentOutOfRangeException">
385 /// <paramref name="index"/> is less than zero.\n
387 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.\n
389 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
390 public Artwork GetPicture(int index)
394 throw new ArgumentOutOfRangeException(nameof(index), index,
395 "Index should not be less than zero.");
398 if (index >= PictureCount)
400 throw new ArgumentOutOfRangeException(nameof(index), index,
401 "Index should not be greater thor or equal to PictureCount.");
404 IntPtr data = IntPtr.Zero;
405 IntPtr mimeType = IntPtr.Zero;
409 Interop.MetadataEditor.GetPicture(Handle, index, out data, out var size, out mimeType).
410 ThrowIfError("Failed to get the value");
414 byte[] tmpBuf = new byte[size];
415 Marshal.Copy(data, tmpBuf, 0, size);
417 return new Artwork(tmpBuf, Marshal.PtrToStringAnsi(mimeType));
424 if (data != IntPtr.Zero)
426 Interop.Libc.Free(data);
429 if (mimeType != IntPtr.Zero)
431 Interop.Libc.Free(mimeType);
437 /// Appends the picture to the media file.
439 /// <since_tizen> 3 </since_tizen>
440 /// <param name="path">The path of picture for adding to the metadata.</param>
441 /// <exception cref="InvalidOperationException">
442 /// An internal error occurs.\n
444 /// The media file is read-only.
446 /// <exception cref="ArgumentNullException"> Picture path is null</exception>
447 /// <exception cref="FileNotFoundException">File does not exist.</exception>
448 /// <exception cref="UnauthorizedAccessException">Caller does not have required privilege to access the file.</exception>
449 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
450 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
451 public void AddPicture(string path)
455 throw new ArgumentNullException(nameof(path));
458 if (File.Exists(path) == false)
460 throw new FileNotFoundException("File does not exist.", path);
465 throw new InvalidOperationException("The media file is read-only.");
468 Interop.MetadataEditor.AddPicture(Handle, path).
469 ThrowIfError("Failed to append picture");
473 /// Removes the picture from the media file.
475 /// <since_tizen> 3 </since_tizen>
476 /// <param name="index">The index of picture to remove.</param>
477 /// <exception cref="InvalidOperationException">
478 /// An internal error occurs.\n
480 /// The media file is read-only.
482 /// <exception cref="ArgumentOutOfRangeException">
483 /// <paramref name="index"/> is less than zero.\n
485 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.\n
487 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed.</exception>
488 public void RemovePicture(int index)
492 throw new ArgumentOutOfRangeException("Index should be larger than 0 [" + index + "]");
495 if (index >= PictureCount)
497 throw new ArgumentOutOfRangeException(nameof(index), index,
498 "Index should not be greater thor or equal to PictureCount.");
503 throw new InvalidOperationException("The media file is read-only.");
506 Interop.MetadataEditor.RemovePicture(Handle, index).ThrowIfError("Failed to remove picture");
514 protected virtual void Dispose(bool disposing)
518 if (_handle != IntPtr.Zero)
520 Interop.MetadataEditor.Destroy(_handle);
521 _handle = IntPtr.Zero;
529 /// Releases all resources used by the <see cref="MetadataEditor"/> object.
531 public void Dispose()
534 GC.SuppressFinalize(this);