From 42d38d7bfb17ce448a085eae22763b8a97c2307c Mon Sep 17 00:00:00 2001 From: Aleksey Sulzhenko Date: Sat, 18 Sep 2021 18:34:17 +0300 Subject: [PATCH] Added genre parsing for mp4 (atom) file metadata --- mp4.go | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 452 insertions(+), 3 deletions(-) diff --git a/mp4.go b/mp4.go index 6523397..8d89693 100644 --- a/mp4.go +++ b/mp4.go @@ -31,6 +31,8 @@ var atoms = atomNames(map[string]string{ "\xa9day": "year", "\xa9nam": "title", "\xa9gen": "genre", + "gnre": "genre ID3v1 ID", + "geID": "genre ID", "trkn": "track", "\xa9wrt": "composer", "\xa9too": "encoder", @@ -45,6 +47,438 @@ var atoms = atomNames(map[string]string{ "disk": "disc", }) +var genreIDValues = map[int]string{ + 2: "Blues", + 3: "Comedy", + 4: "Children's Music", + 5: "Classical", + 6: "Country", + 7: "Electronic", + 8: "Holiday", + 9: "Classical|Opera", + 10: "Singer/Songwriter", + 11: "Jazz", + 12: "Latino", + 13: "New Age", + 14: "Pop", + 15: "R&B/Soul", + 16: "Soundtrack", + 17: "Dance", + 18: "Hip-Hop/Rap", + 19: "World", + 20: "Alternative", + 21: "Rock", + 22: "Christian & Gospel", + 23: "Vocal", + 24: "Reggae", + 25: "Easy Listening", + 27: "J-Pop", + 28: "Enka", + 29: "Anime", + 30: "Kayokyoku", + 50: "Fitness & Workout", + 51: "Pop|K-Pop", + 52: "Karaoke", + 53: "Instrumental", + 1001: "Alternative|College Rock", + 1002: "Alternative|Goth Rock", + 1003: "Alternative|Grunge", + 1004: "Alternative|Indie Rock", + 1005: "Alternative|New Wave", + 1006: "Alternative|Punk", + 1007: "Blues|Chicago Blues", + 1009: "Blues|Classic Blues", + 1010: "Blues|Contemporary Blues", + 1011: "Blues|Country Blues", + 1012: "Blues|Delta Blues", + 1013: "Blues|Electric Blues", + 1014: "Children's Music|Lullabies", + 1015: "Children's Music|Sing-Along", + 1016: "Children's Music|Stories", + 1017: "Classical|Avant-Garde", + 1018: "Classical|Baroque Era", + 1019: "Classical|Chamber Music", + 1020: "Classical|Chant", + 1021: "Classical|Choral", + 1022: "Classical|Classical Crossover", + 1023: "Classical|Early Music", + 1024: "Classical|Impressionist", + 1025: "Classical|Medieval Era", + 1026: "Classical|Minimalism", + 1027: "Classical|Modern Era", + 1028: "Classical|Opera", + 1029: "Classical|Orchestral", + 1030: "Classical|Renaissance", + 1031: "Classical|Romantic Era", + 1032: "Classical|Wedding Music", + 1033: "Country|Alternative Country", + 1034: "Country|Americana", + 1035: "Country|Bluegrass", + 1036: "Country|Contemporary Bluegrass", + 1037: "Country|Contemporary Country", + 1038: "Country|Country Gospel", + 1039: "Country|Honky Tonk", + 1040: "Country|Outlaw Country", + 1041: "Country|Traditional Bluegrass", + 1042: "Country|Traditional Country", + 1043: "Country|Urban Cowboy", + 1044: "Dance|Breakbeat", + 1045: "Dance|Exercise", + 1046: "Dance|Garage", + 1047: "Dance|Hardcore", + 1048: "Dance|House", + 1049: "Dance|Jungle/Drum'n'bass", + 1050: "Dance|Techno", + 1051: "Dance|Trance", + 1052: "Jazz|Big Band", + 1053: "Jazz|Bop", + 1054: "Easy Listening|Lounge", + 1055: "Easy Listening|Swing", + 1056: "Electronic|Ambient", + 1057: "Electronic|Downtempo", + 1058: "Electronic|Electronica", + 1060: "Electronic|IDM/Experimental", + 1061: "Electronic|Industrial", + 1062: "Singer/Songwriter|Alternative Folk", + 1063: "Singer/Songwriter|Contemporary Folk", + 1064: "Singer/Songwriter|Contemporary Singer/Songwriter", + 1065: "Singer/Songwriter|Folk-Rock", + 1066: "Singer/Songwriter|New Acoustic", + 1067: "Singer/Songwriter|Traditional Folk", + 1068: "Hip-Hop/Rap|Alternative Rap", + 1069: "Hip-Hop/Rap|Dirty South", + 1070: "Hip-Hop/Rap|East Coast Rap", + 1071: "Hip-Hop/Rap|Gangsta Rap", + 1072: "Hip-Hop/Rap|Hardcore Rap", + 1073: "Hip-Hop/Rap|Hip-Hop", + 1074: "Hip-Hop/Rap|Latin Rap", + 1075: "Hip-Hop/Rap|Old School Rap", + 1076: "Hip-Hop/Rap|Rap", + 1077: "Hip-Hop/Rap|Underground Rap", + 1078: "Hip-Hop/Rap|West Coast Rap", + 1079: "Holiday|Chanukah", + 1080: "Holiday|Christmas", + 1081: "Holiday|Christmas: Children's", + 1082: "Holiday|Christmas: Classic", + 1083: "Holiday|Christmas: Classical", + 1084: "Holiday|Christmas: Jazz", + 1085: "Holiday|Christmas: Modern", + 1086: "Holiday|Christmas: Pop", + 1087: "Holiday|Christmas: R&B", + 1088: "Holiday|Christmas: Religious", + 1089: "Holiday|Christmas: Rock", + 1090: "Holiday|Easter", + 1091: "Holiday|Halloween", + 1092: "Holiday|Holiday: Other", + 1093: "Holiday|Thanksgiving", + 1094: "Christian & Gospel|CCM", + 1095: "Christian & Gospel|Christian Metal", + 1096: "Christian & Gospel|Christian Pop", + 1097: "Christian & Gospel|Christian Rap", + 1098: "Christian & Gospel|Christian Rock", + 1099: "Christian & Gospel|Classic Christian", + 1100: "Christian & Gospel|Contemporary Gospel", + 1101: "Christian & Gospel|Gospel", + 1103: "Christian & Gospel|Praise & Worship", + 1104: "Christian & Gospel|Southern Gospel", + 1105: "Christian & Gospel|Traditional Gospel", + 1106: "Jazz|Avant-Garde Jazz", + 1107: "Jazz|Contemporary Jazz", + 1108: "Jazz|Crossover Jazz", + 1109: "Jazz|Dixieland", + 1110: "Jazz|Fusion", + 1111: "Jazz|Latin Jazz", + 1112: "Jazz|Mainstream Jazz", + 1113: "Jazz|Ragtime", + 1114: "Jazz|Smooth Jazz", + 1115: "Latino|Latin Jazz", + 1116: "Latino|Contemporary Latin", + 1117: "Latino|Pop Latino", + 1118: "Latino|Raices", + 1119: "Latino|Urbano latino", + 1120: "Latino|Baladas y Boleros", + 1121: "Latino|Rock y Alternativo", + 1122: "Brazilian", + 1123: "Latino|Musica Mexicana", + 1124: "Latino|Musica tropical", + 1125: "New Age|Environmental", + 1126: "New Age|Healing", + 1127: "New Age|Meditation", + 1128: "New Age|Nature", + 1129: "New Age|Relaxation", + 1130: "New Age|Travel", + 1131: "Pop|Adult Contemporary", + 1132: "Pop|Britpop", + 1133: "Pop|Pop/Rock", + 1134: "Pop|Soft Rock", + 1135: "Pop|Teen Pop", + 1136: "R&B/Soul|Contemporary R&B", + 1137: "R&B/Soul|Disco", + 1138: "R&B/Soul|Doo Wop", + 1139: "R&B/Soul|Funk", + 1140: "R&B/Soul|Motown", + 1141: "R&B/Soul|Neo-Soul", + 1142: "R&B/Soul|Quiet Storm", + 1143: "R&B/Soul|Soul", + 1144: "Rock|Adult Alternative", + 1145: "Rock|American Trad Rock", + 1146: "Rock|Arena Rock", + 1147: "Rock|Blues-Rock", + 1148: "Rock|British Invasion", + 1149: "Rock|Death Metal/Black Metal", + 1150: "Rock|Glam Rock", + 1151: "Rock|Hair Metal", + 1152: "Rock|Hard Rock", + 1153: "Rock|Metal", + 1154: "Rock|Jam Bands", + 1155: "Rock|Prog-Rock/Art Rock", + 1156: "Rock|Psychedelic", + 1157: "Rock|Rock & Roll", + 1158: "Rock|Rockabilly", + 1159: "Rock|Roots Rock", + 1160: "Rock|Singer/Songwriter", + 1161: "Rock|Southern Rock", + 1162: "Rock|Surf", + 1163: "Rock|Tex-Mex", + 1165: "Soundtrack|Foreign Cinema", + 1166: "Soundtrack|Musicals", + 1167: "Comedy|Novelty", + 1168: "Soundtrack|Original Score", + 1169: "Soundtrack|Soundtrack", + 1171: "Comedy|Standup Comedy", + 1172: "Soundtrack|TV Soundtrack", + 1173: "Vocal|Standards", + 1174: "Vocal|Traditional Pop", + 1175: "Jazz|Vocal Jazz", + 1176: "Vocal|Vocal Pop", + 1177: "African|Afro-Beat", + 1178: "African|Afro-Pop", + 1179: "World|Cajun", + 1180: "World|Celtic", + 1181: "World|Celtic Folk", + 1182: "World|Contemporary Celtic", + 1183: "Reggae|Modern Dancehall", + 1184: "World|Drinking Songs", + 1185: "Indian|Indian Pop", + 1186: "World|Japanese Pop", + 1187: "World|Klezmer", + 1188: "World|Polka", + 1189: "World|Traditional Celtic", + 1190: "World|Worldbeat", + 1191: "World|Zydeco", + 1192: "Reggae|Roots Reggae", + 1193: "Reggae|Dub", + 1194: "Reggae|Ska", + 1195: "World|Caribbean", + 1196: "World|South America", + 1197: "Arabic", + 1198: "World|North America", + 1199: "World|Hawaii", + 1200: "World|Australia", + 1201: "World|Japan", + 1202: "World|France", + 1203: "African", + 1204: "World|Asia", + 1205: "World|Europe", + 1206: "World|South Africa", + 1207: "Jazz|Hard Bop", + 1208: "Jazz|Trad Jazz", + 1209: "Jazz|Cool Jazz", + 1210: "Blues|Acoustic Blues", + 1211: "Classical|High Classical", + 1220: "Brazilian|Axe", + 1221: "Brazilian|Bossa Nova", + 1222: "Brazilian|Choro", + 1223: "Brazilian|Forro", + 1224: "Brazilian|Frevo", + 1225: "Brazilian|MPB", + 1226: "Brazilian|Pagode", + 1227: "Brazilian|Samba", + 1228: "Brazilian|Sertanejo", + 1229: "Brazilian|Baile Funk", + 1230: "Alternative|Chinese Alt", + 1231: "Alternative|Korean Indie", + 1232: "Chinese", + 1233: "Chinese|Chinese Classical", + 1234: "Chinese|Chinese Flute", + 1235: "Chinese|Chinese Opera", + 1236: "Chinese|Chinese Orchestral", + 1237: "Chinese|Chinese Regional Folk", + 1238: "Chinese|Chinese Strings", + 1239: "Chinese|Taiwanese Folk", + 1240: "Chinese|Tibetan Native Music", + 1241: "Hip-Hop/Rap|Chinese Hip-Hop", + 1242: "Hip-Hop/Rap|Korean Hip-Hop", + 1243: "Korean", + 1244: "Korean|Korean Classical", + 1245: "Korean|Korean Trad Song", + 1246: "Korean|Korean Trad Instrumental", + 1247: "Korean|Korean Trad Theater", + 1248: "Rock|Chinese Rock", + 1249: "Rock|Korean Rock", + 1250: "Pop|C-Pop", + 1251: "Pop|Cantopop/HK-Pop", + 1252: "Pop|Korean Folk-Pop", + 1253: "Pop|Mandopop", + 1254: "Pop|Tai-Pop", + 1255: "Pop|Malaysian Pop", + 1256: "Pop|Pinoy Pop", + 1257: "Pop|Original Pilipino Music", + 1258: "Pop|Manilla Sound", + 1259: "Pop|Indo Pop", + 1260: "Pop|Thai Pop", + 1261: "Vocal|Trot", + 1262: "Indian", + 1263: "Indian|Bollywood", + 1264: "Indian|Regional Indian|Tamil", + 1265: "Indian|Regional Indian|Telugu", + 1266: "Indian|Regional Indian", + 1267: "Indian|Devotional & Spiritual", + 1268: "Indian|Sufi", + 1269: "Indian|Indian Classical", + 1270: "Russian|Russian Chanson", + 1271: "World|Dini", + 1272: "Turkish|Halk", + 1273: "Turkish|Sanat", + 1274: "World|Dangdut", + 1275: "World|Indonesian Religious", + 1276: "World|Calypso", + 1277: "World|Soca", + 1278: "Indian|Ghazals", + 1279: "Indian|Indian Folk", + 1280: "Turkish|Arabesque", + 1281: "African|Afrikaans", + 1282: "World|Farsi", + 1283: "World|Israeli", + 1284: "Arabic|Khaleeji", + 1285: "Arabic|North African", + 1286: "Arabic|Arabic Pop", + 1287: "Arabic|Islamic", + 1288: "Soundtrack|Sound Effects", + 1289: "Folk", + 1290: "Orchestral", + 1291: "Marching", + 1293: "Pop|Oldies", + 1294: "Country|Thai Country", + 1295: "World|Flamenco", + 1296: "World|Tango", + 1297: "World|Fado", + 1298: "World|Iberia", + 1299: "Russian", + 1300: "Turkish", + 100000: "Christian & Gospel", + 100001: "Classical|Art Song", + 100002: "Classical|Brass & Woodwinds", + 100003: "Classical|Solo Instrumental", + 100004: "Classical|Contemporary Era", + 100005: "Classical|Oratorio", + 100006: "Classical|Cantata", + 100007: "Classical|Electronic", + 100008: "Classical|Sacred", + 100009: "Classical|Guitar", + 100010: "Classical|Piano", + 100011: "Classical|Violin", + 100012: "Classical|Cello", + 100013: "Classical|Percussion", + 100014: "Electronic|Dubstep", + 100015: "Electronic|Bass", + 100016: "Hip-Hop/Rap|UK Hip-Hop", + 100017: "Reggae|Lovers Rock", + 100018: "Alternative|EMO", + 100019: "Alternative|Pop Punk", + 100020: "Alternative|Indie Pop", + 100021: "New Age|Yoga", + 100022: "Pop|Tribute", + 100023: "Pop|Shows", + 100024: "Cuban", + 100025: "Cuban|Mambo", + 100026: "Cuban|Chachacha", + 100027: "Cuban|Guajira", + 100028: "Cuban|Son", + 100029: "Cuban|Bolero", + 100030: "Cuban|Guaracha", + 100031: "Cuban|Timba", + 100032: "Soundtrack|Video Game", + 100033: "Indian|Regional Indian|Punjabi|Punjabi Pop", + 100034: "Indian|Regional Indian|Bengali|Rabindra Sangeet", + 100035: "Indian|Regional Indian|Malayalam", + 100036: "Indian|Regional Indian|Kannada", + 100037: "Indian|Regional Indian|Marathi", + 100038: "Indian|Regional Indian|Gujarati", + 100039: "Indian|Regional Indian|Assamese", + 100040: "Indian|Regional Indian|Bhojpuri", + 100041: "Indian|Regional Indian|Haryanvi", + 100042: "Indian|Regional Indian|Odia", + 100043: "Indian|Regional Indian|Rajasthani", + 100044: "Indian|Regional Indian|Urdu", + 100045: "Indian|Regional Indian|Punjabi", + 100046: "Indian|Regional Indian|Bengali", + 100047: "Indian|Indian Classical|Carnatic Classical", + 100048: "Indian|Indian Classical|Hindustani Classical", + 100049: "African|Afro House", + 100050: "African|Afro Soul", + 100051: "African|Afrobeats", + 100052: "African|Benga", + 100053: "African|Bongo-Flava", + 100054: "African|Coupe-Decale", + 100055: "African|Gqom", + 100056: "African|Highlife", + 100057: "African|Kuduro", + 100058: "African|Kizomba", + 100059: "African|Kwaito", + 100060: "African|Mbalax", + 100061: "African|Ndombolo", + 100062: "African|Shangaan Electro", + 100063: "African|Soukous", + 100064: "African|Taarab", + 100065: "African|Zouglou", + 100066: "Turkish|Ozgun", + 100067: "Turkish|Fantezi", + 100068: "Turkish|Religious", + 100069: "Pop|Turkish Pop", + 100070: "Rock|Turkish Rock", + 100071: "Alternative|Turkish Alternative", + 100072: "Hip-Hop/Rap|Turkish Hip-Hop/Rap", + 100073: "African|Maskandi", + 100074: "Russian|Russian Romance", + 100075: "Russian|Russian Bard", + 100076: "Russian|Russian Pop", + 100077: "Russian|Russian Rock", + 100078: "Russian|Russian Hip-Hop", + 100079: "Arabic|Levant", + 100080: "Arabic|Levant|Dabke", + 100081: "Arabic|Maghreb Rai", + 100082: "Arabic|Khaleeji|Khaleeji Jalsat", + 100083: "Arabic|Khaleeji|Khaleeji Shailat", + 100084: "Tarab", + 100085: "Tarab|Iraqi Tarab", + 100086: "Tarab|Egyptian Tarab", + 100087: "Tarab|Khaleeji Tarab", + 100088: "Pop|Levant Pop", + 100089: "Pop|Iraqi Pop", + 100090: "Pop|Egyptian Pop", + 100091: "Pop|Maghreb Pop", + 100092: "Pop|Khaleeji Pop", + 100093: "Hip-Hop/Rap|Levant Hip-Hop", + 100094: "Hip-Hop/Rap|Egyptian Hip-Hop", + 100095: "Hip-Hop/Rap|Maghreb Hip-Hop", + 100096: "Hip-Hop/Rap|Khaleeji Hip-Hop", + 100097: "Alternative|Indie Levant", + 100098: "Alternative|Indie Egyptian", + 100099: "Alternative|Indie Maghreb", + 100100: "Electronic|Levant Electronic", + 100101: "Electronic|Electro-Cha'abi", + 100102: "Electronic|Maghreb Electronic", + 100103: "Folk|Iraqi Folk", + 100104: "Folk|Khaleeji Folk", + 100105: "Dance|Maghreb Dance", + 50000061: "Spoken Word", + 50000063: "Disney", + 50000064: "French Pop", + 50000066: "German Pop", + 50000068: "German Folk", +} + // Detect PNG image if "implicit" class is used var pngHeader = []byte{137, 80, 78, 71, 13, 10, 26, 10} @@ -148,10 +582,15 @@ func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32, pro // "data" + size (4 bytes each) b = b[8:] - if len(b) < 4 { return fmt.Errorf("invalid encoding: expected at least %d bytes, for class, got %d", 4, len(b)) } + + if name == "gnre" { + m.data[name] = getInt(b[len(b)-1:]) + return nil + } + class := getInt(b[1:4]) var ok bool contentType, ok = atomTypes[class] @@ -201,7 +640,7 @@ func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32, pro if len(b) < 1 { return fmt.Errorf("invalid encoding: expected at least %d bytes, for integer tag data, got %d", 1, len(b)) } - data = getInt(b[:1]) + data = getInt(b[len(b)-1:]) case "jpeg", "png": data = &Picture{ @@ -318,7 +757,17 @@ func (m metadataMP4) Composer() string { } func (m metadataMP4) Genre() string { - return m.getString(atoms.Name("genre")) + genre := m.getString(atoms.Name("genre")) + if len(genre) < 1 { + genreID := m.getInt(atoms.Name("genre ID")) + if genreID == 0 { + genreID := m.getInt(atoms.Name("genre ID3v1 ID")) - 1 + genre = id3v1Genres[genreID] + } else { + genre = genreIDValues[genreID] + } + } + return genre } func (m metadataMP4) Year() int {