aboutsummaryrefslogtreecommitdiff
path: root/bin/psycamp
diff options
context:
space:
mode:
authorpsyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet>2016-08-10 15:28:27 +0000
committerpsyc://loupsycedyglgamf.onion/~lynX <ircs://psyced.org/youbroketheinternet>2016-08-10 15:28:27 +0000
commitfe051bf569dd9d9647e5445359b0d0f36fc8fba4 (patch)
tree58c86fe377d02e2e9be6315e960aaa31a6daa431 /bin/psycamp
parentd88f17ce2a1c1290d4f99a5c34b024fa2c4a282c (diff)
downloadperlpsyc-fe051bf569dd9d9647e5445359b0d0f36fc8fba4.tar.gz
perlpsyc-fe051bf569dd9d9647e5445359b0d0f36fc8fba4.zip
psycamp deserves proper perldoc!
Diffstat (limited to 'bin/psycamp')
-rwxr-xr-xbin/psycamp423
1 files changed, 252 insertions, 171 deletions
diff --git a/bin/psycamp b/bin/psycamp
index 821c6b8..2502819 100755
--- a/bin/psycamp
+++ b/bin/psycamp
@@ -3,15 +3,7 @@
3my $TITLE = "PSYC media console 4.0"; 3my $TITLE = "PSYC media console 4.0";
4my $AUTHOR = "by the symbolic lynX\@psycamp.pages.de"; 4my $AUTHOR = "by the symbolic lynX\@psycamp.pages.de";
5 5
6# been around as 'psycmp3' for almost twenty years, but since 2017 it 6# see end of file for end-user-oriented documentation
7# can also play other formats.. so i shalt rename it to 'psycamp'
8#
9# command line front-end to audio engines with PSYC remote control
10#
11# this media player is over a decade old, but it still is my tool of
12# choice. i gave it functions i didn't find in any other.. like,
13# how useful is a media player if you can't easily reorganize or at
14# least delete files you don't want to consume ever again?
15# 7#
16# uses 'mplayer' - the most popular media player on linux with the 8# uses 'mplayer' - the most popular media player on linux with the
17# worst scripting API ever seen in a lifetime. it is documented 9# worst scripting API ever seen in a lifetime. it is documented
@@ -23,16 +15,14 @@ my $AUTHOR = "by the symbolic lynX\@psycamp.pages.de";
23# been using since 1997 or so has a sha256sum of 15# been using since 1997 or so has a sha256sum of
24# ddb096ad42d9b6b543db8a3a6d9b4a9d52943e75e96697dbbadbc779140c498e. 16# ddb096ad42d9b6b543db8a3a6d9b4a9d52943e75e96697dbbadbc779140c498e.
25# although the general public never saw any source codes to it, it is 17# although the general public never saw any source codes to it, it is
26# viable to assume that it didn't ship any backdoors. grab a copy from 18# viable to assume that it's not a trojan. grab a copy from
27# http://mp3.pages.de/files/rxaudio 19# http://mp3.pages.de/files/rxaudio
28# 20#
29# furthermore psycamp requires the Net/PSYC.pm module since it uses its 21# psycamp requires the Net/PSYC.pm module since it uses its event
30# event multiplexing abilities, not just to receive PSYC messages, but 22# multiplexing abilities, not just to receive PSYC messages, but
31# also to handle stdin and engine input in parallel. 23# also to handle stdin and engine input in parallel.
32# 24#
33# you can use the 'psyccmd' script to remote control this script, which 25# things still in "makenoise": marks, volume levels.
34# therefore can act as a music jukebox or media player daemon. also,
35# psycamp can obviously generate 'playing now' notifications.
36 26
37# since perl has no native preprocessor, this code is 27# since perl has no native preprocessor, this code is
38# managed by the 'jaggler' preprocessor. 28# managed by the 'jaggler' preprocessor.
@@ -43,85 +33,19 @@ my $AUTHOR = "by the symbolic lynX\@psycamp.pages.de";
43# 'X' := "rxaudio" - use the old mp3 engine instead of mplayer 33# 'X' := "rxaudio" - use the old mp3 engine instead of mplayer
44# 34#
45# psycamp in 'distribution' default mode 35# psycamp in 'distribution' default mode
46# jaggler -x -c# -j% psycamp 36# jaggler -x -c# -j% -DO psycamp
47# psycamp supporting 'T' and 'O' extras: 37# psycamp supporting 'T' and 'O' extras:
48# jaggler -x -c# -j% -DOT psycamp 38# jaggler -x -c# -j% -DOT psycamp
49 39
50# things still in "makenoise": cdrom-file-caching, des-decoding,
51# marks, volumes, support for non-mp3s.
52#
53# HINTS & HACKS: 40# HINTS & HACKS:
54# in order to play only high quality files from a folder, you can use 41# in order to play only high quality files from a folder, you can use
55# "lm -Lb 193 >/tmp/playlist-$USER.m3u; psycamp" 42# "lm -Lb 193 >/tmp/playlist-$USER.m3u; psycamp"
56# lm is available from http://perl.pages.de. such functionality could 43# lm is available from http://perl.pages.de. such functionality could
57# obviously be integrated into here, allowing us also to remove file 44# obviously be integrated into here, allowing us also to remove file
58# type guessing - but in exchange we slow down the playlist creation 45# type guessing - but that would slow down the playlist creation
59# process if every file were thrown at ffmpeg to discover its media 46# process, if every file were thrown at ffmpeg to discover its media
60# properties. 47# properties.
61 48
62use Term::ANSIColor qw( :constants );
63use Term::ReadKey qw(GetTerminalSize);
64($TWIDTH, $THEIGHT) = GetTerminalSize();
65
66sub help { print BOLD, BLACK, &head, RESET, <<X, &sep('='); }
67
68basics: (q)uit (h)elp
69
70motion: (p)lay (s)top pa(u)se
71 [ j(ump) ] <mm:ss> jump to an absolute point in the song
72 [ (g)oto ] <pos> [<range>] can do smart guessing of range value
73 (for example you can simply type '0' thru '9' to jump to a point in the song)
74
75files: (o)pen <file> immediately load this new song
76 <file> a filename by itself will first fade current song
77 (l)ist [<dir>] simply calls 'ls'
78 (n)ext next file from playlist
79 '?' show a list of the next 9 songs in the queue
80 (w)rite or (e)dit playlist
81 e(x)it exit without updating playlist
82
83volume: (v)olume [0..100] default is maximum volume
84 v+ v- increase or decrease volume a bit
85 (f)ade [<volume> [<psecs>]] psecs: time between volume steps
86 (r)ise [<volume> [<psecs>]] (example: fade 33 0.1)
87
88extra commands for scripting:
89 sleep <time> wait for <time> before executing next command
90
91type (H)elp for organizing commands
92X
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, &sep('='); }
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
119Consider also the -d and -D options which delete files automatically
120after consumption depending on the name of the directory.
121
122type (h)elp for regular commands
123X
124
125# default PSYC address for this service, UDP port 1144 on this host. 49# default PSYC address for this service, UDP port 1144 on this host.
126# can be overridden with -b 50# can be overridden with -b
127# 51#
@@ -131,22 +55,24 @@ $UNI = 'psyc://127.0.0.1:1144d/';
131# remote control commands back to you, even if you bind localhost here. 55# remote control commands back to you, even if you bind localhost here.
132# you may like to have such chat-based remote control, or you may not.. 56# you may like to have such chat-based remote control, or you may not..
133 57
134# volume values, since volume doesn't seem to be linear as it should 58# volume values, since volume doesn't seem to be linear as it should be.
135# at least not my soundcard, check for yourself.. 59# at least not my soundcard, check for yourself..
136# 60#
137@VV = ( 0,2,5,8,11,14,17,20,24,28,33,38,43,49,56,64,72,81,90,100 ); 61@VV = ( 0,2,5,8,11,14,17,20,24,28,33,38,43,49,56,64,72,81,90,100 );
138 62
63# would prefer not to enumerate media formats understood by mplayer,
64# but it is tricky to sort out non-media files when recursively
65# spidering input directories.
66#
139# when running rxaudio, psycamp can only handle .mp3 and .sdj 67# when running rxaudio, psycamp can only handle .mp3 and .sdj
140# "part" and "dl" are the temporary filenames of some download tools 68# "part" and "dl" are the temporary filenames of some download tools
141$FILETYPES = 69$FILETYPES =
142#% "(mp3|sdj|part|dl)"; #? X 70#% "(mp3|sdj|part|dl)"; #? X
143 "(mp\\d|sdj|part|dl|flac|wav|aif|aiff|ogg|m4a|aac|opus|au)"; #? !X 71 "(mp\\d|sdj|part|dl|flac|wav|aif|aiff|ogg|m4a|aac|opus|au)"; #? !X
144# "(sea|mod|gz|lha|Z|lzh|zip|s3m)"; # |med|mmd0)";
145# should also be able to handle .pls
146# 72#
147# would prefer not to enumerate media formats understood by mplayer, 73# 'makenoise' had support for these formats:
148# but it is tricky to sort out non-media files when recursively 74# "(sea|mod|gz|lha|Z|lzh|zip|s3m)"; # |med|mmd0)";
149# spidering input directories. 75# should also be able to handle .pls..?
150 76
151# starting pcm volume. system volume is kept at maximum anyway. 77# starting pcm volume. system volume is kept at maximum anyway.
152#%$VOL = 0; #? X 78#%$VOL = 0; #? X
@@ -189,15 +115,15 @@ sub PATHMATCH () { 12 }
189sub HATEINDEX () { "$ENV{HOME}/.media/TRASH-$ENV{HOST}.ix" } 115sub HATEINDEX () { "$ENV{HOME}/.media/TRASH-$ENV{HOST}.ix" }
190 116
191# default output for video is the main screen, so if you have 117# default output for video is the main screen, so if you have
192# a second monitor, it makes sense to have the console there 118# a second monitor, it makes sense to run psycamp over there
193my $screen = 0; 119my $screen = 0;
194 120
195$tmpdir='/temp';
196$tmpdir='/tmp' unless -d $tmpdir and -w _;
197$tmpdir='.' unless -d $tmpdir and -w _;
198# $tmplock="$tmpdir/.psycamp-copylock";
199$playlist="$tmpdir/playlist-$ENV{USER}.m3u";
200 121
122### BEGINNING OF REGULAR CODE
123
124 require 5.000;
125 use POSIX ":sys_wait_h";
126 use Pod::Usage qw( pod2usage );
201 use Getopt::Std; 127 use Getopt::Std;
202 use Cwd qw(chdir); # maintains PWD in ENV 128 use Cwd qw(chdir); # maintains PWD in ENV
203 use File::Path qw(mkpath); 129 use File::Path qw(mkpath);
@@ -210,12 +136,21 @@ $playlist="$tmpdir/playlist-$ENV{USER}.m3u";
210 use Net::PSYC qw( :event ); 136 use Net::PSYC qw( :event );
211 # would need to extend this to actually use it: 137 # would need to extend this to actually use it:
212 #use Audio::Play::MPlayer; 138 #use Audio::Play::MPlayer;
139 use Term::ANSIColor qw( :constants );
140 use Term::ReadKey qw(GetTerminalSize);
141 ($TWIDTH, $THEIGHT) = GetTerminalSize();
213 142
214 *name = *File::Find::name; # ugly style works 143 *name = *File::Find::name; # ugly style works
215 $scan = 0; 144 $scan = 0;
216 my %I; 145 my %I;
217 my $CDUR = 0; 146 my $CDUR = 0;
218 147
148 $tmpdir='/temp';
149 $tmpdir='/tmp' unless -d $tmpdir and -w _;
150 $tmpdir='.' unless -d $tmpdir and -w _;
151 # $tmplock="$tmpdir/.psycamp-copylock";
152 $playlist="$tmpdir/playlist-$ENV{USER}.m3u";
153
219MAIN: { 154MAIN: {
220 if ($#ARGV >= 0) { 155 if ($#ARGV >= 0) {
221 getopt('bMnsS'); 156 getopt('bMnsS');
@@ -230,34 +165,13 @@ MAIN: {
230 165
231 print "Using playlist: $playlist\n" if $opt_v; 166 print "Using playlist: $playlist\n" if $opt_v;
232 if ($opt_h) { 167 if ($opt_h) {
233 print BOLD, BLACK, &head, RESET, <<X, &sorthelp; 168 print BOLD, BLACK, &head, RESET, "\n";
234 169# Old options removed from SYNOPSYS:
235usage: $0 [<flags>] [-b <uniform>] [-n <nick>] [-s <mode>]
236 [-M <UNI>] [-S <screen>] [<files|dirs>]
237
238 [-b]ind PSYC uniform and accept commands from both PSYC and stdin
239 [-M] sends currently playing title to a monitoring entity via PSYC
240 [-n]ickname to use for monitoring, otherwise '$nick' will be used
241 [-S]creen number to display videos on (default: 0)
242 [-s] provides for several sort options, see below
243flags:
244 [-H] shows an explanation what this tool is good for, try it!
245 [-r]andomize using a smart shuffle algorithm, much better than "-s r"
246 [-m]ono output
247 [-v]erbose: shows some output from rxaudio
248 [-q]uiet: shows close to no output
249 [-c]alculate cumulative duration of selections
250 [-L]oad the tracks in the playlist only if they really exist
251 [-x] will terminate perl and exec xaudio, use only when short on memory
252 [-I]nitialize rxaudio anew for each song, special hack
253 [-d]elete files after playing if the path contains the word '$VOLATILE'.
254 [-D]elete files after playing unless the path contains the word '$KEEP'.
255
256 without arguments psycamp resumes from last run´s playlist.
257X
258# [-S]kip files if the path contains the word '$KEEP', dont play them. 170# [-S]kip files if the path contains the word '$KEEP', dont play them.
259# [-l]ist filenames, sizes and bitrates (for archive documentation) 171# [-l]ist filenames, sizes and bitrates (for archive documentation)
260# ... broken and prolly useless 172# [-m]ono output
173# [-I]nitialize rxaudio anew for each song, special hack
174 pod2usage;
261 exit; 175 exit;
262 } 176 }
263 # initialize randomizer 177 # initialize randomizer
@@ -289,6 +203,7 @@ X
289#% !$has_rxaudio #? X 203#% !$has_rxaudio #? X
290 !$has_mplayer #? !X 204 !$has_mplayer #? !X
291 ) { 205 ) {
206 # this block of text not moved into pod because of $UNI
292 print BOLD, BLACK, &head, RESET, <<X; 207 print BOLD, BLACK, &head, RESET, <<X;
293 208
294This media player brings you a threefold functionality which you may combine 209This media player brings you a threefold functionality which you may combine
@@ -374,7 +289,7 @@ Y
374enter (h) for help 289enter (h) for help
375 290
376X 291X
377 &gin('channels mono') if $opt_m; 292#% &gin('channels mono') if $opt_m; #? X
378 $CS = -1; # global var for current song 293 $CS = -1; # global var for current song
379 294
380 &next(0); 295 &next(0);
@@ -409,21 +324,21 @@ sub head {
409} 324}
410 325
411 #{ X 326 #{ X
412sub timeout { 327#%sub timeout {
413 if (!$paused) { 328#% if (!$paused) {
414 # HACK for rxaudio which sometimes gets enchanted 329#% # HACK for rxaudio which sometimes gets enchanted
415 #y $trick = rand(2)>1 ? 'pause' : 'seek 1 1'; 330#% #y $trick = rand(2)>1 ? 'pause' : 'seek 1 1';
416 #y $trick = ('pause', 'seek 1 1', 'play')[rand(3)]; 331#% #y $trick = ('pause', 'seek 1 1', 'play')[rand(3)];
417 #y $trick = 'seek 1 1'; 332#% #y $trick = 'seek 1 1';
418# print " (kicking rxaudio with '$trick')\n" if DEBUG & 32; 333#%# print " (kicking rxaudio with '$trick')\n" if DEBUG & 32;
419# &gin( $trick ); 334#%# &gin( $trick );
420 print RED, "\n\t\t(kick) ", RESET if DEBUG & 32; 335#% print RED, "\n\t\t(kick) ", RESET if DEBUG & 32;
421#% &gin('seek 1 1'); #? X 336#% &gin('seek 1 1'); #? X
422 &gin('seek 0 1'); #? !X 337#% &gin('seek 0 1'); #? !X
423 &gin('pause'); #? !X 338#% &gin('pause'); #? !X
424 } 339#% }
425 return 3; 340#% return 3;
426} 341#%}
427 #} X 342 #} X
428 343
429sub ginread { 344sub ginread {
@@ -447,13 +362,13 @@ sub ginread {
447 /^EOF code: 1\b/ #? !X 362 /^EOF code: 1\b/ #? !X
448 ) { 363 ) {
449 # &progress(''); 364 # &progress('');
450 if ($deleteLater eq $CurrentFile) { 365 if ($moveLater) {
366 &moveFile($moveLater);
367 $moveLater = undef;
368 } elsif ($deleteLater eq $CurrentFile) {
451 print RED, unlink ($deleteLater) ? 369 print RED, unlink ($deleteLater) ?
452 "\r***" : "\r - ", BOLD, GREEN, "[\n", RESET; 370 "\r***" : "\r - ", BOLD, GREEN, "[\n", RESET;
453 $deleteLater = undef; 371 $deleteLater = undef;
454 } elsif ($moveLater) {
455 &moveFile($moveLater);
456 $moveLater = undef;
457 } elsif ($opt_d && ($Volatile || 372 } elsif ($opt_d && ($Volatile ||
458 $CurrentFile =~ /\b$VOLATILE\b/oi)) { 373 $CurrentFile =~ /\b$VOLATILE\b/oi)) {
459 print RED, unlink ($CurrentFile) ? 374 print RED, unlink ($CurrentFile) ?
@@ -485,6 +400,11 @@ sub ginread {
485 400
486sub stdread { 401sub stdread {
487 $_ = scalar <STDIN>; 402 $_ = scalar <STDIN>;
403 # should be in timeout instead
404 if (waitpid(-1, WNOHANG)) {
405 print BOLD, RED, "\r>>> Media engine has terminated.", RESET, "\n";
406 exit;
407 }
488 &parse( $_ ); 408 &parse( $_ );
489} 409}
490 410
@@ -617,7 +537,7 @@ X
617 $f = '..'.substr($f,length($p)-$TWIDTH+16) if 537 $f = '..'.substr($f,length($p)-$TWIDTH+16) if
618 length($f) > $TWIDTH-14; 538 length($f) > $TWIDTH-14;
619 $any++; 539 $any++;
620 printf "%3d.%8d %s\n", $i, $s, $f; 540 printf "%2d.%9d %s\n", $i, $s, $f;
621 } 541 }
622 print $any ? "\n" : "<no more songs in playlist>\n\n"; 542 print $any ? "\n" : "<no more songs in playlist>\n\n";
623 print &sep('-'); 543 print &sep('-');
@@ -654,6 +574,7 @@ X
654 $_ = $1? 'DD': 'D'; 574 $_ = $1? 'DD': 'D';
655 } 575 }
656 if ( /^(_|DD)\s*$/ ) { 576 if ( /^(_|DD)\s*$/ ) {
577 $moveLater = undef;
657 $deleteLater = $CurrentFile; 578 $deleteLater = $CurrentFile;
658 print BOLD, BLUE, ">> scheduled for removal\n", RESET; 579 print BOLD, BLUE, ">> scheduled for removal\n", RESET;
659 next; 580 next;
@@ -673,6 +594,7 @@ X
673 print BOLD, RED, ">> command $1$r not defined\n", RESET; 594 print BOLD, RED, ">> command $1$r not defined\n", RESET;
674 next; 595 next;
675 } 596 }
597 $deleteLater = undef;
676 my $t = $1 eq 'J' ? 'DEEJAY' : 598 my $t = $1 eq 'J' ? 'DEEJAY' :
677 $1 eq 'U' ? 'USE' : 599 $1 eq 'U' ? 'USE' :
678 $1 eq 'K' ? 'KEEP' : 600 $1 eq 'K' ? 'KEEP' :
@@ -903,31 +825,16 @@ sub which {
903sub sortsongs { 825sub sortsongs {
904 my $style = shift; 826 my $style = shift;
905 return (1 .. $NS) unless $style; 827 return (1 .. $NS) unless $style;
906# lc $style; -- eh? this breaks half of the algorithms
907 my @order; 828 my @order;
908 print STDERR "sorting by_$style 1 .. $NS\n" if DEBUG & 8; 829 print STDERR "sorting by_$style 1 .. $NS\n" if DEBUG & 8;
909 eval "\@order = sort by_$style 1 .. $NS"; 830 eval "\@order = sort by_$style 1 .. $NS";
910 croak <<X, &sorthelp if $@; 831 croak BOLD, RED, <<X, RESET if $@;
911invalid sort option '$style'
912X
913 return @order;
914}
915sub sorthelp { return <<X; }
916 832
917available sort algorithms: 833Invalid sort option '$style'. Look up '$0 -h' again.
918 n(ame) # sorts by file path (directory first)
919 N(ame) # sorts by file ending (reverse of -n)
920 s(ize) # hear silly small sound snippets first
921 S(ize) # hear big epic pieces first
922 m(odification) # hear newest tracks first
923 M(odification) # hear oldest tracks first
924 a(ccessTime) # hear tracks you haven't heard in a long time first
925 A(ccessTime) # hear tracks you recently accessed first
926 cr # gives the order given on commandline a slight shuffle
927 834
928you can append 'r' to each algorithm (as in 'nr' or 'Ar') to apply a slight
929random shuffle of the items while roughly following the sorting principle.
930X 835X
836 return @order;
837}
931 # r(andom) # bad randomizer algorithm (use -r instead) 838 # r(andom) # bad randomizer algorithm (use -r instead)
932# this actually produces VERY pseudo random results, says randal 839# this actually produces VERY pseudo random results, says randal
933# see http://www.perlmonks.org/?node_id=199901 840# see http://www.perlmonks.org/?node_id=199901
@@ -1215,16 +1122,26 @@ sub ginparse {
1215 } 1122 }
1216 if ( /^Starting playback/ ) { 1123 if ( /^Starting playback/ ) {
1217 if ( (!$I{duration} || !$I{bitrate}) and $has_ffmpeg and 1124 if ( (!$I{duration} || !$I{bitrate}) and $has_ffmpeg and
1125 # while mplayer is starting to play the track
1126 # we fetch further information on it...
1218 (my $pid = open3(my $FFW, my $FF, my $FFE, 1127 (my $pid = open3(my $FFW, my $FF, my $FFE,
1219 $has_ffmpeg, '-i', $CurrentFile))) { 1128 $has_ffmpeg, '-i', $CurrentFile))) {
1220 while(<$FF>) { 1129 while(<$FF>) {
1130 # quite ridiculous that we have to also run
1131 # an ffmpeg to obtain track duration which
1132 # mplayer knows but does not show.
1221 if (/\bDuration: (\d\d):(\d\d):(\d\d)\.\d\d.+ bitrate: (\d\d+) kb/ and $1 ne '00:00:00') { 1133 if (/\bDuration: (\d\d):(\d\d):(\d\d)\.\d\d.+ bitrate: (\d\d+) kb/ and $1 ne '00:00:00') {
1222 my $hh = $1; 1134 my $hh = $1;
1223 my $mm = $2; 1135 my $mm = $2;
1224 my $ss = $3; 1136 my $ss = $3;
1225 my $br = $4; 1137 my $br = $4;
1138 # unfortunately the bitrate we got from
1139 # mplayer frequently just uses some frame
1140 # from the track, not producing a median
1141 # value for VBR tracks. amazing how people
1142 # still don't dig VBR after 20 years.
1226 print RED, "\rInconsistent bitrate info: ", RESET, 1143 print RED, "\rInconsistent bitrate info: ", RESET,
1227 "$I{bitrate} vs $br\n" if !$opt_q and $br 1144 "$I{bitrate} vs $br\n" if $opt_v and $br
1228 and $I{bitrate} and abs($br-$I{bitrate}) > 1; 1145 and $I{bitrate} and abs($br-$I{bitrate}) > 1;
1229 $I{bitrate} = $br if $br; 1146 $I{bitrate} = $br if $br;
1230 $I{duration} = $hh eq '00' ? "$mm:$ss" : "$hh:$mm:$ss"; 1147 $I{duration} = $hh eq '00' ? "$mm:$ss" : "$hh:$mm:$ss";
@@ -1233,7 +1150,6 @@ sub ginparse {
1233 break; 1150 break;
1234 } 1151 }
1235 } 1152 }
1236 # no time for waitpid
1237 close $FF; 1153 close $FF;
1238 close $FFW; 1154 close $FFW;
1239 close $FFE; 1155 close $FFE;
@@ -1258,7 +1174,7 @@ sub ginparse {
1258} 1174}
1259 1175
1260sub ginstart { 1176sub ginstart {
1261 return if $pid; 1177 return if $GINPID;
1262 if ($opt_I) { 1178 if ($opt_I) {
1263 system("soundoff"); 1179 system("soundoff");
1264 system("soundon"); 1180 system("soundon");
@@ -1267,21 +1183,21 @@ sub ginstart {
1267 $R = new FileHandle; $R->autoflush; 1183 $R = new FileHandle; $R->autoflush;
1268 $W = new FileHandle; $W->autoflush; 1184 $W = new FileHandle; $W->autoflush;
1269 #{ X 1185 #{ X
1270#% $pid = open3( \*W, \*R, \*R, "$wrapper $has_rxaudio"); 1186#% $GINPID = open3( \*W, \*R, \*R, "$wrapper $has_rxaudio");
1271#% $pid || die "$wrapper $has_rxaudio: $!"; 1187#% $GINPID || die "$wrapper $has_rxaudio: $!";
1272#% &ginxpect('ready'); 1188#% &ginxpect('ready');
1273 #: X 1189 #: X
1274 # -v is needed to receive the EOF! 1190 # -v is needed to receive the EOF!
1275 # '-osdlevel', '3' can be useful 1191 # '-osdlevel', '3' can be useful
1276 # to allow for fading we want to have the PCM channel, 1192 # to allow for fading we want to have the PCM channel,
1277 # this works for ALSA - does it fail otherwise? 1193 # this works for ALSA - does it fail otherwise?
1278 $pid = open3( \*W, \*R, \*R, $has_mplayer, '-slave', '-idle', '-v', '-fs', '-zoom', '-screen', $opt_S || $screen, '-mixer-channel', 'PCM' ); 1194 $GINPID = open3( \*W, \*R, \*R, $has_mplayer, '-slave', '-idle', '-v', '-fs', '-zoom', '-screen', $opt_S || $screen, '-mixer-channel', 'PCM' );
1279 R->blocking(0); 1195 R->blocking(0);
1280 $pid || die "$has_mplayer: $!"; 1196 $GINPID || die "$has_mplayer: $!";
1281 &ginxpect('^MPlayer'); 1197 &ginxpect('^MPlayer');
1282 #} X 1198 #} X
1283 print STDERR <<X if DEBUG & 64; 1199 print STDERR <<X if DEBUG & 64;
1284audio engine running as $pid 1200audio engine running as $GINPID
1285X 1201X
1286 add( \*R, 'r', \&ginread ); 1202 add( \*R, 'r', \&ginread );
1287 $output_open = 1; 1203 $output_open = 1;
@@ -1294,7 +1210,8 @@ sub ginstop {
1294 gin('quit'); 1210 gin('quit');
1295 #} X 1211 #} X
1296 close R; 1212 close R;
1297 undef $pid; 1213 waitpid( $GINPID, 0 );
1214 undef $GINPID;
1298 $output_open = 0; 1215 $output_open = 0;
1299} 1216}
1300sub ginclose { 1217sub ginclose {
@@ -1367,3 +1284,167 @@ sub edit {
1367 &printinfo; 1284 &printinfo;
1368} 1285}
1369 1286
1287sub help { print BOLD, BLACK, &head, RESET, <<X, &sep('='); }
1288
1289basics: (q)uit (h)elp
1290
1291motion: (p)lay (s)top pa(u)se
1292 [ (j)ump ] <mm:ss> jump to an absolute point in the song
1293 [ (g)oto ] <pos> [<range>] can do smart guessing of range value
1294 (for example you can simply type '0' thru '9' to jump to a point in the song)
1295
1296files: (o)pen <file> immediately load this new song
1297 <file> a filename by itself will first fade current song
1298 (l)ist [<dir>] simply calls 'ls'
1299 (n)ext next file from playlist
1300 '?' show a list of the next 9 songs in the queue
1301 (w)rite or (e)dit playlist
1302 e(x)it exit without updating playlist
1303
1304volume: (v)olume [0..100] default is maximum volume
1305 v+ v- increase or decrease volume a bit
1306 (f)ade [<volume> [<psecs>]] psecs: time between volume steps
1307 (r)ise [<volume> [<psecs>]] (example: fade 33 0.1)
1308
1309extra commands for scripting:
1310 sleep <time> wait for <time> before executing next command
1311
1312type (H)elp for organizing commands
1313X
1314
1315 #{ O
1316# when organizing mode has been activated in psycamp, you can reorganize
1317# your media files as follows:
1318sub help2 { print BOLD, BLACK, &head, RESET, <<X, &sep('='); }
1319
1320Whenever media is in a directory like INCOMING, NEW or TODO, it can be
1321moved into a different subdirectory on the same hierarchy level by using
1322the following uppercase commands:
1323
1324 J = send to DEEJAY folder
1325 U = send to USE
1326 K = send to KEEP
1327 X = send to EXPORT
1328 S = send to SECONDARY
1329 R = send to REPERTOIRE
1330
1331If you prefer to execute the command *after* having finished playing it,
1332you can schedule the move for later by doubling the command letter. So
1333you type 'KK' if you want to keep the file after having listened to it.
1334You can change your mind while the track is playing - the last command
1335you schedule is the one that counts.
1336The commands for deleting items are similar:
1337
1338 D = delete the file now
1339 DD = delete the file after having played it
1340 T = delete the file and mark as trash
1341 TT = mark as trash and delete later
1342
1343Consider also the -d and -D options which delete files automatically
1344after consumption depending on the name of the directory.
1345
1346type (h)elp for regular commands
1347X
1348 #: O
1349#%sub help2 { print RED, "\r", <<X, RESET, "\n"; }
1350#%Sorry, organizing functions have been disabled.
1351#%X
1352 #} O
1353
1354__END__
1355
1356=pod
1357
1358=head1 NAME
1359
1360 psycamp - command line media player with PSYC remote control
1361
1362=head1 SYNOPSIS
1363
1364 psycamp [<flags>] [-b <uniform>] [-n <nick>] [-s <sort-algorithm>]
1365 [-M <UNI>] [-S <screen>] [<media-files|directories>]
1366
1367 [-b]ind PSYC uniform and accept commands from both PSYC and stdin
1368 [-M] sends currently playing title to a monitoring entity via PSYC
1369 [-n]ickname to use for monitoring, otherwise a default will be used
1370 [-s]ort playlist according to one of the algorithms explained below
1371 [-S]creen number to display videos on (default: 0)
1372
1373 Flags:
1374 [-H] shows an explanation what this tool is good for, try it!
1375 [-r]andomize using a smart shuffle algorithm, much better than "-s r"
1376 [-v]erbose: shows some extra output
1377 [-q]uiet: shows close to no output
1378 [-c]alculate cumulative duration of selections
1379 [-L]oad the tracks in the playlist only if they really exist
1380 [-x] will terminate perl and exec mplayer, use when short on memory
1381 [-d]elete files after playing if the path contains the word '$VOLATILE'.
1382 [-D]elete files after playing unless the path contains the word '$KEEP'.
1383
1384 Without arguments, psycamp resumes from last run's playlist.
1385
1386=head2 Sort algorithms:
1387
1388 n(ame) # sorts by file path (directory first)
1389 N(ame) # sorts by file ending (reverse of -n)
1390 s(ize) # hear silly small sound snippets first
1391 S(ize) # hear big epic pieces first
1392 m(odification) # hear newest tracks first
1393 M(odification) # hear oldest tracks first
1394 a(ccessTime) # hear tracks you haven't heard in a long time first
1395 A(ccessTime) # hear tracks you recently accessed first
1396 cr # gives the order given on commandline a slight shuffle
1397
1398 You can append 'r' to each algorithm (as in 'nr' or 'Ar') to apply a slight
1399 random shuffle of the items while roughly following the sorting principle.
1400
1401=head1 DESCRIPTION
1402
1403This has been around as 'psycmp3', but since 2017 it can also
1404play other formats.. so I shalt rename it to 'psycamp', the
1405psyc amplifier, or the psyca media player... :)
1406
1407This media player is over a decade old, but it still is my tool of
1408choice. I gave it functions i didn't find in any other.. How
1409useful is a media player that can't easily reorganize or at least
1410delete files you don't want to consume ever again?
1411
1412For further help on how to actually use the beast, type 'h' and
1413ENTER once it is playing some media.
1414
1415=head2 PSYC Remote Control
1416
1417You can use the 'psyccmd' script to remote control psycamp which
1418therefore can act as a music jukebox or media player daemon. You
1419could be generating those PSYC messages from whichever other
1420tool you find appropriate. The format is easy. You may need to
1421use '-b' to bind to an accessible network interface.
1422
1423=head2 PSYC Notification
1424
1425psycamp can obviously generate 'playing now' notifications. Just
1426provide '-M' and '-b' accordingly.
1427
1428=head1 CAVEATS
1429
1430psycamp uses a line mode interface. Each command needs to be
1431submitted to the program by hitting the ENTER key.
1432It's unusual, but not a problem.
1433
1434psycamp ignores so-called ID3. it expects tracks to have meaningful
1435file and directory names instead, since that is where you need the
1436information when shuffling files around.
1437
1438When watching video with psycamp, keystrokes in the video frame
1439will go directly to mplayer, bypassing psycamp. This is fine for
1440several interactions but psycamp gets stuck if you quit mplayer.
1441
1442=head1 AUTHORS
1443
1444carlo von lynX.
1445
1446=head1 COPYRIGHT
1447
1448This program is free software, published under the Affero GNU Public
1449License. A disclaimer isn't necessary in my country, nor do I need
1450to mention the current year to assert a copyright.