• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

benrr101 / node-taglib-sharp / 51161498

13 Dec 2024 04:24AM UTC coverage: 92.554%. Remained the same
51161498

push

appveyor

benrr101
Merge branch 'release/v6.0.1' into develop

3251 of 4131 branches covered (78.7%)

Branch coverage included in aggregate %.

26753 of 28287 relevant lines covered (94.58%)

471.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.14
/src/aiff/aiffFile.ts
1
import AiffFileSettings from "./aiffFileSettings";
1✔
2
import AiffStreamHeader from "./aiffStreamHeader";
1✔
3
import Id3v2Tag from "../id3v2/id3v2Tag";
1✔
4
import {ByteVector, StringType} from "../byteVector";
1✔
5
import {CorruptFileError} from "../errors";
1✔
6
import {File, FileAccessMode, ReadStyle} from "../file";
1✔
7
import {IFileAbstraction} from "../fileAbstraction";
8
import {Properties} from "../properties";
1✔
9
import {Tag, TagTypes} from "../tag";
1✔
10
import {SeekOrigin} from "../stream";
1✔
11
import {NumberUtils} from "../utils";
1✔
12

13
export default class AiffFile extends File {
1✔
14

15
    // #region Constants
16

17
    /**
18
     * Identifier used to recognize an AIFF form type.
19
     */
20
    // @TODO: Add support for AIFF-C files - it's pretty much the same
21
    public static readonly AIFF_FORM_TYPE = ByteVector.fromString("AIFF", StringType.UTF8).makeReadOnly();
1✔
22

23
    /**
24
     * Identifier used to recognize an AIFF common chunk.
25
     */
26
    public static readonly COMM_IDENTIFIER = ByteVector.fromString("COMM", StringType.UTF8).makeReadOnly();
1✔
27

28
    /**
29
     * Identifier used to recognize an AIFF file.
30
     */
31
    public static readonly FILE_IDENTIFIER = ByteVector.fromString("FORM", StringType.UTF8).makeReadOnly();
1✔
32

33
    /**
34
     * Identifier used to recognize an AIFF ID3 chunk.
35
     */
36
    public static readonly ID3_IDENTIFIER = ByteVector.fromString("ID3 ", StringType.UTF8).makeReadOnly();
1✔
37

38
    /**
39
     * Identifier used to recognize an AIFF sound data chunk.
40
     */
41
    public static readonly SOUND_IDENTIFIER = ByteVector.fromString("SSND", StringType.UTF8).makeReadOnly();
1✔
42

43
    // #endregion
44

45
    private _headerBlock: ByteVector;
46
    private _properties: Properties;
47
    private _tag: Id3v2Tag;
48

49
    /**
50
     * Constructs and initializes a new instance of {@link AiffFile} for a specified file
51
     * abstraction and specified read style.
52
     * @param file File abstraction to use when reading and writing to the file
53
     * @param propertiesStyle Level of accuracy to read the media properties, or
54
     *     {@link ReadStyle.None} to ignore the properties
55
     */
56
    public constructor(file: IFileAbstraction|string, propertiesStyle: ReadStyle) {
57
        super(file);
×
58

59
        // Read the file
60
        this.mode = FileAccessMode.Read;
×
61
        try {
×
62
            this.read(true, propertiesStyle);
×
63
        } finally {
64
            this.mode = FileAccessMode.Closed;
×
65
        }
66

67
        // Create default tags if desired
68
        this.tagTypesOnDisk = this.tagTypes;
×
69
        if (NumberUtils.hasFlag(AiffFileSettings.defaultTagTypes, TagTypes.Id3v2)) {
×
70
            this.getTag(TagTypes.Id3v2, true);
×
71
        }
72
    }
73

74
    // #region Properties
75

76
    /** @inheritDoc */
77
    public get properties(): Properties { return this._properties; }
×
78

79
    /** @inheritDoc */
80
    public get tag(): Tag { return this._tag; }
×
81

82
    // #endregion
83

84
    // #region Public Methods
85

86
    /** @inheritDoc */
87
    public getTag(type: TagTypes, create: boolean): Tag {
88
        let id3v2Tag: Tag;
89

90
        switch (type) {
×
91
            case TagTypes.Id3v2:
×
92
                if (!this._tag && create) {
×
93
                    this._tag = Id3v2Tag.fromEmpty();
×
94
                    this._tag.version = 2;
×
95
                }
96

97
                id3v2Tag = this._tag;
×
98
                break;
×
99
        }
100

101
        return id3v2Tag;
×
102
    }
103

104
    /** @inheritDoc */
105
    public removeTags(types: TagTypes): void {
106
        if (NumberUtils.hasFlag(types, TagTypes.Id3v2)) {
×
107
            this._tag = undefined;
×
108
        }
109
    }
110

111
    /** @inheritDoc */
112
    public save(): void {
113
        this.preSave();
×
114

115
        this.mode = FileAccessMode.Write;
×
116
        try {
×
117
            // Add the ID3 chunk and ID3v2 tag to the vector
118
            const id3Chunk = ByteVector.empty();
×
119
            if (this._tag) {
×
120
                const tagData = this._tag.render();
×
121
                if (tagData.length > 10) {
×
122
                    // Add padding if tag data length is odd
123
                    if (tagData.length % 2 === 1) {
×
124
                        tagData.addByte(0);
×
125
                    }
126

127
                    id3Chunk.addByteVector(AiffFile.ID3_IDENTIFIER);
×
128
                    id3Chunk.addByteVector(ByteVector.fromUint(tagData.length, true));
×
129
                    id3Chunk.addByteVector(tagData);
×
130
                }
131
            }
132

133
            const readResult = this.read(false, ReadStyle.None);
×
134

135
            // If tagging info cannot be found, place it at the end of the file
136
            if (readResult.tagStart < 0 || readResult.tagEnd < 0) {
×
137
                readResult.tagStart = readResult.tagEnd = this.length;
×
138
            }
139
            const originalTagChunkLength = readResult.tagEnd - readResult.tagStart + 8;
×
140

141
            // Insert the tagging data
142
            this.insert(id3Chunk, readResult.tagStart, originalTagChunkLength);
×
143

144
            // Update the AIFF size
145
            const aiffSize = this.length - 8;
×
146
            this.insert(ByteVector.fromUint(aiffSize, true), 4, 4);
×
147

148
            // Update the tag types
149
            this.tagTypesOnDisk = this.tagTypes;
×
150
        } finally {
151
            this.mode = FileAccessMode.Closed;
×
152
        }
153
    }
154

155
    // #endregion
156

157
    // #region Private Helpers
158

159
    private findChunk(chunkName: ByteVector, startPos: number): number {
160
        const initialPosition = this.position;
×
161
        try {
×
162
            // Start at the given position
163
            this.seek(startPos);
×
164

165
            // While we're not at the end of the file
166
            while (this.position < this.length) {
×
167
                // Read 4-byte chunk name
168
                if (this.readBlock(4).equals(chunkName)) {
×
169
                    // We found a matching chunk, return the position of the header start
170
                    return this.position - 4;
×
171
                } else {
172
                    // This chunk is not the one we are looking for
173
                    // Continue the search, seeking over the chunk
174
                    const chunkSize = this.readBlock(4).toUint();
×
175
                    this.seek(chunkSize, SeekOrigin.Current);
×
176
                }
177
            }
178

179
            // We did not find the chunk
180
            return -1;
×
181
        } finally {
182
            // Reset the position to where we started
183
            this.seek(initialPosition);
×
184
        }
185
    }
186

187
    private read(readTags: boolean, style: ReadStyle): {fileSize: number, tagEnd: number, tagStart: number} {
188
        this.seek(0);
×
189
        if (!this.readBlock(4).equals(AiffFile.FILE_IDENTIFIER)) {
×
190
            throw new CorruptFileError("File does not begin with AIFF identifier");
×
191
        }
192

193
        const aiffSize = this.readBlock(4).toUint(true);
×
194
        let tagStart = -1;
×
195
        let tagEnd = -1;
×
196

197
        // Check form type
198
        if (!this.readBlock(4).equals(AiffFile.AIFF_FORM_TYPE)) {
×
199
            throw new CorruptFileError("File form type is not AIFF");
×
200
        }
201

202
        // Read the properties of the file
203
        if (!this._headerBlock && style !== ReadStyle.None) {
×
204
            const commonChunkPos = this.findChunk(AiffFile.COMM_IDENTIFIER, this.position);
×
205
            if (commonChunkPos === -1) {
×
206
                throw new CorruptFileError("No common chunk available in this AIFF file");
×
207
            }
208

209
            this.seek(commonChunkPos);
×
210
            this._headerBlock = this.readBlock(AiffStreamHeader.SIZE);
×
211

212
            const header = new AiffStreamHeader(this._headerBlock, aiffSize);
×
213
            this._properties = new Properties(0, [header]);
×
214
        }
215

216
        // Search for the sound chunk
217
        const soundChunkPos = this.findChunk(AiffFile.SOUND_IDENTIFIER, this.position);
×
218
        if (soundChunkPos === -1) {
×
219
            throw new CorruptFileError("No sound chunk available in this AIFF file");
×
220
        }
221
        this.seek(this.position + 4);
×
222
        const soundChunkLength = this.readBlock(4).toUint();
×
223

224
        // Search for the ID3 chunk
225
        const id3ChunkPos = this.findChunk(AiffFile.ID3_IDENTIFIER, this.position + soundChunkLength);
×
226
        if (id3ChunkPos >= 0) {
×
227
            if (readTags && !this._tag) {
×
228
                this._tag = Id3v2Tag.fromFileStart(this, id3ChunkPos + 8, style);
×
229
            }
230

231
            // Get the length of the tag from the ID3 chunk
232
            this.seek(id3ChunkPos + 4);
×
233
            const tagSize = this.readBlock(4).toUint(true) + 8;
×
234

235
            tagStart = id3ChunkPos;
×
236
            tagEnd = tagStart + tagSize;
×
237
        }
238

239
        return {
×
240
            fileSize: this.length,
241
            tagEnd: tagEnd,
242
            tagStart: tagStart
243
        };
244
    }
245

246
    // #endregion
247
}
248

249
// /////////////////////////////////////////////////////////////////////////
250
// Register the file type
251
[
1✔
252
    "taglib/aif",
253
    "taglib/aiff",
254
    "audio/x-aiff",
255
    "audio/aiff",
256
    "sound/aiff",
257
    "application/x-aiff"
258
].forEach((mt) => File.addFileType(mt, AiffFile));
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc