-
-
Notifications
You must be signed in to change notification settings - Fork 109
Expand file tree
/
Copy pathFrameHeader.ts
More file actions
107 lines (93 loc) · 3.18 KB
/
FrameHeader.ts
File metadata and controls
107 lines (93 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// lib/id3v2/FrameHeader.ts
import * as Token from 'token-types';
import * as util from '../common/Util.js';
import { UINT32SYNCSAFE, type ID3v2MajorVersion } from './ID3v2Token.js';
import type { IWarningCollector } from '../common/MetadataCollector.js';
import { TextDecoder } from '@exodus/bytes/encoding.js';
import { Id3v2ContentError } from './FrameParser.js';
export interface IFrameFlags {
status: {
tag_alter_preservation: boolean;
file_alter_preservation: boolean;
read_only: boolean;
};
format: {
grouping_identity: boolean;
compression: boolean;
encryption: boolean;
unsynchronisation: boolean;
data_length_indicator: boolean;
};
}
export interface IFrameHeader {
id: string;
length: number;
flags?: IFrameFlags;
}
/**
* Frame header length (bytes) depending on ID3v2 major version.
*/
export function getFrameHeaderLength(majorVer: number): 6 | 10 {
switch (majorVer) {
case 2: return 6;
case 3:
case 4: return 10;
default: throw makeUnexpectedMajorVersionError(majorVer);
}
}
function readFrameFlags(b: Uint8Array): IFrameFlags {
return {
status: {
tag_alter_preservation: util.getBit(b, 0, 6),
file_alter_preservation: util.getBit(b, 0, 5),
read_only: util.getBit(b, 0, 4)
},
format: {
grouping_identity: util.getBit(b, 1, 7),
compression: util.getBit(b, 1, 3),
encryption: util.getBit(b, 1, 2),
unsynchronisation: util.getBit(b, 1, 1),
data_length_indicator: util.getBit(b, 1, 0)
}
};
}
/**
* Factory: parse a frame header from its header bytes (6 for v2.2, 10 for v2.3/v2.4).
*
* Note: It only *parses* and does light validation. It does not read payload bytes.
*/
export function readFrameHeader(uint8Array: Uint8Array, majorVer: ID3v2MajorVersion, warningCollector: IWarningCollector): IFrameHeader {
switch (majorVer) {
case 2:
return parseFrameHeaderV22(uint8Array, majorVer, warningCollector);
case 3:
case 4:
return parseFrameHeaderV23V24(uint8Array, majorVer, warningCollector);
default:
throw makeUnexpectedMajorVersionError(majorVer);
}
}
function parseFrameHeaderV22(uint8Array: Uint8Array, majorVer: 2, warningCollector: IWarningCollector): IFrameHeader {
const header: IFrameHeader = {
id: new TextDecoder('ascii').decode(uint8Array.subarray(0, 3)),
length: Token.UINT24_BE.get(uint8Array, 3)
};
if (!header.id.match(/^[A-Z0-9]{3}$/)) {
warningCollector.addWarning(`Invalid ID3v2.${majorVer} frame-header-ID: ${header.id}`);
}
return header;
}
function parseFrameHeaderV23V24(uint8Array: Uint8Array, majorVer: 3 | 4, warningCollector: IWarningCollector): IFrameHeader {
const header: IFrameHeader = {
id: new TextDecoder('ascii').decode(uint8Array.subarray(0, 4)),
length: (majorVer === 4 ? UINT32SYNCSAFE : Token.UINT32_BE).get(uint8Array, 4),
flags: readFrameFlags(uint8Array.subarray(8, 10))
};
if (!header.id.match(/^[A-Z0-9]{4}$/)) {
warningCollector.addWarning(`Invalid ID3v2.${majorVer} frame-header-ID: ${header.id}`);
}
return header;
}
function makeUnexpectedMajorVersionError(majorVer: number): never {
throw new Id3v2ContentError(`Unexpected majorVer: ${majorVer}`);
}