Initial commit

This commit is contained in:
Thastertyn committed 2024-06-07 00:47:07 +02:00
commit 262601edb8
2025 files changed
+218754

No files matched your search

+363
View File
@@ -0,0 +1,363 @@
//
// EndTag.cs: Provides support for accessing and modifying a collection of tags
// appearing at the end of a file.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace TagLib.NonContainer
{
/// <summary>
/// This class extends <see cref="CombinedTag" />, providing support
/// for accessing and modifying a collection of tags appearing at the
/// end of a file.
/// </summary>
/// <remarks>
/// <para>This class is used by <see cref="TagLib.NonContainer.File"
/// /> to read all the tags appearing at the end of the file but
/// could be used by other classes. It currently supports ID3v1,
/// ID3v2, and APE tags.</para>
/// </remarks>
public class EndTag : CombinedTag
{
#region Private Fields
/// <summary>
/// Contains the file to operate on.
/// </summary>
readonly TagLib.File file;
/// <summary>
/// Contains the number of bytes that must be read to
/// hold all applicable indicators.
/// </summary>
static readonly int read_size = (int)Math.Max (Math.Max (Ape.Footer.Size, Id3v2.Footer.Size), Id3v1.Tag.Size);
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="EndTag" /> for a specified <see cref="TagLib.File"
/// />.
/// </summary>
/// <param name="file">
/// A <see cref="TagLib.File" /> object on which the new
/// instance will perform its operations.
/// </param>
/// <remarks>
/// Constructing a new instance does not automatically read
/// the contents from the disk. <see cref="Read" /> must be
/// called to read the tags.
/// </remarks>
public EndTag (TagLib.File file)
{
this.file = file;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the total size of the tags located at the end of the
/// file by reading from the file.
/// </summary>
public long TotalSize {
get {
long start = file.Length;
while (ReadTagInfo (ref start) != TagTypes.None)
;
return file.Length - start;
}
}
#endregion
#region Public Methods
/// <summary>
/// Reads the tags stored at the end of the file into the
/// current instance.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the read tags begin. This also
/// marks the seek position at which the media ends.
/// </returns>
public long Read (ReadStyle style)
{
TagLib.Tag tag;
ClearTags ();
long start = file.Length;
while ((tag = ReadTag (ref start, style)) != null)
InsertTag (0, tag);
return start;
}
/// <summary>
/// Renders the tags contained in the current instance.
/// </summary>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// physical representation of the tags stored in the current
/// instance.
/// </returns>
/// <remarks>
/// The tags are rendered in the order that they are stored
/// in the current instance.
/// </remarks>
public ByteVector Render ()
{
ByteVector data = new ByteVector ();
foreach (TagLib.Tag t in Tags) {
if (t is Ape.Tag tag)
data.Add (tag.Render ());
else if (t is Id3v2.Tag tag1)
data.Add (tag1.Render ());
else if (t is Id3v1.Tag tag2)
data.Add (tag2.Render ());
}
return data;
}
/// <summary>
/// Writes the tags contained in the current instance to the
/// end of the file that created it, overwriting the existing
/// tags.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the written tags begin. This also
/// marks the seek position at which the media ends.
/// </returns>
public long Write ()
{
long total_size = TotalSize;
ByteVector data = Render ();
file.Insert (data, file.Length - total_size, total_size);
return file.Length - data.Count;
}
/// <summary>
/// Removes a set of tag types from the current instance.
/// </summary>
/// <param name="types">
/// A bitwise combined <see cref="TagLib.TagTypes" /> value
/// containing tag types to be removed from the file.
/// </param>
/// <remarks>
/// In order to remove all tags from a file, pass <see
/// cref="TagTypes.AllTags" /> as <paramref name="types" />.
/// </remarks>
public void RemoveTags (TagTypes types)
{
for (int i = Tags.Length - 1; i >= 0; i--) {
var tag = Tags[i];
if (types == TagTypes.AllTags || (tag.TagTypes & types) == tag.TagTypes) {
RemoveTag (tag);
}
}
}
/// <summary>
/// Adds a tag of a specified type to the current instance,
/// optionally copying values from an existing type.
/// </summary>
/// <param name="type">
/// A <see cref="TagTypes" /> value specifying the type of
/// tag to add to the current instance. At the time of this
/// writing, this is limited to <see cref="TagTypes.Ape" />,
/// <see cref="TagTypes.Id3v1" />, and <see
/// cref="TagTypes.Id3v2" />.
/// </param>
/// <param name="copy">
/// A <see cref="TagLib.Tag" /> to copy values from using
/// <see cref="TagLib.Tag.CopyTo" />, or <see
/// langword="null" /> if no tag is to be copied.
/// </param>
/// <returns>
/// The <see cref="TagLib.Tag" /> object added to the current
/// instance, or <see langword="null" /> if it couldn't be
/// created.
/// </returns>
/// <remarks>
/// ID3v2 tags are added at the end of the current instance,
/// while other tags are added to the beginning.
/// </remarks>
public TagLib.Tag AddTag (TagTypes type, TagLib.Tag copy)
{
TagLib.Tag tag = null;
if (type == TagTypes.Id3v1) {
tag = new TagLib.Id3v1.Tag ();
} else if (type == TagTypes.Id3v2) {
Id3v2.Tag tag32 = new Id3v2.Tag {
Version = 4
};
tag32.Flags |= Id3v2.HeaderFlags.FooterPresent;
tag = tag32;
} else if (type == TagTypes.Ape) {
tag = new TagLib.Ape.Tag ();
}
if (tag != null) {
copy?.CopyTo (tag, true);
if (type == TagTypes.Id3v1)
AddTag (tag);
else
InsertTag (0, tag);
}
return tag;
}
#endregion
#region Private Methods
/// <summary>
/// Reads a tag ending at a specified position and moves the
/// cursor to its start position.
/// </summary>
/// <param name="end">
/// A <see cref="long" /> value reference specifying at what
/// position the potential tag ends at. If a tag is found,
/// this value will be updated to the position at which the
/// found tag starts.
/// </param>
/// <param name="style">
/// A <see cref="ReadStyle"/> value specifying how the media
/// data is to be read into the current instance.
/// </param>
/// <returns>
/// A <see cref="TagLib.Tag" /> object representing the tag
/// found at the specified position, or <see langword="null"
/// /> if no tag was found.
/// </returns>
TagLib.Tag ReadTag (ref long end, ReadStyle style)
{
long start = end;
TagTypes type = ReadTagInfo (ref start);
TagLib.Tag tag = null;
try {
switch (type) {
case TagTypes.Ape:
tag = new TagLib.Ape.Tag (file, end - Ape.Footer.Size);
break;
case TagTypes.Id3v2:
tag = new TagLib.Id3v2.Tag (file, start, style);
break;
case TagTypes.Id3v1:
tag = new TagLib.Id3v1.Tag (file, start);
break;
}
end = start;
} catch (CorruptFileException) {
}
return tag;
}
/// <summary>
/// Looks for a tag ending at a specified position and moves
/// the cursor to its start position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying at what
/// position the potential tag ends. If a tag is found,
/// this value will be updated to the position at which the
/// found tag starts.
/// </param>
/// <returns>
/// A <see cref="TagLib.TagTypes" /> value specifying the
/// type of tag found at the specified position, or <see
/// cref="TagTypes.None" /> if no tag was found.
/// </returns>
TagTypes ReadTagInfo (ref long position)
{
if (position - read_size < 0)
return TagTypes.None;
file.Seek (position - read_size);
ByteVector data = file.ReadBlock (read_size);
try {
int offset = (int)(data.Count - Ape.Footer.Size);
if (data.ContainsAt (Ape.Footer.FileIdentifier, offset)) {
Ape.Footer footer = new Ape.Footer (data.Mid (offset));
// If the complete tag size is zero or
// the tag is a header, this indicates
// some sort of corruption.
if (footer.CompleteTagSize == 0 ||
(footer.Flags & Ape.FooterFlags.IsHeader) != 0)
return TagTypes.None;
position -= footer.CompleteTagSize;
return TagTypes.Ape;
}
offset = (int)(data.Count - Id3v2.Footer.Size);
if (data.ContainsAt (Id3v2.Footer.FileIdentifier,
offset)) {
var footer = new Id3v2.Footer (data.Mid (offset));
position -= footer.CompleteTagSize;
return TagTypes.Id3v2;
}
if (data.StartsWith (Id3v1.Tag.FileIdentifier)) {
position -= Id3v1.Tag.Size;
return TagTypes.Id3v1;
}
} catch (CorruptFileException) {
}
return TagTypes.None;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b20e4c11d4e603cf7b5f02f321bc6c4e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+398
View File
@@ -0,0 +1,398 @@
//
// File.cs: Provides tagging and properties for files that contain an
// indeterminite number of tags at their beginning or end.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace TagLib.NonContainer
{
/// <summary>
/// This abstract class extends <see cref="TagLib.File" /> to provide
/// tagging and properties for files that contain an indeterminite
/// number of tags at their beginning or end.
/// </summary>
/// <remarks>
/// <para>When extending this class, <see cref="ReadStart" />, <see
/// cref="ReadEnd" />, and <see cref="ReadProperties" /> should be
/// overrided methods that read the format specific information from
/// the file.</para>
/// <para>The file is read upon construction in the following
/// manner:</para>
/// <list type="number">
/// <item><term>The file is opened for reading.</term></item>
/// <item><term>The tags at the start of the file are
/// read.</term></item>
/// <item><term><see cref="ReadStart" /> is called.</term></item>
/// <item><term>The tags at the end of the file are
/// read.</term></item>
/// <item><term><see cref="ReadEnd" /> is called.</term></item>
/// <item><term>If reading with a style other than <see
/// cref="ReadStyle.None" />, <see cref="ReadProperties" /> is
/// called.</term></item>
/// <item><term>The file is closed.</term></item>
/// </list>
/// </remarks>
public abstract class File : TagLib.File
{
#region Private Fields
/// <summary>
/// Contains the tags.
/// </summary>
Tag tag;
/// <summary>
/// Contains the media properties.
/// </summary>
Properties properties;
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified path in the local file
/// system and specified read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
protected File (string path, ReadStyle propertiesStyle)
: base (path)
{
Read (propertiesStyle);
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified path in the local file
/// system with an average read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
protected File (string path) : this (path, ReadStyle.Average)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified file abstraction and
/// specified read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
protected File (IFileAbstraction abstraction, ReadStyle propertiesStyle)
: base (abstraction)
{
Read (propertiesStyle);
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified file abstraction with an
/// average read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
protected File (IFileAbstraction abstraction)
: this (abstraction, ReadStyle.Average)
{
}
#endregion
#region Public Properties
/// <summary>
/// Gets a abstract representation of all tags stored in the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="TagLib.Tag" /> object representing all tags
/// stored in the current instance.
/// </value>
public override TagLib.Tag Tag {
get { return tag; }
}
/// <summary>
/// Gets the media properties of the file represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="TagLib.Properties" /> object containing the
/// media properties of the file represented by the current
/// instance.
/// </value>
public override Properties Properties {
get { return properties; }
}
#endregion
#region Public Methods
/// <summary>
/// Saves the changes made in the current instance to the
/// file it represents.
/// </summary>
public override void Save ()
{
// Boilerplate
PreSave ();
Mode = AccessMode.Write;
try {
tag.Write (out var start, out var end);
InvariantStartPosition = start;
InvariantEndPosition = end;
TagTypesOnDisk = TagTypes;
} finally {
Mode = AccessMode.Closed;
}
}
/// <summary>
/// Removes a set of tag types from the current instance.
/// </summary>
/// <param name="types">
/// A bitwise combined <see cref="TagLib.TagTypes" /> value
/// containing tag types to be removed from the file.
/// </param>
/// <remarks>
/// In order to remove all tags from a file, pass <see
/// cref="TagTypes.AllTags" /> as <paramref name="types" />.
/// </remarks>
public override void RemoveTags (TagTypes types)
{
tag.RemoveTags (types);
}
#endregion
#region Protected Properties
/// <summary>
/// Gets the collection of tags appearing at the start of the
/// file.
/// </summary>
/// <value>
/// A <see cref="TagLib.NonContainer.StartTag" /> storing the
/// tags for the start of the file.
/// </value>
protected StartTag StartTag {
get { return tag.StartTag; }
}
/// <summary>
/// Gets the collection of tags appearing at the end of the
/// file.
/// </summary>
/// <value>
/// A <see cref="TagLib.NonContainer.EndTag" /> storing the
/// tags for the end of the file.
/// </value>
protected EndTag EndTag {
get { return tag.EndTag; }
}
#endregion
#region Protected Methods
/// <summary>
/// Reads format specific information at the start of the
/// file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <remarks>
/// This method is called by the constructor immediately
/// after the tags at the start of the file have been read
/// and as such (so the internal seek mechanism is close to
/// the start). It should be used for reading any content
/// specific information, such as an audio header from the
/// start of the file.
/// </remarks>
protected virtual void ReadStart (long start, ReadStyle propertiesStyle)
{
}
/// <summary>
/// Reads format specific information at the end of the
/// file.
/// </summary>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <remarks>
/// This method is called by the constructor immediately
/// after the tags at the end of the file have been read
/// and as such (so the internal seek mechanism is close to
/// the end). It should be used for reading any content
/// specific information, such as an audio header from the
/// end of the file.
/// </remarks>
protected virtual void ReadEnd (long end, ReadStyle propertiesStyle)
{
}
/// <summary>
/// Reads the audio properties from the file represented by
/// the current instance.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <returns>
/// A <see cref="TagLib.Properties" /> object describing the
/// media properties of the file represented by the current
/// instance.
/// </returns>
/// <remarks>
/// This method is called ONLY IF the file is constructed
/// with a read style other than <see cref="ReadStyle.None"
/// />, and as such MUST NOT return <see langword="null" />.
/// It is guaranteed that <see cref="ReadStart" /> and <see
/// cref="ReadEnd" /> will have been called first and this
/// method should be strictly used to perform final
/// processing on already read data.
/// </remarks>
protected abstract Properties ReadProperties (long start, long end, ReadStyle propertiesStyle);
#endregion
#region Private Methods
/// <summary>
/// Reads the file with a specified read style.
/// </summary>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
void Read (ReadStyle propertiesStyle)
{
Mode = AccessMode.Read;
try {
tag = new Tag (this);
// Read the tags and property data at the beginning of
// the file.
InvariantStartPosition = tag.ReadStart (propertiesStyle);
TagTypesOnDisk |= StartTag.TagTypes;
ReadStart (InvariantStartPosition, propertiesStyle);
// Read the tags and property data at the end of the
// file.
InvariantEndPosition =
(InvariantStartPosition == Length) ?
Length : tag.ReadEnd (propertiesStyle);
TagTypesOnDisk |= EndTag.TagTypes;
ReadEnd (InvariantEndPosition, propertiesStyle);
// Read the audio properties.
properties = (propertiesStyle & ReadStyle.Average) != 0 ?
ReadProperties (InvariantStartPosition,
InvariantEndPosition, propertiesStyle) :
null;
} finally {
Mode = AccessMode.Closed;
}
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91aa532d414f80daf872bb59170f7f74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,328 @@
//
// StartTag.cs: Provides support for accessing and modifying a collection of
// tags appearing at the start of a file.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace TagLib.NonContainer
{
/// <summary>
/// This class extends <see cref="CombinedTag" />, providing support
/// for accessing and modifying a collection of tags appearing at the
/// start of a file.
/// </summary>
/// <remarks>
/// <para>This class is used by <see cref="TagLib.NonContainer.File"
/// /> to read all the tags appearing at the start of the file but
/// could be used by other classes. It currently supports ID3v2
/// and APE tags.</para>
/// </remarks>
public class StartTag : CombinedTag
{
#region Private Fields
/// <summary>
/// Contains the file to operate on.
/// </summary>
readonly TagLib.File file;
/// <summary>
/// Contains the number of bytes that must be read to
/// hold all applicable indicators.
/// </summary>
readonly int read_size = (int)Math.Max (Ape.Footer.Size, Id3v2.Header.Size);
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="StartTag" /> for a specified <see
/// cref="TagLib.File" />.
/// </summary>
/// <param name="file">
/// A <see cref="TagLib.File" /> object on which the new
/// instance will perform its operations.
/// </param>
/// <remarks>
/// Constructing a new instance does not automatically read
/// the contents from the disk. <see cref="Read" /> must be
/// called to read the tags.
/// </remarks>
public StartTag (TagLib.File file)
{
this.file = file;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the total size of the tags located at the end of the
/// file by reading from the file.
/// </summary>
public long TotalSize {
get {
long size = 0;
while (ReadTagInfo (ref size) != TagTypes.None)
;
return size;
}
}
#endregion
#region Public Methods
/// <summary>
/// Reads the tags stored at the start of the file into the
/// current instance.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the read tags end. This also
/// marks the seek position at which the media begins.
/// </returns>
public long Read (ReadStyle style)
{
TagLib.Tag tag;
ClearTags ();
long end = 0;
while ((tag = ReadTag (ref end, style)) != null)
AddTag (tag);
return end;
}
/// <summary>
/// Renders the tags contained in the current instance.
/// </summary>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// physical representation of the tags stored in the current
/// instance.
/// </returns>
/// <remarks>
/// The tags are rendered in the order that they are stored
/// in the current instance.
/// </remarks>
public ByteVector Render ()
{
ByteVector data = new ByteVector ();
foreach (TagLib.Tag t in Tags) {
if (t is Ape.Tag)
data.Add ((t as TagLib.Ape.Tag).Render ());
else if (t is Id3v2.Tag)
data.Add ((t as TagLib.Id3v2.Tag).Render ());
}
return data;
}
/// <summary>
/// Writes the tags contained in the current instance to the
/// beginning of the file that created it, overwriting the
/// existing tags.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the written tags end. This also
/// marks the seek position at which the media begins.
/// </returns>
public long Write ()
{
ByteVector data = Render ();
file.Insert (data, 0, TotalSize);
return data.Count;
}
/// <summary>
/// Removes a set of tag types from the current instance.
/// </summary>
/// <param name="types">
/// A bitwise combined <see cref="TagLib.TagTypes" /> value
/// containing tag types to be removed from the file.
/// </param>
/// <remarks>
/// In order to remove all tags from a file, pass <see
/// cref="TagTypes.AllTags" /> as <paramref name="types" />.
/// </remarks>
public void RemoveTags (TagTypes types)
{
for (int i = Tags.Length - 1; i >= 0; i--) {
var tag = Tags[i];
if (types == TagTypes.AllTags || (tag.TagTypes & types) == tag.TagTypes) {
RemoveTag (tag);
}
}
}
/// <summary>
/// Adds a tag of a specified type to the current instance,
/// optionally copying values from an existing type.
/// </summary>
/// <param name="type">
/// A <see cref="TagTypes" /> value specifying the type of
/// tag to add to the current instance. At the time of this
/// writing, this is limited to <see cref="TagTypes.Ape" />
/// and <see cref="TagTypes.Id3v2" />.
/// </param>
/// <param name="copy">
/// A <see cref="TagLib.Tag" /> to copy values from using
/// <see cref="TagLib.Tag.CopyTo" />, or <see
/// langword="null" /> if no tag is to be copied.
/// </param>
/// <returns>
/// The <see cref="TagLib.Tag" /> object added to the current
/// instance, or <see langword="null" /> if it couldn't be
/// created.
/// </returns>
/// <remarks>
/// ID3v2 tags are added at the end of the current instance,
/// while other tags are added to the beginning.
/// </remarks>
public TagLib.Tag AddTag (TagTypes type, TagLib.Tag copy)
{
TagLib.Tag tag = null;
if (type == TagTypes.Id3v2) {
tag = new TagLib.Id3v2.Tag ();
} else if (type == TagTypes.Ape) {
tag = new TagLib.Ape.Tag ();
((Ape.Tag) tag).HeaderPresent = true;
}
if (tag != null) {
copy?.CopyTo (tag, true);
AddTag (tag);
}
return tag;
}
#endregion
#region Private Methods
/// <summary>
/// Reads a tag starting at a specified position and moves the
/// cursor to its start position.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value reference specifying at what
/// position the potential tag starts. If a tag is found,
/// this value will be updated to the position at which the
/// found tag ends.
/// </param>
/// <param name="style">
/// A <see cref="ReadStyle"/> value specifying how the media
/// data is to be read into the current instance.
/// </param>
/// <returns>
/// A <see cref="TagLib.Tag" /> object representing the tag
/// found at the specified position, or <see langword="null"
/// /> if no tag was found.
/// </returns>
TagLib.Tag ReadTag (ref long start, ReadStyle style)
{
long end = start;
TagTypes type = ReadTagInfo (ref end);
TagLib.Tag tag = null;
switch (type) {
case TagTypes.Ape:
tag = new TagLib.Ape.Tag (file, start);
break;
case TagTypes.Id3v2:
tag = new TagLib.Id3v2.Tag (file, start, style);
break;
}
start = end;
return tag;
}
/// <summary>
/// Looks for a tag starting at a specified position and moves
/// the cursor to its start position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying at what
/// position the potential tag starts. If a tag is found,
/// this value will be updated to the position at which the
/// found tag ends.
/// </param>
/// <returns>
/// A <see cref="TagLib.TagTypes" /> value specifying the
/// type of tag found at the specified position, or <see
/// cref="TagTypes.None" /> if no tag was found.
/// </returns>
TagTypes ReadTagInfo (ref long position)
{
file.Seek (position);
ByteVector data = file.ReadBlock (read_size);
try {
if (data.StartsWith (Ape.Footer.FileIdentifier)) {
Ape.Footer footer =
new Ape.Footer (data);
position += footer.CompleteTagSize;
return TagTypes.Ape;
}
if (data.StartsWith (Id3v2.Header.FileIdentifier)) {
Id3v2.Header header =
new Id3v2.Header (data);
position += header.CompleteTagSize;
return TagTypes.Id3v2;
}
} catch (CorruptFileException) {
}
return TagTypes.None;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fffde839a40fa2dca6c46e99e9d8e66
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+246
View File
@@ -0,0 +1,246 @@
//
// Tag.cs: Combines StartTag and EndTag in such a way as their children appear
// as its children.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace TagLib.NonContainer
{
/// <summary>
/// This class extends <see cref="CombinedTag" />, combining <see
/// cref="StartTag" /> and <see cref="EndTag" /> in such a way as
/// their children appear as its children.
/// </summary>
public class Tag : CombinedTag
{
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Tag" /> for a specified <see cref="TagLib.File" />.
/// </summary>
/// <param name="file">
/// A <see cref="TagLib.File" /> object on which the new
/// instance will perform its operations.
/// </param>
/// <remarks>
/// Constructing a new instance does not automatically read
/// the contents from the disk. <see cref="Read" /> must be
/// called to read the tags.
/// </remarks>
public Tag (File file)
{
StartTag = new StartTag (file);
EndTag = new EndTag (file);
AddTag (StartTag);
AddTag (EndTag);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the collection of tags appearing at the start of the
/// file.
/// </summary>
/// <value>
/// A <see cref="TagLib.NonContainer.StartTag" /> storing the
/// tags for the start of the file.
/// </value>
public StartTag StartTag { get; private set; }
/// <summary>
/// Gets the collection of tags appearing at the end of the
/// file.
/// </summary>
/// <value>
/// A <see cref="TagLib.NonContainer.EndTag" /> storing the
/// tags for the end of the file.
/// </value>
public EndTag EndTag { get; private set; }
/// <summary>
/// Gets the tag types contained in the current instance.
/// </summary>
/// <value>
/// A bitwise combined <see cref="TagLib.TagTypes" />
/// containing the tag types contained in the current
/// instance.
/// </value>
public override TagTypes TagTypes {
get { return StartTag.TagTypes | EndTag.TagTypes; }
}
/// <summary>
/// Gets the tags combined in the current instance.
/// </summary>
/// <value>
/// A <see cref="T:TagLib.Tag[]" /> containing the tags combined in
/// the current instance.
/// </value>
/// <remarks>
/// This contains the combined children of <see
/// cref="Tag.StartTag" /> and <see cref="Tag.EndTag" />.
/// </remarks>
public override TagLib.Tag[] Tags {
get {
List<TagLib.Tag> tags = new List<TagLib.Tag> ();
tags.AddRange (StartTag.Tags);
tags.AddRange (EndTag.Tags);
return tags.ToArray ();
}
}
#endregion
#region Public Methods
/// <summary>
/// Gets a tag of a specified type from the current instance.
/// </summary>
/// <param name="type">
/// A <see cref="TagLib.TagTypes" /> value indicating the
/// type of tag to read.
/// </param>
/// <returns>
/// A <see cref="Tag" /> object containing the tag that was
/// found in the current instance. If no
/// matching tag was found and none was created, <see
/// langword="null" /> is returned.
/// </returns>
public TagLib.Tag GetTag (TagTypes type)
{
foreach (TagLib.Tag t in Tags) {
if (type == TagTypes.Id3v1 && t is Id3v1.Tag)
return t;
if (type == TagTypes.Id3v2 && t is Id3v2.Tag)
return t;
if (type == TagTypes.Ape && t is Ape.Tag)
return t;
}
return null;
}
/// <summary>
/// Removes a set of tag types from the current instance.
/// </summary>
/// <param name="types">
/// A bitwise combined <see cref="TagLib.TagTypes" /> value
/// containing tag types to be removed from the file.
/// </param>
/// <remarks>
/// In order to remove all tags from a file, pass <see
/// cref="TagTypes.AllTags" /> as <paramref name="types" />.
/// </remarks>
public void RemoveTags (TagTypes types)
{
StartTag.RemoveTags (types);
EndTag.RemoveTags (types);
}
/// <summary>
/// Reads the tags at the start and end of the file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value reference which will be set
/// to contain the seek position in the file at which the
/// tags at the start end. This also marks the seek position
/// at which the media begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value reference which will be set
/// to contain the seek position in the file at which the
/// tags at the end begin. This also marks the seek position
/// at which the media ends.
/// </param>
public void Read (out long start, out long end)
{
start = ReadStart (ReadStyle.None);
end = ReadEnd (ReadStyle.None);
}
/// <summary>
/// Reads the tags stored at the start of the file into the
/// current instance.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the read tags end. This also
/// marks the seek position at which the media begins.
/// </returns>
public long ReadStart (ReadStyle style)
{
return StartTag.Read (style);
}
/// <summary>
/// Reads the tags stored at the end of the file into the
/// current instance.
/// </summary>
/// <returns>
/// A <see cref="long" /> value indicating the seek position
/// in the file at which the read tags begin. This also
/// marks the seek position at which the media ends.
/// </returns>
public long ReadEnd (ReadStyle style)
{
return EndTag.Read (style);
}
/// <summary>
/// Writes the tags to the start and end of the file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value reference which will be set
/// to contain the new seek position in the file at which the
/// tags at the start end. This also marks the seek position
/// at which the media begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value reference which will be set
/// to contain the new seek position in the file at which the
/// tags at the end begin. This also marks the seek position
/// at which the media ends.
/// </param>
public void Write (out long start, out long end)
{
start = StartTag.Write ();
end = EndTag.Write ();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f989da13f485d8b94b8b06320aa07630
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: