analyze.pike (4621B)
1 #! /usr/bin/env pike 2 #pike 7.5 3 4 // 5 // A small program that analyses ID3v1/ID3v1.1 tags and 6 // displays its findings. 7 // Copyright (c) 2003 Martin Nilsson 8 // 9 10 #if !constant(ADT.Struct) 11 #error This Pike is too old for this application. 12 #endif 13 14 // The structure of an ID3v1 tag. 15 class ID3_1 { 16 inherit ADT.Struct; 17 // Item head = Chars(3); 18 Item title = Chars(30); 19 Item artist = Chars(30); 20 Item album = Chars(30); 21 Item year = Chars(4); 22 Item comment = Chars(30); 23 Item genre = Byte(); 24 } 25 26 // The structure of an ID3v1.1 tag. 27 class ID3_11 { 28 inherit ADT.Struct; 29 // Item head = Chars(3); 30 Item title = Chars(30); 31 Item artist = Chars(30); 32 Item album = Chars(30); 33 Item year = Chars(4); 34 Item comment = Chars(28); 35 Item null = Byte(); 36 Item track = Byte(); 37 Item genre = Byte(); 38 } 39 40 array(string) id3_genres = ({ 41 "Blues", // 0 42 "Classic Rock", 43 "Country", 44 "Dance", 45 "Disco", 46 "Funk", 47 "Grunge", 48 "Hip-Hop", 49 "Jazz", 50 "Metal", 51 "New Age", 52 "Oldies", 53 "Other", 54 "Pop", 55 "R&B", 56 "Rap", 57 "Reggae", 58 "Rock", 59 "Techno", 60 "Industrial", 61 "Alternative", 62 "Ska", 63 "Death Metal", 64 "Pranks", 65 "Soundtrack", 66 "Euro-Techno", 67 "Ambient", 68 "Trip-Hop", 69 "Vocal", 70 "Jazz+Funk", 71 "Fusion", 72 "Trance", 73 "Classical", 74 "Instrumental", 75 "Acid", 76 "House", 77 "Game", 78 "Sound Clip", 79 "Gospel", 80 "Noise", 81 "AlternRock", 82 "Bass", 83 "Soul", 84 "Punk", 85 "Space", 86 "Meditative", 87 "Instrumental Pop", 88 "Instrumental Rock", 89 "Ethnic", 90 "Gothic", 91 "Darkwave", 92 "Techno-Industrial", 93 "Electronic", 94 "Pop-Folk", 95 "Eurodance", 96 "Dream", 97 "Southern Rock", 98 "Comedy", 99 "Cult", 100 "Gangsta", 101 "Top 40", 102 "Christian Rap", 103 "Pop/Funk", 104 "Jungle", 105 "Native American", 106 "Cabaret", 107 "New Wave", 108 "Psychadelic", 109 "Rave", 110 "Showtunes", 111 "Trailer", 112 "Lo-Fi", 113 "Tribal", 114 "Acid Punk", 115 "Acid Jazz", 116 "Polka", 117 "Retro", 118 "Musical", 119 "Rock & Roll", 120 "Hard Rock", // 79 121 "Folk", 122 "Folk-Rock", 123 "National Folk", 124 "Swing", 125 "Fast Fusion", 126 "Bebob", 127 "Latin", 128 "Revival", 129 "Celtic", 130 "Bluegrass", 131 "Avantgarde", 132 "Gothic Rock", 133 "Progressive Rock", 134 "Psychedelic Rock", 135 "Symphonic Rock", 136 "Slow Rock", 137 "Big Band", 138 "Chorus", 139 "Easy Listening", 140 "Acoustic", 141 "Humour", 142 "Speech", 143 "Chanson", 144 "Opera", 145 "Chamber Music", 146 "Sonata", 147 "Symphony", 148 "Booty Bass", 149 "Primus", 150 "Porn Groove", 151 "Satire", 152 "Slow Jam", 153 "Club", 154 "Tango", 155 "Samba", 156 "Folklore", 157 "Ballad", 158 "Power Ballad", 159 "Rhythmic Soul", 160 "Freestyle", 161 "Duet", 162 "Punk Rock", 163 "Drum Solo", 164 "A capella", 165 "Euro-House", 166 "Dance Hall", // 125 167 "Goa", 168 "Drum & Bass", 169 "Club-House", 170 "Hardcore", 171 "Terror", 172 "Indie", 173 "BritPop", 174 "Negerpunk", 175 "Polsk Punk", 176 "Beat", 177 "Christian", 178 "Heavy Metal", 179 "Black Metal", 180 "Crossover", 181 "Contemporary", 182 "Christian Rock", 183 "Merengue", 184 "Salsa", 185 "Thrash Metal", 186 "Anime", 187 "JPop", 188 "Synthpop", 189 }); 190 191 string genre(int i) { 192 if( i>=sizeof(id3_genres) ) return "Not defined"; 193 return "\""+id3_genres[i]+"\""; 194 } 195 196 int(0..1) exitcode; 197 198 // Test an ID3 field. 199 void handle_str(string what, string s) { 200 // Strip trailing zero 201 if(s[-1]==0) { 202 sscanf(reverse(s), "%*[\0]%s", s); 203 s=reverse(s); 204 } 205 string str=s; 206 sscanf(str, "%s\0", str); 207 write("%-11s: %O\n", what, str); 208 if(has_value(s,0)) { 209 write("%13sString contains illegal zero character(s).\n",""); 210 exitcode = 1; 211 } 212 catch { 213 if(utf8_to_string(s)!=s) 214 write("%13sString can be UTF-8 decoded.\n",""); 215 }; 216 } 217 218 void main(int num, array(string) args) { 219 if(num!=2) { 220 werror("Usage: analyse.pike <file>\n"); 221 exit(1); 222 } 223 224 Stdio.File f = Stdio.File(args[-1]); 225 if(!f) { 226 werror("Couldn't find file %O.\n", args[-1]); 227 exit(2); 228 } 229 if(f->stat()->size < 128) { 230 werror("File too small to hold an ID3v1 tag.\n"); 231 exit(3); 232 } 233 234 f->seek(-128); 235 if(f->read(3)!="TAG") { 236 werror("File contains no ID3 tag.\n"); 237 exit(4); 238 } 239 240 string data = f->read(); 241 object tag; 242 if(data[-3]==0 && data[-2]!=0) { 243 write("Tag version: 1.1\n"); 244 tag = ID3_11(data); 245 } 246 else { 247 write("Tag version: 1.0\n"); 248 tag = ID3_1(data); 249 } 250 251 handle_str("Title",tag->title); 252 handle_str("Artist",tag->artist); 253 handle_str("Album",tag->album); 254 handle_str("Year",tag->year); 255 string y=tag->year; 256 sscanf(y, "%[0-9]", y); 257 if( sizeof(y)!=4 ) { 258 write("%13sMalformed year field data.\n",""); 259 exitcode = 1; 260 } 261 write("%-11s: %d (%s)\n", "Genre", tag->genre, genre(tag->genre)); 262 handle_str("Comment",tag->comment); 263 if(tag->track) 264 write("%-11s: %d\n", "Track", tag->track); 265 266 if(exitcode) 267 exit(5); 268 }