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

benrr101 / node-taglib-sharp / 46462135

pending completion
46462135

push

appveyor

Benjamin Russell
Merge branch 'release/v5.1.0'

3096 of 3788 branches covered (81.73%)

Branch coverage included in aggregate %.

2171 of 2171 new or added lines in 47 files covered. (100.0%)

25320 of 26463 relevant lines covered (95.68%)

437.0 hits per line

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

86.36
/src/id3v2/frames/frameHeader.ts
1
import SyncData from "../syncData";
1✔
2
import {ByteVector, StringType} from "../../byteVector";
1✔
3
import {CorruptFileError} from "../../errors";
1✔
4
import {FrameIdentifier, FrameIdentifiers} from "../frameIdentifiers";
1✔
5
import {Guards, NumberUtils} from "../../utils";
1✔
6

7
/**
8
 * Indicates the flags applied to a {@link Id3v2FrameHeader} object.
9
 */
10
export enum Id3v2FrameFlags {
1✔
11
    /**
12
     * Header contains no flags.
13
     */
14
    None = 0,
1✔
15

16
    /**
17
     * Frame is to be deleted if the tag is altered.
18
     */
19
    TagAlterPreservation = 0x4000,
1✔
20

21
    /**
22
     * Frame is to be deleted if the file is altered.
23
     */
24
    FileAlterPreservation = 0x2000,
1✔
25

26
    /**
27
     * Frame is read-only and should not be altered.
28
     */
29
    ReadOnly = 0x1000,
1✔
30

31
    /**
32
     * Frame has a grouping identity.
33
     */
34
    GroupingIdentity = 0x0040,
1✔
35

36
    /**
37
     * Frame data is compressed.
38
     */
39
    Compression = 0x0008,
1✔
40

41
    /**
42
     * Frame data is encrypted.
43
     */
44
    Encryption = 0x0004,
1✔
45

46
    /**
47
     * Frame data has been unsynchronized using the ID3v2 unsynchronization scheme.
48
     */
49
    Unsynchronized = 0x0002,
1✔
50

51
    /**
52
     * Frame has a data length indicator.
53
     */
54
    DataLengthIndicator = 0x0001
1✔
55
}
56

57
/**
58
 * This class provides a representation of an ID3v2 frame header which can be read from and
59
 * written to disk.
60
 */
61
export class Id3v2FrameHeader {
1✔
62
    private _flags: Id3v2FrameFlags;
63
    private _frameId: FrameIdentifier;
64
    private _frameSize: number;
65

66
    /**
67
     * Constructs and initializes a new instance by processing the data for the frame header.
68
     * @param id Identifier of the frame
69
     * @param flags Flags to assign to the frame (if omitted, defaults to
70
     *     {@link Id3v2FrameFlags.None})
71
     * @param frameSize Size of the frame in bytes, excluding the size of the header (if omitted,
72
     *     defaults to 0)
73
     */
74
    public constructor(id: FrameIdentifier, flags: Id3v2FrameFlags = Id3v2FrameFlags.None, frameSize: number = 0) {
1,529✔
75
        Guards.truthy(id, "id");
1,222✔
76
        Guards.uint(frameSize, "frameSize");
1,222✔
77

78
        this._frameId = id;
1,222✔
79
        this._frameSize = frameSize;
1,222✔
80
        this.flags = flags; // Force the compression/encryption checks
1,222✔
81
    }
82

83
    /**
84
     * Constructs and initializes a new instance of {@link Id3v2FrameHeader} by reading it from raw
85
     * header data of a specified version.
86
     * @param data Raw data to build the new instance from.
87
     *     If the data size is smaller than the size of a full header, the data is just treated as
88
     *     a frame identifier and the remaining values are zeroed.
89
     * @param version ID3v2 version with which the data in `data` was encoded.
90
     */
91
    public static fromData(data: ByteVector, version: number): Id3v2FrameHeader {
92
        Guards.truthy(data, "data");
438✔
93
        Guards.byte(version, "version");
438✔
94
        Guards.betweenInclusive(version, 2, 4, "version");
438✔
95

96
        if (data.length < (version === 2 ? 3 : 4)) {
438!
97
            throw new Error("Data must contain at least a frame ID");
×
98
        }
99

100
        let frameId;
101
        let flags = 0;
438✔
102
        let frameSize = 0;
438✔
103
        switch (version) {
438✔
104
            case 2:
438✔
105
                if (data.length < 3) {
2!
106
                    throw new CorruptFileError("Data must contain at least a 3 byte frame identifier");
×
107
                }
108

109
                // Set frame ID -- first 3 bytes
110
                frameId = FrameIdentifiers[data.subarray(0, 3).toString(StringType.Latin1)];
2✔
111

112
                // If the full header information was not passed in, do not continue to the steps
113
                // to parse the frame size and flags.
114
                if (data.length < 6) {
2!
115
                    break;
×
116
                }
117

118
                frameSize = data.subarray(3, 3).toUint();
2✔
119
                break;
2✔
120

121
            case 3:
122
                if (data.length < 4) {
46!
123
                    throw new CorruptFileError("Data must contain at least a 4 byte frame identifier");
×
124
                }
125

126
                // Set the frame ID -- first 4 bytes
127
                frameId = FrameIdentifiers[data.subarray(0, 4).toString(StringType.Latin1)];
46✔
128

129
                // If the full header information was not passed in, do not continue to the steps
130
                // to parse the frame size and flags.
131
                if (data.length < 10) {
46!
132
                    break;
×
133
                }
134

135
                // Store the flags internally as version 2.4
136
                frameSize = data.subarray(4, 4).toUint();
46✔
137
                flags = NumberUtils.uintOr(
46✔
138
                    NumberUtils.uintAnd(NumberUtils.uintLShift(data.get(8), 7), 0x7000),
139
                    NumberUtils.uintAnd(NumberUtils.uintRShift(data.get(9), 4), 0x000C),
140
                    NumberUtils.uintAnd(NumberUtils.uintLShift(data.get(9), 1), 0x0040)
141
                );
142
                break;
46✔
143

144
            case 4:
145
                if (data.length < 4) {
390!
146
                    throw new CorruptFileError("Data must contain at least 4 byte frame identifier");
×
147
                }
148

149
                // Set the frame ID -- the first 4 bytes
150
                frameId = FrameIdentifiers[data.subarray(0, 4).toString(StringType.Latin1)];
390✔
151

152
                // If the full header information was not passed in, do not continue to the steps to
153
                // ... eh, you probably get it by now.
154
                if (data.length < 10) {
390!
155
                    return;
×
156
                }
157

158
                frameSize = SyncData.toUint(data.subarray(4, 4));
390✔
159
                flags = data.subarray(8, 2).toUshort();
390✔
160
                break;
390✔
161
        }
162

163
        return new Id3v2FrameHeader(frameId, flags, frameSize);
438✔
164
    }
165

166
    /**
167
     * Constructs and initializes a new, blank frame header of size 0, with the
168
     * provided frame identifier.
169
     * @param id Identifier for the frame
170
     */
171
    public static fromFrameIdentifier(id: FrameIdentifier): Id3v2FrameHeader {
172
        return new Id3v2FrameHeader(id, Id3v2FrameFlags.None, 0);
×
173
    }
174

175
    // #region Properties
176

177
    /**
178
     * Gets the flags applied to the current instance.
179
     */
180
    public get flags(): Id3v2FrameFlags { return this._flags; }
3,766✔
181
    /**
182
     * Sets the flags applied to the current instance.
183
     */
184
    public set flags(value: Id3v2FrameFlags) {
185
        if (NumberUtils.hasFlag(value, (Id3v2FrameFlags.Compression | Id3v2FrameFlags.Encryption))) {
1,281✔
186
            throw new Error("Argument invalid: Encryption and compression are not supported");
3✔
187
        }
188
        this._flags = value;
1,278✔
189
    }
190

191
    /**
192
     * Gets the identifier of the frame described by the current instance.
193
     */
194
    public get frameId(): FrameIdentifier { return this._frameId; }
2,292✔
195
    /**
196
     * Sets the identifier of the frame described by the current instance.
197
     */
198
    public set frameId(value: FrameIdentifier) {
199
        Guards.truthy(value, "value");
5✔
200
        this._frameId = value;
5✔
201
    }
202

203
    /**
204
     * Gets the size of the frame described by the current instance, minus the header.
205
     */
206
    public get frameSize(): number { return this._frameSize; }
435✔
207
    /**
208
     * Sets the size of the frame described by the current instance, minus the header.
209
     * Must be a positive, safe integer.
210
     */
211
    public set frameSize(value: number) {
212
        Guards.uint(value, "value");
523✔
213
        this._frameSize = value;
523✔
214
    }
215

216
    // #endregion
217

218
    // #region Public Methods
219

220
    /**
221
     * Gets the size of a header for a specified ID3v2 version.
222
     * @param version Version of ID3v2 to get the size for. Must be a positive integer < 256
223
     */
224
    public static getSize(version: number): number {
225
        Guards.byte(version, "version");
406✔
226
        return version < 3 ? 6 : 10;
406✔
227
    }
228

229
    /**
230
     * Renders the current instance, encoded in a specified ID3v2 version.
231
     * @param version Version of ID3v2 to use when encoding the current instance.
232
     */
233
    public render(version: number): ByteVector {
234
        Guards.byte(version, "version");
496✔
235
        Guards.betweenInclusive(version, 2, 4, "version");
496✔
236

237
        // Start by rendering the frame identifier
238
        const byteVectors = [this._frameId.render(version)];
496✔
239

240
        switch (version) {
494✔
241
            case 2:
494✔
242
                byteVectors.push(ByteVector.fromUint(this._frameSize).subarray(1, 3));
5✔
243
                break;
5✔
244

245
            case 3:
246
                const newFlags = NumberUtils.uintOr(
55✔
247
                    NumberUtils.uintAnd(NumberUtils.uintLShift(this._flags, 1), 0xE000),
248
                    NumberUtils.uintAnd(NumberUtils.uintLShift(this._flags, 4), 0x00C0),
249
                    NumberUtils.uintAnd(NumberUtils.uintRShift(this._flags, 1), 0x0020)
250
                );
251

252
                byteVectors.push(ByteVector.fromUint(this._frameSize));
55✔
253
                byteVectors.push(ByteVector.fromUshort(newFlags));
55✔
254
                break;
55✔
255

256
            case 4:
257
                byteVectors.push(SyncData.fromUint(this._frameSize));
434✔
258
                byteVectors.push(ByteVector.fromUshort(this._flags));
434✔
259
                break;
434✔
260
        }
261

262
        return ByteVector.concatenate(... byteVectors);
494✔
263
    }
264

265
    // #endregion
266
}
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

© 2026 Coveralls, Inc