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