aboutsummaryrefslogtreecommitdiff
path: root/bin/psycamp
diff options
context:
space:
mode:
authorpsyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet>2016-08-10 15:28:23 +0000
committerpsyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet>2016-08-10 15:28:23 +0000
commit08ced0d625ea5769ab77bf90c47ea30398da54e9 (patch)
tree4d0346fc0a9b2e360d3a03f93d54b8191b4fc4e7 /bin/psycamp
parent2aa603dd57e2d14f7e2702140fd0b2d7a62ff60b (diff)
downloadperlpsyc-08ced0d625ea5769ab77bf90c47ea30398da54e9.tar.gz
perlpsyc-08ced0d625ea5769ab77bf90c47ea30398da54e9.zip
renamed after almost 20 years: psycmp3 turns to psycamp
Diffstat (limited to 'bin/psycamp')
-rwxr-xr-xbin/psycamp1258
1 files changed, 1258 insertions, 0 deletions
diff --git a/bin/psycamp b/bin/psycamp
new file mode 100755
index 0000000..0972f70
--- /dev/null
+++ b/bin/psycamp
@@ -0,0 +1,1258 @@
1#!/usr/bin/perl -I/usr/depot/lib/perl5
2use Term::ANSIColor qw( :constants );
3#
4# been around as 'psycmp3' for almost twenty years, but since 2017 it
5# can also play other formats.. so i shalt rename it to 'psycamp'
6#
7sub head { return <<X; }
8=============================================================================
9PSYC media console 3.2 by the symbolic lynX\@psycamp.pages.de
10-----------------------------------------------------------------------------
11X
12# command line front-end to audio engines with PSYC remote control
13#
14# this media player is over a decade old, but it still is my tool of
15# choice. i gave it functions i didn't find in any other.. like,
16# how useful is a media player if you can't easily reorganize or at
17# least delete files you don't want to consume ever again?
18#
19# uses 'mplayer' - the most popular media player on linux with the
20# worst scripting API ever seen in a lifetime. it is documented
21# in mplayer's source distribution in DOC/tech/slave.txt.
22#
23# can alternatively be configured back to use the 'rxaudio' server
24# engine from http://www.mpeg.org/xaudio/ which has a lot more
25# reasonable API, but it only plays mp3 - the copy that I have
26# been using since 1997 or so has a sha256sum of
27# ddb096ad42d9b6b543db8a3a6d9b4a9d52943e75e96697dbbadbc779140c498e.
28# although the general public never saw any source codes to it, it is
29# viable to assume that it didn't ship any backdoors. grab a copy from
30# http://mp3.pages.de/files/rxaudio
31#
32# furthermore psycamp requires the Net/PSYC.pm module since it uses its
33# event multiplexing abilities, not just to receive PSYC messages, but
34# also to handle stdin and engine input in parallel.
35#
36# you can use the 'psyccmd' script to remote control this script, which
37# therefore can act as a music jukebox or media player daemon. also,
38# psycamp can obviously generate 'playing now' notifications.
39
40# since perl has no native preprocessor, this code is
41# managed by the 'jaggler' preprocessor.
42#
43# list of available jaggler flags:
44# 'T' := "time" - activates output of access and modification time
45# 'O' := "org" - enable shift-key functions to reorganize collection
46# 'X' := "rxaudio" - use the old mp3 engine instead of mplayer
47#
48# psycamp in 'distribution' default mode
49# jaggler -x -c# -j% psycamp
50# psycamp supporting 'T' and 'O' extras:
51# jaggler -x -c# -j% -DOT psycamp
52
53# things still in "makenoise": cdrom-file-caching, des-decoding,
54# marks, volumes, support for non-mp3s.
55#
56# HINTS & HACKS:
57# in order to play only high quality files from a folder, you can use
58# "lm -Lb 193 >/tmp/playlist-$USER.m3u; psycamp"
59# lm is available from http://perl.pages.de. such functionality could
60# obviously be integrated into here, allowing us also to remove file
61# type guessing - but in exchange we slow down the playlist creation
62# process if every file were thrown at ffmpeg to discover its media
63# properties.
64
65sub help { print BOLD, BLACK, &head, RESET, <<X; }
66
67basics: (q)uit (h)elp
68
69motion: (p)lay (s)top pa(u)se
70 [ j(ump) ] <mm:ss> jump to an absolute point in the song
71 [ (g)oto ] <pos> [<range>] can do smart guessing of range value
72 (for example you can simply type '0' thru '9' to jump to a point in the song)
73
74files: (o)pen <file> immediately load this new song
75 <file> a filename by itself will first fade current song
76 (l)ist [<dir>] simply calls 'ls'
77 (n)ext next file from playlist
78 '?' show a list of the next 9 songs in the queue
79 (w)rite or (e)dit playlist
80 e(x)it exit without updating playlist
81
82volume: (v)olume [0..100] default is maximum volume
83 (f)ade [<volume> [<psecs>]] psecs: time between volume steps
84 (r)ise [<volume> [<psecs>]] (example: fade 33 0.1)
85
86extra commands for scripting:
87 sleep <time> wait for <time> before executing next command
88
89type (H)elp for organizing commands
90=============================================================================
91X
92# FIXME: volume commands
93
94# when organizing mode has been activated in psycamp, you can reorganize
95# your media files as follows:
96sub help2 { print BOLD, BLACK, &head, RESET, <<X; }
97
98whenever media is in a directory like INCOMING, NEW or TODO - it can be
99moved into a different subdirectory on the same hierarchy level by using
100the following uppercase commands:
101
102 J = send to DEEJAY folder
103 U = send to USE
104 K = send to KEEP
105 X = send to EXPORT
106 S = send to SECONDARY
107 R = send to REPERTOIRE
108
109if you prefer to execute the command *after* having finished playing it,
110you can schedule the move for later by doubling the command letter. so
111you type 'KK' if you want to keep the file after having listened to it.
112the commands for deleting items are similar:
113
114 D = delete the file now
115 DD = delete the file after having played it
116 T = delete the file and mark as trash
117 TT = mark as trash and delete later
118
119type (h)elp for regular commands
120=============================================================================
121X
122
123# default PSYC address for this service, UDP port 1144 on this host.
124# can be overridden with -b
125#
126$UNI = 'psyc://127.0.0.1:1144d/';
127#
128# be mindful that if you use outgoing -M the receiving side can send
129# remote control commands back to you, even if you bind localhost here.
130# you may like to have such chat-based remote control, or you may not..
131
132# volume values, since volume doesn't seem to be linear as it should
133# at least not my soundcard, check for yourself..
134#
135@VV = ( 0,2,5,8,11,14,17,20,24,28,33,38,43,49,56,64,72,81,90,100 );
136
137# when running rxaudio, psycamp can only handle .mp3 and .sdj
138# "part" and "dl" are the temporary filenames of some download tools
139$FILETYPES =
140#% "(mp3|sdj|part|dl)"; #? X
141 "(mp\\d|sdj|part|dl|flac|wav|aif|aiff|ogg|m4a|aac|opus|au)"; #? !X
142# "(sea|mod|gz|lha|Z|lzh|zip|s3m)"; # |med|mmd0)";
143# should also be able to handle .pls
144#
145# would prefer not to enumerate media formats understood by mplayer,
146# but it is tricky to sort out non-media files when recursively
147# spidering input directories.
148
149# starting pcm volume. system volume is kept at maximum anyway.
150#%$VOL = 0; #? X
151$VOL = 100; #? !X
152
153# allow 'D' key to delete the file currently being listened to (!!)
154# ignored if -DO was not provided in jaggler
155$ALLOW_DELETE_KEY = 1;
156
157# string contained in path for files that are deleted in -d mode
158$VOLATILE = 'VOLATILE';
159
160# string contained in path for files that are kept even when in -D mode
161$KEEP = 'LOCAL';
162
163# debugging (inline macro, if undefined all debugging code is removed)
164# 1: examine progress
165# 2: debug file recursion
166# 4: debug randomizer
167# 8: show playlist order
168# 16: debug PSYC transactions
169# 32: weird things that shouldn't happen
170# 64: show server pid
171# 128: show songs of same size when using -s S
172# 256: show all output from media engine
173# 512: watch only engine parse
174# 1024: watch timer events (timeout simulation)
175# 2048: show engine debug messages
176sub DEBUG () { 32 + 128 }
177
178# also activate PSYC debugging
179#Net::PSYC::setDEBUG(3);
180
181# used by randomize algorithm - how much of the path is compared?
182# would be smart to choose this value dynamically..
183sub PATHMATCH () { 12 }
184
185# file to put media information into about files that got trashed
186sub HATEINDEX () { "$ENV{HOME}/.media/TRASH-$ENV{HOST}.ix" }
187
188$tmpdir='/temp';
189$tmpdir='/tmp' unless -d $tmpdir and -w _;
190$tmpdir='.' unless -d $tmpdir and -w _;
191# $tmplock="$tmpdir/.psycamp-copylock";
192$playlist="$tmpdir/playlist-$ENV{USER}.m3u";
193
194 use Getopt::Std;
195 use File::Find ();
196 use FileHandle;
197 use Carp;
198 #use MPEG::MP3Info; # no longer necessary
199 use Date::Format qw( time2str ); ## T
200 use IPC::Open3 qw( open3 );
201 use Net::PSYC qw( :event );
202
203 *name = *File::Find::name; # ugly style works
204 $scan = 0;
205
206MAIN: {
207 if ($#ARGV >= 0) {
208 getopt('bMns');
209 }
210 $nick = $opt_n
211 || $ENV{'PSYCNICK'}
212 || $ENV{'NICK'} # this one should work with any chat system
213 || $ENV{'IRCNICK'}
214 || $ENV{'USER'}
215 || $ENV{'HOST'}
216 || 'unixer';
217
218 print "Using playlist: $playlist\n" if $opt_v;
219 if ($opt_h) {
220 print BOLD, BLACK, &head, YELLOW, <<X, BOLD, BLUE, &sorthelp, RESET;
221
222usage: $0 [<flags>] [-b <uniform>] [-s <mode>] [-M <UNI>] [<files|dirs>]
223
224 [-b]ind PSYC uniform and accept commands from both PSYC and stdin
225 [-M] sends currently playing title to a monitoring entity via PSYC
226 [-n]ickname to use for monitoring, otherwise '$nick' will be used
227 [-s] provides for several sort options, see below
228flags:
229 [-H] shows an explanation what this tool is good for, try it!
230 [-r]andomize using a smart shuffle algorithm, much better than "-s r"
231 [-m]ono output
232 [-v]erbose: shows some output from rxaudio
233 [-q]uiet: shows close to no output
234 [-c]alculate cumulative duration of selections
235 [-L]oad the tracks in the playlist only if they really exist
236 [-x] will terminate perl and exec xaudio, use only when short on memory
237 [-I]nitialize rxaudio anew for each song, special hack
238 [-d]elete files after playing if the path contains the word '$VOLATILE'.
239 [-D]elete files after playing unless the path contains the word '$KEEP'.
240 [-S]kip files if the path contains the word '$KEEP', dont play them.
241
242 without arguments psycamp resumes from last run´s playlist.
243X
244# [-l]ist filenames, sizes and bitrates (for archive documentation)
245# ... broken and prolly useless
246 exit;
247 }
248 # initialize randomizer
249# my $a = time() ^ $$; $a = reverse $a; srand($a);
250 # no longer necessary with newer perls,
251 # even the following is optional:
252 srand;
253
254 #{ X
255#% print <<X unless $has_rxaudio = &which('rxaudio');
256#%cannot find rxaudio. cannot play any mp3s without
257#%rxaudio from http://www.mpeg.org/xaudio/
258#%or, just for friends, from http://mp3.pages.de/files/ (old linux binary)
259#%of course you are welcome to update psycamp to work with xmms or mplayer or..
260#%
261#%X
262#% # aoss: Wrapper to facilitate use of the ALSA OSS compatibility library.
263#% # in case you do not have it in form of kernel modules (snd-pcm-oss etc)
264#% # padsp: Wrapper to do the same with pulseaudio.
265#% $wrapper = &which('aoss') || &which('padsp') || "";
266#% print "Using wrapper: $wrapper\n" if $opt_v;
267 #: X
268 print <<X unless $has_mplayer = &which('mplayer');
269cannot find mplayer. cannot play media without it.
270
271X
272 #} X
273 if ($opt_H ||
274#% !$has_rxaudio #? X
275 !$has_mplayer #? !X
276 ) {
277 print BOLD, BLACK, &head, RESET, <<X;
278
279This media player brings you a threefold functionality which you may combine
280at will:
281
2821. a command line media player which gives you possibilities to navigate
283 media and similar functions by entering commands on the keyboard, so you
284 don't need a GUI to achieve the same effects.
285
2862. the player can be remote controlled with UDP messages according to the
287 PSYC protocol for synchronous conferencing - an upcoming chat protocol
288 which can be used for all sorts of messaging, so it's fine for this
289 purpose too. This enables you to implement CGI-based remote controls
290 or suchlike. the _request_execute method family is understood via PSYC.
291 unless you specify the -b option, $UNI will be used
292 as PSYC address for reception of commands. currently no authentication
293 is requested, so it is generally good to bind to localhost. a message
294 can contain several lines of instructions. no further input will be
295 accepted while processing these instructions.
296
2973. this player is scriptable by "scripting deejay" files (extension .sdj),
298 they allow you to automate operations on media files, even simulate
299 simple remixes without actually modifying the source material.
300
301see '$0 -h' for usage instructions
302X
303 exit;
304 }
305 &enqueue(@ARGV);
306 if ($NS) { # global var for number of enqueued songs
307 do {
308 @order = $opt_r ? &randomize : &sortsongs($opt_s);
309 print STDERR "\r[order] ", join(' ', @order), "\n\n" if DEBUG & 8;
310 foreach my $i (@order) {
311 unless ($i) {
312 print STDERR " (weird bug encountered)\n" if DEBUG & 32;
313 undef @order;
314 next;
315 }
316 }
317 } until (@order && $order[0]);
318 &save(-1);
319 } elsif (-r $playlist) {
320 &load unless $opt_x;
321 system "$ENV{EDITOR} $playlist;clear" if $opt_e;
322 }
323 #exec "xaudio `cat $playlist`" if $opt_x;
324 exec 'mplayer "`cat $playlist`"' if $opt_x;
325 #exec "mpg123 --remain --aggressive -@ $playlist" if $opt_x;
326
327 print STDERR BOLD, YELLOW, "binding to $opt_b ...\n", RESET if DEBUG & 16 && $opt_b;
328 bind_uniform( $opt_b || $UNI );
329 register_uniform();
330 $rc = sendmsg ($opt_M, '_notice_summary_play_music',
331"[_nick_application]: [_nick] is going to listen to [_amount_tracks] tracks.",
332 { _nick => $nick, _nick_application => 'psycamp',
333 _amount_tracks => $NS } ) if $opt_M;
334 print STDERR BOLD, YELLOW, "sent greeting to $opt_M ...\n", RESET if DEBUG & 16 && $opt_M;
335 add( \*STDIN, 'r', \&stdread );
336 &ginstart;
337
338 # use Cwd; # also used by &save()
339 use Cwd qw(chdir); # maintains PWD in ENV
340 print STDERR $ENV{PWD} . " = PWD\n" if DEBUG & 1;
341 if ($opt_d) {
342 print STDERR BOLD, RED, ($Volatile = $ENV{PWD}
343 =~ /\b$VOLATILE\b/oi) ? <<X : <<Y, RESET;
344Warning: ALL files will be deleted after consumption.
345X
346Warning: Files tagged $VOLATILE will be deleted after consumption.
347Y
348 }
349 if ($opt_D) {
350 print STDERR ($Keep = $ENV{PWD} =~ /\b$KEEP\b/oi) ?<<X:<<Y;
351Warning: NO files will be deleted.
352X
353Warning: All files not tagged $KEEP will be deleted after consumption.
354Y
355 }
356 print BOLD, BLACK, &head, <<X, RESET unless $opt_q;
357enter (h) for help
358
359X
360 &gin('channels mono') if $opt_m;
361 $CS = -1; # global var for current song
362
363 &next(0);
364# &vol(100);
365 $|=1;
366
367 # Net::PSYC::Event doesn't support idle events yet.. TODO
368 add(3, 'i', \&timeout, 1);
369 # higher frequency necessary to detect timeouts this way..
370 # then again, if it's too high timer is sometimes faster
371 # than rxaudio and produces an erroneous kick..
372 start_loop();
373}
374
375
376### SUBS & SANDWICHES ###
377
378sub timeout {
379 if (!$paused) {
380 # HACK for rxaudio which sometimes gets enchanted
381 #y $trick = rand(2)>1 ? 'pause' : 'seek 1 1';
382 #y $trick = ('pause', 'seek 1 1', 'play')[rand(3)];
383 #y $trick = 'seek 1 1';
384# print " (kicking rxaudio with '$trick')\n" if DEBUG & 32;
385# &gin( $trick );
386 print RED, "\n\t\t(kick) ", RESET if DEBUG & 32;
387#% &gin('seek 1 1'); #? X
388 &gin('seek 0 1'); #? !X
389 &gin('pause'); #? !X
390 }
391 return 3;
392}
393
394sub ginread {
395 $_ = <R>;
396 print STDERR BOLD, BLUE, $_, RESET if DEBUG & 256;
397 #{ X
398#% # example: MSG notify position [offset=20, range=400]
399#% if ( /^MSG notify position / ) {
400#% /\boffset=(\d+), range=(\d+)\b/;
401#% # HACK! HACK!
402#% # send something that will flush the EOF to us
403#% print W "get_player_mode\n" if 5+$1 > $2;
404#% } else
405 #} X
406 {
407 $_ = &ginparse( $_ );
408# ds_fill_buffer: EOF reached (stream: audio)
409# "EOF code: 2" happens when we ask to load a different file
410 if (
411#% /^MSG notify player state \[EOF\]$/ #? X
412 /^EOF code: 1\b/ #? !X
413 ) {
414 # &progress('');
415 if ($deleteLater eq $CurrentFile) {
416 print RED, unlink ($deleteLater) ?
417 "\r***" : "\r - ", BOLD, GREEN, "[\n", RESET;
418 $deleteLater = undef;
419 } elsif ($moveLater) {
420 &moveFile($moveLater);
421 $moveLater = undef;
422 } elsif ($opt_d && ($Volatile ||
423 $CurrentFile =~ /\b$VOLATILE\b/oi)) {
424 print RED, unlink ($CurrentFile) ?
425 "\r***" : "\r - ", BOLD, GREEN, "[\n", RESET;
426# [could not delete $CurrentFile]
427 } elsif ($opt_D && !$Keep &&
428 $CurrentFile !~ /\b$KEEP\b/oi) {
429 print RED, unlink ($CurrentFile) ?
430 "\r***" : "\r - ", BOLD, GREEN, "[\n", RESET;
431 } else {
432 my $m = $date[$order[$CS]];
433 # touch the access time of the file
434# print "\rto be accessed: ", isotime(time),
435# "\tto be changed: ", isotime($m), "\n";
436 utime time, $m, $CurrentFile;
437 #{ T
438#%# ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
439#%# $atime,$mtime,$ctime,$blksize,$blocks) = lstat($CurrentFile);
440#%# print "\rset accessed: ", isotime($atime),
441#%# "\tset changed: ", isotime($mtime), "\n";
442 #} T
443 print BOLD, GREEN, "\r [\n", RESET unless $opt_q;
444 }
445 &ginstop if $opt_I;
446 &next(0);
447 }
448 }
449}
450
451sub stdread {
452 $_ = scalar <STDIN>;
453 &parse( $_ );
454}
455
456sub msg {
457 my ($source, $mc, $data, $vars) = @_;
458 return if $mc =~ /_circuit/;
459 unless ($opt_q) {
460 my $tx = psyctext($data, $vars);
461 print BOLD, YELLOW, <<X, RESET;
462
463>> $source ($mc) >>>>>> $tx <<<<<<
464
465X
466 }
467 # disable when using -M... hmm, why?
468 return if $opt_M;
469 &parse($data) if $mc =~
470 /^(_request_execute|_command|_message)/;
471}
472
473 #{ T
474#%sub isotime {
475#% my $t = shift;
476#% return time2str('%Y-%m-%d %T', $t);
477#%}
478 #} T
479
480sub open {
481 my $file = shift;
482 return unless $file;
483 #{ T
484#% my $m = $date[$order[$CS]];
485#% my $a = $adate[$order[$CS]];
486#% print "\rlast accessed: ", isotime($a),
487#% "\tlast changed: ", isotime($m), "\n";
488 #} T
489# &ginopen if $opt_I;
490 &ginstart if $opt_I;
491 if ($file =~ /\.sdj$/i and -T $file) {
492 # calling a script from a script is like a "skip"..
493 $SCRIPT = $file;
494 print "[executing script $SCRIPT]\n" unless $opt_q;
495 open(F, $file) || die "$file: $!";
496 while(<F>) {
497 &parse($_);
498 }
499 close F;
500 print "[script terminated]\n" unless $opt_q;
501 undef $SCRIPT;
502 &next(0); # hmmmm?
503 } elsif (! -r $file) {
504 print RED, "[oh! $file has disappeared]\n", RESET unless $opt_q;
505 &next(0); # hmmmm?
506 } else {
507 # xaudio does this for us now
508# if (defined( &get_mp3info )) {
509# $mp3 = get_mp3info($file);
510# printf "$file length is %02d:%02d\n",
511# $mp3->{MM}, $mp3->{SS};
512# }
513 #{ X
514#% $TC = '[--:--:--:--]';
515#% &gin("open $file");
516 #: X
517 $TC = '[--:--:--]';
518 &gin("pause");
519 # will fail when encountering a filename containing
520 # a doublequote. could use urlquote instead.. FIXME
521 &gin("loadfile \"$file\"");
522 # wicked hacks described in api spec
523 &gin("pausing_keep_force pt_step 1");
524 &gin("get_property pause"); # ANS_pause=no
525 #} X
526 $DUR = 0;
527 &sleep(0.3); # have to wait for file to load?
528 $scan=0, &seek(5) if $scan;
529# &seek(@_) if @_;
530# &vol(100);
531 }
532}
533sub next {
534 my $a = shift;
535 &fade(43, $a) if $a and $VOL > 50;
536 my $n = $file[$order[++$CS]]; # || $file[$CS];
537 &exit(0) if !$n or ($n eq '') or !$NS or $CS >= $NS;
538 return &next(0) if $opt_S and $n =~ /\b$KEEP\b/oi;
539 &open( $n );
540 # if (!$a or $opt_I) {
541 # print "(not waiting) " if DEBUG & 32;
542# &sleep(.4);
543 # &gin('play');
544 # }
545#% &gin('play'); #? X
546 if ($opt_M) {
547 undef $!;
548 $rc = sendmsg ($opt_M, '_notice_play_music_title',
549 "[_nick] is listening to: [_music_title]",
550 { _nick => $nick, _music_title => $n
551 } );
552 die "sendmsg $rc: $!" if $!;
553 print STDERR BOLD, YELLOW, "sent to $opt_M!\n", RESET if DEBUG & 16;
554 }
555 &vol($Volume) if $Volume;
556 $paused = 0;
557 # next;
558}
559
560sub parse {
561 $_ = shift;
562 s/^\s+//;
563 chomp;
564 if ($SKIP) {
565 next unless /^:$SKIP/;
566 undef $SKIP;
567 next;
568 }
569 if (/^\?$/) {
570 my $any = 0;
571 print YELLOW, <<X, RESET;
572
573currently playing: $CurrentFile
574X
575 my $max = $NS-$CS > 9 ? $CS+9 : $NS-1;
576 for ($i = $CS+1; $i <= $max; $i++) {
577 my $f = $file[$order[$i]];
578 my $s = $size[$order[$i]] || -s $f;
579# my $a = $adate[$order[$i]];
580 $f =~ s/\.\S+$//i;
581 $f = '..'.substr($f,length($p)-61) if
582 length($f) > 63;
583 $any++;
584 printf "%3d.%8d %s\n", $i, $s, $f;
585 }
586 print $any ? "\n" : "<no more songs in playlist>\n\n";
587 next;
588 }
589 next unless /^\w/;
590 print "> $_\n" if $opt_v and $SCRIPT;
591
592 # techniques to seek in media file
593 /^([\d:']+)$/ and &seek($1), next;
594 /^(\d+)\s+(\d+)$/ and &seek($1,$2), next;
595 /^(g|go|goto|seek)\b\s*(\S*)(.*)$/ and &seek($2,$3), next;
596 /^(j|jump)\b\s*(\S*)$/ and &jump($2), next;
597
598 /^skip\s(\w+)$/ and $SKIP=$1, next;
599 # /^open\s+(\S*)\s*\b(\S*)\s*\b(\S*)\s*$/ and &open($1,$2,$3), next;
600 /^o\s+(.*)\s*$/ and &open($1), next;
601 /^scan\s+(.*)\s*$/ and $scan=1, &open($1), next;
602 /^(n|next)\b\s*(\S*)$/ and &next($2 || .05), next;
603 #{ O
604 if ( $ALLOW_DELETE_KEY ) {
605 if ( /^T(T?)\s*$/ ) {
606 # 'T' deletes song and remembers it in the index of trash music
607 #{ L
608 use MP3::List; # terrible dependency FIXME!
609 my ($out, $dur) = &MP3::List::format($CurrentFile);
610 #} :
611#% $out = "$CurrentFile\n";
612 #} L
613 if ($out and open(HATE, ">>", HATEINDEX)) {
614 print HATE $out;
615 close HATE;
616 print RED, ">> $CurrentFile marked as trash\n", RESET;
617 } else {
618 print RED, "*** Failed to write to ", HATEINDEX, ":\n", RESET, $out;
619 }
620 # dirty way to fall through into one of the following ifs
621 $_ = $1? 'DD': 'D';
622 }
623 if ( /^(_|DD)\s*$/ ) {
624 $deleteLater = $CurrentFile;
625 print BOLD, BLUE, ">> scheduled for removal\n", RESET;
626 next;
627 }
628 if ( /^D\s*$/ ) {
629 my $f = $CurrentFile;
630 &ginclose;
631 print BOLD, RED, ">> deleted: $f\n***", RESET if unlink $f;
632 &ginopen;
633 &next(0);
634 next;
635 }
636 }
637 if ( /^(J|U|K|E|M|X|C|S|R)(\w?)\s*$/ ) {
638 my $r = $2;
639 if ($r and $r ne $1) {
640 print BOLD, RED, ">> command $1$r not defined\n", RESET;
641 next;
642 }
643 my $t = $1 eq 'J' ? 'DEEJAY' :
644 $1 eq 'U' ? 'USE' :
645 $1 eq 'K' ? 'KEEP' :
646 $1 eq 'E' ? 'EDITABLE' :
647 $1 eq 'M' ? 'REMASTER' :
648 $1 eq 'X' ? 'EXPORT' :
649 $1 eq 'C' ? 'CRITICIZE' :
650 $1 eq 'S' ? 'SECONDARY' : 'REPERTOIRE';
651 my $f = $CurrentFile;
652 $f = $ENV{PWD}. '/'. $f unless $f =~ m!^/!;
653 unless ($f =~ s:\b(SHARE|T|COMPLETE|KEEP|EDITABLE|SECONDARY|REPERTOIRE|NEW|SEEK|TODO|USE|DEEJAY|REMASTER|INCOMING)\b:$t:i)
654 {
655 print BOLD, RED, ">> not applicable for $f\n", RESET;
656 next;
657 }
658 if ($r) {
659 $moveLater = $f;
660 print BOLD, BLUE, ">> scheduled to move to $t\n", RESET;
661 } else {
662 &moveFile($f);
663 &next(0);
664 }
665 next;
666 }
667 #} O
668 # NEW: relative volume changes, only with mplayer
669 # FIXME: why does it only work with leading v?
670 /^v\+\s*$/ and &gin('volume 9'), next; #? !X
671 /^v\-\s*$/ and &gin('volume -9'), next; #? !X
672 /^(v|vol|volume)\b\s*(\d+)$/ and &vol($Volume = $2), next;
673 /^(f|fade)\b\s*(\S*)(.*)$/ and &fade($2,$3), next;
674 /^(r|rise)\b\s*(\S*)(.*)$/ and &rise($2,$3), next;
675 /^sleep\b\s*(\S*)$/ and &sleep($1), next;
676 /^(d|duration)\b\s*(\S*)$/ and &duration($2), next;
677 /^(l|list)\b\s*(\S*)$/ and system("ls $2"), next;
678 /^(h|help)\b/ and &help, next;
679 /^H\b/ and &help2, next;
680 /^(e|edit)\b/ and &edit($CS), next;
681
682 if ( /^\s*(.....+)\s*\b(\S*)\s*\b(\S*)\s*$/ and -r $1 ) {
683 &fade(33, .1) if $VOL > 50;
684 &open($1, $2, $3);
685 # &sleep(.2);
686 # &gin('play');
687 # &vol(100);
688 next;
689
690 # &gin("open $1");
691 # &sleep(.4);
692 # &sleep;
693 }
694# if ( s/^(\S+\.mp3)\s+(\d+)\b// ) {
695# &gin("open $1");
696# &sleep;
697# &gin("seek $2 1000");
698# &sleep;
699# &gin('play');
700# }
701# &gin("open $_") if s!\b(\S+\.mp3)\b!!;
702# if ( s/\b(\d+)\s+(\d+)\b// ) {
703# &gin("seek $1 $2");
704# }
705
706 /^(q|quit)\b/ and &save($CS-1), &exit(0);
707 /^(x|exit)\b/ and &exit(0);
708 /^(w|write)\b/ and &save($CS-1), next;
709 # s/^o\b/open/;
710 s/^p\b/play/ and $paused = 0;
711 s/^s\b/stop/ and $paused = 1;
712 s/^u\b/pause/ and $paused = !$paused;
713 &gin($_) if $_;
714}
715
716sub moveFile {
717 my $f = shift;
718 my $d = $f;
719 $d =~ s:/[^/]+$::;
720 use File::Path;
721 mkpath $d;
722# unless (mkpath($d)) {
723# print ">> could not mkdirhier $d\n";
724# next;
725# }
726 unless (rename ($CurrentFile, $f)) {
727 print BOLD, RED, ">> could not move file to $d\n", RESET;
728 next;
729 }
730 print BOLD, BLUE, ">> moved to $d\n", RESET;
731}
732
733sub sleep {
734 my $t = shift;
735 if ($t) {
736 if ( $t =~ /(\d+)(:|')(\S+)/ ) {
737 $t = $1*60+$3;
738 }
739 }
740 else {
741 $t = 0.1;
742 }
743 print "[sleeping $t secs]\n" unless $opt_q or $t < 1;
744 select (undef,undef,undef,$t);
745 return $t;
746}
747
748# stuff being sent to the engine
749sub gin {
750 my $p = shift;
751 print YELLOW, "==> $p\n", RESET if $opt_v;
752 print W $p, "\n";
753 &sleep(0.1);
754}
755
756sub fade {
757 my $s = shift;
758#% $s = 33 unless $s; #? X
759 $s = 12 unless $s; #? !X
760 my $p = shift;
761 $p = 0.4 unless $p;
762 foreach $i ( reverse @VV ) {
763 next if $i >= $VOL;
764 last if $i <= $s;
765 &vol($i);
766 &sleep($p);
767 }
768 if ($s) {
769 &vol($s);
770 } else {
771 &gin( 'pause' );
772 $paused = !$paused;
773 }
774 &sleep($p);
775 return 1;
776}
777sub rise {
778 my $s = shift;
779 $s = 100 unless $s;
780 my $p = shift;
781 $p = 0.2 unless $p;
782 &gin( 'play' ) unless $VOL;
783 foreach $i ( @VV ) {
784 next if $i <= $VOL;
785 last if $i >= $s;
786 &vol($i);
787 &sleep($p);
788 }
789 if ($s) {
790 &vol($s);
791 &sleep(0.5);
792 }
793 $paused = 0;
794 return 1;
795}
796
797sub vol {
798 $VOL = shift;
799 # &gin( "volume 100 $VOL 50" );
800 #rint W "volume 100 $VOL 50\n";
801 &gin( "volume $VOL $VOL 50" );
802 #rint W "volume $VOL $VOL 50\n"; # bypasses debug
803 return 1;
804}
805
806sub seek {
807 my $p = shift;
808 my $r = shift;
809
810 &duration($r) if $r =~ /[:']/;
811 return &jump($p) if $p =~ /[:']/;
812
813 $p = $1 if $p =~ /(\d+)/;
814 #{ X
815#% $r = 10 ** length($p) unless ($r);
816#% &gin( "seek $p $r" );
817 #: X
818 $p = 11 * $p if $p < 10;
819 &gin( "seek $p 1" );
820 ## $r not supported in mplayer version.. yet?
821 #} X
822 return 1;
823}
824
825sub duration {
826 my $t = shift;
827
828 if ( $t =~ /(\d*)(:|')(\S+)/ ) {
829 $t = $1 ? $1*60+$3 : $3;
830 }
831 $DUR = $t * 1000;
832 print YELLOW, "[duration is $t secs]\n", RESET unless $opt_q;
833 return 1;
834}
835
836sub jump {
837 my $t = shift;
838 unless ($DUR) {
839 print STDERR "[you must specify the song duration first]\n";
840 return 1;
841 }
842 if ( $t =~ /(\d*)(:|')(\S*)/ ) {
843 $t = $1 ? $1*60+$3 : $3;
844 }
845 &gin( "seek ". $t*1000 .' '. $DUR );
846 return 1;
847}
848
849# if your system doesn't have "which" we're in trouble
850sub which {
851 my $cmd = shift;
852 $_ = `which $cmd 2>&1`;
853 print STDERR "which $cmd: $_" if DEBUG & 1;
854 /^(\S+)\s*$/;
855 return $1;
856}
857
858sub sortsongs {
859 my $style = shift;
860 return (1 .. $NS) unless $style;
861 lc $style;
862 my @order;
863 eval "\@order = sort by_$style 1 .. $NS";
864 croak <<X, &sorthelp if $@;
865invalid sort option '$style'
866X
867 return @order;
868}
869sub sorthelp { return <<X; }
870
871available sort algorithms:
872 n(ame) # sorts by file path (directory first)
873 N(ame) # sorts by file ending (reverse of -n)
874 nr # gives an order by name a slight shuffle
875 cr # gives the order given on commandline a slight shuffle
876 s(ize) # hear silly small sound snippets first
877 S(ize) # hear big epic pieces first
878 m(odification) # hear newest tracks first
879 M(odification) # hear oldest tracks first
880 a(ccessTime) # hear tracks you haven't heard in a long time first
881 A(ccessTime) # hear tracks you recently accessed first
882X
883 # r(andom) # bad randomizer algorithm (use -r instead)
884sub by_n { $file[$a] cmp $file[$b]; }
885sub by_N { reverse($file[$a]) cmp reverse($file[$b]); }
886# this actually produces VERY pseudo random results, says randal
887# see http://www.perlmonks.org/?node_id=199901
888sub by_r { rand(10) < 5; }
889sub by_nr { rand(9) > 3 ? &by_n : &by_r; }
890sub by_cr { rand(9) > 3 ? $a <=> $b : &by_r; }
891sub by_m { $date[$a] <=> $date[$b]; }
892sub by_M { $date[$b] <=> $date[$a]; }
893sub by_a { $adate[$a] <=> $adate[$b]; }
894sub by_A { $adate[$b] <=> $adate[$a]; }
895sub by_s {
896 # one side of this if/else clause gets optimized away at compilation
897 if (DEBUG & 128) {
898 my $B = $size[$b]; my $A = $size[$a];
899 if ($A == $B) {
900 my ($ad, $ai) = stat $a;
901 my ($bd, $bi) = stat $b;
902 # inform of duplicate files in file system
903 # unless they are hard- or softlinked
904 print MAGENTA, <<X, RESET
905 unless $ai == $bi and $ad == $bd;
906Same file size $B:
907 $file[$a] and
908 $file[$b]
909X
910 # we could also be calling cmp, but then we
911 # are doing a different job than playing music
912 }
913 return $A <=> $B;
914 } else {
915 return $size[$a] <=> $size[$b];
916 }
917}
918sub by_S {
919 # one side of this if/else clause gets optimized away at compilation
920 if (DEBUG & 128) {
921 my $B = $size[$b]; my $A = $size[$a];
922 print MAGENTA, <<X, RESET if $A == $B;
923Same file size $B:
924 $file[$a] and
925 $file[$b]
926X
927 return $B <=> $A;
928 } else {
929 return $size[$b] <=> $size[$a];
930 }
931}
932
933# FIXME: maybe update to use List::Util 'shuffle';
934# or maybe this is fine because it does NOT use sort+rand
935sub randomize {
936 my @tmp = 1 .. $NS;
937 my @order = ();
938 my $lr = -99;
939 for my $j (1 .. $NS) {
940 my $r = int(rand($#tmp));
941 my $ir = $tmp[$r];
942
943 # this tries to avoid items being too near
944 # even if randomizer suggests so -
945 # in particular try not to play the same artist
946 # in a row, that's why we look at the filename
947 if ($#tmp>7 and abs($lr-$ir)<23 and
948 substr($file[$lr],0,PATHMATCH) eq
949 substr($file[$ir],0,PATHMATCH)) {
950 print STDERR <<X if DEBUG & 4;
951last=$lr\t[$file[$lr]]
952near=$ir\t[$file[$ir]] ($r)
953X
954 $r = int(rand($#tmp));
955 $ir = $tmp[$r];
956 if (substr($file[$lr],0,PATHMATCH) eq
957 substr($file[$ir],0,PATHMATCH)) {
958 print STDERR <<X if DEBUG & 4;
959new =$ir\t[$file[$ir]] ($r)
960\t\tno good, one more try:
961X
962 $r = int(rand($#tmp));
963 $ir = $tmp[$r];
964 }
965 print STDERR <<X if DEBUG & 4;
966new =$ir\t[$file[$ir]] ($r)
967
968X
969 }
970 $lr = splice @tmp, $r, 1;
971 push @order, $lr;
972 }
973 return @order;
974}
975
976sub enqueue {
977 my $saveNS = $NS;
978 for $_ (@_) {
979 if (-d $_ && -x _ && -r _) {
980 print STDERR "\n[finddepth: $_]\n" if DEBUG & 2;
981 &File::Find::finddepth(\&wanted, $_);
982 next; # $_ is corrupted after finddepth
983 }
984 $name = $_;
985 print STDERR "\n[file wanted: $_]\n" if DEBUG & 2;
986 &wanted;
987 }
988 if (my $new = $NS - $saveNS) {
989 &progress($new, ' songs found');
990 print "\n";
991 }
992}
993sub wanted {
994 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
995 $atime,$mtime,$ctime,$blksize,$blocks) = lstat($_);
996 my $neat = length($name)>70 ? "...".substr($name,-67) : $name
997 unless $opt_q;
998 if (-f _ && -r _ && -s _ > 9999 && /\.$FILETYPES$/io) {
999 $file[++$NS] = $name;
1000 $size[$NS] = $size || -s _;
1001 $date[$NS] = $mtime || -M _;
1002 $adate[$NS] = $atime;
1003 &progress('yes: ', $neat);
1004 } elsif ( -d _ && -e "$_/.prune" ) {
1005 $prune = 1;
1006 &progress('pruned: ', $name);
1007 print "\n";
1008# } elsif ( $decode && -f _ && /\bindex\.etx$/i ) {
1009# open(I, "$decode <$_ |")
1010# and @index = (@index, <I>) and close I;
1011# &progress('inf: ', $neat);
1012 } else {
1013 &progress('no : ', $neat) if -f _;
1014 }
1015}
1016sub progress {
1017 return if $opt_q;
1018 my $tx = join('', @_);
1019 my $len = length $tx;
1020 print "\r", GREEN, $tx, RESET, ' ';
1021 print '_' x (74-$len), ' ' if $len < 74;
1022 print "\n" if DEBUG & 1;
1023}
1024
1025sub ginxpect {
1026 local($match) = @_;
1027
1028 while(<R>) {
1029 last if /$match/i; # not /o!
1030 &ginparse($_);
1031 }
1032 # print "\n";
1033}
1034sub ginparse {
1035 # most frequent events first!
1036 if (
1037#% /^MSG notify timecode (\S+)/ #? X
1038 /^A:\s+([\d]+\.\d)\s/ #? !X
1039 ) {
1040 unless (
1041#% $TC eq $1 #? X
1042 $tc == $1 #? !X
1043 ) {
1044#% $TC = $1; #? X
1045 $tc = $1; #? !X
1046 $TC = time2str('[%T]', $tc, 0); #? !X
1047 print BOLD, GREEN, "\r$TC ", RESET unless $opt_q;
1048 }
1049 return 0;
1050 }
1051 print STDERR BOLD, BLUE, $_, RESET if $opt_V or DEBUG & 512;
1052 #{ X
1053#% if (/^MSG notify duration \[(\d+)\]/) {
1054#% # kludge to get around a bug in xaudio (duration output twice)
1055#% if ($1 != $LDUR) {
1056#% $LDUR = $DUR = $1;
1057#% $DUR *= 1000 if $DUR < 1000;
1058#% $CDUR += $DUR; # cumulative duration
1059#% }
1060#% return 0;
1061#% }
1062#% if (/ stream info \[(.+)\]/) {
1063#% %I = split /[=,\s]+/, $1;
1064#% my $d = $DUR / 1000;
1065#% $I{duration} = sprintf("%02d:%02d", $d / 60, $d % 60);
1066#%# if ($opt_l) {
1067#%# my $f = $file[$order[$CS]];
1068#%# my $s = $CurrentFile eq $f ? -$size[$order[$CS]] : -s $f;
1069#%# printf ("%9d %5s %3s\t%s\n", $s,
1070#%# $I{duration}, $I{bitrate}, $CurrentFile);
1071#%# }
1072#% $I{mode} = lc $I{mode};
1073#% if ($opt_c) {
1074#% my $d = $CDUR / 1000;
1075#% $I{cumulative} = sprintf("%02d:%02d", $d / 60, $d % 60);
1076#% &progress("$TC -> [$I{duration}] {$I{cumulative}} $I{bitrate} mp$I{layer}.$I{level} $I{frequency} $I{mode} ");
1077#% } else {
1078#% &progress("$TC -> [$I{duration}] mp$I{layer}.$I{level} with $I{frequency} Hz at $I{bitrate} kbps in $I{mode}");
1079#% }
1080#% return $_;
1081#% }
1082#%# if ($opt_l and / ack \[XA_MSG_COMMAND_INPUT_OPEN\]/) {
1083#%# my $f = $file[$order[$CS]];
1084#%# my $s = $CurrentFile eq $f ? -$size[$order[$CS]] : -s $f;
1085#%# printf ("%9d\t%s\n", $s, $CurrentFile);
1086#%# }
1087 #: X
1088 if ( /^\[file\] File size is (\d+) bytes$/ ) {
1089 $size = $1;
1090 print STDERR "Got size: $size\n" if DEBUG & 512;
1091 return 0;
1092 }
1093# [lavf] stream 0: audio (opus), -aid 0, -alang eng
1094# INFO: libavcodec "aac" init OK!
1095# Selected audio codec: [ffaac] afm: ffmpeg (FFmpeg AAC (MPEG-2/MPEG-4 Audio))
1096 if ( /^\[lavf\] .* audio \((\S+)\),/ ) {
1097 $codec = $1;
1098 print STDERR "Got codec: $codec\n" if DEBUG & 512;
1099 return 0;
1100 }
1101 #} X
1102 if (
1103#% / input name \[(.+)\]/ #? X
1104# /^Playing (.+)\.$/ #? !X
1105 /^STREAM: \[file\] (.+)$/ #? !X
1106 ) {
1107 # global vars for current filename
1108 my $nf = $1;
1109 # we do indeed not receive a proper info for mp2, mp3...
1110 $CurrentCodec = $codec;
1111 $CurrentSize = $size || -s $nf;
1112 if ($CurrentFile ne $nf) {
1113 $moveLater = undef;
1114 $deleteLater = undef;
1115 $size = undef;
1116 $codec = undef;
1117 }
1118 $CurrentFile = $nf;
1119 return $_;
1120 }
1121 #{ X
1122#% #return 0 if /^(play|close|open|volume)/;
1123#% return 0 if /^(play|volume|get_player_mode)/;
1124#% return $_ if $opt_q or / notify (position|ack|play|state|can seek)/;
1125#% if ( /^MSG notify debug \[.* message=\"no audio device found\"\]/ ) {
1126#% print "\n\r", <<X;
1127#%*** No audio device accessible. Try modprobe snd-pcm-oss!
1128#%X
1129#% &exit(-1);
1130#% }
1131#% if (DEBUG & 2048 and /^MSG notify debug \[.* message=\"(.+)\"\]/) {
1132#% print "\r*** ", $1, "\n";
1133#% return 0;
1134#% }
1135#% return $_ if !$opt_v && /notify (debug|output|input|nack)/;
1136#% # my $o = $_;
1137#% s/^MSG notify //i;
1138#% print BOLD, MAGENTA, "\r*** ", $_, RESET;
1139 #: X
1140 if ( /^AUDIO: (\d+) Hz, (\d+) ch, \S+ (\d+)\.\d kbit/ ) {
1141 $I{frequency} = $1;
1142 $I{channels} = $2;
1143 $I{bitrate} = $3;
1144 return 0;
1145 }
1146 if ( /^Starting playback/ ) {
1147 my $nf = $CurrentFile;
1148 $nf =~ s/\.(\S+)$//i;
1149 $codec = $1 unless $codec;
1150 $PrintCurrentFile = $codec? "<$codec> $CurrentSize " : $CurrentSize .' ';
1151 $PrintCurrentFile .= (length($PrintCurrentFile)+length($nf)>74)
1152 ? ("...".substr($nf,length($PrintCurrentFile)-72)) : $nf;
1153 print "\r$PrintCurrentFile\n";
1154 my $txt = "$TC -> [$I{duration}] $I{frequency} Hz at $I{bitrate} kbps with $I{channels} channels";
1155 &progress($txt) unless $opt_v;
1156 print GREEN, $txt, RESET, "\n" if $opt_v;
1157 return 0;
1158 }
1159 return if / supported but disabled$/; # unimportant
1160 return if /^Configuration: --prefix/; # useless ffmpeg compilation infos
1161 #} X
1162 return $_;
1163}
1164
1165sub ginstart {
1166 return if $pid;
1167 if ($opt_I) {
1168 system("soundoff");
1169 system("soundon");
1170 system("clear");
1171 }
1172 $R = new FileHandle; $R->autoflush;
1173 $W = new FileHandle; $W->autoflush;
1174 #{ X
1175#% $pid = open3( \*W, \*R, \*R, "$wrapper $has_rxaudio");
1176#% $pid || die "$wrapper $has_rxaudio: $!";
1177#% &ginxpect('ready');
1178 #: X
1179 # -v is needed to receive the EOF!
1180 $pid = open3( \*W, \*R, \*R, $has_mplayer, '-slave', '-idle', '-v', '-fs', '-osdlevel', '3', '-zoom' );
1181 R->blocking(0);
1182 $pid || die "$has_mplayer: $!";
1183 &ginxpect('^MPlayer');
1184 &gin('use_master');
1185 #} X
1186 print STDERR <<X if DEBUG & 64;
1187audio engine running as $pid
1188X
1189 add( \*R, 'r', \&ginread );
1190 $output_open = 1;
1191}
1192sub ginstop {
1193 remove( \*R );
1194 #{ X
1195#% gin('exit');
1196 #: X
1197 gin('quit');
1198 #} X
1199 close R;
1200 undef $pid;
1201 $output_open = 0;
1202}
1203sub ginclose {
1204#% gin('output_drain'); #? X
1205 $output_open = 0;
1206#% gin('output_close'); #? X
1207}
1208sub ginopen {
1209 return if $output_open;
1210 if ($opt_I) {
1211 system("soundoff");
1212 system("soundon");
1213 system("clear");
1214 }
1215#% gin('output_open'); #? X
1216 $output_open = 1;
1217}
1218
1219sub exit { &ginstop, exit(shift); }
1220
1221sub load {
1222 my $songs = $opt_L? 'songs': 'entries';
1223 local *L;
1224 return unless open (L, $playlist);
1225 for ($NS=0; <L>;) {
1226 chomp;
1227 if ($opt_L and not -r $_) {
1228 print "Skipping $_\n";
1229 next;
1230 }
1231 $order[$NS] = $NS;
1232 $file[$NS++] = $_;
1233 }
1234 close L;
1235 print YELLOW, "\rLoaded $NS $songs from $playlist\n\n", RESET
1236 unless $opt_q;
1237}
1238sub save {
1239 my $cs = shift;
1240 local *L;
1241 umask 077;
1242 return unless open (L, ">$playlist");
1243 print YELLOW, "\rSaving playlist into $playlist\n\n", RESET
1244 unless $opt_q;
1245 foreach my $i ($cs+1 .. $NS-1) {
1246 my $f = $file[$order[$i]];
1247 print L $ENV{PWD}, '/' unless $f =~ m!^/!;
1248 print L $f, "\n";
1249 }
1250 close L;
1251}
1252sub edit {
1253 &save(shift);
1254 system "$ENV{EDITOR} $playlist;clear";
1255 &load;
1256 $CS = -1;
1257 print "$PrintCurrentFile\n";
1258}