fix .ogg info update
[soepkiptng.git] / soepkiptng.lib
blob932f39666c9d72178cd0899c3f689fd4b081ed23
1 ############################################################################
2 # soepkiptng (c) copyright 2000 Eric Lammerts <eric@lammerts.org>.
3 # $Id: soepkiptng.lib,v 1.49 2005/07/03 04:33:03 eric Exp $
4 ############################################################################
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License, version 2, as 
7 # published by the Free Software Foundation.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
14 # A copy of the GNU General Public License is available on the World Wide Web
15 # at `http://www.gnu.org/copyleft/gpl.html'.  You can also obtain it by
16 # writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 # Boston, MA 02111-1307, USA.
18 ############################################################################
20 use IO::Socket;
21 use Socket;
22 use POSIX qw(locale_h);
24 setlocale(LC_CTYPE, "en_US.ISO-8859-1");
26 my @globalconfigfiles = ($ENV{SOEPKIPTNG_CONFIGFILE},
27    "$ENV{HOME}/.soepkiptng.conf", "/etc/soepkiptng.conf");
29 sub read_configfile1($$);
31 sub read_configfile1($$) {
32         my ($conf, $cf) = @_;
33         local *F;
35         open F, $cf or return undef;
36         while(<F>) {
37                 /^#/ and next;
38                 s/\s+$//;
39                 /./ or next;
40                 if(/^include\s+(.*\S)/) {
41                         read_configfile1($conf, $1);
42                         next;
43                 }
44                 if(/^(\w[-.\w]*)\s*=\s*(.*?)\s*$/) {
45                         $f = lc $1;
46                         $conf->{$f} = $2;
47                 } elsif(/^\s+(.*?)\s*$/) {
48                         # continuation line
49                         $conf->{$f} .= "\n$1";
50                 } else {
51                         die "$cf line $.: invalid format\n";
52                 }
53         }
54         close F;
55         return 1;
58 sub read_configfile($;@) {
59         my ($conf, @extraconfigfiles) = @_;
60         my $cf;
62         foreach $cf ((@extraconfigfiles, @globalconfigfiles)) {
63                 defined($cf) and read_configfile1($conf, $cf) and return;
64         }
65         die sprintf "no configuration file found (tried %s)\n",
66                 join(" ", @extraconfigfiles, @globalconfigfiles);
69 %latin9_to_ascii = (
70         128 => '_', 129 => '_', 130 => '_', 131 => '_',
71         132 => '_', 133 => '_', 134 => '_', 135 => '_',
72         136 => '_', 137 => '_', 138 => '_', 139 => '_',
73         140 => '_', 141 => '_', 142 => '_', 143 => '_',
74         144 => '_', 145 => '_', 146 => '_', 147 => '_',
75         148 => '_', 149 => '_', 150 => '_', 151 => '_',
76         152 => '_', 153 => '_', 154 => '_', 155 => '_',
77         156 => '_', 157 => '_', 158 => '_', 159 => '_',
78         160 => '_',             # no-break space
79         161 => '_',             # ¡ inverted exclamation mark
80         162 => 'c',             # ¢ cent sign
81         163 => 'L',             # £ pound sign
82         164 => 'EUR',           # ¤ euro sign
83         165 => 'Y',             # ¥ yen sign
84         166 => 'S',             # ¦ latin capital letter s with caron
85         167 => '_',             # § section sign
86         168 => 's',             # ¨ latin small letter s with caron
87         169 => 'C',             # © copyright sign
88         170 => 'a',             # ª feminine ordinal indicator
89         171 => '_',             # « left-pointing double angle quotation mark
90         172 => '_',             # ¬ not sign
91         173 => '_',             # ­ soft hyphen
92         174 => 'R',             # ® registered sign
93         175 => '_',             # ¯ macron
94         176 => 'o',             # ° degree sign
95         177 => '_',             # ± plus-minus sign
96         178 => '2',             # ² superscript two
97         179 => '3',             # ³ superscript three
98         180 => 'Z',             # ´ latin capital letter z with caron
99         181 => 'u',             # µ micro sign
100         182 => '_',             # ¶ pilcrow sign
101         183 => '_',             # · middle dot
102         184 => 'z',             # ¸ latin small letter z with caron
103         185 => '1',             # ¹ superscript one
104         186 => 'o',             # º masculine ordinal indicator
105         187 => '_',             # » right-pointing double angle quotation mark
106         188 => 'OE',            # ¼ latin capital ligature oe
107         189 => 'oe',            # ½ latin small ligature oe
108         190 => 'IJ',            # ¾ latin capital letter y with diaeresis
109         191 => '_',             # ¿ inverted question mark
110         192 => 'A',             # capital A, grave accent
111         193 => 'A',             # capital A, acute accent
112         194 => 'A',             # capital A, circumflex accent
113         195 => 'A',             # capital A, tilde
114         196 => 'A',             # capital A, dieresis or umlaut mark
115         197 => 'A',             # capital A, ring
116         198 => 'AE',            # capital AE diphthong (ligature)
117         199 => 'C',             # capital C, cedilla
118         200 => 'E',             # capital E, grave accent
119         201 => 'E',             # capital E, acute accent
120         202 => 'E',             # capital E, circumflex accent
121         203 => 'E',             # capital E, dieresis or umlaut mark
122         204 => 'I',             # capital I, grave accent
123         205 => 'I',             # capital I, acute accent
124         206 => 'I',             # capital I, circumflex accent
125         207 => 'I',             # capital I, dieresis or umlaut mark
126         208 => '_',             # Ð latin capital letter eth
127         209 => 'N',             # capital N, tilde
128         210 => 'O',             # capital O, grave accent
129         211 => 'O',             # capital O, acute accent
130         212 => 'O',             # capital O, circumflex accent
131         213 => 'O',             # capital O, tilde
132         214 => 'O',             # capital O, dieresis or umlaut mark
133         215 => 'x',             # ×, multiplication sign
134         216 => 'O',             # capital O, slash
135         217 => 'U',             # capital U, grave accent
136         218 => 'U',             # capital U, acute accent
137         219 => 'U',             # capital U, circumflex accent
138         220 => 'U',             # capital U, dieresis or umlaut mark
139         221 => 'Y',             # capital Y, acute accent
140         222 => '_',             # Þ latin capital letter thorn
141         223 => 'ss',            # small sharp s, German (sz ligature)
142         224 => 'a',             # small a, grave accent
143         225 => 'a',             # small a, acute accent
144         226 => 'a',             # small a, circumflex accent
145         227 => 'a',             # small a, tilde
146         228 => 'a',             # small a, dieresis or umlaut mark
147         229 => 'a',             # small a, ring
148         230 => 'ae',            # small ae diphthong (ligature)
149         231 => 'c',             # small c, cedilla
150         232 => 'e',             # small e, grave accent
151         233 => 'e',             # small e, acute accent
152         234 => 'e',             # small e, circumflex accent
153         235 => 'e',             # small e, dieresis or umlaut mark
154         236 => 'i',             # small i, grave accent
155         237 => 'i',             # small i, acute accent
156         238 => 'i',             # small i, circumflex accent
157         239 => 'i',             # small i, dieresis or umlaut mark
158         240 => '_',             # ð latin small letter eth
159         241 => 'n',             # small n, tilde
160         242 => 'o',             # small o, grave accent
161         243 => 'o',             # small o, acute accent
162         244 => 'o',             # small o, circumflex accent
163         245 => 'o',             # small o, tilde
164         246 => 'o',             # small o, dieresis or umlaut mark
165         247 => '_',             # ÷ division sign
166         248 => 'o',             # small o, slash
167         249 => 'u',             # small u, grave accent
168         250 => 'u',             # small u, acute accent
169         251 => 'u',             # small u, circumflex accent
170         252 => 'u',             # small u, dieresis or umlaut mark
171         253 => 'y',             # small y, acute accent
172         254 => '_',             # þ latin small letter thorn
173         255 => 'ij',            # small y, dieresis or umlaut mark
176 sub string_to_filename($;$) {
177         my ($a, $lang) = @_;
179         my $ampr = $lang eq "dut"? "en" : $lang eq "fre"? "et" : $lang eq "ger"? "und" : "and";
181         $a =~ s/-/_/g;
182         $a =~ s/[\[(](.*)/-$1/;
183         $a =~ s/[ _]?&[_ ]?/_${ampr}_/g;
184         $a =~ s/([\x80-\xff])/lc($latin9_to_ascii{ord($1)}) || $1/ge;
185         $a =~ s/[^-A-Za-z0-9]+/_/g;
186         $a =~ s/_?-_?/-/g;
187         $a =~ s/_$//;
188         return lc($a);
191 sub encurl($) {
192         my ($a) = @_;
194         $a =~ s|([^-./\w])|sprintf "%%%02x", ord($1)|ge;
195 #       $a =~ s| |+|g;
196         $a;
199 sub enchtml($;$) {
200         my ($a, $do_nbsp) = @_;
202         $a =~ s|&|&amp;|g;
203         $a =~ s|"|&quot;|g;
204         $a =~ s|<|&lt;|g;
205         $a =~ s|>|&gt;|g;
206         $a =~ s| |&nbsp;|g if $do_nbsp;
207         $a;
210 sub del_song($$@) {
211         my ($db, $tbl, @ids) = @_;
212         @ids or return;
213         $db->do("DELETE FROM $tbl WHERE song_id=" . join(" OR song_id=", @ids));
216 sub add_song_nolock($$$$@) {
217         my ($db, $tbl, $order, $user, @ids) = @_;
219         @ids or return;
220         my $firstid = shift @ids;
221         my $q = "INSERT INTO $tbl (song_order, song_id, user) " .
222                 "VALUES ($order, $firstid, \"$user\")";
223         foreach(@ids) {
224                 $order++;
225                 $q .= ",($order,$_,\"$user\")";
226         }
227         $db->do($q) or return undef;
228         return 1;
231 sub get_table_ids($$$) {
232         my ($db, $tbl, $rest) = @_;
234         my $res = $db->selectcol_arrayref("SELECT song_id FROM $tbl $rest");
235         return @$res;
239 sub add_song($$$@) {
240         my ($db, $tbl, $user, @ids) = @_;
242         $db->do("LOCK TABLES $tbl WRITE");
243         del_song($db, $tbl, @ids);
244         my ($order) = $db->selectrow_array("SELECT MAX(song_order) FROM $tbl");
245         $order = 0 if $order < 0;
246         my $retval = add_song_nolock($db, $tbl, $order + 1, $user, @ids);
247         $db->do("UNLOCK TABLES");
248         return $retval;
251 sub reorder_table($$$@) {
252         my ($db, $tbl, $order, @ids) = @_;
254         foreach(@ids) {
255                 $db->do("UPDATE $tbl SET song_order=$order WHERE song_id=$_");
256                 $order++;
257         }
260 sub move_song_to_top($$@) {
261         my ($db, $tbl, @ids) = @_;
263         $db->do("LOCK TABLES $tbl WRITE");
264         reorder_table($db, $tbl, $#ids + 2, get_table_ids($db, $tbl, "ORDER BY song_order"));
265         reorder_table($db, $tbl, 1, @ids);
266         $db->do("UNLOCK TABLES");
269 sub move_song_to_bottom($$@) {
270         my ($db, $tbl, @ids) = @_;
271         my %ids;
273         foreach(@ids) { $ids{$_} = 1; };
274         $db->do("LOCK TABLES $tbl WRITE");
275         my @q = get_table_ids($db, $tbl, "ORDER BY song_order");
276         my (@q1, @q2);
277         foreach(@q) {
278                 if($ids{$_}) { push @q2, $_; } else { push @q1, $_; }
279         }
280         reorder_table($db, $tbl, 1, @q1, @q2);
281         $db->do("UNLOCK TABLES");
284 sub shuffle_table($$) {
285         my ($db, $tbl) = @_;
287         $db->do("LOCK TABLES $tbl WRITE");
288         reorder_table($db, $tbl, 1, get_table_ids($db, $tbl, "ORDER BY rand()"));
289         $db->do("UNLOCK TABLES");
292 sub kill_song(;$$) {
293         my ($user, $id) = @_;
294         my ($plid, $host, $port);
295         local *F;
297         open F, $conf{statusfile}
298                 or die "$conf{statusfile}: $!\n";
299         $plid = <F>;
300         <F>; <F>; <F>;
301         chop($host = <F>);
302         $port = <F>;
303         close F;
305         if(defined($id) && $id != $plid) { return undef; }
307         if($conf{kill_song_external}) {
308                 system $conf{kill_song_external}, $user || '';
310                 open F, $conf{statusfile}
311                         or die "$conf{statusfile}: $!\n";
312                 my @lines;
313                 chop(@lines = <F>);
314                 close F;
315                 return @lines;
316         }
317         return kill_song_internal($host, $port);
320 sub kill_song_internal($$) {
321         my ($host, $port) = @_;
322         local *F;
324         $host && $port or return undef;
326         socket(F, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
327                 or die "socket: $!\n";
328         connect(F, sockaddr_in($port, inet_aton($host)))
329                 or die "connect $host:$port: $!\n";
330         my @status;
331         @status = <F>;
332         close F;
333         return @status;
336 sub player_cmd(;@) {
337         my $res = 1;
338         local *F;
339         my $host;
341         open F, $conf{statusfile}
342                 or die "$conf{statusfile}: $!\n";
343         <F>; <F>; <F>; <F>;
344         chop($host = <F>);
345         close F;
347         my $sock = IO::Socket::INET->new("$host:2222") or return undef;
348         $sock->autoflush(1);
349         $response = <$sock>;    #greeting
350         foreach(@_) {
351                 $sock->print("$_\n");
352                 $res = 0 if <$sock> !~ /^\+/;
353         }
354         $sock->close;
355         return $res;
359 sub get_player_pid() {
360         local *F;
361         my $pid;
363         open F, $conf{statusfile}
364                 or die "$conf{statusfile}: $!\n";
365         <F>; <F>; <F>;
366         $pid = 0 + <F>;
367         close F;
368         return $pid;
371 sub get_id($$$) {
372         my ($db, $table, $value) = @_;
374         my $sth = $db->prepare("SELECT id,name FROM $table WHERE binary name=?");
375         if($sth->execute($value || "") >= 1) {
376                 my ($id, $v) = $sth->fetchrow_array;
377                 return $id;
378         } else {
379                 $db->do("REPLACE INTO $table SET name=?", undef, $value)
380                         or die;
381                 return $db->{'mysql_insertid'};
382         }
385 sub get_playlist_contents($$) {
386         my ($dbh, $list) = @_;
387         my %songids;
388         my (%artistids, %albumsids, %listids);
389         my (%artistids_done, %albumsids_done, %listids_done);
391         my $sth_list = $dbh->prepare("SELECT type,entity_id" .
392                 " FROM list_contents WHERE list_id=?");
393         my $sth_artist = $dbh->prepare("SELECT id FROM song WHERE artist_id=?");
394         my $sth_album = $dbh->prepare("SELECT id FROM song WHERE album_id=?");
395         
396         $listids{$list} = 1;
397         while(%artistids || %albumsids || %listids) {
398                 foreach(keys %listids) {
399                         $listids_done{$_} = 1;
400                         delete $listids{$_};
402                         $sth_list->execute($_);
403                         while($_ = $sth_list->fetchrow_hashref) {
404                                 if($_->{'type'} eq 'list') {
405                                         next if $listids_done{$_->{'entity_id'}};
406                                         $listids{$_->{'entity_id'}} = 1;
407                                 } elsif($_->{'type'} eq 'artist') {
408                                         next if $artistids_done{$_->{'entity_id'}};
409                                         $artistids{$_->{'entity_id'}} = 1;
410                                 } elsif($_->{'type'} eq 'album') {
411                                         next if $albumids_done{$_->{'entity_id'}};
412                                         $albumids{$_->{'entity_id'}} = 1;
413                                 } elsif($_->{'type'} eq 'song') {
414                                         $songids{$_->{'entity_id'}} = 1;
415                                 }
416                         }
417                 }
418                 foreach(keys %artistids) {
419                         $artistids_done{$_} = 1;
420                         delete $artistids{$_};
422                         $sth_artist->execute($_);
423                         while($_ = $sth_artist->fetchrow_hashref) {
424                                 $songids{$_->{'id'}} = 1;
425                         }
426                 }
427                 foreach(keys %albumids) {
428                         $albumids_done{$_} = 1;
429                         delete $albumids{$_};
431                         $sth_album->execute($_);
432                         while($_ = $sth_album->fetchrow_hashref) {
433                                 $songids{$_->{'id'}} = 1;
434                         }
435                 }
436         }
437         return keys %songids;
440 sub get_nowplaying(;$) {
441         my ($dbh) = @_;
442         local *F;
443         my $s = undef;
445         open F, $conf{statusfile} or return undef;
446         chop (($s->{id}, $s->{filename}, $s->{pid}, $s->{cdrplaypid},
447                $s->{killhost}, $s->{killport}, $s->{type},
448                $s->{user}, $s->{artist}, $s->{title}, $s->{album},
449                $s->{track}, $s->{length}, $s->{encoding}) = <F>);
450         close F;
452         if($dbh) {
453                 my $sth = $dbh->prepare(
454                         "SELECT artist.name as artist,album.name as album," .
455                         " song.artist_id as arid,song.album_id as alid, song.*" .
456                         " FROM song,artist,album WHERE song.artist_id=artist.id" .
457                         " AND song.album_id=album.id AND song.id=?");
458                 $sth->execute($s->{id});
459                 if(my $d = $sth->fetchrow_hashref) {
460                         $s->{alid} = $d->{alid};
461                         $s->{arid} = $d->{arid};
462                         $s->{filename} = $d->{filename};
463                         $s->{artist} = $d->{artist};
464                         $s->{title} = $d->{title};
465                         $s->{album} = $d->{album};
466                         $s->{track} = $d->{track};
467                         $s->{length} = $d->{length};
468                         $s->{encoding} = $d->{encoding};
469                 }
470         }
471         return $s;
474 sub construct_url($@) {
475         my ($baseurl, $argsref) = @_;
476         my $sep = "?";
477         foreach(keys %$argsref) {
478                 $baseurl .= "$sep$_=" . encurl($$argsref{$_});
479                 $sep = "&";
480         }
481         return $baseurl;
484 sub cleanup_name {
485         use locale;
486         my ($s) = @_;
487         $s =~ s/\.mp3\s*$//i;
488         $s =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig;
489         $s =~ s/([a-zA-Z])([A-Z][a-z])/$1 $2/g;
490         $s = lc($s);
491         $s =~ s/_/ /g;
492         if($s !~ / /) { $s =~ s/\./ /g; }
493         $s =~ s/^\s+|\s+$//g;
494         $s =~ s/([^'\w\xc0-\xff]|^)(\w)/$1\U$2/g;
495         $s =~ s/\b(o'\w)/\U$1/ig;
496         $s =~ s/n T\b/n't/g;
497         $s =~ s/ S\b/'s/g;
498         $s =~ s/\bI M\b/I'm/g;
499         $s =~ s/ ll\b/'ll/ig;
500         $s =~ s/\b(i+)\b/\U$1/ig;
501         $s =~ s/([eiuy]) ([rv]e)\b/$1'\L$2/ig;
502         $s =~ s/\s*-\s*/ - /g;
503         $s;
506 sub add_song_next($$$;$) {
507         my ($dbh, $table, $currentsong, $user) = @_;
509         my $ids = $dbh->selectcol_arrayref("SELECT song_id FROM $table,song ".
510                 "WHERE $table.song_id=song.id AND song.present");
511         my %idsq;
512         foreach(@$ids) { $idsq{$_} = 1; }
514         my $sth = $dbh->prepare("SELECT song2.* FROM song AS song1,".
515                 " song AS song2 WHERE song1.id=$currentsong AND".
516                 " song1.artist_id = song2.artist_id AND".
517                 " song1.album_id = song2.album_id AND".
518                 " song2.track > song1.track AND".
519                 " song2.present ORDER BY song2.track");
520         $sth->execute;
521         while($_ = $sth->fetchrow_hashref) {
522                 if(!$idsq{$_->{id}}) {
523                         add_song($dbh, $table, $user, $_->{id})
524                                 or warn "can't add song.\n";
525                         return $_;
526                 }
527         }
530 %iso_639_2 = (
531         'aar' => 'Afar',
532         'abk' => 'Abkhazian',
533         'ace' => 'Achinese',
534         'ach' => 'Acoli',
535         'ada' => 'Adangme',
536         'afa' => 'Afro-Asiatic',
537         'afh' => 'Afrihili',
538         'afr' => 'Afrikaans',
539         'aka' => 'Akan',
540         'akk' => 'Akkadian',
541         'alb' => 'Albanian',
542         'ale' => 'Aleut',
543         'alg' => 'Algonquian Languages',
544         'amh' => 'Amharic',
545         'ang' => 'English, Old',
546         'apa' => 'Apache Languages',
547         'ara' => 'Arabic',
548         'arc' => 'Aramaic',
549         'arm' => 'Armenian',
550         'arn' => 'Araucanian',
551         'arp' => 'Arapaho',
552         'art' => 'Artificial',
553         'arw' => 'Arawak',
554         'asm' => 'Assamese',
555         'ath' => 'Athapascan Languages',
556         'ava' => 'Avaric',
557         'ave' => 'Avestan',
558         'awa' => 'Awadhi',
559         'aym' => 'Aymara',
560         'aze' => 'Azerbaijani',
561         'bad' => 'Banda',
562         'bai' => 'Bamileke Languages',
563         'bak' => 'Bashkir',
564         'bal' => 'Baluchi',
565         'bam' => 'Bambara',
566         'ban' => 'Balinese',
567         'baq' => 'Basque',
568         'bas' => 'Basa',
569         'bat' => 'Baltic',
570         'bej' => 'Beja',
571         'bel' => 'Byelorussian',
572         'bem' => 'Bemba',
573         'ben' => 'Bengali',
574         'ber' => 'Berber',
575         'bho' => 'Bhojpuri',
576         'bih' => 'Bihari',
577         'bik' => 'Bikol',
578         'bin' => 'Bini',
579         'bis' => 'Bislama',
580         'bla' => 'Siksika',
581         'bnt' => 'Bantu',
582         'bod' => 'Tibetan',
583         'bra' => 'Braj',
584         'bre' => 'Breton',
585         'bua' => 'Buriat',
586         'bug' => 'Buginese',
587         'bul' => 'Bulgarian',
588         'bur' => 'Burmese',
589         'cad' => 'Caddo',
590         'cai' => 'Central American Indian',
591         'car' => 'Carib',
592         'cat' => 'Catalan',
593         'cau' => 'Caucasian',
594         'ceb' => 'Cebuano',
595         'cel' => 'Celtic',
596         'ces' => 'Czech',
597         'cha' => 'Chamorro',
598         'chb' => 'Chibcha',
599         'che' => 'Chechen',
600         'chg' => 'Chagatai',
601         'chi' => 'Chinese',
602         'chm' => 'Mari',
603         'chn' => 'Chinook jargon',
604         'cho' => 'Choctaw',
605         'chr' => 'Cherokee',
606         'chu' => 'Church Slavic',
607         'chv' => 'Chuvash',
608         'chy' => 'Cheyenne',
609         'cop' => 'Coptic',
610         'cor' => 'Cornish',
611         'cos' => 'Corsican',
612         'cpe' => 'Creoles and Pidgins, English-based',
613         'cpf' => 'Creoles and Pidgins, French-based',
614         'cpp' => 'Creoles and Pidgins, Portuguese-based',
615         'cre' => 'Cree',
616         'crp' => 'Creoles and Pidgins',
617         'cus' => 'Cushitic',
618         'cym' => 'Welsh',
619         'cze' => 'Czech',
620         'dak' => 'Dakota',
621         'dan' => 'Danish',
622         'del' => 'Delaware',
623         'deu' => 'German',
624         'din' => 'Dinka',
625         'div' => 'Divehi',
626         'doi' => 'Dogri',
627         'dra' => 'Dravidian',
628         'dua' => 'Duala',
629         'dum' => 'Dutch, Middle',
630         'dut' => 'Dutch',
631         'dyu' => 'Dyula',
632         'dzo' => 'Dzongkha',
633         'efi' => 'Efik',
634         'egy' => 'Egyptian (Ancient)',
635         'eka' => 'Ekajuk',
636         'ell' => 'Greek, Modern',
637         'elx' => 'Elamite',
638         'eng' => 'English',
639         'enm' => 'English, Middle',
640         'epo' => 'Esperanto',
641         'esk' => 'Eskimo',
642         'esl' => 'Spanish',
643         'est' => 'Estonian',
644         'eus' => 'Basque',
645         'ewe' => 'Ewe',
646         'ewo' => 'Ewondo',
647         'fan' => 'Fang',
648         'fao' => 'Faroese',
649         'fas' => 'Persian',
650         'fat' => 'Fanti',
651         'fij' => 'Fijian',
652         'fin' => 'Finnish',
653         'fiu' => 'Finno-Ugrian',
654         'fon' => 'Fon',
655         'fra' => 'French',
656         'fre' => 'French',
657         'frm' => 'French, Middle',
658         'fro' => 'French, Old',
659         'fry' => 'Frisian',
660         'ful' => 'Fulah',
661         'gaa' => 'Ga',
662         'gae' => 'Gaelic (Scots)',
663         'gai' => 'Irish',
664         'gay' => 'Gayo',
665         'gdh' => 'Gaelic (Scots)',
666         'gem' => 'Germanic',
667         'geo' => 'Georgian',
668         'ger' => 'German',
669         'gez' => 'Geez',
670         'gil' => 'Gilbertese',
671         'glg' => 'Gallegan',
672         'gmh' => 'German, Middle High',
673         'goh' => 'German, Old High',
674         'gon' => 'Gondi',
675         'got' => 'Gothic',
676         'grb' => 'Grebo',
677         'grc' => 'Greek, Ancient',
678         'gre' => 'Greek, Modern',
679         'grn' => 'Guarani',
680         'guj' => 'Gujarati',
681         'hai' => 'Haida',
682         'hau' => 'Hausa',
683         'haw' => 'Hawaiian',
684         'heb' => 'Hebrew',
685         'her' => 'Herero',
686         'hil' => 'Hiligaynon',
687         'him' => 'Himachali',
688         'hin' => 'Hindi',
689         'hmo' => 'Hiri Motu',
690         'hun' => 'Hungarian',
691         'hup' => 'Hupa',
692         'hye' => 'Armenian',
693         'iba' => 'Iban',
694         'ibo' => 'Igbo',
695         'ice' => 'Icelandic',
696         'ijo' => 'Ijo',
697         'iku' => 'Inuktitut',
698         'ilo' => 'Iloko',
699         'ina' => 'Interlingua',
700         'inc' => 'Indic',
701         'ind' => 'Indonesian',
702         'ine' => 'Indo-European',
703         'ine' => 'Interlingue',
704         'ipk' => 'Inupiak',
705         'ira' => 'Iranian',
706         'iri' => 'Irish',
707         'iro' => 'Iroquoian uages',
708         'isl' => 'Icelandic',
709         'ita' => 'Italian',
710         'jav' => 'Javanese',
711         'jaw' => 'Javanese',
712         'jpn' => 'Japanese',
713         'jpr' => 'Judeo-Persian',
714         'jrb' => 'Judeo-Arabic',
715         'kaa' => 'Kara-Kalpak',
716         'kab' => 'Kabyle',
717         'kac' => 'Kachin',
718         'kal' => 'Greenlandic',
719         'kam' => 'Kamba',
720         'kan' => 'Kannada',
721         'kar' => 'Karen',
722         'kas' => 'Kashmiri',
723         'kat' => 'Georgian',
724         'kau' => 'Kanuri',
725         'kaw' => 'Kawi',
726         'kaz' => 'Kazakh',
727         'kha' => 'Khasi',
728         'khi' => 'Khoisan',
729         'khm' => 'Khmer',
730         'kho' => 'Khotanese',
731         'kik' => 'Kikuyu',
732         'kin' => 'Kinyarwanda',
733         'kir' => 'Kirghiz',
734         'kok' => 'Konkani',
735         'kom' => 'Komi',
736         'kon' => 'Kongo',
737         'kor' => 'Korean',
738         'kpe' => 'Kpelle',
739         'kro' => 'Kru',
740         'kru' => 'Kurukh',
741         'kua' => 'Kuanyama',
742         'kum' => 'Kumyk',
743         'kur' => 'Kurdish',
744         'kus' => 'Kusaie',
745         'kut' => 'Kutenai',
746         'lad' => 'Ladino',
747         'lah' => 'Lahnda',
748         'lam' => 'Lamba',
749         'lao' => 'Lao',
750         'lat' => 'Latin',
751         'lav' => 'Latvian',
752         'lez' => 'Lezghian',
753         'lin' => 'Lingala',
754         'lit' => 'Lithuanian',
755         'lol' => 'Mongo',
756         'loz' => 'Lozi',
757         'ltz' => 'Letzeburgesch',
758         'lub' => 'Luba-Katanga',
759         'lug' => 'Ganda',
760         'lui' => 'Luiseno',
761         'lun' => 'Lunda',
762         'luo' => 'Luo (Kenya and Tanzania)',
763         'mac' => 'Macedonian',
764         'mad' => 'Madurese',
765         'mag' => 'Magahi',
766         'mah' => 'Marshall',
767         'mai' => 'Maithili',
768         'mak' => 'Macedonian',
769         'mak' => 'Makasar',
770         'mal' => 'Malayalam',
771         'man' => 'Mandingo',
772         'mao' => 'Maori',
773         'map' => 'Austronesian',
774         'mar' => 'Marathi',
775         'mas' => 'Masai',
776         'max' => 'Manx',
777         'may' => 'Malay',
778         'men' => 'Mende',
779         'mga' => 'Irish, Middle',
780         'mic' => 'Micmac',
781         'min' => 'Minangkabau',
782         'mis' => 'Miscellaneous',
783         'mkh' => 'Mon-Kmer',
784         'mlg' => 'Malagasy',
785         'mlt' => 'Maltese',
786         'mni' => 'Manipuri',
787         'mno' => 'Manobo Languages',
788         'moh' => 'Mohawk',
789         'mol' => 'Moldavian',
790         'mon' => 'Mongolian',
791         'mos' => 'Mossi',
792         'mri' => 'Maori',
793         'msa' => 'Malay',
794         'mul' => 'Multiple Languages',
795         'mun' => 'Munda Languages',
796         'mus' => 'Creek',
797         'mwr' => 'Marwari',
798         'mya' => 'Burmese',
799         'myn' => 'Mayan Languages',
800         'nah' => 'Aztec',
801         'nai' => 'North American Indian',
802         'nau' => 'Nauru',
803         'nav' => 'Navajo',
804         'nbl' => 'Ndebele, South',
805         'nde' => 'Ndebele, North',
806         'ndo' => 'Ndongo',
807         'nep' => 'Nepali',
808         'new' => 'Newari',
809         'nic' => 'Niger-Kordofanian',
810         'niu' => 'Niuean',
811         'nla' => 'Dutch',
812         'nno' => 'Norwegian (Nynorsk)',
813         'non' => 'Norse, Old',
814         'nor' => 'Norwegian',
815         'nso' => 'Sotho, Northern',
816         'nub' => 'Nubian Languages',
817         'nya' => 'Nyanja',
818         'nym' => 'Nyamwezi',
819         'nyn' => 'Nyankole',
820         'nyo' => 'Nyoro',
821         'nzi' => 'Nzima',
822         'oci' => 'Langue d\'Oc',
823         'oji' => 'Ojibwa',
824         'ori' => 'Oriya',
825         'orm' => 'Oromo',
826         'osa' => 'Osage',
827         'oss' => 'Ossetic',
828         'ota' => 'Turkish, Ottoman',
829         'oto' => 'Otomian Languages',
830         'paa' => 'Papuan-Australian',
831         'pag' => 'Pangasinan',
832         'pal' => 'Pahlavi',
833         'pam' => 'Pampanga',
834         'pan' => 'Panjabi',
835         'pap' => 'Papiamento',
836         'pau' => 'Palauan',
837         'peo' => 'Persian, Old',
838         'per' => 'Persian',
839         'phn' => 'Phoenician',
840         'pli' => 'Pali',
841         'pol' => 'Polish',
842         'pon' => 'Ponape',
843         'por' => 'Portuguese',
844         'pra' => 'Prakrit uages',
845         'pro' => 'Provencal, Old',
846         'pus' => 'Pushto',
847         'que' => 'Quechua',
848         'raj' => 'Rajasthani',
849         'rar' => 'Rarotongan',
850         'roa' => 'Romance',
851         'roh' => 'Rhaeto-Romance',
852         'rom' => 'Romany',
853         'ron' => 'Romanian',
854         'rum' => 'Romanian',
855         'run' => 'Rundi',
856         'rus' => 'Russian',
857         'sad' => 'Sandawe',
858         'sag' => 'Sango',
859         'sah' => 'Yakut',
860         'sai' => 'South American Indian',
861         'sal' => 'Salishan Languages',
862         'sam' => 'Samaritan Aramaic',
863         'san' => 'Sanskrit',
864         'sco' => 'Scots',
865         'scr' => 'Serbo-Croatian',
866         'sel' => 'Selkup',
867         'sem' => 'Semitic',
868         'sga' => 'Irish, Old',
869         'shn' => 'Shan',
870         'sid' => 'Sidamo',
871         'sin' => 'Singhalese',
872         'sio' => 'Siouan Languages',
873         'sit' => 'Sino-Tibetan',
874         'sla' => 'Slavic',
875         'slk' => 'Slovak',
876         'slo' => 'Slovak',
877         'slv' => 'Slovenian',
878         'smi' => 'Sami Languages',
879         'smo' => 'Samoan',
880         'sna' => 'Shona',
881         'snd' => 'Sindhi',
882         'sog' => 'Sogdian',
883         'som' => 'Somali',
884         'son' => 'Songhai',
885         'sot' => 'Sotho, Southern',
886         'spa' => 'Spanish',
887         'sqi' => 'Albanian',
888         'srd' => 'Sardinian',
889         'srr' => 'Serer',
890         'ssa' => 'Nilo-Saharan',
891         'ssw' => 'Siswant',
892         'ssw' => 'Swazi',
893         'suk' => 'Sukuma',
894         'sun' => 'Sudanese',
895         'sus' => 'Susu',
896         'sux' => 'Sumerian',
897         'sve' => 'Swedish',
898         'swa' => 'Swahili',
899         'swe' => 'Swedish',
900         'syr' => 'Syriac',
901         'tah' => 'Tahitian',
902         'tam' => 'Tamil',
903         'tat' => 'Tatar',
904         'tel' => 'Telugu',
905         'tem' => 'Timne',
906         'ter' => 'Tereno',
907         'tgk' => 'Tajik',
908         'tgl' => 'Tagalog',
909         'tha' => 'Thai',
910         'tib' => 'Tibetan',
911         'tig' => 'Tigre',
912         'tir' => 'Tigrinya',
913         'tiv' => 'Tivi',
914         'tli' => 'Tlingit',
915         'tmh' => 'Tamashek',
916         'tog' => 'Tonga (Nyasa)',
917         'ton' => 'Tonga (Tonga Islands)',
918         'tru' => 'Truk',
919         'tsi' => 'Tsimshian',
920         'tsn' => 'Tswana',
921         'tso' => 'Tsonga',
922         'tuk' => 'Turkmen',
923         'tum' => 'Tumbuka',
924         'tur' => 'Turkish',
925         'tut' => 'Altaic',
926         'twi' => 'Twi',
927         'tyv' => 'Tuvinian',
928         'uga' => 'Ugaritic',
929         'uig' => 'Uighur',
930         'ukr' => 'Ukrainian',
931         'umb' => 'Umbundu',
932         'und' => 'Undetermined',
933         'urd' => 'Urdu',
934         'uzb' => 'Uzbek',
935         'vai' => 'Vai',
936         'ven' => 'Venda',
937         'vie' => 'Vietnamese',
938         'vol' => 'Volapu:k',
939         'vot' => 'Votic',
940         'wak' => 'Wakashan Languages',
941         'wal' => 'Walamo',
942         'war' => 'Waray',
943         'was' => 'Washo',
944         'wel' => 'Welsh',
945         'wen' => 'Sorbian Languages',
946         'wol' => 'Wolof',
947         'xho' => 'Xhosa',
948         'yao' => 'Yao',
949         'yap' => 'Yap',
950         'yid' => 'Yiddish',
951         'yor' => 'Yoruba',
952         'zap' => 'Zapotec',
953         'zen' => 'Zenaga',
954         'zha' => 'Zhuang',
955         'zho' => 'Chinese',
956         'zul' => 'Zulu',
957         'zun' => 'Zuni',