userscripts/AnimeBytes/deps/mediainfo.js

979 lines
25 KiB
JavaScript

class Mediainfo {
constructor() {
if (this instanceof Mediainfo) {
throw Error('A static class cannot be instantiated.');
}
}
static audioCodecs = [
{
name: "HE-AAC",
match: {
codec_id: /^(A_)?AAC\/MPEG4\/LC\/SBR/
},
legacy: "AAC"
},
{
name: "AAC-LC",
match: {
codec_id: /^(A_)?AAC/,
format_profile: "LC"
},
legacy: "AAC"
},
{
name: "HE-AAC",
match: {
codec_id: /^(A_)?AAC/,
format_profile: /HE-AAC/
},
legacy: "AAC"
},
{
name: "HE-AAC",
match: {
codec_id: /^(A_)?AAC/,
commercial_settings: /HE-AAC/
},
legacy: "AAC"
},
{
name: "HE-AAC",
match: {
format: "AAC LC SBR",
codec_id: /^(A_)?AAC/
},
legacy: "AAC"
},
{
name: "HE-AAC",
match: {
codec_id: "mp4a-40-5"
},
legacy: "AAC"
},
{
name: "AAC-LC",
match: {
codec_id: /^(A_AAC-2|mp4a-40-2|AAC LC)$/
},
legacy: "AAC"
},
{
name: "AAC-LC",
match: {
format: "AAC LC"
},
legacy: "AAC"
},
{
name: "AAC-Main",
match: {
codec_id: "A_AAC-1"
},
legacy: "AAC"
},
{
name: "AAC-Main",
match: {
codec_id: /^(A_)?AAC/,
format_profile: "Main"
},
legacy: "AAC"
},
{
name: "AAC-Main",
match: {
codec_id: /^(A_)?AAC/,
format_profile: "Main"
},
legacy: "AAC"
},
{
name: "AAC",
match: {
codec_id: /^(A_)?AAC/
},
legacy: "AAC"
},
{
name: "AC3",
match: {
codec_id: /^(A_)?AC-?3$/i
},
legacy: "AC3"
},
{
name: "Opus",
match: {
codec_id: /^(A_)?OPUS/i
},
legacy: "Opus",
open: true
},
{
name: "Vorbis",
match: {
codec_id: /^(A_)?VORBIS/i
},
legacy: "Vorbis",
open: true
},
{
name: "MP1",
match: {
codec_id: /^(MP1|A_MPEG\/L1)$/i
},
legacy: "MP1"
},
{
name: "MP1",
match: {
format: "MPEG Audio",
format_profile: "Layer 1"
},
legacy: "MP1"
},
{
name: "MP2",
match: {
codec_id: /^(MP2|A_MPEG\/L2)$/i
},
legacy: "MP2"
},
{
name: "MP2",
match: {
format: "MPEG Audio",
format_profile: "Layer 2"
},
legacy: "MP2"
},
{
name: "MP3",
match: {
codec_id: /^(MP3|A_MPEG\/L3)$/i
},
legacy: "MP3",
open: true //Patents expired
},
{
name: "MP3",
match: {
format: "MPEG Audio",
format_profile: "Layer 3"
},
legacy: "MP3",
open: true //Patents expired
},
{
name: "WMA",
match: {
codec_id: /^(A_)?WMA/i
},
legacy: "WMA",
},
{
name: "WMA",
match: {
format: /^(A_)?WMA/i
},
legacy: "WMA",
},
{
name: "ALAC",
match: {
codec_id: /^(A_)?ALAC/i
},
legacy: "ALAC",
lossless: true,
open: true
},
{
name: "TrueAudio",
match: {
codec_id: /^(A_)?TTA/
},
legacy: "TTA",
lossless: true,
open: true
},
{
name: "RealAudio 14.4",
match: {
codec_id: /^((A_)?REAL\/)?14_4/i
},
legacy: "RealAudio"
},
{
name: "RealAudio 28.8",
match: {
codec_id: /^((A_)?REAL\/)?28_8/i
},
legacy: "RealAudio"
},
{
name: "RealAudio ATRAC3",
match: {
codec_id: /^((A_)?REAL\/)?ATRC/i
},
legacy: "RealAudio"
},
{
name: "RealAudio COOK",
match: {
codec_id: /^((A_)?REAL\/)?COOK/i
},
legacy: "RealAudio"
},
{
name: "RealAudio SIPR",
match: {
codec_id: /^((A_)?REAL\/)?SIPR/i
},
legacy: "RealAudio"
},
{
name: "WavPack",
match: {
codec_id: "A_WAVPACK4"
},
legacy: "WAV",
lossless: true,
open: true
},
{
name: "FLAC",
match: {
codec_id: /^(A_)?FLAC/i
},
legacy: "FLAC",
lossless: true,
open: true
},
{
name: "PCM",
match: {
format: /^PCM$/i
},
legacy: "PCM",
lossless: true,
open: true
},
{
name: "PCM",
match: {
codec_id: /^(A_)?PCM/
},
legacy: "PCM",
lossless: true,
open: true
},
{
name: "E-AC3",
match: {
codec_id: /(A_)?E-?AC-?3/,
number_of_dynamic_objects: null
},
legacy: "AC3"
},
{
name: "E-AC3 Atmos",
match: {
codec_id: /(A_)?E-?AC-?3/,
number_of_dynamic_objects: /^[0-9]+$/
},
legacy: "AC3"
},
{
name: "TrueHD Atmos",
match: {
codec_id: /^(A_)?TRUEHD/i,
number_of_dynamic_objects: /^[0-9]+$/
},
legacy: "TrueHD",
lossless: true
},
{
name: "TrueHD Atmos",
match: {
codec_id: /^(A_)?TRUEHD/i,
channel_positions: /^Object Based/
},
legacy: "TrueHD",
lossless: true
},
{
name: "TrueHD",
match: {
codec_id: /^(A_)?TRUEHD/i,
number_of_dynamic_objects: null
},
legacy: "TrueHD",
lossless: true
},
{
name: "TrueHD",
match: {
codec_id: "MLP FBA",
commercial_name: /TrueHD/,
number_of_dynamic_objects: null
},
legacy: "TrueHD",
lossless: true
},
{
name: "TrueHD Atmos",
match: {
format: /^AC-3 MLP FBA/,
commercial_name: /TrueHD/,
number_of_dynamic_objects: /^[0-9]+$/
},
legacy: "TrueHD",
lossless: true
},
{
name: "TrueHD",
match: {
format: /^AC-3 MLP FBA/,
commercial_name: /TrueHD/,
number_of_dynamic_objects: null
},
legacy: "TrueHD",
lossless: true
},
{
name: "DTS:X MA",
match: {
codec_id: /^(A_)?DTS$/,
format: "DTS XLL X",
channels_original: "Object Based"
},
legacy: "DTS-HD MA",
lossless: true
},
{
name: "DTS-HD MA",
match: {
codec_id: /^(A_)?DTS$/,
format: "DTS XLL",
channels_original: null,
},
legacy: "DTS-HD MA",
lossless: true
},
{
name: "DTS-HD",
match: {
codec_id: /^(A_)?DTS$/,
format: "DTS XBR",
},
legacy: "DTS-HD"
},
{
name: "DTS",
match: {
codec_id: /^(A_)?DTS$/,
format: "DTS",
format_profile: null,
},
legacy: "DTS"
},
{
name: "DTS",
match: {
codec_id: /^(A_)?DTS$/,
format: "DTS",
format_profile: null,
},
legacy: "DTS"
},
{
name: "DTS-ES",
match: {
codec_id: /^(A_)?DTS$/,
format: /^(DTS )?ES/,
},
legacy: "DTS-ES"
},
{
name: "DTS-HD MA",
match: {
codec_id: /^(A_)?DTS$/,
format_profile: /^MA/,
channels_original: null,
},
legacy: "DTS-HD MA",
lossless: true
},
{ //Old way of identifying
name: "DTS-ES",
match: {
codec_id: /^(A_)?DTS$/,
format_profile: /^(DTS )?ES/,
},
legacy: "DTS-ES"
},
{ //Old way of identifying
name: "DTS-HD MA",
match: {
codec_id: /^(A_)?DTS$/,
format_profile: "MA / Core",
channels_original: null,
},
legacy: "DTS-HD MA",
lossless: true
},
{ //Even Older way of identifying
name: "DTS-HD MA",
match: {
codec_id: /^((A_)?DTS|DTS ES XLL)/,
commercial_name: "DTS-HD Master Audio",
channels_original: null,
},
legacy: "DTS-HD MA",
lossless: true
}
];
static videoCodecs = [
{
name: "MPEG-1",
match: {
codec_id: "V_MPEG1",
},
legacy: "MPEG-1/2"
},
{
name: "MPEG-2",
match: {
codec_id: "V_MPEG2",
},
legacy: "MPEG-1/2"
},
{
name: "MPEG-$version",
match: {
format: "MPEG Video",
format_version: /^Version (?<version>[12])$/,
},
legacy: "MPEG-1/2"
},
{
name: "VC-1 $codec",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?((?<codec>WMV3|WMVA|WVC1)|VC-?1)/,
bit_depth: "8 bits"
},
legacy: "VC-1"
},
{
name: "DivX 5",
match: {
codec_id: /(^V_MPEG4\/ISO\/ASP|DX50)$/,
},
legacy: "DivX"
},
{
name: "DivX 5",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?DX50/,
},
legacy: "DivX"
},
{
name: "DivX 4",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?DIVX/,
},
legacy: "DivX"
},
{
name: "DivX 4",
match: {
codec_id: /^V_MPEG4\/ISO\/SP$/,
},
legacy: "DivX"
},
{
name: "DivX 3",
match: {
codec_id: /(^V_MPEG4\/MS\/V3|DIV3)$/,
},
legacy: "DivX"
},
{
name: "XviD",
match: {
codec_id: /^V_MS\/VFW\/FOURCC \/ (XVID|mp4v)/i,
},
legacy: "XviD"
},
{
name: "XviD",
match: {
codec_id: /(^V_MPEG4\/ISO\/ASP|XVID)$/,
},
legacy: "XviD"
},
{
name: "Sorenson Video",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?SVQ1/i
},
legacy: "Sorenson Video"
},
{
name: "Sorenson Video 3",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?SVQ3/i
},
legacy: "Sorenson Video 3"
},
{
name: "Sorenson Spark",
match: {
codec_id: /^(V_MS\/VFW\/FOURCC \/ )?FLV1/i
},
legacy: "Sorenson Spark"
},
{
name: "RealVideo 1",
match: {
codec_id: /^((V_)?REAL\/)?RV10/i
},
legacy: "RealVideo"
},
{
name: "RealVideo G2",
match: {
codec_id: /^((V_)?REAL\/)?RV20/i
},
legacy: "RealVideo"
},
{
name: "RealVideo 3",
match: {
codec_id: /^((V_)?REAL\/)?RV30/i
},
legacy: "RealVideo"
},
{
name: "RealVideo 4",
match: {
codec_id: /^((V_)?REAL\/)?RV40/i
},
legacy: "RealVideo"
},
{
name: "h264 $chroma",
match: {
codec_id: /^(V_MPEG4\/ISO\/AVC|avc1|AVC|V_MS\/VFW\/FOURCC \/ H264)$/,
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h264"
},
{
name: "h264 $bitDepth-bit $chroma",
match: {
codec_id: /^(V_MPEG4\/ISO\/AVC|avc1|V_MS\/VFW\/FOURCC \/ H264)$/,
bit_depth: /^(?<bitDepth>10) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h264 $bitDepth-bit"
},
{
name: "h264 $chroma",
match: {
codec_id: /^(V_MPEG4\/ISO\/AVC|avc1|AVC|V_MS\/VFW\/FOURCC \/ H264)$/,
resolution: "24 bits",
colorimetry: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h264"
},
{
name: "h264 10-bit $chroma",
match: {
codec_id: /^(V_MPEG4\/ISO\/AVC|avc1|V_MS\/VFW\/FOURCC \/ H264)$/,
resolution: /^30 bits/,
colorimetry: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h264 $bitDepth-bit"
},
{
name: "Dirac",
match: {
codec_id: /^(V_)?DIRAC/i
},
legacy: "Dirac",
open: true,
},
{
name: "Snow",
match: {
codec_id: /^(V_)?SNOW/i
},
legacy: "Snow",
open: true,
},
{
name: "Theora",
match: {
codec_id: /^(V_)?THEORA/i
},
legacy: "Theora",
open: true,
},
{
name: "VP6",
match: {
codec_id: /^(V_)?VP6/i
},
legacy: "VP6",
open: true,
},
{
name: "VP6",
match: {
format: /^VP6$/i
},
legacy: "VP6",
open: true,
},
{
name: "VP8 $chroma",
match: {
codec_id: /^(V_)?VP8/i,
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "VP8",
open: true,
},
{
name: "VP8 $bitDepth-bit $chroma",
match: {
codec_id: /^(V_)?VP8/i,
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "VP8",
open: true,
},
{
name: "VP9 $chroma",
match: {
codec_id: /^(V_)?VP9/i,
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "VP9",
open: true,
},
{
name: "VP9 $bitDepth-bit $chroma",
match: {
codec_id: /^(V_)?VP9/i,
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "VP9",
open: true,
},
{
name: "AV1 $chroma",
match: {
codec_id: /^((V_)?AV1$|av01)/i,
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "AV1",
open: true,
},
{
name: "AV1 $bitDepth-bit $chroma",
match: {
codec_id: /^((V_)?AV1$|av01)/i,
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "AV1",
open: true,
},
{
name: "h265 $chroma",
match: {
format: "HEVC",
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h265"
},
{
name: "h265 $bitDepth-bit $chroma",
match: {
format: "HEVC",
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h265 $bitDepth-bit"
},
{
name: "h265 $chroma",
match: {
codec_id: /^(V_MPEGH\/ISO\/HEVC|hvc1|hev1)$/,
bit_depth: "8 bits",
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h265"
},
{
name: "h265 $bitDepth-bit $chroma",
match: {
codec_id: /^(V_MPEGH\/ISO\/HEVC|hvc1|hev1)$/,
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: /^(4:2:0|(?<chroma>[0-9]:[0-9]:[0-9]))/
},
legacy: "h265 $bitDepth-bit"
},
{ //Fallback
name: "h265",
match: {
codec_id: /^(V_MPEGH\/ISO\/HEVC|hvc1|hev1)$/,
bit_depth: null,
},
legacy: "h265"
},
{ //Fallback
name: "h265 $bitDepth-bit",
match: {
codec_id: /^(V_MPEGH\/ISO\/HEVC|hvc1|hev1)$/,
bit_depth: /^(?<bitDepth>10|12) bits/,
chroma_subsampling: null
},
legacy: "h265 $bitDepth-bit"
},
{
name: "FOURCC-$fourcc",
match: {
codec_id: /^V_MS\/VFW\/FOURCC \/ (?<fourcc>.{4})$/,
},
legacy: "$fourcc"
},
];
static makeVideoObject(ob){
const codec_id = ("codec_id" in ob && ob.codec_id.match(/^[0-9]+$/) === null) ? ob.codec_id : ("codec_id_hint" in ob ? ob.codec_id_hint : ob.format);
ob.codec_id = codec_id;
ob.__videoCodec = Mediainfo.matchCodec(Mediainfo.videoCodecs, ob, {
name: codec_id
});
}
static makeAudioObject(ob){
const channels = parseInt(("channels" in ob ? ob.channels : ("channel_count" in ob ? ob.channel_count : "1 channel")).match(/(?<channels>[0-9]+) channels?[\s]*(\/|$)/).groups.channels);
const codec_id = ("codec_id" in ob && ob.codec_id.match(/^[0-9]+$/) === null) ? ob.codec_id : ("codec_id_hint" in ob ? ob.codec_id_hint : ob.format);
ob.channels = channels;
ob.codec_id = codec_id;
ob.__audioCodec = Mediainfo.matchCodec(Mediainfo.audioCodecs, ob, {
name: codec_id
});
}
static matchCodec(codecs, ob, fallback = null){
let matchObject = fallback;
codecs.every((codec) => {
let match = true;
let n = codec.name;
let l = codec.legacy;
let m = null;
for(const [key, value] of Object.entries(codec.match)){
if(value === null){
if(key in ob){
match = false;
break;
}
continue;
}
if(!(key in ob)){
match = false;
break;
}else if (value instanceof RegExp){
if((m = ob[key].match(value)) === null){
match = false;
break;
}
if("groups" in m && typeof m.groups === "object"){
for(const [k, v] of Object.entries(m.groups)){
if(typeof v !== "undefined"){
n = n.replace('$' + k, v);
l = l.replace('$' + k, v);
}else{
n = n.replace('$' + k, "");
l = l.replace('$' + k, "");
}
}
}
}else if (!(value instanceof RegExp) && ob[key] !== value){
match = false;
break;
}
}
if(match){
matchObject = Object.assign({}, codec);
matchObject.name = n.trim();
matchObject.legacy = l.trim();
}
return !match;
});
return matchObject;
}
static getAudioChannels(a){
if(a === 3){
return "2.1";
}else if(a === 5){
return "4.1";
}else if(a === 6){
return "5.1";
}else if(a === 7){
return "6.1";
}else if(a === 8){
return "7.1";
}
return a + ".0";
}
static parseText(text) {
let ob = {};
let currentKey = null;
let currentOb = null;
try{
text.split("\n").forEach((line) => {
const cleanLine = line.trim();
if(cleanLine === ""){
return;
}else if(!cleanLine.includes(":")){
if(currentOb !== null && Object.keys(currentOb).length === 0){
//Nothing added, error, invalid mediainfo
throw ("Invalid key " + currentKey);
}
if(currentKey === null && cleanLine.toLowerCase() !== "general"){
throw ("Invalid first key " + cleanLine.toLowerCase());
}else if (currentKey !== null && cleanLine.toLowerCase() === "general"){
throw ("Found another general entry");
}
currentKey = cleanLine.toLowerCase();
let listMatch = currentKey.match(/^(?<kind>(video|audio|text))( #)?(?<number>[0-9]*)$/i);
currentOb = {};
if(listMatch !== null){
let k = listMatch.groups.kind;
if(!(k in ob)){
ob[k] = [];
}
ob[k].push(currentOb);
}else{
ob[currentKey] = currentOb;
}
}else if(currentOb !== null){
const matches = currentKey === "menu" ? cleanLine.match(/^(?<key>([0-9]+:)+[0-9]+(\.[0-9]+)?)[\s]*:(?<value>.+)$/) : cleanLine.match(/^(?<key>((?!\s?:).)+)[\s]*:(?<value>.+)$/);
if(matches !== null){
let key = matches.groups.key.trim().toLowerCase().replace(/[ \/*]/g, "_").replace(/[(),]/g, "");
currentOb[key] = matches.groups.value.trim();
}else{
throw "Invalid entry: " + cleanLine;
}
}else{
throw "Invalid state: " + cleanLine;
}
});
}catch (e){
console.log(text);
console.log(e);
if("general" in ob && "video" in ob){ //Return whatever we have
return ob;
}
return null;
}
return ob;
}
}