cplay

changeset 0:aa5f022eac8a 1.49

Use upstream cplay-1.49 as a start
author markus schnalke <meillo@marmaro.de>
date Wed, 27 Sep 2017 09:22:32 +0200
parents
children 7db0dc9d32d3
files ChangeLog LICENSE Makefile README TODO cplay cplay.1 cplay.list cplayrc lircrc po/Makefile po/da.po po/de.po po/hu.po po/pl.po
diffstat 15 files changed, 4044 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ChangeLog	Wed Sep 27 09:22:32 2017 +0200
     1.3 @@ -0,0 +1,649 @@
     1.4 +2003-12-05  Ulf Betlehem  <flu@iki.fi>
     1.5 +
     1.6 +	*** 1.49 released ***
     1.7 +
     1.8 +	* README, cplay.1: - document restricted mode
     1.9 +
    1.10 +2003-11-08  Ulf Betlehem  <flu@iki.fi>
    1.11 +
    1.12 +	*** 1.49pre4 ***
    1.13 +
    1.14 +	* cplay:
    1.15 +	- restricted mode (suggested by Yoann AUBINEAU)
    1.16 +	- connect player stdin to a pipe
    1.17 +	- removed sleep(1) if player exec failed
    1.18 +	- combined pause/unpause -> toggle_pause
    1.19 +	- no parse_buf() if stopped or seeking
    1.20 +	- removed --no-tty-control from madplay (stdin no longer tty)
    1.21 +	- reduced codesize
    1.22 +
    1.23 +2003-11-06  Ulf Betlehem  <flu@iki.fi>
    1.24 +
    1.25 +	* cplay:
    1.26 +	- use 'm' for bookmarking instead of 'b'
    1.27 +	- minor code clean-up
    1.28 +	- modified help page
    1.29 +
    1.30 +2003-11-02  Ulf Betlehem  <flu@iki.fi>
    1.31 +
    1.32 +	*** 1.49pre3 ***
    1.33 +
    1.34 +	* cplay.list:
    1.35 +	- ESP Package Manager support (http://www.easysw.com/epm/)
    1.36 +
    1.37 +	* cplay:
    1.38 +	- removed excessive update() from get_bookmark()
    1.39 +	- rewritten delete and move commands for speed
    1.40 +
    1.41 +2003-11-01  Ulf Betlehem  <flu@iki.fi>
    1.42 +
    1.43 +	* cplay:
    1.44 +	- move active status support from ListEntry to PlaylistEntry
    1.45 +
    1.46 +2003-10-04  Ulf Betlehem  <flu@iki.fi>
    1.47 +
    1.48 +	* cplayrc:
    1.49 +	- removed execute permissions
    1.50 +
    1.51 +2003-10-01  Ulf Betlehem  <flu@iki.fi>
    1.52 +
    1.53 +	* cplay:
    1.54 +	- possible bugfix for increasing CPU usage
    1.55 +
    1.56 +2003-10-01  Ulf Betlehem  <flu@iki.fi>
    1.57 +
    1.58 +	*** 1.49pre2 ***
    1.59 +	
    1.60 +	* cplay:
    1.61 +	- possible bugfix for increasing CPU usage
    1.62 +
    1.63 +2003-09-28  Ulf Betlehem  <flu@iki.fi>
    1.64 +
    1.65 +	* cplay:
    1.66 +	- use curses.KEY_ENTER for xwsh (wave++)
    1.67 +
    1.68 +2003-09-13  Ulf Betlehem  <flu@iki.fi>
    1.69 +
    1.70 +	*** 1.49pre1 ***
    1.71 +	
    1.72 +	* cplay:
    1.73 +	- support and prefer ossaudiodev (dorphell)
    1.74 +
    1.75 +2003-09-01  Ulf Betlehem  <flu@iki.fi>
    1.76 +
    1.77 +	* cplay:
    1.78 +	- fixed playlist identification for 1.48 (Jean-Nicolas Kuttler)
    1.79 +
    1.80 +2003-08-28  Ulf Betlehem  <flu@iki.fi>
    1.81 +
    1.82 +	* Makefile:
    1.83 +	- cplayrc generation
    1.84 +
    1.85 +	* cplay.1:
    1.86 +	- execute both /etc/cplayrc and ~/.cplayrc
    1.87 +	- ignore /etc/cplayrc if ~/.cplayrc exists
    1.88 +	- speex
    1.89 +	- xmp
    1.90 +
    1.91 +	* README:
    1.92 +	- speex
    1.93 +	- ~/.cplayrc
    1.94 +
    1.95 +2003-08-26  Ulf Betlehem  <flu@iki.fi>
    1.96 +
    1.97 +	*** 1.48 released ***
    1.98 +
    1.99 +	* cplay:
   1.100 +	- xmp regexp (Yuri D'Elia)
   1.101 +	- URL support in mpg123 regexp (Martin Michlmayr)
   1.102 +	- rudimentary /etc/cplayrc support (Toni Timonen)
   1.103 +
   1.104 +	* cplay.1:
   1.105 +	- xmp, play and cplayrc references
   1.106 +
   1.107 +	* cplayrc:
   1.108 +	- new file
   1.109 +
   1.110 +2003-08-20  Ulf Betlehem  <flu@iki.fi>
   1.111 +
   1.112 +	* cplay(1.48pre1):
   1.113 +	- discontinue python1.5 support
   1.114 +	- mixer/volume control using python-oss module
   1.115 +	- horizontal scrolling with < and >
   1.116 +	- show tail (was head) of long input lines
   1.117 +	- import random instead of whrandom
   1.118 +	- minor progress parsing modification
   1.119 +	- NoOffsetPlayer simply counts seconds (Martin Michlmayr)
   1.120 +	- TimeOffsetPlayer with full madplay support
   1.121 +	- added partial xmp and play (sox) support
   1.122 +
   1.123 +2003-08-17  Ulf Betlehem  <flu@iki.fi>
   1.124 +
   1.125 +	* po/hu.po:
   1.126 +	- new file (Gergely Nagy)
   1.127 +	
   1.128 +	* po/pl.po:
   1.129 +	- new file (Perry)
   1.130 +	- fixed help text not showing
   1.131 +
   1.132 +	* po/da.po:
   1.133 +	- new file (Christian Storgaard)
   1.134 +	- specified charset/encoding
   1.135 +
   1.136 +2003-05-13  Ulf Betlehem  <flu@iki.fi>
   1.137 +
   1.138 +	* cplay:
   1.139 +	- display "Adding tagged files" instead of a separate message for each file (Martin Michlmayr)
   1.140 +	- avoid error-messages when interrupting cplay when started via xargs (Moshe Zadka)
   1.141 +
   1.142 +2003-04-13  Ulf Betlehem  <flu@iki.fi>
   1.143 +
   1.144 +	*** 1.47 released ***
   1.145 +
   1.146 +	* README:
   1.147 +	- mp3info and ogginfo modules are both required
   1.148 +
   1.149 +	* TODO: *** empty log message ***
   1.150 +
   1.151 +	* cplay.1:
   1.152 +	- mention help window
   1.153 +	- shell command and positional parameters
   1.154 +	- document control_fifo in FILES section
   1.155 +	- BUGS section
   1.156 +
   1.157 +2003-04-11  Ulf Betlehem  <flu@iki.fi>
   1.158 +
   1.159 +	* cplay(1.47rc4):
   1.160 +	- removed "quit silently" command-line option (use Q instead)
   1.161 +	- fixed missing ": " for isearch-prompt
   1.162 +	- always add absolute paths to playlist (args and stdin)
   1.163 +
   1.164 +2003-04-10  Ulf Betlehem  <flu@iki.fi>
   1.165 +
   1.166 +	* cplay(1.47rc3):
   1.167 +	- uses glob.glob() instead of fnmatch.filter()
   1.168 +
   1.169 +2003-04-08  Ulf Betlehem  <flu@iki.fi>
   1.170 +
   1.171 +	* cplay(1.47rc2):
   1.172 +	- bugfix
   1.173 +
   1.174 +	* cplay(1.47rc1):
   1.175 +	- status and title now use viewpoints (l)
   1.176 +	- hide cursor after shell command
   1.177 +	- help window updates
   1.178 +
   1.179 +2003-04-07  Ulf Betlehem  <flu@iki.fi>
   1.180 +
   1.181 +	* cplay(1.47pre5):
   1.182 +	- '!' shell command with positional args
   1.183 +	- TAB completion
   1.184 +	- kill word/line with C-w/C-u
   1.185 +	- invert tags command 'i'
   1.186 +	- removed hide partial pathnames feature
   1.187 +	- renamed 'X' from [manual] to [stop]
   1.188 +	- bookmarks
   1.189 +	- fixed .. -> ../
   1.190 +	- actually chdir in filelist
   1.191 +	- fixed seek/stop/pause crash
   1.192 +	- minor code cleanup
   1.193 +
   1.194 +2003-03-02  Ulf Betlehem  <flu@iki.fi>
   1.195 +
   1.196 +	* cplay(1.47pre4):
   1.197 +	- X toggles manual/automatic playlist advancement  (Väinö Järvelä)
   1.198 +	- C-s C-s now remembers previous isearch string
   1.199 +	- minor code cleanup here and there
   1.200 +	- absolute seeking with C-a and ^ for bof, C-e and $ for eof
   1.201 +	- HelpWindow includes "Undocumented commands"
   1.202 +	- seeking now yield similar results when stopped and paused
   1.203 +	- fixed byteorder issues with mixer on different architectures?
   1.204 +
   1.205 +2003-02-09  Ulf Betlehem  <flu@iki.fi>
   1.206 +
   1.207 +	* cplay(1.47pre3):
   1.208 +	- The "Quit? (y/N)" prompt no longer requires Enter.
   1.209 +	- The number of dirs to hide can be adjusted with < and > for the
   1.210 +	  pathname viewpoint. However, this might still change.
   1.211 +	- Sorting is now done according to viewpoint, which means that 'S'
   1.212 +	  no longer toggles sorting methods.
   1.213 +	- Minor help window updates.
   1.214 +
   1.215 +2003-01-30  Ulf Betlehem  <flu@iki.fi>
   1.216 +
   1.217 +	* cplay(1.47pre2):
   1.218 +	- command line option to quit silently without confirmation
   1.219 +	- isearch speedup (suggested by Eric Max Francis)
   1.220 +	- viewpoint speedup
   1.221 +
   1.222 +2003-01-25  Ulf Betlehem  <flu@iki.fi>
   1.223 +
   1.224 +	* cplay(1.47pre1):
   1.225 +	- added os.path.exists check to get_tag()
   1.226 +
   1.227 +2002-12-16  Ulf Betlehem  <flu@iki.fi>
   1.228 +
   1.229 +	* lircrc:
   1.230 +	- new file (Pugo)
   1.231 +
   1.232 +	* cplay:
   1.233 +	- documented @ command
   1.234 +	- get_tag improvement (Martin Michlmayr)
   1.235 +
   1.236 +	* cplay.1:
   1.237 +	- combined v and V options into one.
   1.238 +
   1.239 +2002-12-16  Ulf Betlehem  <flu@iki.fi>
   1.240 +
   1.241 +	*** 1.46 released ***
   1.242 +	
   1.243 +	* cplay:
   1.244 +	- documented @ command
   1.245 +	- get_tag improvement (Martin Michlmayr)
   1.246 +
   1.247 +	* cplay.1:
   1.248 +	- combined v and V options into one.
   1.249 +
   1.250 +2002-12-04  Ulf Betlehem  <flu@iki.fi>
   1.251 +
   1.252 +	* cplay (1.46rc1):
   1.253 +	- includes latest version of Martin's get_tag
   1.254 +
   1.255 +2002-11-30  Ulf Betlehem  <flu@iki.fi>
   1.256 +
   1.257 +	* cplay (1.46pre9):
   1.258 +	- alternative metadata support through get_tag (Martin Michlmayr)
   1.259 +	- misc refactoring: TagListWindow, PlaylistEntry, etc.
   1.260 +	- scrollable Help Window
   1.261 +	- fixed keybinding for toggle counter mode
   1.262 +	- new @ command that jumps to the active playlist entry
   1.263 +	- removed V command and option, v toggles MASTER/PCM instead
   1.264 +	- removed custom normpath
   1.265 +
   1.266 +2002-11-08  Ulf Betlehem  <flu@iki.fi>
   1.267 +
   1.268 +	* cplay.1:
   1.269 +	- Use minuses instead of hyphens for command line options. (Martin)
   1.270 +
   1.271 +2002-10-27  Ulf Betlehem  <flu@iki.fi>
   1.272 +
   1.273 +	* cplay (1.46pre8)
   1.274 +	- modified keymap!
   1.275 +	- updated help window
   1.276 +	- filelist tagging support (based on a patch by Jason M. Felice)
   1.277 +	- improved status message behavior
   1.278 +	- added retry if resize failed
   1.279 +	- show cursor in input mode
   1.280 +
   1.281 +2002-10-24  Ulf Betlehem  <flu@iki.fi>
   1.282 +
   1.283 +	* cplay (1.46pre7)
   1.284 +	- a couple of status message changes
   1.285 +	- faster delete when not in random mode
   1.286 +	- rudimentary .pls playlist support
   1.287 +	- improved streaming support
   1.288 +	- advance playlist if player not found
   1.289 +	- changed player priority order
   1.290 +
   1.291 +2002-10-21  Ulf Betlehem  <flu@iki.fi>
   1.292 +
   1.293 +	* cplay (1.46pre6)
   1.294 +	- new and improved random mode (Radu)
   1.295 +
   1.296 +2002-10-20  Ulf Betlehem  <flu@iki.fi>
   1.297 +
   1.298 +	* cplay:
   1.299 +	- refactoring
   1.300 +	- list mode (l = toggle viewpoints)
   1.301 +	- q = quit (y/n) and Q = Quit immediately
   1.302 +	- isearch turnaround change
   1.303 +	- input cursor position
   1.304 +	- recursive search duplicates fix
   1.305 +	- case insensitive regex marking
   1.306 +	- regex marking matches viewpoint
   1.307 +	- VALID_SONG regex matches basename
   1.308 +	- playlist sorting by filename or pathname
   1.309 +	- don't move empty list of marked entries
   1.310 +	- SIGTERM -> SIGINT (again)
   1.311 +	- updated mikmod switches
   1.312 +
   1.313 +2002-10-15  Ulf Betlehem  <flu@iki.fi>
   1.314 +
   1.315 +	* cplay:
   1.316 +	- pad input with space for cursor position
   1.317 +
   1.318 +2002-10-11  Ulf Betlehem  <flu@iki.fi>
   1.319 +
   1.320 +	* cplay (1.46pre5)
   1.321 +	- string.punctuation kludge for python 1.5
   1.322 +	- recursive search in filelist!
   1.323 +        - include 669|mtm|it in mikmod regex (Samium Gromoff)
   1.324 +
   1.325 +2002-08-28  Ulf Betlehem  <flu@iki.fi>
   1.326 +
   1.327 +	* cplay (1.46pre4)
   1.328 +	- bugfix
   1.329 +
   1.330 +2002-08-28  Ulf Betlehem  <flu@iki.fi>
   1.331 +
   1.332 +	* cplay (1.46pre3)
   1.333 +	- LIRC support via control FIFO (Pugo)
   1.334 +
   1.335 +2002-08-21  Ulf Betlehem  <flu@iki.fi>
   1.336 +
   1.337 +	* cplay (1.46pre2)
   1.338 +	- allow printable chars as input
   1.339 +	- alias commands: Q for q and = for +
   1.340 +	- grid bug removed from line number display
   1.341 +	- keep current position after auto filelist updates
   1.342 +	- quiet auto filelist updates (Martin Michlmayr)
   1.343 +	- select child in filelist after a parent command
   1.344 +	- parse player output only once every second
   1.345 +	- PCM/MASTER volume commands show current volume
   1.346 +
   1.347 +	* LICENSE: new file
   1.348 +
   1.349 +2002-03-31  Ulf Betlehem  <flu@iki.fi>
   1.350 +	
   1.351 +	* cplay (1.46pre1)
   1.352 +	- remember playlist filename (Patrice Neff)
   1.353 +	
   1.354 +2002-03-24  Ulf Betlehem  <flu@iki.fi>
   1.355 +	
   1.356 +	*** 1.45 released ***
   1.357 +	
   1.358 +2002-03-19  Ulf Betlehem  <flu@iki.fi>
   1.359 +	
   1.360 +	* cplay (1.45pre5):
   1.361 +	- emulate insstr() for python1.5
   1.362 +	- new commands m/M = move after/before
   1.363 +	- new command D = delete current (Jay Felice)
   1.364 +	- line numbers
   1.365 +	
   1.366 +2002-01-19  Ulf Betlehem  <flu@iki.fi>
   1.367 +
   1.368 +	* cplay (1.45pre4):
   1.369 +	- added options -v and -V to control either PCM or MASTER volume
   1.370 +	- increase and decrease volume in steps of 3% (kludge)
   1.371 +
   1.372 +2002-01-13  Ulf Betlehem  <flu@iki.fi>
   1.373 +
   1.374 +	* cplay (1.45pre3):
   1.375 +	- progressbar cosmetics
   1.376 +	- tilde expansion (Patrice Neff)
   1.377 +	
   1.378 +2001-12-27  Ulf Betlehem  <flu@iki.fi>
   1.379 +
   1.380 +	* cplay (1.45pre2):
   1.381 +	- added "--no-tty-control" option for madplay
   1.382 +	- removed "-d oss" option from ogg123 (Han)
   1.383 +	- use insstr instead of addstr to work around a classical curses-
   1.384 +	  problem with writing the rightmost character without scrolling.
   1.385 +
   1.386 +2001-12-01  Ulf Betlehem  <flu@iki.fi>
   1.387 +
   1.388 +	*** 1.44 released ***
   1.389 +	
   1.390 +	* cplay:
   1.391 +	- partial support for madplay
   1.392 +	- partial support for mikmod (yason)
   1.393 +	- removed sox support - unless someone needs it
   1.394 +	- toggle counter mode: time done / time left
   1.395 +	- seek acceleration based on song length
   1.396 +	- avoid listing dot-files (Martin Michlmayr)
   1.397 +	- remove ".." entry from root (Martin Michlmayr)
   1.398 +	- show playlist upon startup if playing (Patrice Neff)
   1.399 +	- removed TERMIOS warning with recent python versions
   1.400 +	- add directories from command line (Han)
   1.401 +	- fixed x-bug (Chris Liechti)
   1.402 +	- changed write_playlist key from 'o' to 'w'
   1.403 +	- changed goto command key from 'g' to 'o'
   1.404 +	- added 'g' (home) and 'G' (end) keys
   1.405 +	- added '/' and '?' keys for searching
   1.406 +	- misc tweaks
   1.407 +	
   1.408 +	* cplay.1:
   1.409 +	- update
   1.410 +	
   1.411 +	* README:
   1.412 +	- update
   1.413 +	
   1.414 +2001-03-15  Ulf Betlehem  <flu@iki.fi>
   1.415 +
   1.416 +	*** 1.43 released ***
   1.417 +	
   1.418 +	* cplay:
   1.419 +	- partial support for splay
   1.420 +	- commandline arguments: repeat/random (Gerald Stieglbauer)
   1.421 +	- volume fine tuning via +/- (Martin Michlmayr)
   1.422 +	- simplified player framework
   1.423 +	- mark/clear regexp
   1.424 +	
   1.425 +2001-01-18  Ulf Betlehem  <flu@iki.fi>
   1.426 +
   1.427 +	*** 1.42 released ***
   1.428 +	
   1.429 +	* cplay:
   1.430 +	- ignore bogus gettext module
   1.431 +	- correct devfs paths
   1.432 +	- use seconds instead of frames
   1.433 +	- shuffle speedup (Martin Persson)
   1.434 +	- changed player hierarchy
   1.435 +	- improved ogg123 support
   1.436 +
   1.437 +2000-12-08  Ulf Betlehem  <flu@iki.fi>
   1.438 +
   1.439 +	*** 1.41 released ***
   1.440 +
   1.441 +	* README: a few words about mpg123 and streaming
   1.442 +	
   1.443 +	* po/de.po, cplay.1: updated (Martin Michlmayr)
   1.444 +
   1.445 +	* po/Makefile, Makefile: use "install -c" for compatibility
   1.446 +
   1.447 +	* cplay:
   1.448 +	  - autoplay initial playlist
   1.449 +	  - is now a front-end for various audio players
   1.450 +	  - ogg123 support (Martin Michlmayr)
   1.451 +	  - devfs paths (Martin Michlmayr)
   1.452 +	  - playlist url support (Charl P. Botha)
   1.453 +	  - fixed signalling bug
   1.454 +	  - minor code cleanup
   1.455 +
   1.456 +2000-10-19  Ulf Betlehem  <flu@iki.fi>
   1.457 +
   1.458 +	*** v1.40 released ***
   1.459 +
   1.460 +	* README: added instructions on how to change player options
   1.461 +
   1.462 +	* cplay: new versioning scheme
   1.463 +	fixed locale setting
   1.464 +	python 2.0 compatible
   1.465 +	prefers standard gettext to fintl
   1.466 +	delayed warnings for missing players and unknown fileformats
   1.467 +	fixed hline with zero length in progressline
   1.468 +	set title to xterm in cleanup
   1.469 +	better support for mpg123 buffers by signalling progress groups
   1.470 +
   1.471 +	* README: modified usage
   1.472 +
   1.473 +	* Makefile: install man page
   1.474 +
   1.475 +	* cplay.1: man page (Martin Michlmayr)
   1.476 +
   1.477 +	* ChangeLog, TODO: new entry
   1.478 +
   1.479 +	* po/de.po: update (Martin Michlmayr)
   1.480 +
   1.481 +2000-09-06  Ulf Betlehem  <flu@iki.fi>
   1.482 +
   1.483 +	* cplay: Python 1.6 compatible
   1.484 +
   1.485 +2000-08-09  Ulf Betlehem  <flu@iki.fi>
   1.486 +
   1.487 +	* po/de.po: new file
   1.488 +
   1.489 +	* po/Makefile: new Makefile
   1.490 +
   1.491 +	* killpgmodule.c: *** empty log message ***
   1.492 +
   1.493 +	* README: new README
   1.494 +
   1.495 +	* Makefile: new Makefile
   1.496 +
   1.497 +2000-07-31  Ulf Betlehem  <flu@iki.fi>
   1.498 +
   1.499 +	* cplay: added i18n support by Martin Michlmayr
   1.500 +	fixed locale support
   1.501 +
   1.502 +2000-07-25  Ulf Betlehem  <flu@iki.fi>
   1.503 +
   1.504 +	* cplay:
   1.505 +	added support for sox to play .wav, .au and other sound formats
   1.506 +
   1.507 +	* cplay: shows status in titlebar under X -- thanks Chmouel Boudjnah
   1.508 +
   1.509 +2000-05-23  Ulf Betlehem  <flu@iki.fi>
   1.510 +
   1.511 +	* cplay: doesn't stat() cwd when idle
   1.512 +
   1.513 +	* cplay: supports both pyncurses and the old cursesmodule
   1.514 +
   1.515 +2000-04-24  Ulf Betlehem  <flu@iki.fi>
   1.516 +
   1.517 +	* cplay: - restores terminal settings on exceptions
   1.518 +	- global mp3 and m3u regexps
   1.519 +	- new and improved keymap class
   1.520 +	- removed a possible "division by zero" bug
   1.521 +
   1.522 +2000-03-24  Ulf Betlehem  <flu@iki.fi>
   1.523 +
   1.524 +	* cplay: translate evil characters to '?'
   1.525 +
   1.526 +2000-02-07  Ulf Betlehem  <flu@iki.fi>
   1.527 +
   1.528 +	* cplay: fixed a bug in FilelistWindow.add_dir()
   1.529 +
   1.530 +2000-01-20  Ulf Betlehem  <flu@iki.fi>
   1.531 +
   1.532 +	* cplay:
   1.533 +	- changed the player class so that one can hold down 'n' or 'p' when
   1.534 +	  changing tracks without cplay crashing ;)
   1.535 +
   1.536 +2000-01-19  Ulf Betlehem  <flu@iki.fi>
   1.537 +
   1.538 +	* cplay: Enter now plays files bypassing the playlist
   1.539 +	Space adds files to playlist
   1.540 +	a adds recursively
   1.541 +	z toggles pause
   1.542 +	x toggles stop
   1.543 +	m3u lines beginning with '#' are now silently discarded
   1.544 +
   1.545 +1999-12-22  Ulf Betlehem  <flu@iki.fi>
   1.546 +
   1.547 +	* cplay: - lot's of small changes
   1.548 +
   1.549 +1999-12-13  Ulf Betlehem  <flu@iki.fi>
   1.550 +
   1.551 +	* cplay: handles SIGWINCH correctly
   1.552 +	automatically rereads current dir when modified
   1.553 +	lot's of minor changes
   1.554 +
   1.555 +1999-10-26  Ulf Betlehem  <flu@iki.fi>
   1.556 +
   1.557 +	* cplay: Added two commands:
   1.558 +	 R = random play order (keeps your playlist intact)
   1.559 +	 S = sort playlist by filename
   1.560 +
   1.561 +	Removed a seldom used (also undocumented) command:
   1.562 +	 N = previous track
   1.563 +
   1.564 +1999-05-10  Ulf Betlehem  <flu@iki.fi>
   1.565 +
   1.566 +	* cplay: catches os.error if os.listfiles() fails.
   1.567 +
   1.568 +1999-02-13  Ulf Betlehem  <flu@iki.fi>
   1.569 +
   1.570 +	* cplay: Added error-checking to prevent manipulating empty playlists.
   1.571 +	Raised default seek speed from Pi to 4.
   1.572 +
   1.573 +1999-02-07  Ulf Betlehem  <flu@iki.fi>
   1.574 +
   1.575 +	* cplay: Corrected a feature that caused automatic loading of playlists
   1.576 +	upon entering a directory where the cursor was over a .m3u file.
   1.577 +
   1.578 +1999-01-29  Ulf Betlehem  <flu@iki.fi>
   1.579 +
   1.580 +	* cplay:
   1.581 +	Uses frames_done and frames_left instead of time_done and time_left.
   1.582 +	Minor code clean-up.
   1.583 +
   1.584 +	* cplay: Now supports at least mpg123 v0.59o through v0.59q
   1.585 +
   1.586 +1999-01-19  Ulf Betlehem  <flu@iki.fi>
   1.587 +
   1.588 +	* cplay: o  Is now "pure Python", which means there is no need for the
   1.589 +	   killpgmodule.so anymore. Oh, joy!
   1.590 +	o  mpg123 is now automatically located in the PATH if not specified absolutely.
   1.591 +	o  Moved mark() to 'space' and pause_or_unpause() to 'p' and stop_or_unstop()
   1.592 +	   to 'k'.
   1.593 +	o  Playlists are now always saved with the extension .m3u.
   1.594 +
   1.595 +1998-12-11  Ulf Betlehem  <flu@iki.fi>
   1.596 +
   1.597 +	* cplay: now consumes anything written on stdout
   1.598 +
   1.599 +1998-11-29  Ulf Betlehem  <flu@iki.fi>
   1.600 +
   1.601 +	* cplay: select() now only timeouts when necessary.
   1.602 +
   1.603 +1998-11-20  Ulf Betlehem  <flu@iki.fi>
   1.604 +
   1.605 +	* cplay: added PlaylistWindow.command_mark_all()
   1.606 +
   1.607 +1998-11-12  Ulf Betlehem  <flu@iki.fi>
   1.608 +
   1.609 +	* cplay: SIGTERM -> SIGINT
   1.610 +
   1.611 +	* cplay: fixed sigchld bug
   1.612 +	added help window
   1.613 +
   1.614 +1998-11-11  Ulf Betlehem  <flu@iki.fi>
   1.615 +
   1.616 +	* cplay: random -> whrandom
   1.617 +
   1.618 +	* cplay: Too many changes!
   1.619 +	Reorganization
   1.620 +	Change of policy
   1.621 +
   1.622 +1998-10-29  Ulf Betlehem  <flu@iki.fi>
   1.623 +
   1.624 +	* cplay: separated PLAYER and COMMAND
   1.625 +	checks if the PLAYER is valid before it continues
   1.626 +
   1.627 +1998-10-27  Ulf Betlehem  <flu@iki.fi>
   1.628 +
   1.629 +	* cplay: kludged mixed case in curses constants
   1.630 +
   1.631 +1998-10-12  Ulf Betlehem  <flu@iki.fi>
   1.632 +
   1.633 +	* cplay: support for curses module versions with different key-case.
   1.634 +
   1.635 +1998-10-05  Ulf Betlehem  <flu@iki.fi>
   1.636 +
   1.637 +	* cplay: changed progress bar
   1.638 +
   1.639 +	* killpgmodule.c: New file.
   1.640 +
   1.641 +1998-04-29  Ulf Betlehem  <flu@iki.fi>
   1.642 +
   1.643 +	* cplay: remember bufptr of directories
   1.644 +
   1.645 +1998-04-20  Ulf Betlehem  <flu@iki.fi>
   1.646 +
   1.647 +	* cplay: code cleanup
   1.648 +
   1.649 +1998-04-18  Ulf Betlehem  <flu@iki.fi>
   1.650 +
   1.651 +	* cplay: New file.
   1.652 +
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/LICENSE	Wed Sep 27 09:22:32 2017 +0200
     2.3 @@ -0,0 +1,340 @@
     2.4 +		    GNU GENERAL PUBLIC LICENSE
     2.5 +		       Version 2, June 1991
     2.6 +
     2.7 + Copyright (C) 1989, 1991 Free Software Foundation, Inc.
     2.8 +                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     2.9 + Everyone is permitted to copy and distribute verbatim copies
    2.10 + of this license document, but changing it is not allowed.
    2.11 +
    2.12 +			    Preamble
    2.13 +
    2.14 +  The licenses for most software are designed to take away your
    2.15 +freedom to share and change it.  By contrast, the GNU General Public
    2.16 +License is intended to guarantee your freedom to share and change free
    2.17 +software--to make sure the software is free for all its users.  This
    2.18 +General Public License applies to most of the Free Software
    2.19 +Foundation's software and to any other program whose authors commit to
    2.20 +using it.  (Some other Free Software Foundation software is covered by
    2.21 +the GNU Library General Public License instead.)  You can apply it to
    2.22 +your programs, too.
    2.23 +
    2.24 +  When we speak of free software, we are referring to freedom, not
    2.25 +price.  Our General Public Licenses are designed to make sure that you
    2.26 +have the freedom to distribute copies of free software (and charge for
    2.27 +this service if you wish), that you receive source code or can get it
    2.28 +if you want it, that you can change the software or use pieces of it
    2.29 +in new free programs; and that you know you can do these things.
    2.30 +
    2.31 +  To protect your rights, we need to make restrictions that forbid
    2.32 +anyone to deny you these rights or to ask you to surrender the rights.
    2.33 +These restrictions translate to certain responsibilities for you if you
    2.34 +distribute copies of the software, or if you modify it.
    2.35 +
    2.36 +  For example, if you distribute copies of such a program, whether
    2.37 +gratis or for a fee, you must give the recipients all the rights that
    2.38 +you have.  You must make sure that they, too, receive or can get the
    2.39 +source code.  And you must show them these terms so they know their
    2.40 +rights.
    2.41 +
    2.42 +  We protect your rights with two steps: (1) copyright the software, and
    2.43 +(2) offer you this license which gives you legal permission to copy,
    2.44 +distribute and/or modify the software.
    2.45 +
    2.46 +  Also, for each author's protection and ours, we want to make certain
    2.47 +that everyone understands that there is no warranty for this free
    2.48 +software.  If the software is modified by someone else and passed on, we
    2.49 +want its recipients to know that what they have is not the original, so
    2.50 +that any problems introduced by others will not reflect on the original
    2.51 +authors' reputations.
    2.52 +
    2.53 +  Finally, any free program is threatened constantly by software
    2.54 +patents.  We wish to avoid the danger that redistributors of a free
    2.55 +program will individually obtain patent licenses, in effect making the
    2.56 +program proprietary.  To prevent this, we have made it clear that any
    2.57 +patent must be licensed for everyone's free use or not licensed at all.
    2.58 +
    2.59 +  The precise terms and conditions for copying, distribution and
    2.60 +modification follow.
    2.61 +
    2.62 +		    GNU GENERAL PUBLIC LICENSE
    2.63 +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    2.64 +
    2.65 +  0. This License applies to any program or other work which contains
    2.66 +a notice placed by the copyright holder saying it may be distributed
    2.67 +under the terms of this General Public License.  The "Program", below,
    2.68 +refers to any such program or work, and a "work based on the Program"
    2.69 +means either the Program or any derivative work under copyright law:
    2.70 +that is to say, a work containing the Program or a portion of it,
    2.71 +either verbatim or with modifications and/or translated into another
    2.72 +language.  (Hereinafter, translation is included without limitation in
    2.73 +the term "modification".)  Each licensee is addressed as "you".
    2.74 +
    2.75 +Activities other than copying, distribution and modification are not
    2.76 +covered by this License; they are outside its scope.  The act of
    2.77 +running the Program is not restricted, and the output from the Program
    2.78 +is covered only if its contents constitute a work based on the
    2.79 +Program (independent of having been made by running the Program).
    2.80 +Whether that is true depends on what the Program does.
    2.81 +
    2.82 +  1. You may copy and distribute verbatim copies of the Program's
    2.83 +source code as you receive it, in any medium, provided that you
    2.84 +conspicuously and appropriately publish on each copy an appropriate
    2.85 +copyright notice and disclaimer of warranty; keep intact all the
    2.86 +notices that refer to this License and to the absence of any warranty;
    2.87 +and give any other recipients of the Program a copy of this License
    2.88 +along with the Program.
    2.89 +
    2.90 +You may charge a fee for the physical act of transferring a copy, and
    2.91 +you may at your option offer warranty protection in exchange for a fee.
    2.92 +
    2.93 +  2. You may modify your copy or copies of the Program or any portion
    2.94 +of it, thus forming a work based on the Program, and copy and
    2.95 +distribute such modifications or work under the terms of Section 1
    2.96 +above, provided that you also meet all of these conditions:
    2.97 +
    2.98 +    a) You must cause the modified files to carry prominent notices
    2.99 +    stating that you changed the files and the date of any change.
   2.100 +
   2.101 +    b) You must cause any work that you distribute or publish, that in
   2.102 +    whole or in part contains or is derived from the Program or any
   2.103 +    part thereof, to be licensed as a whole at no charge to all third
   2.104 +    parties under the terms of this License.
   2.105 +
   2.106 +    c) If the modified program normally reads commands interactively
   2.107 +    when run, you must cause it, when started running for such
   2.108 +    interactive use in the most ordinary way, to print or display an
   2.109 +    announcement including an appropriate copyright notice and a
   2.110 +    notice that there is no warranty (or else, saying that you provide
   2.111 +    a warranty) and that users may redistribute the program under
   2.112 +    these conditions, and telling the user how to view a copy of this
   2.113 +    License.  (Exception: if the Program itself is interactive but
   2.114 +    does not normally print such an announcement, your work based on
   2.115 +    the Program is not required to print an announcement.)
   2.116 +
   2.117 +These requirements apply to the modified work as a whole.  If
   2.118 +identifiable sections of that work are not derived from the Program,
   2.119 +and can be reasonably considered independent and separate works in
   2.120 +themselves, then this License, and its terms, do not apply to those
   2.121 +sections when you distribute them as separate works.  But when you
   2.122 +distribute the same sections as part of a whole which is a work based
   2.123 +on the Program, the distribution of the whole must be on the terms of
   2.124 +this License, whose permissions for other licensees extend to the
   2.125 +entire whole, and thus to each and every part regardless of who wrote it.
   2.126 +
   2.127 +Thus, it is not the intent of this section to claim rights or contest
   2.128 +your rights to work written entirely by you; rather, the intent is to
   2.129 +exercise the right to control the distribution of derivative or
   2.130 +collective works based on the Program.
   2.131 +
   2.132 +In addition, mere aggregation of another work not based on the Program
   2.133 +with the Program (or with a work based on the Program) on a volume of
   2.134 +a storage or distribution medium does not bring the other work under
   2.135 +the scope of this License.
   2.136 +
   2.137 +  3. You may copy and distribute the Program (or a work based on it,
   2.138 +under Section 2) in object code or executable form under the terms of
   2.139 +Sections 1 and 2 above provided that you also do one of the following:
   2.140 +
   2.141 +    a) Accompany it with the complete corresponding machine-readable
   2.142 +    source code, which must be distributed under the terms of Sections
   2.143 +    1 and 2 above on a medium customarily used for software interchange; or,
   2.144 +
   2.145 +    b) Accompany it with a written offer, valid for at least three
   2.146 +    years, to give any third party, for a charge no more than your
   2.147 +    cost of physically performing source distribution, a complete
   2.148 +    machine-readable copy of the corresponding source code, to be
   2.149 +    distributed under the terms of Sections 1 and 2 above on a medium
   2.150 +    customarily used for software interchange; or,
   2.151 +
   2.152 +    c) Accompany it with the information you received as to the offer
   2.153 +    to distribute corresponding source code.  (This alternative is
   2.154 +    allowed only for noncommercial distribution and only if you
   2.155 +    received the program in object code or executable form with such
   2.156 +    an offer, in accord with Subsection b above.)
   2.157 +
   2.158 +The source code for a work means the preferred form of the work for
   2.159 +making modifications to it.  For an executable work, complete source
   2.160 +code means all the source code for all modules it contains, plus any
   2.161 +associated interface definition files, plus the scripts used to
   2.162 +control compilation and installation of the executable.  However, as a
   2.163 +special exception, the source code distributed need not include
   2.164 +anything that is normally distributed (in either source or binary
   2.165 +form) with the major components (compiler, kernel, and so on) of the
   2.166 +operating system on which the executable runs, unless that component
   2.167 +itself accompanies the executable.
   2.168 +
   2.169 +If distribution of executable or object code is made by offering
   2.170 +access to copy from a designated place, then offering equivalent
   2.171 +access to copy the source code from the same place counts as
   2.172 +distribution of the source code, even though third parties are not
   2.173 +compelled to copy the source along with the object code.
   2.174 +
   2.175 +  4. You may not copy, modify, sublicense, or distribute the Program
   2.176 +except as expressly provided under this License.  Any attempt
   2.177 +otherwise to copy, modify, sublicense or distribute the Program is
   2.178 +void, and will automatically terminate your rights under this License.
   2.179 +However, parties who have received copies, or rights, from you under
   2.180 +this License will not have their licenses terminated so long as such
   2.181 +parties remain in full compliance.
   2.182 +
   2.183 +  5. You are not required to accept this License, since you have not
   2.184 +signed it.  However, nothing else grants you permission to modify or
   2.185 +distribute the Program or its derivative works.  These actions are
   2.186 +prohibited by law if you do not accept this License.  Therefore, by
   2.187 +modifying or distributing the Program (or any work based on the
   2.188 +Program), you indicate your acceptance of this License to do so, and
   2.189 +all its terms and conditions for copying, distributing or modifying
   2.190 +the Program or works based on it.
   2.191 +
   2.192 +  6. Each time you redistribute the Program (or any work based on the
   2.193 +Program), the recipient automatically receives a license from the
   2.194 +original licensor to copy, distribute or modify the Program subject to
   2.195 +these terms and conditions.  You may not impose any further
   2.196 +restrictions on the recipients' exercise of the rights granted herein.
   2.197 +You are not responsible for enforcing compliance by third parties to
   2.198 +this License.
   2.199 +
   2.200 +  7. If, as a consequence of a court judgment or allegation of patent
   2.201 +infringement or for any other reason (not limited to patent issues),
   2.202 +conditions are imposed on you (whether by court order, agreement or
   2.203 +otherwise) that contradict the conditions of this License, they do not
   2.204 +excuse you from the conditions of this License.  If you cannot
   2.205 +distribute so as to satisfy simultaneously your obligations under this
   2.206 +License and any other pertinent obligations, then as a consequence you
   2.207 +may not distribute the Program at all.  For example, if a patent
   2.208 +license would not permit royalty-free redistribution of the Program by
   2.209 +all those who receive copies directly or indirectly through you, then
   2.210 +the only way you could satisfy both it and this License would be to
   2.211 +refrain entirely from distribution of the Program.
   2.212 +
   2.213 +If any portion of this section is held invalid or unenforceable under
   2.214 +any particular circumstance, the balance of the section is intended to
   2.215 +apply and the section as a whole is intended to apply in other
   2.216 +circumstances.
   2.217 +
   2.218 +It is not the purpose of this section to induce you to infringe any
   2.219 +patents or other property right claims or to contest validity of any
   2.220 +such claims; this section has the sole purpose of protecting the
   2.221 +integrity of the free software distribution system, which is
   2.222 +implemented by public license practices.  Many people have made
   2.223 +generous contributions to the wide range of software distributed
   2.224 +through that system in reliance on consistent application of that
   2.225 +system; it is up to the author/donor to decide if he or she is willing
   2.226 +to distribute software through any other system and a licensee cannot
   2.227 +impose that choice.
   2.228 +
   2.229 +This section is intended to make thoroughly clear what is believed to
   2.230 +be a consequence of the rest of this License.
   2.231 +
   2.232 +  8. If the distribution and/or use of the Program is restricted in
   2.233 +certain countries either by patents or by copyrighted interfaces, the
   2.234 +original copyright holder who places the Program under this License
   2.235 +may add an explicit geographical distribution limitation excluding
   2.236 +those countries, so that distribution is permitted only in or among
   2.237 +countries not thus excluded.  In such case, this License incorporates
   2.238 +the limitation as if written in the body of this License.
   2.239 +
   2.240 +  9. The Free Software Foundation may publish revised and/or new versions
   2.241 +of the General Public License from time to time.  Such new versions will
   2.242 +be similar in spirit to the present version, but may differ in detail to
   2.243 +address new problems or concerns.
   2.244 +
   2.245 +Each version is given a distinguishing version number.  If the Program
   2.246 +specifies a version number of this License which applies to it and "any
   2.247 +later version", you have the option of following the terms and conditions
   2.248 +either of that version or of any later version published by the Free
   2.249 +Software Foundation.  If the Program does not specify a version number of
   2.250 +this License, you may choose any version ever published by the Free Software
   2.251 +Foundation.
   2.252 +
   2.253 +  10. If you wish to incorporate parts of the Program into other free
   2.254 +programs whose distribution conditions are different, write to the author
   2.255 +to ask for permission.  For software which is copyrighted by the Free
   2.256 +Software Foundation, write to the Free Software Foundation; we sometimes
   2.257 +make exceptions for this.  Our decision will be guided by the two goals
   2.258 +of preserving the free status of all derivatives of our free software and
   2.259 +of promoting the sharing and reuse of software generally.
   2.260 +
   2.261 +			    NO WARRANTY
   2.262 +
   2.263 +  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
   2.264 +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
   2.265 +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
   2.266 +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
   2.267 +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   2.268 +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
   2.269 +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
   2.270 +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
   2.271 +REPAIR OR CORRECTION.
   2.272 +
   2.273 +  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
   2.274 +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
   2.275 +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
   2.276 +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
   2.277 +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
   2.278 +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
   2.279 +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
   2.280 +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
   2.281 +POSSIBILITY OF SUCH DAMAGES.
   2.282 +
   2.283 +		     END OF TERMS AND CONDITIONS
   2.284 +
   2.285 +	    How to Apply These Terms to Your New Programs
   2.286 +
   2.287 +  If you develop a new program, and you want it to be of the greatest
   2.288 +possible use to the public, the best way to achieve this is to make it
   2.289 +free software which everyone can redistribute and change under these terms.
   2.290 +
   2.291 +  To do so, attach the following notices to the program.  It is safest
   2.292 +to attach them to the start of each source file to most effectively
   2.293 +convey the exclusion of warranty; and each file should have at least
   2.294 +the "copyright" line and a pointer to where the full notice is found.
   2.295 +
   2.296 +    <one line to give the program's name and a brief idea of what it does.>
   2.297 +    Copyright (C) <year>  <name of author>
   2.298 +
   2.299 +    This program is free software; you can redistribute it and/or modify
   2.300 +    it under the terms of the GNU General Public License as published by
   2.301 +    the Free Software Foundation; either version 2 of the License, or
   2.302 +    (at your option) any later version.
   2.303 +
   2.304 +    This program is distributed in the hope that it will be useful,
   2.305 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   2.306 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   2.307 +    GNU General Public License for more details.
   2.308 +
   2.309 +    You should have received a copy of the GNU General Public License
   2.310 +    along with this program; if not, write to the Free Software
   2.311 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   2.312 +
   2.313 +
   2.314 +Also add information on how to contact you by electronic and paper mail.
   2.315 +
   2.316 +If the program is interactive, make it output a short notice like this
   2.317 +when it starts in an interactive mode:
   2.318 +
   2.319 +    Gnomovision version 69, Copyright (C) year name of author
   2.320 +    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
   2.321 +    This is free software, and you are welcome to redistribute it
   2.322 +    under certain conditions; type `show c' for details.
   2.323 +
   2.324 +The hypothetical commands `show w' and `show c' should show the appropriate
   2.325 +parts of the General Public License.  Of course, the commands you use may
   2.326 +be called something other than `show w' and `show c'; they could even be
   2.327 +mouse-clicks or menu items--whatever suits your program.
   2.328 +
   2.329 +You should also get your employer (if you work as a programmer) or your
   2.330 +school, if any, to sign a "copyright disclaimer" for the program, if
   2.331 +necessary.  Here is a sample; alter the names:
   2.332 +
   2.333 +  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
   2.334 +  `Gnomovision' (which makes passes at compilers) written by James Hacker.
   2.335 +
   2.336 +  <signature of Ty Coon>, 1 April 1989
   2.337 +  Ty Coon, President of Vice
   2.338 +
   2.339 +This General Public License does not permit incorporating your program into
   2.340 +proprietary programs.  If your program is a subroutine library, you may
   2.341 +consider it more useful to permit linking proprietary applications with the
   2.342 +library.  If this is what you want to do, use the GNU Library General
   2.343 +Public License instead of this License.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/Makefile	Wed Sep 27 09:22:32 2017 +0200
     3.3 @@ -0,0 +1,21 @@
     3.4 +PREFIX = /usr/local
     3.5 +ENV = PREFIX=$(PREFIX)
     3.6 +
     3.7 +SUBDIRS = po
     3.8 +
     3.9 +all: recursive-all
    3.10 +
    3.11 +install: recursive-install
    3.12 +	install -c -m 755 cplay $(PREFIX)/bin
    3.13 +	install -c -m 644 cplay.1 $(PREFIX)/man/man1
    3.14 +
    3.15 +clean: recursive-clean
    3.16 +
    3.17 +recursive-all recursive-install recursive-clean:
    3.18 +	@target=$@; \
    3.19 +	for i in $(SUBDIRS); do \
    3.20 +		(cd $$i && make $(ENV) $${target#recursive-}); \
    3.21 +	done
    3.22 +
    3.23 +cplayrc: cplay
    3.24 +	awk '/^PLAYERS/ {p=1} /^$$/ {p=0} {if (p==1) {print}}' cplay > cplayrc
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/README	Wed Sep 27 09:22:32 2017 +0200
     4.3 @@ -0,0 +1,59 @@
     4.4 +Description:
     4.5 +
     4.6 +        cplay is a curses front-end for various audio players. It aims
     4.7 +        to provide a power-user-friendly interface with simple filelist
     4.8 +        and playlist control. cplay is written in Python and can use
     4.9 +        either pyncurses or the standard curses module.
    4.10 +
    4.11 +Requirements:
    4.12 +
    4.13 +        - cplay                   http://www.tf.hut.fi/~flu/cplay/
    4.14 +        - python                  http://www.python.org/
    4.15 +        - python-oss  (optional)  http://net.indra.com/~tim/ossmodule/
    4.16 +        - mpg321      (optional)  http://sourceforge.net/projects/mpg321/
    4.17 +        - ogg123      (optional)  http://www.vorbis.com/
    4.18 +        - mpg123      (optional)  http://www.mpg123.org/
    4.19 +        - splay       (optional)  http://splay.sourceforge.net/
    4.20 +        - madplay     (optional)  http://www.mars.org/home/rob/proj/mpeg/
    4.21 +        - mikmod      (optional)  http://www.mikmod.org/
    4.22 +        - fintl       (optional)  http://www.python.org/sigs/i18n-sig/
    4.23 +        - pyncurses   (optional)  http://pyncurses.sourceforge.net/
    4.24 +        - ID3-py      (optional)  http://id3-py.sourceforge.net/
    4.25 +        - pyvorbis    (optional)  http://www.duke.edu/~ahc4/pyogg/
    4.26 +        - xmp         (optional)  http://xmp.sf.net/
    4.27 +        - sox         (optional)  http://sox.sf.net/
    4.28 +        - speex       (optional)  http://www.speex.org/
    4.29 +
    4.30 +Installation:
    4.31 +
    4.32 +        make install
    4.33 +
    4.34 +Usage:
    4.35 +
    4.36 +        cplay [-nrRv] [ file | dir | playlist ] ...
    4.37 +
    4.38 +        When in doubt, press 'h' for a friendly help page.
    4.39 +
    4.40 +Configuration:
    4.41 +
    4.42 +        If you would like to change options passed to the actual players
    4.43 +        just edit the PLAYERS list in the cplay script or put the PLAYERS
    4.44 +        definition in either ~/.cplayrc or /etc/cplayrc. If one of these
    4.45 +        files is available, it is executed by cplay before initialization.
    4.46 +
    4.47 +        Note, currently there is no version control for the rc-file!
    4.48 +
    4.49 +Miscellaneous:
    4.50 +
    4.51 +        In order for either mp3info (ID3) or ogginfo to work,
    4.52 +        both corresponding python modules have to be installed.
    4.53 +        This will probably change someday.
    4.54 +
    4.55 +        A playlist can contain URLs, but the playlist itself will
    4.56 +        have to be local. For mpeg streaming, splay is recommended.
    4.57 +
    4.58 +        It is also possible to pipe a playlist to cplay, as stdin
    4.59 +        will be reopened on startup unless it is attached to a tty.
    4.60 +
    4.61 +        Remote control via /var/tmp/cplay_control; see lircrc.
    4.62 +
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/TODO	Wed Sep 27 09:22:32 2017 +0200
     5.3 @@ -0,0 +1,126 @@
     5.4 +Keymap
     5.5 +------------------------------------------
     5.6 +Filelist: _b_def___________r____w_y_ ABCDEF_HI__LMNOP_RS__VWXYZ
     5.7 +Playlist: ab__ef________o_________y_ ABC_EF_HI__L_NOP_____VW_YZ 
     5.8 +Overload: ____________m_____s_______ __________________________
     5.9 +
    5.10 +TODO
    5.11 +------------------------------------------
    5.12 +x - external mixer framework (MIXERS list?)
    5.13 +x - select player based on magic?
    5.14 +1 - use 00:00:00 instead of 00h 00m 00s ?
    5.15 +x - remove inc_volume and dec_volume when control fifo takes args
    5.16 +4 - support wma (via mplayer?)
    5.17 +3 - support musepack
    5.18 +3 - support midi players
    5.19 +3 - support flac (command line player?)
    5.20 +3 - support xmms?
    5.21 +x - replace current playlist (useful when streaming?)
    5.22 +x - browse bookmarks?
    5.23 +3 - use "file" to both get mime-type and bitrate info?
    5.24 +2 - backspace does not work on gentoo?
    5.25 +x - index-mode: group/sort/search by ctime, filename, metadata, etc.
    5.26 +x - screen title patch
    5.27 +x - tree view patch: expand 2 or more dirs mode for filelist?
    5.28 +4 - don't require both ID3 and pyogg for one of them to work
    5.29 +    - how to enable the metadata viewpoint?
    5.30 +x - get_tag() is messy
    5.31 +5 - don't try to write m3u to search results path
    5.32 +4 - don't write dir/.m3u files for w + enter
    5.33 +x - search results mode vs path
    5.34 +x - shell from playlist? (requires new tab-completion?)
    5.35 +3 - incremental filter command 'f' like *amp 'j'
    5.36 +4 - optionally show all files in a dir
    5.37 +2 - show alternatives in filelist for ambiguous completion?
    5.38 +1 - :commands?
    5.39 +x - macros / bind user commands
    5.40 +3 - center current line (requires one-line scrolling)
    5.41 +x - shell prompt could show number of args? (tagged entries)
    5.42 +5 - change cwd for shell commands (crash if "search results"!?)
    5.43 +x - embed search command in pathname to view in filelist? (bookmark results)
    5.44 +    - /path/name?search=args or /path/name?index=args
    5.45 +    - will allow bookmarks, except for refined searches?
    5.46 +    - don't allow bookmarking in search results (mode)
    5.47 +x - command-line history? (now cursor-up cancels)
    5.48 +x - persistence: save state on exit?
    5.49 +x - replace help window with generic show file window (move help -> file)
    5.50 +x - shuffle & sort tagged entries only?
    5.51 +x - searching for empty string not currently possible with /
    5.52 +x - make isearch use regexes?
    5.53 +x - sanity check: (pause or stopped) and seek => play?
    5.54 +4 - add status message for unbound keys / commands
    5.55 +x - change number of parent dirs to show
    5.56 +    .. makes pathname viewpoint obsolete?
    5.57 +x - search/tag regex/... on str(entry) or entry.vp() ?
    5.58 +    .. currently sorting by vp, tagging by str and searching by str
    5.59 +2 - wait 0.5s between subsequent next/prev. NOT before starting player!
    5.60 +    .. or pause current player immediately on next/prev commands?
    5.61 +    .. compare with repeating enter?
    5.62 +3 - rc-file with
    5.63 +    1. player configs
    5.64 +    2. metadata viewpoint format
    5.65 +    3. pathname viewpoint number of dirs to show (command also?)
    5.66 +    4. persistent bookmarks
    5.67 +4 - enter opens playlists as virtual dir in filelist
    5.68 +5 + restricted mode (bang, write m3u, open path, soft chroot)
    5.69 +4 - pls-playlist TITLE and LENGTH support
    5.70 +3 - programmable delay between songs (useful for synchronized recording)
    5.71 +1 - improve isearch using bookmarks for previous position?
    5.72 +4 - error msg when following broken links (what?)
    5.73 +4 - don't block on slow commands (eg. listdir / add dir / recursive search)
    5.74 +2 - fast Esc (not possible with keypad?)
    5.75 +2 - hide cursor after SUSP & CONT cycle
    5.76 +4 - metadata info page
    5.77 +4 - try small screen layout patch
    5.78 +2 - check against old ogg.and.mp3.info.patch for missing features
    5.79 +3 - use i to toggle *info window?
    5.80 +5 - recursive search should save bufptr of prev dir
    5.81 +3 - clean up app.player.is_stopped() and app.kludge mess
    5.82 +2 - which -> WHICH (upcase global functions?)
    5.83 +3 - howto select player for streaming http://ip:port urls?
    5.84 +
    5.85 +
    5.86 +misc thoughts
    5.87 +------------------------------------------
    5.88 +- use & for delayed commands?
    5.89 +- import on demand wrapper?
    5.90 +- mark or delete duplicate or no-longer existing playlist entries
    5.91 +- locate and jump to (show) current playlist entry in the filelist
    5.92 +- fork and exec /bin/sh and select on pipe for command completion?
    5.93 +- use C as a shortcut for untag/clear all?
    5.94 +- suspend + resize + continue -> should resize
    5.95 +- sort search results
    5.96 +- dynamic help page?
    5.97 +- better input support (readline?)
    5.98 +- toggle header / small screen mode (treshold?)
    5.99 +- randomly play albums (groups / hierarchical playlist)
   5.100 +- delwin before/in/after resize?
   5.101 +- different marks/tags for filelist and playlist?
   5.102 +- dust off id3 patch?
   5.103 +- dust off cddb patch?
   5.104 +- filtered point of view (obsoleted by recursive searching?)
   5.105 +- mark filelist entries already in playlist
   5.106 +- alternative scrolling method
   5.107 +- metadata editor?
   5.108 +- reread dir => search for current entry
   5.109 +- support alsaplayer, xmp, others?
   5.110 +- support cd-players?
   5.111 +- delayed play or mark as next command (play after current is finished)
   5.112 +- use 'N' to mark as next?
   5.113 +- reset progress at end of playlist? (or when changing song?)
   5.114 +- restore xterm title (not possible with aterm, rxvt, etc?)
   5.115 +- mpg123 gives jumpy progress info for vbr files (averaging filter?)
   5.116 +- should there be another way to start playing the playlist
   5.117 +  from the beginning than "next track"? (and Tab+Enter)
   5.118 +- more vi-friendly keymap?
   5.119 +- numerical arguments?
   5.120 +- n and N could correspond to next and previous
   5.121 +- f and b could select next and previous track?
   5.122 +- a/A = Append mp3/dir
   5.123 +- i/I = Insert mp3/dir
   5.124 +- Meta-<, Meta-> commands? (avoid meta)
   5.125 +- show progress-counter while streaming?
   5.126 +- support slang?
   5.127 +- fade in/out mode? (a la repeat/random)
   5.128 +- crossfade mode
   5.129 +- is Enter and cursor position logical / intuitive? (absolutely)
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/cplay	Wed Sep 27 09:22:32 2017 +0200
     6.3 @@ -0,0 +1,1655 @@
     6.4 +#!/usr/bin/env python
     6.5 +# -*- python -*-
     6.6 +
     6.7 +__version__ = "cplay 1.49"
     6.8 +
     6.9 +"""
    6.10 +cplay - A curses front-end for various audio players
    6.11 +Copyright (C) 1998-2003 Ulf Betlehem <flu@iki.fi>
    6.12 +
    6.13 +This program is free software; you can redistribute it and/or
    6.14 +modify it under the terms of the GNU General Public License
    6.15 +as published by the Free Software Foundation; either version 2
    6.16 +of the License, or (at your option) any later version.
    6.17 +
    6.18 +This program is distributed in the hope that it will be useful,
    6.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.21 +GNU General Public License for more details.
    6.22 +
    6.23 +You should have received a copy of the GNU General Public License
    6.24 +along with this program; if not, write to the Free Software
    6.25 +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    6.26 +"""
    6.27 +
    6.28 +# ------------------------------------------
    6.29 +from types import *
    6.30 +
    6.31 +import os
    6.32 +import sys
    6.33 +import time
    6.34 +import getopt
    6.35 +import signal
    6.36 +import string
    6.37 +import select
    6.38 +import re
    6.39 +
    6.40 +try: from ncurses import curses
    6.41 +except ImportError: import curses
    6.42 +
    6.43 +try: import tty
    6.44 +except ImportError: tty = None
    6.45 +
    6.46 +try: import locale; locale.setlocale(locale.LC_ALL, "")
    6.47 +except: pass
    6.48 +
    6.49 +# ------------------------------------------
    6.50 +_locale_domain = "cplay"
    6.51 +_locale_dir = "/usr/local/share/locale"
    6.52 +
    6.53 +try:
    6.54 +    import gettext  # python 2.0
    6.55 +    gettext.install(_locale_domain, _locale_dir)
    6.56 +except ImportError:
    6.57 +    try:
    6.58 +        import fintl
    6.59 +        fintl.bindtextdomain(_locale_domain, _locale_dir)
    6.60 +        fintl.textdomain(_locale_domain)
    6.61 +        _ = fintl.gettext
    6.62 +    except ImportError:
    6.63 +        def _(s): return s
    6.64 +except:
    6.65 +    def _(s): return s
    6.66 +
    6.67 +# ------------------------------------------
    6.68 +XTERM = re.search("rxvt|xterm", os.environ["TERM"])
    6.69 +CONTROL_FIFO = "/var/tmp/cplay_control"
    6.70 +
    6.71 +# ------------------------------------------
    6.72 +def which(program):
    6.73 +    for path in string.split(os.environ["PATH"], ":"):
    6.74 +        if os.path.exists(os.path.join(path, program)):
    6.75 +            return os.path.join(path, program)
    6.76 +
    6.77 +# ------------------------------------------
    6.78 +def cut(s, n, left=0):
    6.79 +    if left: return len(s) > n and "<%s" % s[-n+1:] or s
    6.80 +    else: return len(s) > n and "%s>" % s[:n-1] or s
    6.81 +
    6.82 +# ------------------------------------------
    6.83 +class Stack:
    6.84 +    def __init__(self):
    6.85 +        self.items = ()
    6.86 +
    6.87 +    def push(self, item):
    6.88 +        self.items = (item,) + self.items
    6.89 +
    6.90 +    def pop(self):
    6.91 +        self.items, item = self.items[1:], self.items[0]
    6.92 +        return item
    6.93 +
    6.94 +# ------------------------------------------
    6.95 +class KeymapStack(Stack):
    6.96 +    def process(self, code):
    6.97 +        for keymap in self.items:
    6.98 +            if keymap and keymap.process(code):
    6.99 +                break
   6.100 +
   6.101 +# ------------------------------------------
   6.102 +class Keymap:
   6.103 +    def __init__(self):
   6.104 +        self.methods = [None] * curses.KEY_MAX
   6.105 +
   6.106 +    def bind(self, key, method, args=None):
   6.107 +        if type(key) in (TupleType, ListType):
   6.108 +            for i in key: self.bind(i, method, args)
   6.109 +            return
   6.110 +        if type(key) is StringType:
   6.111 +            key = ord(key)
   6.112 +        self.methods[key] = (method, args)
   6.113 +
   6.114 +    def process(self, key):
   6.115 +        if self.methods[key] is None: return 0
   6.116 +        method, args = self.methods[key]
   6.117 +        if args is None:
   6.118 +            apply(method, (key,))
   6.119 +        else:
   6.120 +            apply(method, args)
   6.121 +        return 1
   6.122 +
   6.123 +# ------------------------------------------
   6.124 +class Window:
   6.125 +    chars = string.letters+string.digits+string.punctuation+string.whitespace
   6.126 +
   6.127 +    t = ['?'] * 256
   6.128 +    for c in chars: t[ord(c)] = c
   6.129 +    translationTable = string.join(t, ""); del t
   6.130 +
   6.131 +    def __init__(self, parent):
   6.132 +        self.parent = parent
   6.133 +        self.children = []
   6.134 +        self.name = None
   6.135 +        self.keymap = None
   6.136 +        self.visible = 1
   6.137 +        self.resize()
   6.138 +        if parent: parent.children.append(self)
   6.139 +
   6.140 +    def insstr(self, s):
   6.141 +        if not s: return
   6.142 +        self.w.addstr(s[:-1])
   6.143 +        self.w.hline(ord(s[-1]), 1)  # insch() work-around
   6.144 +
   6.145 +    def __getattr__(self, name):
   6.146 +        return getattr(self.w, name)
   6.147 +
   6.148 +    def getmaxyx(self):
   6.149 +        y, x = self.w.getmaxyx()
   6.150 +        try: curses.version  # tested with 1.2 and 1.6
   6.151 +        except AttributeError:
   6.152 +            # pyncurses - emulate traditional (silly) behavior
   6.153 +            y, x = y+1, x+1
   6.154 +        return y, x
   6.155 +
   6.156 +    def touchwin(self):
   6.157 +        try: self.w.touchwin()
   6.158 +        except AttributeError: self.touchln(0, self.getmaxyx()[0])
   6.159 +
   6.160 +    def attron(self, attr):
   6.161 +        try: self.w.attron(attr)
   6.162 +        except AttributeError: self.w.attr_on(attr)
   6.163 +
   6.164 +    def attroff(self, attr):
   6.165 +        try: self.w.attroff(attr)
   6.166 +        except AttributeError: self.w.attr_off(attr)
   6.167 +
   6.168 +    def newwin(self):
   6.169 +        return curses.newwin(0, 0, 0, 0)
   6.170 +
   6.171 +    def resize(self):
   6.172 +        self.w = self.newwin()
   6.173 +        self.ypos, self.xpos = self.getbegyx()
   6.174 +        self.rows, self.cols = self.getmaxyx()
   6.175 +        self.keypad(1)
   6.176 +        self.leaveok(0)
   6.177 +        self.scrollok(0)
   6.178 +        for child in self.children:
   6.179 +            child.resize()
   6.180 +
   6.181 +    def update(self):
   6.182 +        self.clear()
   6.183 +        self.refresh()
   6.184 +        for child in self.children:
   6.185 +            child.update()
   6.186 +
   6.187 +# ------------------------------------------
   6.188 +class ProgressWindow(Window):
   6.189 +    def __init__(self, parent):
   6.190 +        Window.__init__(self, parent)
   6.191 +        self.value = 0
   6.192 +
   6.193 +    def newwin(self):
   6.194 +        return curses.newwin(1, self.parent.cols, self.parent.rows-2, 0)
   6.195 +
   6.196 +    def update(self):
   6.197 +        self.move(0, 0)
   6.198 +        self.hline(ord('-'), self.cols)
   6.199 +        if self.value > 0:
   6.200 +            self.move(0, 0)
   6.201 +            x = int(self.value * self.cols)  # 0 to cols-1
   6.202 +            x and self.hline(ord('='), x)
   6.203 +            self.move(0, x)
   6.204 +            self.insstr('|')
   6.205 +        self.touchwin()
   6.206 +        self.refresh()
   6.207 +
   6.208 +    def progress(self, value):
   6.209 +        self.value = min(value, 0.99)
   6.210 +        self.update()
   6.211 +
   6.212 +# ------------------------------------------
   6.213 +class StatusWindow(Window):
   6.214 +    def __init__(self, parent):
   6.215 +        Window.__init__(self, parent)
   6.216 +        self.default_message = ''
   6.217 +        self.current_message = ''
   6.218 +        self.tid = None
   6.219 +
   6.220 +    def newwin(self):
   6.221 +        return curses.newwin(1, self.parent.cols-12, self.parent.rows-1, 0)
   6.222 +
   6.223 +    def update(self):
   6.224 +        msg = string.translate(self.current_message, Window.translationTable)
   6.225 +        self.move(0, 0)
   6.226 +        self.clrtoeol()
   6.227 +        self.insstr(cut(msg, self.cols))
   6.228 +        self.touchwin()
   6.229 +        self.refresh()
   6.230 +
   6.231 +    def status(self, message, duration = 0):
   6.232 +        self.current_message = str(message)
   6.233 +        if self.tid: app.timeout.remove(self.tid)
   6.234 +        if duration: self.tid = app.timeout.add(duration, self.timeout)
   6.235 +        else: self.tid = None
   6.236 +        self.update()
   6.237 +
   6.238 +    def timeout(self):
   6.239 +        self.tid = None
   6.240 +        self.restore_default_status()
   6.241 +
   6.242 +    def set_default_status(self, message):
   6.243 +        if self.current_message == self.default_message: self.status(message)
   6.244 +        self.default_message = message
   6.245 +        XTERM and sys.stderr.write("\033]0;%s\a" % (message or "cplay"))
   6.246 +
   6.247 +    def restore_default_status(self):
   6.248 +        self.status(self.default_message)
   6.249 +
   6.250 +# ------------------------------------------
   6.251 +class CounterWindow(Window):
   6.252 +    def __init__(self, parent):
   6.253 +        Window.__init__(self, parent)
   6.254 +        self.values = [0, 0]
   6.255 +        self.mode = 1
   6.256 +
   6.257 +    def newwin(self):
   6.258 +        return curses.newwin(1, 11, self.parent.rows-1, self.parent.cols-11)
   6.259 +
   6.260 +    def update(self):
   6.261 +        h, s = divmod(self.values[self.mode], 3600)
   6.262 +        m, s = divmod(s, 60)
   6.263 +        self.move(0, 0)
   6.264 +        self.attron(curses.A_BOLD)
   6.265 +        self.insstr("%02dh %02dm %02ds" % (h, m, s))
   6.266 +        self.attroff(curses.A_BOLD)
   6.267 +        self.touchwin()
   6.268 +        self.refresh()
   6.269 +
   6.270 +    def counter(self, values):
   6.271 +        self.values = values
   6.272 +        self.update()
   6.273 +
   6.274 +    def toggle_mode(self):
   6.275 +        self.mode = not self.mode
   6.276 +        tmp = [_("elapsed"), _("remaining")][self.mode]
   6.277 +        app.status(_("Counting %s time") % tmp, 1)
   6.278 +        self.update()
   6.279 +
   6.280 +# ------------------------------------------
   6.281 +class RootWindow(Window):
   6.282 +    def __init__(self, parent):
   6.283 +        Window.__init__(self, parent)
   6.284 +        keymap = Keymap()
   6.285 +        app.keymapstack.push(keymap)
   6.286 +        self.win_progress = ProgressWindow(self)
   6.287 +        self.win_status = StatusWindow(self)
   6.288 +        self.win_counter = CounterWindow(self)
   6.289 +        self.win_tab = TabWindow(self)
   6.290 +        keymap.bind(12, self.update, ()) # C-l
   6.291 +        keymap.bind([curses.KEY_LEFT, 2], app.seek, (-1, 1)) # C-b
   6.292 +        keymap.bind([curses.KEY_RIGHT, 6], app.seek, (1, 1)) # C-f
   6.293 +        keymap.bind([1, '^'], app.seek, (0, 0)) # C-a
   6.294 +        keymap.bind([5, '$'], app.seek, (-1, 0)) # C-e
   6.295 +        keymap.bind(range(48,58), app.key_volume) # 0123456789
   6.296 +        keymap.bind(['+', '='], app.inc_volume, ())
   6.297 +        keymap.bind('-', app.dec_volume, ())
   6.298 +        keymap.bind('n', app.next_song, ())
   6.299 +        keymap.bind('p', app.prev_song, ())
   6.300 +        keymap.bind('z', app.toggle_pause, ())
   6.301 +        keymap.bind('x', app.toggle_stop, ())
   6.302 +        keymap.bind('c', self.win_counter.toggle_mode, ())
   6.303 +        keymap.bind('Q', app.quit, ())
   6.304 +        keymap.bind('q', self.command_quit, ())
   6.305 +        keymap.bind('v', app.mixer, ("toggle",))
   6.306 +
   6.307 +    def command_quit(self):
   6.308 +        app.do_input_hook = self.do_quit
   6.309 +        app.start_input(_("Quit? (y/N)"))
   6.310 +        
   6.311 +    def do_quit(self, ch):
   6.312 +        if chr(ch) == 'y': app.quit()
   6.313 +        app.stop_input()
   6.314 +
   6.315 +# ------------------------------------------
   6.316 +class TabWindow(Window):
   6.317 +    def __init__(self, parent):
   6.318 +        Window.__init__(self, parent)
   6.319 +        self.active_child = 0
   6.320 +
   6.321 +        self.win_filelist = self.add(FilelistWindow)
   6.322 +        self.win_playlist = self.add(PlaylistWindow)
   6.323 +        self.win_help     = self.add(HelpWindow)
   6.324 +
   6.325 +        keymap = Keymap()
   6.326 +        keymap.bind('\t', self.change_window, ()) # tab
   6.327 +        keymap.bind('h', self.help, ())
   6.328 +        app.keymapstack.push(keymap)
   6.329 +        app.keymapstack.push(self.children[self.active_child].keymap)
   6.330 +
   6.331 +    def newwin(self):
   6.332 +        return curses.newwin(self.parent.rows-2, self.parent.cols, 0, 0)
   6.333 +
   6.334 +    def update(self):
   6.335 +        self.update_title()
   6.336 +        self.move(1, 0)
   6.337 +        self.hline(ord('-'), self.cols)
   6.338 +        self.move(2, 0)
   6.339 +        self.clrtobot()
   6.340 +        self.refresh()
   6.341 +        child = self.children[self.active_child]
   6.342 +        child.visible = 1
   6.343 +        child.update()
   6.344 +
   6.345 +    def update_title(self, refresh = 1):
   6.346 +        child = self.children[self.active_child]
   6.347 +        self.move(0, 0)
   6.348 +        self.clrtoeol()
   6.349 +        self.attron(curses.A_BOLD)
   6.350 +        self.insstr(child.get_title())
   6.351 +        self.attroff(curses.A_BOLD)
   6.352 +        if refresh: self.refresh()
   6.353 +
   6.354 +    def add(self, Class):
   6.355 +        win = Class(self)
   6.356 +        win.visible = 0
   6.357 +        return win
   6.358 +
   6.359 +    def change_window(self, window = None):
   6.360 +        app.keymapstack.pop()
   6.361 +        self.children[self.active_child].visible = 0
   6.362 +        if window:
   6.363 +            self.active_child = self.children.index(window)
   6.364 +        else:
   6.365 +            # toggle windows 0 and 1
   6.366 +            self.active_child = not self.active_child
   6.367 +        app.keymapstack.push(self.children[self.active_child].keymap)
   6.368 +        self.update()
   6.369 +
   6.370 +    def help(self):
   6.371 +        if self.children[self.active_child] == self.win_help:
   6.372 +            self.change_window(self.win_last)
   6.373 +        else:
   6.374 +            self.win_last = self.children[self.active_child]
   6.375 +            self.change_window(self.win_help)
   6.376 +            app.status(__version__, 2)
   6.377 +
   6.378 +# ------------------------------------------
   6.379 +class ListWindow(Window):
   6.380 +    def __init__(self, parent):
   6.381 +        Window.__init__(self, parent)
   6.382 +        self.buffer = []
   6.383 +        self.bufptr = self.scrptr = 0
   6.384 +        self.search_direction = 0
   6.385 +        self.last_search = ""
   6.386 +        self.hoffset = 0
   6.387 +        self.keymap = Keymap()
   6.388 +        self.keymap.bind(['k', curses.KEY_UP, 16], self.cursor_move, (-1,))
   6.389 +        self.keymap.bind(['j', curses.KEY_DOWN, 14], self.cursor_move, (1,))
   6.390 +        self.keymap.bind(['K', curses.KEY_PPAGE], self.cursor_ppage, ())
   6.391 +        self.keymap.bind(['J', curses.KEY_NPAGE], self.cursor_npage, ())
   6.392 +        self.keymap.bind(['g', curses.KEY_HOME], self.cursor_home, ())
   6.393 +        self.keymap.bind(['G', curses.KEY_END], self.cursor_end, ())
   6.394 +        self.keymap.bind(['?', 18], self.start_search,
   6.395 +                         (_("backward-isearch"), -1))
   6.396 +        self.keymap.bind(['/', 19], self.start_search,
   6.397 +                         (_("forward-isearch"), 1))
   6.398 +        self.keymap.bind(['>'], self.hscroll, (8,))
   6.399 +        self.keymap.bind(['<'], self.hscroll, (-8,))
   6.400 +
   6.401 +    def newwin(self):
   6.402 +        return curses.newwin(self.parent.rows-2, self.parent.cols,
   6.403 +                             self.parent.ypos+2, self.parent.xpos)
   6.404 +
   6.405 +    def update(self, force = 1):
   6.406 +        self.bufptr = max(0, min(self.bufptr, len(self.buffer) - 1))
   6.407 +        scrptr = (self.bufptr / self.rows) * self.rows
   6.408 +        if force or self.scrptr != scrptr:
   6.409 +            self.scrptr = scrptr
   6.410 +            self.move(0, 0)
   6.411 +            self.clrtobot()
   6.412 +            i = 0
   6.413 +            for entry in self.buffer[self.scrptr:]:
   6.414 +                self.move(i, 0)
   6.415 +                i = i + 1
   6.416 +                self.putstr(entry)
   6.417 +                if self.getyx()[0] == self.rows - 1: break
   6.418 +            if self.visible:
   6.419 +                self.refresh()
   6.420 +                self.parent.update_title()
   6.421 +        self.update_line(curses.A_REVERSE)
   6.422 +
   6.423 +    def update_line(self, attr = None, refresh = 1):
   6.424 +        if not self.buffer: return
   6.425 +        ypos = self.bufptr - self.scrptr
   6.426 +        if attr: self.attron(attr)
   6.427 +        self.move(ypos, 0)
   6.428 +        self.hline(ord(' '), self.cols)
   6.429 +        self.putstr(self.current())
   6.430 +        if attr: self.attroff(attr)
   6.431 +        if self.visible and refresh: self.refresh()
   6.432 +
   6.433 +    def get_title(self, data=""):
   6.434 +        pos = "%s-%s/%s" % (self.scrptr+min(1, len(self.buffer)),
   6.435 +                            min(self.scrptr+self.rows, len(self.buffer)),
   6.436 +                            len(self.buffer))
   6.437 +        width = self.cols-len(pos)-2
   6.438 +        data = cut(data, width-len(self.name), 1)
   6.439 +        return "%-*s  %s" % (width, cut(self.name+data, width), pos)
   6.440 +
   6.441 +    def putstr(self, entry, *pos):
   6.442 +        s = string.translate(str(entry), Window.translationTable)
   6.443 +        pos and apply(self.move, pos)
   6.444 +        if self.hoffset: s = "<%s" % s[self.hoffset+1:]
   6.445 +        self.insstr(cut(s, self.cols))
   6.446 +
   6.447 +    def current(self):
   6.448 +        if self.bufptr >= len(self.buffer): self.bufptr = len(self.buffer) - 1
   6.449 +        return self.buffer[self.bufptr]
   6.450 +
   6.451 +    def cursor_move(self, ydiff):
   6.452 +        if app.input_mode: app.cancel_input()
   6.453 +        if not self.buffer: return
   6.454 +        self.update_line(refresh = 0)
   6.455 +        self.bufptr = (self.bufptr + ydiff) % len(self.buffer)
   6.456 +        self.update(force = 0)
   6.457 +
   6.458 +    def cursor_ppage(self):
   6.459 +        tmp = self.bufptr % self.rows
   6.460 +        if tmp == self.bufptr:
   6.461 +            self.cursor_move(-(tmp+(len(self.buffer) % self.rows) or self.rows))
   6.462 +        else:
   6.463 +            self.cursor_move(-(tmp+self.rows))
   6.464 +
   6.465 +    def cursor_npage(self):
   6.466 +        tmp = self.rows - self.bufptr % self.rows
   6.467 +        if self.bufptr + tmp > len(self.buffer):
   6.468 +            self.cursor_move(len(self.buffer) - self.bufptr)
   6.469 +        else:
   6.470 +            self.cursor_move(tmp)
   6.471 +
   6.472 +    def cursor_home(self): self.cursor_move(-self.bufptr)
   6.473 +
   6.474 +    def cursor_end(self): self.cursor_move(-self.bufptr - 1)
   6.475 +
   6.476 +    def start_search(self, type, direction):
   6.477 +        self.search_direction = direction
   6.478 +        self.not_found = 0
   6.479 +        if app.input_mode:
   6.480 +            app.input_prompt = "%s: " % type
   6.481 +            self.do_search(advance = direction)
   6.482 +        else:
   6.483 +            app.do_input_hook = self.do_search
   6.484 +            app.stop_input_hook = self.stop_search
   6.485 +            app.start_input(type)
   6.486 +
   6.487 +    def stop_search(self):
   6.488 +        self.last_search = app.input_string
   6.489 +        app.status(_("ok"), 1)
   6.490 +
   6.491 +    def do_search(self, ch = None, advance = 0):
   6.492 +        if ch in [8, 127]: app.input_string = app.input_string[:-1]
   6.493 +        elif ch: app.input_string = "%s%c" % (app.input_string, ch)
   6.494 +        else: app.input_string = app.input_string or self.last_search
   6.495 +        index = self.bufptr + advance
   6.496 +        while 1:
   6.497 +            if not 0 <= index < len(self.buffer):
   6.498 +                app.status(_("Not found: %s ") % app.input_string)
   6.499 +                self.not_found = 1
   6.500 +                break
   6.501 +            line = string.lower(str(self.buffer[index]))
   6.502 +            if string.find(line, string.lower(app.input_string)) != -1:
   6.503 +                app.show_input()
   6.504 +                self.update_line(refresh = 0)
   6.505 +                self.bufptr = index
   6.506 +                self.update(force = 0)
   6.507 +                self.not_found = 0
   6.508 +                break
   6.509 +            if self.not_found:
   6.510 +                app.status(_("Not found: %s ") % app.input_string)
   6.511 +                break
   6.512 +            index = index + self.search_direction
   6.513 +
   6.514 +    def hscroll(self, value):
   6.515 +        self.hoffset = max(0, self.hoffset + value)
   6.516 +        self.update()
   6.517 +
   6.518 +# ------------------------------------------
   6.519 +class HelpWindow(ListWindow):
   6.520 +    def __init__(self, parent):
   6.521 +        ListWindow.__init__(self, parent)
   6.522 +        self.name = _("Help")
   6.523 +        self.keymap.bind('q', self.parent.help, ())
   6.524 +        self.buffer = string.split(_("""\
   6.525 +  Global                               t, T  : tag current/regex
   6.526 +  ------                               u, U  : untag current/regex
   6.527 +  Up, Down, k, j, C-p, C-n,            Sp, i : invert current/all
   6.528 +  PgUp, PgDn, K, J,                    !     : shell ($@ = tagged or current)
   6.529 +  Home, End, g, G : movement
   6.530 +  Enter           : chdir or play      Filelist
   6.531 +  Tab             : filelist/playlist  --------
   6.532 +  n, p            : next/prev track    a     : add (tagged) to playlist
   6.533 +  z, x            : toggle pause/stop  s     : recursive search
   6.534 +                                       BS, o : goto parent/specified dir
   6.535 +  Left, Right,                         m, '  : set/get bookmark
   6.536 +  C-f, C-b    : seek forward/backward  
   6.537 +  C-a, C-e    : restart/end track      Playlist
   6.538 +  C-s, C-r, / : isearch                --------
   6.539 +  C-g, Esc    : cancel                 d, D  : delete (tagged) tracks/playlist
   6.540 +  1..9, +, -  : volume control         m, M  : move tagged tracks after/before
   6.541 +  c, v        : counter/volume mode    r, R  : toggle repeat/Random mode
   6.542 +  <, >        : horizontal scrolling   s, S  : shuffle/Sort playlist
   6.543 +  C-l, l      : refresh, list mode     w, @  : write playlist, jump to active
   6.544 +  h, q, Q     : help, quit?, Quit!     X     : stop playlist after each track
   6.545 +"""), "\n")
   6.546 +
   6.547 +# ------------------------------------------
   6.548 +class ListEntry:
   6.549 +    def __init__(self, pathname, dir=0):
   6.550 +        self.filename = os.path.basename(pathname)
   6.551 +        self.pathname = pathname
   6.552 +        self.slash = dir and "/" or ""
   6.553 +        self.tagged = 0
   6.554 +
   6.555 +    def set_tagged(self, value):
   6.556 +        self.tagged = value
   6.557 +
   6.558 +    def is_tagged(self):
   6.559 +        return self.tagged == 1
   6.560 +
   6.561 +    def __str__(self):
   6.562 +        mark = self.is_tagged() and "#" or " "
   6.563 +        return "%s %s%s" % (mark, self.vp(), self.slash)
   6.564 +
   6.565 +    def vp(self):
   6.566 +        return self.vps[0][1](self)
   6.567 +
   6.568 +    def vp_filename(self):
   6.569 +        return self.filename or self.pathname
   6.570 +
   6.571 +    def vp_pathname(self):
   6.572 +        return self.pathname
   6.573 +
   6.574 +    vps = [[_("filename"), vp_filename],
   6.575 +           [_("pathname"), vp_pathname]]
   6.576 +
   6.577 +# ------------------------------------------
   6.578 +class PlaylistEntry(ListEntry):
   6.579 +    def __init__(self, pathname):
   6.580 +        ListEntry.__init__(self, pathname)
   6.581 +        self.metadata = None
   6.582 +        self.active = 0
   6.583 +
   6.584 +    def set_active(self, value):
   6.585 +        self.active = value
   6.586 +
   6.587 +    def is_active(self):
   6.588 +        return self.active == 1
   6.589 +
   6.590 +    def vp_metadata(self):
   6.591 +        return self.metadata or self.read_metadata()
   6.592 +
   6.593 +    def read_metadata(self):
   6.594 +        self.metadata = get_tag(self.pathname)
   6.595 +        return self.metadata
   6.596 +
   6.597 +    vps = ListEntry.vps[:]
   6.598 +
   6.599 +# ------------------------------------------
   6.600 +class TagListWindow(ListWindow):
   6.601 +    def __init__(self, parent):
   6.602 +        ListWindow.__init__(self, parent)
   6.603 +        self.keymap.bind(' ', self.command_tag_untag, ())
   6.604 +        self.keymap.bind('i', self.command_invert_tags, ())
   6.605 +        self.keymap.bind('t', self.command_tag, (1,))
   6.606 +        self.keymap.bind('u', self.command_tag, (0,))
   6.607 +        self.keymap.bind('T', self.command_tag_regexp, (1,))
   6.608 +        self.keymap.bind('U', self.command_tag_regexp, (0,))
   6.609 +        self.keymap.bind('l', self.command_change_viewpoint, ())
   6.610 +
   6.611 +    def command_change_viewpoint(self, klass=ListEntry):
   6.612 +        klass.vps.append(klass.vps.pop(0))
   6.613 +        app.status(_("Listing %s") % klass.vps[0][0], 1)
   6.614 +        app.player.update_status()
   6.615 +        self.update()
   6.616 +
   6.617 +    def command_invert_tags(self):
   6.618 +        for i in self.buffer:
   6.619 +            i.set_tagged(not i.is_tagged())
   6.620 +        self.update()
   6.621 +
   6.622 +    def command_tag_untag(self):
   6.623 +        if not self.buffer: return
   6.624 +        tmp = self.buffer[self.bufptr]
   6.625 +        tmp.set_tagged(not tmp.is_tagged())
   6.626 +        self.cursor_move(1)
   6.627 +
   6.628 +    def command_tag(self, value):
   6.629 +        if not self.buffer: return
   6.630 +        self.buffer[self.bufptr].set_tagged(value)
   6.631 +        self.cursor_move(1)
   6.632 +
   6.633 +    def command_tag_regexp(self, value):
   6.634 +        self.tag_value = value
   6.635 +        app.stop_input_hook = self.stop_tag_regexp
   6.636 +        app.start_input(value and _("Tag regexp") or _("Untag regexp"))
   6.637 +
   6.638 +    def stop_tag_regexp(self):
   6.639 +        try:
   6.640 +            r = re.compile(app.input_string, re.I)
   6.641 +            for entry in self.buffer:
   6.642 +                if r.search(str(entry)):
   6.643 +                    entry.set_tagged(self.tag_value)
   6.644 +            self.update()
   6.645 +            app.status(_("ok"), 1)
   6.646 +        except re.error, e:
   6.647 +            app.status(e, 2)
   6.648 +
   6.649 +    def get_tagged(self):
   6.650 +        return filter(lambda x: x.is_tagged(), self.buffer)
   6.651 +
   6.652 +    def not_tagged(self, l):
   6.653 +        return filter(lambda x: not x.is_tagged(), l)
   6.654 +
   6.655 +# ------------------------------------------
   6.656 +class FilelistWindow(TagListWindow):
   6.657 +    def __init__(self, parent):
   6.658 +        TagListWindow.__init__(self, parent)
   6.659 +        self.oldposition = {}
   6.660 +        try: self.chdir(os.getcwd())
   6.661 +        except OSError: self.chdir(os.environ['HOME'])
   6.662 +        self.startdir = self.cwd
   6.663 +        self.mtime_when = 0
   6.664 +        self.mtime = None
   6.665 +        self.keymap.bind(['\n', curses.KEY_ENTER],
   6.666 +                         self.command_chdir_or_play, ())
   6.667 +        self.keymap.bind(['.', curses.KEY_BACKSPACE],
   6.668 +                         self.command_chparentdir, ())
   6.669 +        self.keymap.bind('a', self.command_add_recursively, ())
   6.670 +        self.keymap.bind('o', self.command_goto, ())
   6.671 +        self.keymap.bind('s', self.command_search_recursively, ())
   6.672 +        self.keymap.bind('m', self.command_set_bookmark, ())
   6.673 +        self.keymap.bind("'", self.command_get_bookmark, ())
   6.674 +        self.keymap.bind('!', self.command_shell, ())
   6.675 +        self.bookmarks = { 39: [self.cwd, 0] }
   6.676 +
   6.677 +    def command_shell(self):
   6.678 +        if app.restricted: return
   6.679 +        app.stop_input_hook = self.stop_shell
   6.680 +        app.complete_input_hook = self.complete_shell
   6.681 +        app.start_input(_("shell$ "), colon=0)
   6.682 +
   6.683 +    def stop_shell(self):
   6.684 +        s = app.input_string
   6.685 +        curses.endwin()
   6.686 +        sys.stderr.write("\n")
   6.687 +        argv = map(lambda x: x.pathname, self.get_tagged() or [self.current()])
   6.688 +        argv = ["/bin/sh", "-c", s, "--"] + argv
   6.689 +        pid = os.fork()
   6.690 +        if pid == 0:
   6.691 +            try: os.execv(argv[0], argv)
   6.692 +            except: os._exit(1)
   6.693 +        pid, r = os.waitpid(pid, 0)
   6.694 +        sys.stderr.write("\nshell returned %s, press return!\n" % r)
   6.695 +        sys.stdin.readline()
   6.696 +        app.win_root.update()
   6.697 +        app.restore_default_status()
   6.698 +        app.cursor(0)
   6.699 +
   6.700 +    def complete_shell(self, line):
   6.701 +        return self.complete_generic(line, quote=1)
   6.702 +
   6.703 +    def complete_generic(self, line, quote=0):
   6.704 +        import glob
   6.705 +        if quote:
   6.706 +            s = re.sub('.*[^\\\\][ \'"()\[\]{}$`]', '', line)
   6.707 +            s, part = re.sub('\\\\', '', s), line[:len(line)-len(s)]
   6.708 +        else:
   6.709 +            s, part = line, ""
   6.710 +        results = glob.glob(os.path.expanduser(s)+"*")
   6.711 +        if len(results) == 0:
   6.712 +            return line
   6.713 +        if len(results) == 1:
   6.714 +            lm = results[0]
   6.715 +            lm = lm + (os.path.isdir(lm) and "/" or "")
   6.716 +        else:
   6.717 +            lm = results[0]
   6.718 +            for result in results:
   6.719 +                for i in range(min(len(result), len(lm))):
   6.720 +                   if result[i] != lm[i]:
   6.721 +                        lm = lm[:i]
   6.722 +                        break
   6.723 +        if quote: lm = re.sub('([ \'"()\[\]{}$`])', '\\\\\\1', lm)
   6.724 +        return part + lm
   6.725 +
   6.726 +    def command_get_bookmark(self):
   6.727 +        app.do_input_hook = self.do_get_bookmark
   6.728 +        app.start_input(_("bookmark"))
   6.729 +
   6.730 +    def do_get_bookmark(self, ch):
   6.731 +        app.input_string = ch
   6.732 +        bookmark = self.bookmarks.get(ch)
   6.733 +        if bookmark:
   6.734 +            self.bookmarks[39] = [self.cwd, self.bufptr]
   6.735 +            dir, pos = bookmark
   6.736 +            self.chdir(dir)
   6.737 +            self.listdir()
   6.738 +            self.bufptr = pos
   6.739 +            self.update()
   6.740 +            app.status(_("ok"), 1)
   6.741 +        else:
   6.742 +            app.status(_("Not found!"), 1)
   6.743 +        app.stop_input()
   6.744 +
   6.745 +    def command_set_bookmark(self):
   6.746 +        app.do_input_hook = self.do_set_bookmark
   6.747 +        app.start_input(_("set bookmark"))
   6.748 +        
   6.749 +    def do_set_bookmark(self, ch):
   6.750 +        app.input_string = ch
   6.751 +        self.bookmarks[ch] = [self.cwd, self.bufptr]
   6.752 +        ch and app.status(_("ok"), 1) or app.stop_input()
   6.753 +
   6.754 +    def command_search_recursively(self):
   6.755 +        app.stop_input_hook = self.stop_search_recursively
   6.756 +        app.start_input(_("search"))
   6.757 +
   6.758 +    def stop_search_recursively(self):
   6.759 +        try: re_tmp = re.compile(app.input_string, re.I)
   6.760 +        except re.error, e:
   6.761 +            app.status(e, 2)
   6.762 +            return
   6.763 +        app.status(_("Searching..."))
   6.764 +        results = []
   6.765 +        for entry in self.buffer:
   6.766 +            if entry.filename == "..":
   6.767 +                continue
   6.768 +            if re_tmp.search(entry.filename):
   6.769 +                results.append(entry)
   6.770 +            elif os.path.isdir(entry.pathname):
   6.771 +                try: self.search_recursively(re_tmp, entry.pathname, results)
   6.772 +                except: pass
   6.773 +        if not self.search_mode:
   6.774 +            self.chdir(os.path.join(self.cwd,_("search results")))
   6.775 +            self.search_mode = 1
   6.776 +        self.buffer = results
   6.777 +        self.bufptr = 0
   6.778 +        self.parent.update_title()
   6.779 +        self.update()
   6.780 +        app.restore_default_status()
   6.781 +
   6.782 +    def search_recursively(self, re_tmp, dir, results):
   6.783 +        for filename in os.listdir(dir):
   6.784 +            pathname = os.path.join(dir, filename)
   6.785 +            if re_tmp.search(filename):
   6.786 +                if os.path.isdir(pathname):
   6.787 +                    results.append(ListEntry(pathname, 1))
   6.788 +                elif VALID_PLAYLIST(filename) or VALID_SONG(filename):
   6.789 +                    results.append(ListEntry(pathname))
   6.790 +            elif os.path.isdir(pathname):
   6.791 +                self.search_recursively(re_tmp, pathname, results)
   6.792 +
   6.793 +    def get_title(self):
   6.794 +        self.name = _("Filelist: ")
   6.795 +        return ListWindow.get_title(self, re.sub("/?$", "/", self.cwd))
   6.796 +
   6.797 +    def listdir_maybe(self, now=0):
   6.798 +        if now < self.mtime_when+2: return
   6.799 +        self.mtime_when = now
   6.800 +        self.oldposition[self.cwd] = self.bufptr
   6.801 +        try: self.mtime == os.stat(self.cwd)[8] or self.listdir(quiet=1)
   6.802 +        except os.error: pass
   6.803 +
   6.804 +    def listdir(self, quiet=0, prevdir=None):
   6.805 +        quiet or app.status(_("Reading directory..."))
   6.806 +        self.search_mode = 0
   6.807 +        dirs = []
   6.808 +        files = []
   6.809 +        try:
   6.810 +            self.mtime = os.stat(self.cwd)[8]
   6.811 +            self.mtime_when = time.time()
   6.812 +            filenames = os.listdir(self.cwd)
   6.813 +            filenames.sort()
   6.814 +            for filename in filenames:
   6.815 +                if filename[0] == ".": continue
   6.816 +                pathname = os.path.join(self.cwd, filename)
   6.817 +                if os.path.isdir(pathname): dirs.append(pathname)
   6.818 +                elif VALID_SONG(filename): files.append(pathname)
   6.819 +                elif VALID_PLAYLIST(filename): files.append(pathname)
   6.820 +        except os.error: pass
   6.821 +        dots = ListEntry(os.path.join(self.cwd, ".."), 1)
   6.822 +        self.buffer = [[dots], []][self.cwd == "/"]
   6.823 +        for i in dirs: self.buffer.append(ListEntry(i, 1))
   6.824 +        for i in files: self.buffer.append(ListEntry(i))
   6.825 +        if prevdir:
   6.826 +            for self.bufptr in range(len(self.buffer)):
   6.827 +                if self.buffer[self.bufptr].filename == prevdir: break
   6.828 +            else: self.bufptr = 0
   6.829 +        elif self.oldposition.has_key(self.cwd):
   6.830 +            self.bufptr = self.oldposition[self.cwd]
   6.831 +        else: self.bufptr = 0
   6.832 +        self.parent.update_title()
   6.833 +        self.update()
   6.834 +        quiet or app.restore_default_status()
   6.835 +
   6.836 +    def chdir(self, dir):
   6.837 +        if hasattr(self, "cwd"): self.oldposition[self.cwd] = self.bufptr
   6.838 +        self.cwd = os.path.normpath(dir)
   6.839 +        try: os.chdir(self.cwd)
   6.840 +        except: pass
   6.841 +
   6.842 +    def command_chdir_or_play(self):
   6.843 +        if not self.buffer: return
   6.844 +        if self.current().filename == "..":
   6.845 +            self.command_chparentdir()
   6.846 +        elif os.path.isdir(self.current().pathname):
   6.847 +            self.chdir(self.current().pathname)
   6.848 +            self.listdir()
   6.849 +        elif VALID_SONG(self.current().filename):
   6.850 +            app.play(self.current())
   6.851 +
   6.852 +    def command_chparentdir(self):
   6.853 +        if app.restricted and self.cwd == self.startdir: return
   6.854 +        dir = os.path.basename(self.cwd)
   6.855 +        self.chdir(os.path.dirname(self.cwd))
   6.856 +        self.listdir(prevdir=dir)
   6.857 +
   6.858 +    def command_goto(self):
   6.859 +        if app.restricted: return
   6.860 +        app.stop_input_hook = self.stop_goto
   6.861 +        app.complete_input_hook = self.complete_generic
   6.862 +        app.start_input(_("goto"))
   6.863 +
   6.864 +    def stop_goto(self):
   6.865 +        dir = os.path.expanduser(app.input_string)
   6.866 +        if dir[0] != '/': dir = os.path.join(self.cwd, dir)
   6.867 +        if not os.path.isdir(dir):
   6.868 +            app.status(_("Not a directory!"), 1)
   6.869 +            return
   6.870 +        self.chdir(dir)
   6.871 +        self.listdir()
   6.872 +
   6.873 +    def command_add_recursively(self):
   6.874 +        l = self.get_tagged()
   6.875 +        if not l:
   6.876 +            app.win_playlist.add(self.current().pathname)
   6.877 +            self.cursor_move(1)
   6.878 +            return
   6.879 +        app.status(_("Adding tagged files"), 1)
   6.880 +        for entry in l:
   6.881 +            app.win_playlist.add(entry.pathname, quiet=1)
   6.882 +            entry.set_tagged(0)
   6.883 +        self.update()
   6.884 +
   6.885 +# ------------------------------------------
   6.886 +class PlaylistWindow(TagListWindow):
   6.887 +    def __init__(self, parent):
   6.888 +        TagListWindow.__init__(self, parent)
   6.889 +        self.pathname = None
   6.890 +        self.repeat = 0
   6.891 +        self.random = 0
   6.892 +        self.random_prev = []
   6.893 +        self.random_next = []
   6.894 +        self.random_left = []
   6.895 +        self.stop = 0
   6.896 +        self.keymap.bind(['\n', curses.KEY_ENTER],
   6.897 +                         self.command_play, ())
   6.898 +        self.keymap.bind('d', self.command_delete, ())
   6.899 +        self.keymap.bind('D', self.command_delete_all, ())
   6.900 +        self.keymap.bind('m', self.command_move, (1,))
   6.901 +        self.keymap.bind('M', self.command_move, (0,))
   6.902 +        self.keymap.bind('s', self.command_shuffle, ())
   6.903 +        self.keymap.bind('S', self.command_sort, ())
   6.904 +        self.keymap.bind('r', self.command_toggle_repeat, ())
   6.905 +        self.keymap.bind('R', self.command_toggle_random, ())
   6.906 +        self.keymap.bind('X', self.command_toggle_stop, ())
   6.907 +        self.keymap.bind('w', self.command_save_playlist, ())
   6.908 +        self.keymap.bind('@', self.command_jump_to_active, ())
   6.909 +
   6.910 +    def command_change_viewpoint(self, klass=PlaylistEntry):
   6.911 +        if not globals().get("ID3"):
   6.912 +            try:
   6.913 +                global ID3, ogg, codecs
   6.914 +                import ID3, ogg.vorbis, codecs
   6.915 +                klass.vps.append([_("metadata"), klass.vp_metadata])
   6.916 +            except ImportError: pass
   6.917 +        TagListWindow.command_change_viewpoint(self, klass)
   6.918 +
   6.919 +    def get_title(self):
   6.920 +        space_out = lambda value, s: value and s or " "*len(s)
   6.921 +        self.name = _("Playlist %s %s %s") % (
   6.922 +            space_out(self.repeat, _("[repeat]")),
   6.923 +            space_out(self.random, _("[random]")),
   6.924 +            space_out(self.stop, _("[stop]")))
   6.925 +        return ListWindow.get_title(self)
   6.926 +
   6.927 +    def append(self, item):
   6.928 +        self.buffer.append(item)
   6.929 +        if self.random: self.random_left.append(item)
   6.930 +
   6.931 +    def add_dir(self, dir):
   6.932 +        filenames = os.listdir(dir)
   6.933 +        filenames.sort()
   6.934 +        subdirs = []
   6.935 +        for filename in filenames:
   6.936 +            pathname = os.path.join(dir, filename)
   6.937 +            if VALID_SONG(filename):
   6.938 +                self.append(PlaylistEntry(pathname))
   6.939 +            if os.path.isdir(pathname):
   6.940 +                subdirs.append(pathname)
   6.941 +        map(self.add_dir, subdirs)
   6.942 +
   6.943 +    def add_m3u(self, line):
   6.944 +        if re.match("^(#.*)?$", line): return
   6.945 +        if re.match("^(/|http://)", line):
   6.946 +            self.append(PlaylistEntry(self.fix_url(line)))
   6.947 +        else:
   6.948 +            dirname = os.path.dirname(self.pathname)
   6.949 +            self.append(PlaylistEntry(os.path.join(dirname, line)))
   6.950 +
   6.951 +    def add_pls(self, line):
   6.952 +        # todo - support title & length
   6.953 +        m = re.match("File(\d+)=(.*)", line)
   6.954 +        if m: self.append(PlaylistEntry(self.fix_url(m.group(2))))
   6.955 +
   6.956 +    def add_playlist(self, pathname):
   6.957 +        self.pathname = pathname
   6.958 +        if re.search("\.m3u$", pathname, re.I): f = self.add_m3u
   6.959 +        if re.search("\.pls$", pathname, re.I): f = self.add_pls
   6.960 +        file = open(pathname)
   6.961 +        map(f, map(string.strip, file.readlines()))
   6.962 +        file.close()
   6.963 +    
   6.964 +    def add(self, pathname, quiet=0):
   6.965 +        try:
   6.966 +            if os.path.isdir(pathname):
   6.967 +                app.status(_("Working..."))
   6.968 +                self.add_dir(pathname)
   6.969 +            elif VALID_PLAYLIST(pathname):
   6.970 +                self.add_playlist(pathname)
   6.971 +            else:
   6.972 +                pathname = self.fix_url(pathname)
   6.973 +                self.append(PlaylistEntry(pathname))
   6.974 +            # todo - refactor
   6.975 +            filename = os.path.basename(pathname) or pathname
   6.976 +            quiet or app.status(_("Added: %s") % filename, 1)
   6.977 +        except Exception, e:
   6.978 +            app.status(e, 2)
   6.979 +
   6.980 +    def fix_url(self, url):
   6.981 +        return re.sub("(http://[^/]+)/?(.*)", "\\1/\\2", url)
   6.982 +
   6.983 +    def putstr(self, entry, *pos):
   6.984 +        if entry.is_active(): self.attron(curses.A_BOLD)
   6.985 +        apply(ListWindow.putstr, (self, entry) + pos)
   6.986 +        if entry.is_active(): self.attroff(curses.A_BOLD)
   6.987 +
   6.988 +    def change_active_entry(self, direction):
   6.989 +        if not self.buffer: return
   6.990 +        old = self.get_active_entry()
   6.991 +        new = None
   6.992 +        if self.random:
   6.993 +            if direction > 0:
   6.994 +                if self.random_next: new = self.random_next.pop()
   6.995 +                elif self.random_left: pass
   6.996 +                elif self.repeat: self.random_left = self.buffer[:]
   6.997 +                else: return
   6.998 +                if not new:
   6.999 +                    import random
  6.1000 +                    new = random.choice(self.random_left)
  6.1001 +                    self.random_left.remove(new)
  6.1002 +                try: self.random_prev.remove(new)
  6.1003 +                except ValueError: pass
  6.1004 +                self.random_prev.append(new)
  6.1005 +            else:
  6.1006 +                if len(self.random_prev) > 1:
  6.1007 +                    self.random_next.append(self.random_prev.pop())
  6.1008 +                    new = self.random_prev[-1]
  6.1009 +                else: return
  6.1010 +            old and old.set_active(0)
  6.1011 +        elif old:
  6.1012 +            index = self.buffer.index(old)+direction
  6.1013 +            if not (0 <= index < len(self.buffer) or self.repeat): return
  6.1014 +            old.set_active(0)
  6.1015 +            new = self.buffer[index % len(self.buffer)]
  6.1016 +        else:
  6.1017 +            new = self.buffer[0]
  6.1018 +        new.set_active(1)
  6.1019 +        self.update()
  6.1020 +        return new
  6.1021 +
  6.1022 +    def get_active_entry(self):
  6.1023 +        for entry in self.buffer:
  6.1024 +            if entry.is_active(): return entry
  6.1025 +
  6.1026 +    def command_jump_to_active(self):
  6.1027 +        entry = self.get_active_entry()
  6.1028 +        if not entry: return
  6.1029 +        self.bufptr = self.buffer.index(entry)
  6.1030 +        self.update()
  6.1031 +
  6.1032 +    def command_play(self):
  6.1033 +        if not self.buffer: return
  6.1034 +        entry = self.get_active_entry()
  6.1035 +        entry and entry.set_active(0)
  6.1036 +        entry = self.current()
  6.1037 +        entry.set_active(1)
  6.1038 +        self.update()
  6.1039 +        app.play(entry)
  6.1040 +
  6.1041 +    def command_delete(self):
  6.1042 +        if not self.buffer: return
  6.1043 +        current_entry, n = self.current(), len(self.buffer)
  6.1044 +        self.buffer = self.not_tagged(self.buffer)
  6.1045 +        if n > len(self.buffer):
  6.1046 +            try: self.bufptr = self.buffer.index(current_entry)
  6.1047 +            except ValueError: pass
  6.1048 +        else:
  6.1049 +            current_entry.set_tagged(1)
  6.1050 +            del self.buffer[self.bufptr]
  6.1051 +        if self.random:
  6.1052 +            self.random_prev = self.not_tagged(self.random_prev)
  6.1053 +            self.random_next = self.not_tagged(self.random_next)
  6.1054 +            self.random_left = self.not_tagged(self.random_left)
  6.1055 +        self.update()
  6.1056 +
  6.1057 +    def command_delete_all(self):
  6.1058 +        self.buffer = []
  6.1059 +        self.random_prev = []
  6.1060 +        self.random_next = []
  6.1061 +        self.random_left = []
  6.1062 +        app.status(_("Deleted playlist"), 1)
  6.1063 +        self.update()
  6.1064 +
  6.1065 +    def command_move(self, after):
  6.1066 +        if not self.buffer: return
  6.1067 +        current_entry, l = self.current(), self.get_tagged()
  6.1068 +        if not l or current_entry.is_tagged(): return
  6.1069 +        self.buffer = self.not_tagged(self.buffer)
  6.1070 +        self.bufptr = self.buffer.index(current_entry)+after
  6.1071 +        self.buffer[self.bufptr:self.bufptr] = l
  6.1072 +        self.update()
  6.1073 +
  6.1074 +    def command_shuffle(self):
  6.1075 +        import random
  6.1076 +        l = []
  6.1077 +        n = len(self.buffer)
  6.1078 +        while n > 0:
  6.1079 +            n = n-1
  6.1080 +            r = random.randint(0, n)
  6.1081 +            l.append(self.buffer[r])
  6.1082 +            del self.buffer[r]
  6.1083 +        self.buffer = l
  6.1084 +        self.bufptr = 0
  6.1085 +        self.update()
  6.1086 +        app.status(_("Shuffled playlist... Oops?"), 1)
  6.1087 +
  6.1088 +    def command_sort(self):
  6.1089 +        app.status(_("Working..."))
  6.1090 +        self.buffer.sort(lambda x, y: x.vp() > y.vp() or -1)
  6.1091 +        self.bufptr = 0
  6.1092 +        self.update()
  6.1093 +        app.status(_("Sorted playlist"), 1)
  6.1094 +
  6.1095 +    def command_toggle_repeat(self):
  6.1096 +        self.toggle("repeat", _("Repeat: %s"))
  6.1097 +
  6.1098 +    def command_toggle_random(self):
  6.1099 +        self.toggle("random", _("Random: %s"))
  6.1100 +        self.random_prev = []
  6.1101 +        self.random_next = []
  6.1102 +        self.random_left = self.buffer[:]
  6.1103 +
  6.1104 +    def command_toggle_stop(self):
  6.1105 +        self.toggle("stop", _("Stop playlist: %s"))
  6.1106 +
  6.1107 +    def toggle(self, attr, format):
  6.1108 +        setattr(self, attr, not getattr(self, attr))
  6.1109 +        app.status(format % (getattr(self, attr) and _("on") or _("off")), 1)
  6.1110 +        self.parent.update_title()
  6.1111 +
  6.1112 +    def command_save_playlist(self):
  6.1113 +        if app.restricted: return
  6.1114 +        default = self.pathname or "%s/" % app.win_filelist.cwd
  6.1115 +        app.stop_input_hook = self.stop_save_playlist
  6.1116 +        app.start_input(_("Save playlist"), default)
  6.1117 +
  6.1118 +    def stop_save_playlist(self):
  6.1119 +        pathname = app.input_string
  6.1120 +        if pathname[0] != '/':
  6.1121 +            pathname = os.path.join(app.win_filelist.cwd, pathname)
  6.1122 +        if not re.search("\.m3u$", pathname, re.I):
  6.1123 +            pathname = "%s%s" % (pathname, ".m3u")
  6.1124 +        try:
  6.1125 +            file = open(pathname, "w")
  6.1126 +            for entry in self.buffer:
  6.1127 +                file.write("%s\n" % entry.pathname)
  6.1128 +            file.close()
  6.1129 +            self.pathname = pathname
  6.1130 +            app.status(_("ok"), 1)
  6.1131 +        except IOError, e:
  6.1132 +            app.status(e, 2)
  6.1133 +
  6.1134 +# ------------------------------------------
  6.1135 +def get_tag(pathname):
  6.1136 +    if re.compile("^http://").match(pathname) or not os.path.exists(pathname):
  6.1137 +        return pathname
  6.1138 +    tags = {}
  6.1139 +    # FIXME: use magic instead of file extensions to identify OGGs and MP3s
  6.1140 +    if re.compile(".*\.ogg$", re.I).match(pathname):
  6.1141 +        try:
  6.1142 +            vf = ogg.vorbis.VorbisFile(pathname)
  6.1143 +            vc = vf.comment()
  6.1144 +            tags = vc.as_dict()
  6.1145 +        except NameError: pass
  6.1146 +        except (IOError, UnicodeError): return os.path.basename(pathname)
  6.1147 +    elif re.compile(".*\.mp3$", re.I).match(pathname):
  6.1148 +        try:
  6.1149 +            vc = ID3.ID3(pathname, as_tuple=1)
  6.1150 +            tags = vc.as_dict()
  6.1151 +        except NameError: pass
  6.1152 +        except (IOError, ID3.InvalidTagError): return os.path.basename(pathname)
  6.1153 +    else:
  6.1154 +        return os.path.basename(pathname)
  6.1155 +
  6.1156 +    artist = tags.get("ARTIST", [""])[0]
  6.1157 +    title = tags.get("TITLE", [""])[0]
  6.1158 +    tag = os.path.basename(pathname)
  6.1159 +    try:
  6.1160 +        if artist and title:
  6.1161 +            tag = codecs.latin_1_encode(artist)[0] + " - " + codecs.latin_1_encode(title)[0]
  6.1162 +        elif artist:
  6.1163 +            tag = artist
  6.1164 +        elif title:
  6.1165 +            tag = title
  6.1166 +        return codecs.latin_1_encode(tag)[0]
  6.1167 +    except (NameError, UnicodeError): return tag
  6.1168 +
  6.1169 +# ------------------------------------------
  6.1170 +class Player:
  6.1171 +    def __init__(self, commandline, files, fps=1):
  6.1172 +        self.commandline = commandline
  6.1173 +        self.re_files = re.compile(files, re.I)
  6.1174 +        self.fps = fps
  6.1175 +        self.stdin_r, self.stdin_w = os.pipe()
  6.1176 +        self.stdout_r, self.stdout_w = os.pipe()
  6.1177 +        self.stderr_r, self.stderr_w = os.pipe()
  6.1178 +        self.entry = None
  6.1179 +        self.stopped = 0
  6.1180 +        self.paused = 0
  6.1181 +        self.time_setup = None
  6.1182 +        self.buf = ''
  6.1183 +        self.tid = None
  6.1184 +
  6.1185 +    def setup(self, entry, offset):
  6.1186 +        self.argv = string.split(self.commandline)
  6.1187 +        self.argv[0] = which(self.argv[0])
  6.1188 +        for i in range(len(self.argv)):
  6.1189 +            if self.argv[i] == "%s": self.argv[i] = entry.pathname
  6.1190 +            if self.argv[i] == "%d": self.argv[i] = str(offset*self.fps)
  6.1191 +        self.entry = entry
  6.1192 +        if offset == 0:
  6.1193 +            app.progress(0)
  6.1194 +            self.offset = 0
  6.1195 +            self.length = 0
  6.1196 +            self.values = [0, 0]
  6.1197 +        self.time_setup = time.time()
  6.1198 +        return self.argv[0]
  6.1199 +
  6.1200 +    def play(self):
  6.1201 +        self.pid = os.fork()
  6.1202 +        if self.pid == 0:
  6.1203 +            os.dup2(self.stdin_w, sys.stdin.fileno())
  6.1204 +            os.dup2(self.stdout_w, sys.stdout.fileno())
  6.1205 +            os.dup2(self.stderr_w, sys.stderr.fileno())
  6.1206 +            os.setpgrp()
  6.1207 +            try: os.execv(self.argv[0], self.argv)
  6.1208 +            except: os._exit(1)
  6.1209 +        self.stopped = 0
  6.1210 +        self.paused = 0
  6.1211 +        self.step = 0
  6.1212 +        self.update_status()
  6.1213 +
  6.1214 +    def stop(self, quiet=0):
  6.1215 +        self.paused and self.toggle_pause(quiet)
  6.1216 +        try:
  6.1217 +            while 1:
  6.1218 +                try: os.kill(-self.pid, signal.SIGINT)
  6.1219 +                except os.error: pass
  6.1220 +                os.waitpid(self.pid, os.WNOHANG)
  6.1221 +        except Exception: pass
  6.1222 +        self.stopped = 1
  6.1223 +        quiet or self.update_status()
  6.1224 +
  6.1225 +    def toggle_pause(self, quiet=0):
  6.1226 +        try: os.kill(-self.pid, [signal.SIGSTOP, signal.SIGCONT][self.paused])
  6.1227 +        except os.error: return
  6.1228 +        self.paused = not self.paused
  6.1229 +        quiet or self.update_status()
  6.1230 +
  6.1231 +    def parse_progress(self):
  6.1232 +        if self.stopped or self.step: self.tid = None
  6.1233 +        else:
  6.1234 +            self.parse_buf()
  6.1235 +            self.tid = app.timeout.add(1.0, self.parse_progress)
  6.1236 +
  6.1237 +    def read_fd(self, fd):
  6.1238 +        self.buf = os.read(fd, 512)
  6.1239 +        self.tid or self.parse_progress()
  6.1240 +
  6.1241 +    def poll(self):
  6.1242 +        try: os.waitpid(self.pid, os.WNOHANG)
  6.1243 +        except:
  6.1244 +            # something broken? try again
  6.1245 +            if self.time_setup and (time.time() - self.time_setup) < 2.0:
  6.1246 +                self.play()
  6.1247 +                return 0
  6.1248 +            app.set_default_status("")
  6.1249 +            app.counter([0,0])
  6.1250 +            app.progress(0)
  6.1251 +            return 1
  6.1252 +
  6.1253 +    def seek(self, offset, relative):
  6.1254 +        if relative:
  6.1255 +            d = offset * self.length * 0.002
  6.1256 +            self.step = self.step * (self.step * d > 0) + d
  6.1257 +            self.offset = min(self.length, max(0, self.offset+self.step))
  6.1258 +        else:
  6.1259 +            self.step = 1
  6.1260 +            self.offset = (offset < 0) and self.length+offset or offset
  6.1261 +        self.show_position()
  6.1262 +
  6.1263 +    def set_position(self, offset, length, values):
  6.1264 +        self.offset = offset
  6.1265 +        self.length = length
  6.1266 +        self.values = values
  6.1267 +        self.show_position()
  6.1268 +
  6.1269 +    def show_position(self):
  6.1270 +        app.counter(self.values)
  6.1271 +        app.progress(self.length and (float(self.offset) / self.length))
  6.1272 +
  6.1273 +    def update_status(self):
  6.1274 +        if not self.entry:
  6.1275 +            app.set_default_status("")
  6.1276 +        elif self.stopped:
  6.1277 +            app.set_default_status(_("Stopped: %s") % self.entry.vp())
  6.1278 +        elif self.paused:
  6.1279 +            app.set_default_status(_("Paused: %s") % self.entry.vp())
  6.1280 +        else:
  6.1281 +            app.set_default_status(_("Playing: %s") % self.entry.vp())
  6.1282 +
  6.1283 +# ------------------------------------------
  6.1284 +class FrameOffsetPlayer(Player):
  6.1285 +    re_progress = re.compile("Time.*\s(\d+):(\d+).*\[(\d+):(\d+)")
  6.1286 +
  6.1287 +    def parse_buf(self):
  6.1288 +        match = self.re_progress.search(self.buf)
  6.1289 +        if match:
  6.1290 +            m1, s1, m2, s2 = map(string.atoi, match.groups())
  6.1291 +            head, tail = m1*60+s1, m2*60+s2
  6.1292 +            self.set_position(head, head+tail, [head, tail])
  6.1293 +
  6.1294 +# ------------------------------------------
  6.1295 +class TimeOffsetPlayer(Player):
  6.1296 +    re_progress = re.compile("(\d+):(\d+):(\d+)")
  6.1297 +
  6.1298 +    def parse_buf(self):
  6.1299 +        match = self.re_progress.search(self.buf)
  6.1300 +        if match:
  6.1301 +            h, m, s = map(string.atoi, match.groups())
  6.1302 +            tail = h*3600+m*60+s
  6.1303 +            head = max(self.length, tail) - tail
  6.1304 +            self.set_position(head, head+tail, [head, tail])
  6.1305 +
  6.1306 +# ------------------------------------------
  6.1307 +class NoOffsetPlayer(Player):
  6.1308 +
  6.1309 +    def parse_buf(self):
  6.1310 +        head = self.offset+1
  6.1311 +        self.set_position(head, 0, [head, head])
  6.1312 +
  6.1313 +    def seek(self, *dummy):
  6.1314 +        return 1
  6.1315 +
  6.1316 +# ------------------------------------------
  6.1317 +class Timeout:
  6.1318 +    def __init__(self):
  6.1319 +        self.next = 0
  6.1320 +        self.dict = {}
  6.1321 +
  6.1322 +    def add(self, timeout, func, args=()):
  6.1323 +        tid = self.next = self.next + 1
  6.1324 +        self.dict[tid] = (func, args, time.time() + timeout)
  6.1325 +        return tid
  6.1326 +
  6.1327 +    def remove(self, tid):
  6.1328 +        del self.dict[tid]
  6.1329 +
  6.1330 +    def check(self, now):
  6.1331 +        for tid, (func, args, timeout) in self.dict.items():
  6.1332 +            if now >= timeout:
  6.1333 +                self.remove(tid)
  6.1334 +                apply(func, args)
  6.1335 +        return len(self.dict) and 0.2 or None
  6.1336 +
  6.1337 +# ------------------------------------------
  6.1338 +class FIFOControl:
  6.1339 +    def __init__(self):
  6.1340 +        try: self.fd = open(CONTROL_FIFO, "rb+", 0)
  6.1341 +        except: self.fd = None
  6.1342 +        self.commands = {"pause" : app.toggle_pause,
  6.1343 +                         "next" : app.next_song,
  6.1344 +                         "prev" : app.prev_song,
  6.1345 +                         "forward" : self.forward,
  6.1346 +                         "backward" : self.backward,
  6.1347 +                         "play" : app.toggle_stop,
  6.1348 +                         "stop" : app.toggle_stop,
  6.1349 +                         "volup" : app.inc_volume,
  6.1350 +                         "voldown" : app.dec_volume,
  6.1351 +                         "quit" : app.quit}
  6.1352 +
  6.1353 +    def handle_command(self):
  6.1354 +        command = string.strip(self.fd.readline())
  6.1355 +        if command in self.commands.keys():
  6.1356 +            self.commands[command]()
  6.1357 +
  6.1358 +    def forward(self):
  6.1359 +        app.seek(1, 1)
  6.1360 +
  6.1361 +    def backward(self):
  6.1362 +        app.seek(-1, 1)
  6.1363 +
  6.1364 +# ------------------------------------------
  6.1365 +class Application:
  6.1366 +    def __init__(self):
  6.1367 +        self.keymapstack = KeymapStack()
  6.1368 +        self.input_mode = 0
  6.1369 +        self.input_prompt = ""
  6.1370 +        self.input_string = ""
  6.1371 +        self.do_input_hook = None
  6.1372 +        self.stop_input_hook = None
  6.1373 +        self.complete_input_hook = None
  6.1374 +        self.channels = []
  6.1375 +        self.restricted = 0
  6.1376 +        self.input_keymap = Keymap()
  6.1377 +        self.input_keymap.bind(list(Window.chars), self.do_input)
  6.1378 +        self.input_keymap.bind(curses.KEY_BACKSPACE, self.do_input, (8,))
  6.1379 +        self.input_keymap.bind([21, 23], self.do_input)
  6.1380 +        self.input_keymap.bind(['\a', 27], self.cancel_input, ())
  6.1381 +        self.input_keymap.bind(['\n', curses.KEY_ENTER],
  6.1382 +                               self.stop_input, ())
  6.1383 +
  6.1384 +    def setup(self):
  6.1385 +        if tty:
  6.1386 +            self.tcattr = tty.tcgetattr(sys.stdin.fileno())
  6.1387 +            tcattr = tty.tcgetattr(sys.stdin.fileno())
  6.1388 +            tcattr[0] = tcattr[0] & ~(tty.IXON)
  6.1389 +            tty.tcsetattr(sys.stdin.fileno(), tty.TCSANOW, tcattr)
  6.1390 +        self.w = curses.initscr()
  6.1391 +        curses.cbreak()
  6.1392 +        curses.noecho()
  6.1393 +        try: curses.meta(1)
  6.1394 +        except: pass
  6.1395 +        self.cursor(0)
  6.1396 +        signal.signal(signal.SIGCHLD, signal.SIG_IGN)
  6.1397 +        signal.signal(signal.SIGHUP, self.handler_quit)
  6.1398 +        signal.signal(signal.SIGINT, self.handler_quit)
  6.1399 +        signal.signal(signal.SIGTERM, self.handler_quit)
  6.1400 +        signal.signal(signal.SIGWINCH, self.handler_resize)
  6.1401 +        self.win_root = RootWindow(None)
  6.1402 +        self.win_root.update()
  6.1403 +        self.win_tab = self.win_root.win_tab
  6.1404 +        self.win_filelist = self.win_root.win_tab.win_filelist
  6.1405 +        self.win_playlist = self.win_root.win_tab.win_playlist
  6.1406 +        self.win_status = self.win_root.win_status
  6.1407 +        self.status = self.win_status.status
  6.1408 +        self.set_default_status = self.win_status.set_default_status
  6.1409 +        self.restore_default_status = self.win_status.restore_default_status
  6.1410 +        self.counter = self.win_root.win_counter.counter
  6.1411 +        self.progress = self.win_root.win_progress.progress
  6.1412 +        self.player = PLAYERS[0]
  6.1413 +        self.timeout = Timeout()
  6.1414 +        self.play_tid = None
  6.1415 +        self.kludge = 0
  6.1416 +        self.win_filelist.listdir()
  6.1417 +        self.control = FIFOControl()
  6.1418 +
  6.1419 +    def cleanup(self):
  6.1420 +        try: curses.endwin()
  6.1421 +        except curses.error: return
  6.1422 +        XTERM and sys.stderr.write("\033]0;%s\a" % "xterm")
  6.1423 +        tty and tty.tcsetattr(sys.stdin.fileno(), tty.TCSADRAIN, self.tcattr)
  6.1424 +        print
  6.1425 +
  6.1426 +    def run(self):
  6.1427 +        while 1:
  6.1428 +            now = time.time()
  6.1429 +            timeout = self.timeout.check(now)
  6.1430 +            self.win_filelist.listdir_maybe(now)
  6.1431 +            if not self.player.stopped:
  6.1432 +                timeout = 0.5
  6.1433 +                if self.kludge and self.player.poll():
  6.1434 +                    self.player.stopped = 1  # end of playlist hack
  6.1435 +                    if not self.win_playlist.stop:
  6.1436 +                        entry = self.win_playlist.change_active_entry(1)
  6.1437 +                        entry and self.play(entry)
  6.1438 +            R = [sys.stdin, self.player.stdout_r, self.player.stderr_r]
  6.1439 +            self.control.fd and R.append(self.control.fd)
  6.1440 +            try: r, w, e = select.select(R, [], [], timeout)
  6.1441 +            except select.error: continue
  6.1442 +            self.kludge = 1
  6.1443 +            # user
  6.1444 +            if sys.stdin in r:
  6.1445 +                c = self.win_root.getch()
  6.1446 +                self.keymapstack.process(c)
  6.1447 +            # player
  6.1448 +            if self.player.stderr_r in r:
  6.1449 +                self.player.read_fd(self.player.stderr_r)
  6.1450 +            # player
  6.1451 +            if self.player.stdout_r in r:
  6.1452 +                self.player.read_fd(self.player.stdout_r)
  6.1453 +            # remote
  6.1454 +            if self.control.fd in r:
  6.1455 +                self.control.handle_command()
  6.1456 +
  6.1457 +    def play(self, entry, offset = 0):
  6.1458 +        self.kludge = 0
  6.1459 +        self.play_tid = None
  6.1460 +        if entry is None or offset is None: return
  6.1461 +        self.player.stop(quiet=1)
  6.1462 +        for self.player in PLAYERS:
  6.1463 +            if self.player.re_files.search(entry.pathname):
  6.1464 +                if self.player.setup(entry, offset): break
  6.1465 +        else:
  6.1466 +            app.status(_("Player not found!"), 1)
  6.1467 +            self.player.stopped = 0  # keep going
  6.1468 +            return
  6.1469 +        self.player.play()
  6.1470 +
  6.1471 +    def delayed_play(self, entry, offset):
  6.1472 +        if self.play_tid: self.timeout.remove(self.play_tid)
  6.1473 +        self.play_tid = self.timeout.add(0.5, self.play, (entry, offset))
  6.1474 +
  6.1475 +    def next_song(self):
  6.1476 +        self.delayed_play(self.win_playlist.change_active_entry(1), 0)
  6.1477 +
  6.1478 +    def prev_song(self):
  6.1479 +        self.delayed_play(self.win_playlist.change_active_entry(-1), 0)
  6.1480 +
  6.1481 +    def seek(self, offset, relative):
  6.1482 +        if not self.player.entry: return
  6.1483 +        self.player.seek(offset, relative)
  6.1484 +        self.delayed_play(self.player.entry, self.player.offset)
  6.1485 +
  6.1486 +    def toggle_pause(self):
  6.1487 +        if not self.player.entry: return
  6.1488 +        if not self.player.stopped: self.player.toggle_pause()
  6.1489 +
  6.1490 +    def toggle_stop(self):
  6.1491 +        if not self.player.entry: return
  6.1492 +        if not self.player.stopped: self.player.stop()
  6.1493 +        else: self.play(self.player.entry, self.player.offset)
  6.1494 +
  6.1495 +    def inc_volume(self):
  6.1496 +        self.mixer("cue", 1)
  6.1497 +
  6.1498 +    def dec_volume(self):
  6.1499 +        self.mixer("cue", -1)
  6.1500 +
  6.1501 +    def key_volume(self, ch):
  6.1502 +        self.mixer("set", (ch & 0x0f)*10)
  6.1503 +
  6.1504 +    def mixer(self, cmd=None, arg=None):
  6.1505 +        try: self._mixer(cmd, arg)
  6.1506 +        except Exception, e: app.status(e, 2)
  6.1507 +
  6.1508 +    def _mixer(self, cmd, arg):
  6.1509 +        try:
  6.1510 +            import ossaudiodev
  6.1511 +            mixer = ossaudiodev.openmixer()
  6.1512 +            get, set = mixer.get, mixer.set
  6.1513 +            self.channels = self.channels or \
  6.1514 +                [['MASTER', ossaudiodev.SOUND_MIXER_VOLUME],
  6.1515 +                 ['PCM', ossaudiodev.SOUND_MIXER_PCM]]
  6.1516 +        except ImportError:
  6.1517 +            import oss
  6.1518 +            mixer = oss.open_mixer()
  6.1519 +            get, set = mixer.read_channel, mixer.write_channel
  6.1520 +            self.channels = self.channels or \
  6.1521 +                [['MASTER', oss.SOUND_MIXER_VOLUME],
  6.1522 +                 ['PCM', oss.SOUND_MIXER_PCM]]
  6.1523 +        if cmd is "toggle": self.channels.insert(0, self.channels.pop())
  6.1524 +        name, channel = self.channels[0]
  6.1525 +        if cmd is "cue": arg = min(100, max(0, get(channel)[0] + arg))
  6.1526 +        if cmd in ["set", "cue"]: set(channel, (arg, arg))
  6.1527 +        app.status(_("%s volume %s%%") % (name, get(channel)[0]), 1)
  6.1528 +        mixer.close()
  6.1529 +
  6.1530 +    def show_input(self):
  6.1531 +        n = len(self.input_prompt)+1
  6.1532 +        s = cut(self.input_string, self.win_status.cols-n, left=1)
  6.1533 +        app.status("%s%s " % (self.input_prompt, s))
  6.1534 +
  6.1535 +    def start_input(self, prompt="", data="", colon=1):
  6.1536 +        self.input_mode = 1
  6.1537 +        self.cursor(1)
  6.1538 +        app.keymapstack.push(self.input_keymap)
  6.1539 +        self.input_prompt = prompt + (colon and ": " or "")
  6.1540 +        self.input_string = data
  6.1541 +        self.show_input()
  6.1542 +
  6.1543 +    def do_input(self, *args):
  6.1544 +        if self.do_input_hook:
  6.1545 +            return apply(self.do_input_hook, args)
  6.1546 +        ch = args and args[0] or None
  6.1547 +        if ch in [8, 127]: # backspace
  6.1548 +            self.input_string = self.input_string[:-1]
  6.1549 +        elif ch == 9 and self.complete_input_hook:
  6.1550 +            self.input_string = self.complete_input_hook(self.input_string)
  6.1551 +        elif ch == 21: # C-u
  6.1552 +            self.input_string = ""
  6.1553 +        elif ch == 23: # C-w
  6.1554 +            self.input_string = re.sub("((.* )?)\w.*", "\\1", self.input_string)
  6.1555 +        elif ch:
  6.1556 +            self.input_string = "%s%c" % (self.input_string, ch)
  6.1557 +        self.show_input()
  6.1558 +
  6.1559 +    def stop_input(self, *args):
  6.1560 +        self.input_mode = 0
  6.1561 +        self.cursor(0)
  6.1562 +        app.keymapstack.pop()
  6.1563 +        if not self.input_string:
  6.1564 +            app.status(_("cancel"), 1)
  6.1565 +        elif self.stop_input_hook:
  6.1566 +            apply(self.stop_input_hook, args)
  6.1567 +        self.do_input_hook = None
  6.1568 +        self.stop_input_hook = None
  6.1569 +        self.complete_input_hook = None
  6.1570 +
  6.1571 +    def cancel_input(self):
  6.1572 +        self.input_string = ""
  6.1573 +        self.stop_input()
  6.1574 +
  6.1575 +    def cursor(self, visibility):
  6.1576 +        try: curses.curs_set(visibility)
  6.1577 +        except: pass
  6.1578 +
  6.1579 +    def quit(self):
  6.1580 +        self.player.stop(quiet=1)
  6.1581 +        sys.exit(0)
  6.1582 +
  6.1583 +    def handler_resize(self, sig, frame):
  6.1584 +        # curses trickery
  6.1585 +        while 1:
  6.1586 +            try: curses.endwin(); break
  6.1587 +            except: time.sleep(1)
  6.1588 +        self.w.refresh()
  6.1589 +        self.win_root.resize()
  6.1590 +        self.win_root.update()
  6.1591 +
  6.1592 +    def handler_quit(self, sig, frame):
  6.1593 +        self.quit()
  6.1594 +
  6.1595 +# ------------------------------------------
  6.1596 +def main():
  6.1597 +    try:
  6.1598 +        opts, args = getopt.getopt(sys.argv[1:], "nrRv")
  6.1599 +    except:
  6.1600 +        usage = _("Usage: %s [-nrRv] [ file | dir | playlist ] ...\n")
  6.1601 +        sys.stderr.write(usage % sys.argv[0])
  6.1602 +        sys.exit(1)
  6.1603 +
  6.1604 +    global app
  6.1605 +    app = Application()
  6.1606 +
  6.1607 +    playlist = []
  6.1608 +    if not sys.stdin.isatty():
  6.1609 +        playlist = map(string.strip, sys.stdin.readlines())
  6.1610 +        os.close(0)
  6.1611 +        os.open("/dev/tty", 0)
  6.1612 +    try:
  6.1613 +        app.setup()
  6.1614 +        for opt, optarg in opts:
  6.1615 +            if opt == "-n": app.restricted = 1
  6.1616 +            if opt == "-r": app.win_playlist.command_toggle_repeat()
  6.1617 +            if opt == "-R": app.win_playlist.command_toggle_random()
  6.1618 +            if opt == "-v": app.mixer("toggle")
  6.1619 +        if args or playlist:
  6.1620 +            for i in args or playlist:
  6.1621 +                app.win_playlist.add(os.path.abspath(i))
  6.1622 +            app.win_tab.change_window()
  6.1623 +        app.run()
  6.1624 +    except SystemExit:
  6.1625 +        app.cleanup()
  6.1626 +    except Exception:
  6.1627 +        app.cleanup()
  6.1628 +        import traceback
  6.1629 +        traceback.print_exc()
  6.1630 +
  6.1631 +# ------------------------------------------
  6.1632 +PLAYERS = [
  6.1633 +    FrameOffsetPlayer("ogg123 -q -v -k %d %s", "\.ogg$"),
  6.1634 +    FrameOffsetPlayer("splay -f -k %d %s", "(^http://|\.mp[123]$)", 38.28),
  6.1635 +    FrameOffsetPlayer("mpg123 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28),
  6.1636 +    FrameOffsetPlayer("mpg321 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28),
  6.1637 +    TimeOffsetPlayer("madplay -v --display-time=remaining -s %d %s", "\.mp[123]$"),
  6.1638 +    NoOffsetPlayer("mikmod -q -p0 %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm)$"),
  6.1639 +    NoOffsetPlayer("xmp -q %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm|stm)$"),
  6.1640 +    NoOffsetPlayer("play %s", "\.(aiff|au|cdr|mp3|ogg|wav)$"),
  6.1641 +    NoOffsetPlayer("speexdec %s", "\.spx$")
  6.1642 +    ]
  6.1643 +
  6.1644 +def VALID_SONG(name):
  6.1645 +    for player in PLAYERS:
  6.1646 +        if player.re_files.search(name):
  6.1647 +            return 1
  6.1648 +
  6.1649 +def VALID_PLAYLIST(name):
  6.1650 +    if re.search("\.(m3u|pls)$", name, re.I):
  6.1651 +        return 1
  6.1652 +
  6.1653 +for rc in [os.path.expanduser("~/.cplayrc"), "/etc/cplayrc"]:
  6.1654 +    try: execfile(rc); break
  6.1655 +    except IOError: pass
  6.1656 +
  6.1657 +# ------------------------------------------
  6.1658 +if __name__ == "__main__": main()
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/cplay.1	Wed Sep 27 09:22:32 2017 +0200
     7.3 @@ -0,0 +1,80 @@
     7.4 +.\" Copyright (C) 2000, 2002 Martin Michlmayr <tbm@cyrius.com>
     7.5 +.\" This manual is freely distributable under the terms of the GPL.
     7.6 +.\" It was originally written for Debian GNU/Linux (but may be used
     7.7 +.\" by others).
     7.8 +.\"
     7.9 +.TH CPLAY 1 "November 2002"
    7.10 +
    7.11 +.SH NAME
    7.12 +.PP
    7.13 +cplay \- a front-end for various audio players
    7.14 +
    7.15 +.SH SYNOPSIS
    7.16 +.PP
    7.17 +\fBcplay\fR [\-options] [ \fIfile\fP | \fIdir\fP | \fIplaylist\fP ] ...
    7.18 +
    7.19 +.SH DESCRIPTION
    7.20 +.PP
    7.21 +.B cplay
    7.22 +is a front-end for various audio players. It aims to provide a
    7.23 +user-friendly interface with simple filelist and playlist
    7.24 +control.  Invoking
    7.25 +.B cplay
    7.26 +without any options puts you in the filelist in which you
    7.27 +can navigate around looking for audio files.  When you
    7.28 +specify an audio file,
    7.29 +.B cplay
    7.30 +automatically adds it to the playlist which can be accessed
    7.31 +by pressing the tabulator key.
    7.32 +.PP
    7.33 +Use 'h' to display the help window.
    7.34 +.PP
    7.35 +Shell command-lines are executed with tagged or current entries
    7.36 +as positional parameters, which means that "$@" might be useful.
    7.37 +.PP
    7.38 +Currently, the following audio formats are supported: MP3 (through
    7.39 +mpg321, splay, mpg123 or madplay), Ogg Vorbis (through ogg123), and
    7.40 +various module-formats (through mikmod and xmp).
    7.41 +
    7.42 +.SH OPTIONS
    7.43 +.IP \fB\-n
    7.44 +Enable \fIrestricted\fI mode
    7.45 +.IP \fB\-r
    7.46 +Toggles playlist \fIrepeat\fP mode
    7.47 +.IP \fB\-R
    7.48 +Toggles playlist \fIrandom\fP mode
    7.49 +.IP \fB\-v
    7.50 +Toggles PCM and MASTER (default) volume control
    7.51 +
    7.52 +.SH SEE ALSO
    7.53 +.PP
    7.54 +.BR ogg123 (1),
    7.55 +.BR mpg123 (1),
    7.56 +.BR mpg321 (1),
    7.57 +.BR splay (1),
    7.58 +.BR madplay (1),
    7.59 +.BR mikmod (1),
    7.60 +.BR xmp (1),
    7.61 +.BR play (1),
    7.62 +.BR speexdec (1)
    7.63 +
    7.64 +.SH FILES
    7.65 +.PP
    7.66 +.I /var/tmp/cplay_control 
    7.67 +- Optional remote control fifo.
    7.68 +
    7.69 +.I /etc/cplayrc
    7.70 +- Optional configuration file.
    7.71 +
    7.72 +.I ~/.cplayrc
    7.73 +- Optional configuration file.
    7.74 +
    7.75 +.SH AUTHOR
    7.76 +.PP
    7.77 +Ulf Betlehem <flu@iki.fi>
    7.78 +
    7.79 +.SH BUGS
    7.80 +.PP
    7.81 +In order for either mp3info (ID3) or ogginfo to work,
    7.82 +both corresponding python modules have to be installed.
    7.83 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/cplay.list	Wed Sep 27 09:22:32 2017 +0200
     8.3 @@ -0,0 +1,35 @@
     8.4 +# cplay list file
     8.5 +# tested with epm 2.5 and cplay 1.48
     8.6 +
     8.7 +# Manpage extensions...
     8.8 +$MAN1EXT=1
     8.9 +
    8.10 +# Directories...
    8.11 +$prefix=/usr/local
    8.12 +$bindir=${prefix}/bin
    8.13 +$confdir=/etc
    8.14 +$docdir=${prefix}/doc/cplay
    8.15 +$mandir=${prefix}/man/man1
    8.16 +
    8.17 +# Product information
    8.18 +%product cplay
    8.19 +%copyright GPL
    8.20 +%vendor Ulf Betlehem <flu@iki.fi>
    8.21 +%license COPYING
    8.22 +%readme README
    8.23 +%description cplay, a curses front-end for various audio players. 
    8.24 +%version 1.48
    8.25 +%packager Giuseppe "Cowo" Corbelli <cowo@lugbs.linux.it>
    8.26 +
    8.27 +# Executables
    8.28 +%system all
    8.29 +f 0555 root sys ${bindir}/cplay cplay
    8.30 +# Doc
    8.31 +f 0444 root sys ${docdir}/README README
    8.32 +f 0444 root sys ${docdir}/TODO TODO
    8.33 +
    8.34 +# Man pages
    8.35 +f 0444 root sys ${mandir}/cplay.${MAN1EXT} cplay.1
    8.36 +
    8.37 +#Configuration
    8.38 +f 0644 root sys ${confdir}/cplayrc cplayrc
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/cplayrc	Wed Sep 27 09:22:32 2017 +0200
     9.3 @@ -0,0 +1,12 @@
     9.4 +PLAYERS = [
     9.5 +    FrameOffsetPlayer("ogg123 -q -v -k %d %s", "\.ogg$"),
     9.6 +    FrameOffsetPlayer("splay -f -k %d %s", "(^http://|\.mp[123]$)", 38.28),
     9.7 +    FrameOffsetPlayer("mpg123 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28),
     9.8 +    FrameOffsetPlayer("mpg321 -q -v -k %d %s", "(^http://|\.mp[123]$)", 38.28),
     9.9 +    TimeOffsetPlayer("madplay -v --no-tty-control --display-time=remaining -s %d %s", "\.mp[123]$"),
    9.10 +    NoOffsetPlayer("mikmod -q -p0 %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm)$"),
    9.11 +    NoOffsetPlayer("xmp -q %s", "\.(mod|xm|fm|s3m|med|col|669|it|mtm|stm)$"),
    9.12 +    NoOffsetPlayer("play %s", "\.(aiff|au|cdr|mp3|ogg|wav)$"),
    9.13 +    NoOffsetPlayer("speexdec %s", "\.spx$")
    9.14 +    ]
    9.15 +
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/lircrc	Wed Sep 27 09:22:32 2017 +0200
    10.3 @@ -0,0 +1,71 @@
    10.4 +begin
    10.5 +    remote = ANIMAX
    10.6 +    button = SKIP_FORWARD_DOWN
    10.7 +    prog   = irexec
    10.8 +    repeat = 0
    10.9 +    config = echo "next" > /var/tmp/cplay_control
   10.10 +end
   10.11 +
   10.12 +begin
   10.13 +    remote = ANIMAX
   10.14 +    button = SKIP_BACKWARD_DOWN
   10.15 +    prog   = irexec
   10.16 +    repeat = 0
   10.17 +    config = echo "prev" > /var/tmp/cplay_control
   10.18 +end
   10.19 +
   10.20 +begin
   10.21 +    remote = ANIMAX
   10.22 +    button = REWIND_DOWN
   10.23 +    prog   = irexec
   10.24 +    repeat = 1
   10.25 +    config = echo "backward" > /var/tmp/cplay_control
   10.26 +end
   10.27 +
   10.28 +begin
   10.29 +    remote = ANIMAX
   10.30 +    button = FORWARD_DOWN
   10.31 +    prog   = irexec
   10.32 +    repeat = 1
   10.33 +    config = echo "forward" > /var/tmp/cplay_control
   10.34 +end
   10.35 +
   10.36 +begin
   10.37 +    remote = ANIMAX
   10.38 +    button = PLAY_DOWN
   10.39 +    prog   = irexec
   10.40 +    repeat = 0
   10.41 +    config = echo "play" > /var/tmp/cplay_control
   10.42 +end
   10.43 +
   10.44 +begin
   10.45 +    remote = ANIMAX
   10.46 +    button = STOP_DOWN
   10.47 +    prog   = irexec
   10.48 +    repeat = 0
   10.49 +    config = echo "stop" > /var/tmp/cplay_control
   10.50 +end
   10.51 +
   10.52 +begin
   10.53 +    remote = ANIMAX
   10.54 +    button = VOLUME_UP_DOWN
   10.55 +    prog   = irexec
   10.56 +    repeat = 1
   10.57 +    config = echo "volup" > /var/tmp/cplay_control
   10.58 +end
   10.59 +
   10.60 +begin
   10.61 +    remote = ANIMAX
   10.62 +    button = VOLUME_DOWN_DOWN
   10.63 +    prog   = irexec
   10.64 +    repeat = 1
   10.65 +    config = echo "voldown" > /var/tmp/cplay_control
   10.66 +end
   10.67 +
   10.68 +begin
   10.69 +    remote = ANIMAX
   10.70 +    button = POWER_DOWN
   10.71 +    prog   = irexec
   10.72 +    repeat = 0
   10.73 +    config = echo "quit" > /var/tmp/cplay_control
   10.74 +end
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/po/Makefile	Wed Sep 27 09:22:32 2017 +0200
    11.3 @@ -0,0 +1,16 @@
    11.4 +POFILES = $(wildcard *.po)
    11.5 +MOFILES = $(POFILES:.po=.mo)
    11.6 +
    11.7 +$(MOFILES): $(POFILES)
    11.8 +	for i in $(POFILES); do msgfmt $$i -o $${i%.po}.mo; done
    11.9 +
   11.10 +all: $(MOFILES)
   11.11 +
   11.12 +install: all
   11.13 +	for i in $(MOFILES); do \
   11.14 +		mkdir -p $(PREFIX)/share/locale/$${i%.mo}/LC_MESSAGES; \
   11.15 +		install -c -m 644 $$i $(PREFIX)/share/locale/$${i%.mo}/LC_MESSAGES/cplay.mo; \
   11.16 +	done
   11.17 +
   11.18 +clean:
   11.19 +	rm -f $(MOFILES) *~
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/po/da.po	Wed Sep 27 09:22:32 2017 +0200
    12.3 @@ -0,0 +1,278 @@
    12.4 +# SOME DESCRIPTIVE TITLE.
    12.5 +# Copyright (C) YEAR ORGANIZATION
    12.6 +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
    12.7 +#
    12.8 +msgid ""
    12.9 +msgstr ""
   12.10 +"Project-Id-Version: PACKAGE VERSION\n"
   12.11 +"POT-Creation-Date: Tue Jul 29 17:01:48 2003\n"
   12.12 +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
   12.13 +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
   12.14 +"Language-Team: LANGUAGE <LL@li.org>\n"
   12.15 +"MIME-Version: 1.0\n"
   12.16 +"Content-Type: text/plain; charset=iso-8859-1\n"
   12.17 +"Content-Transfer-Encoding: 8bit\n"
   12.18 +"Generated-By: pygettext.py 1.4\n"
   12.19 +
   12.20 +
   12.21 +#: cplay:287
   12.22 +msgid "elapsed"
   12.23 +msgstr "spillet"
   12.24 +
   12.25 +#: cplay:287
   12.26 +msgid "remaining"
   12.27 +msgstr "tilbage"
   12.28 +
   12.29 +#: cplay:288
   12.30 +msgid "Counting %s time"
   12.31 +msgstr "Tćller %s gange"
   12.32 +
   12.33 +#: cplay:320
   12.34 +msgid "Quit? (y/N)"
   12.35 +msgstr "Afslut? (y/N)"
   12.36 +
   12.37 +#: cplay:405
   12.38 +msgid "backward-isearch"
   12.39 +msgstr "baglćns-isřgning"
   12.40 +
   12.41 +#: cplay:407
   12.42 +msgid "forward-isearch"
   12.43 +msgstr "forlćns-isřgning"
   12.44 +
   12.45 +#: cplay:496 cplay:657 cplay:748 cplay:760 cplay:1148
   12.46 +msgid "ok"
   12.47 +msgstr "ok"
   12.48 +
   12.49 +#: cplay:505 cplay:517
   12.50 +msgid "Not found: %s "
   12.51 +msgstr "%s ikke fundet "
   12.52 +
   12.53 +#: cplay:525
   12.54 +msgid "Help"
   12.55 +msgstr "Hjćlp"
   12.56 +
   12.57 +#: cplay:527
   12.58 +msgid ""
   12.59 +"\n"
   12.60 +"  Global                               Space : tag/untag current\n"
   12.61 +"  ------                               t, T  : tag current/regex\n"
   12.62 +"  Up, Down, k, j, C-p, C-n,            u, U  : untag current/regex\n"
   12.63 +"  PgUp, PgDn, K, J,                    i     : invert tags\n"
   12.64 +"  Home, End, g, G : movement\n"
   12.65 +"  Enter           : chdir or play      Filelist\n"
   12.66 +"  Tab             : filelist/playlist  --------\n"
   12.67 +"  n, p            : next/prev track    BkSpc : parent dir\n"
   12.68 +"  z, x            : toggle pause/stop  o, s  : goto, search recursively\n"
   12.69 +"  Left, Right,                         a     : add (tagged) to playlist\n"
   12.70 +"  C-f, C-b    : seek forward/backward\n"
   12.71 +"  c           : counter mode           Playlist\n"
   12.72 +"  C-s, C-r, / : isearch                --------\n"
   12.73 +"  C-g, Esc    : cancel                 d, D  : delete (tagged) tracks/playlist\n"
   12.74 +"  1..9, +, -  : volume control         m, M  : move tagged tracks after/before\n"
   12.75 +"  v           : PCM or MASTER volume   r, R  : toggle repeat/Random mode\n"
   12.76 +"  C-l, l      : refresh, list mode     s, S  : shuffle/Sort playlist\n"
   12.77 +"  h, q, Q     : help, quit?, Quit!     w, @  : write playlist, jump to current\n"
   12.78 +"\n"
   12.79 +"\n"
   12.80 +"\n"
   12.81 +"  Goodies\n"
   12.82 +"  -------\n"
   12.83 +"  b, '   : set/get filelist bookmark\n"
   12.84 +"  X      : stop playlist after each track\n"
   12.85 +"  C-a, ^ : seek to beginning of current track (restart)\n"
   12.86 +"  C-e, $ : seek to end of current track (useless?)\n"
   12.87 +"  !      : shell command ($@ = tagged or current)\n"
   12.88 +"  "
   12.89 +msgstr ""
   12.90 +"\n"
   12.91 +"  Globalt                                  Space : vćlg/fravćlg nuvćrende\n"
   12.92 +"  ------                                   t, T  : vćlg nuvćrende/regex\n"
   12.93 +"  Up, Ned, k, j, C-p, C-n,                 u, U  : fravćlg nuvćrede/regex\n"
   12.94 +"  PgUp, PgDn, K, J,                        i     : vend vćlg om\n"
   12.95 +"  Home, End, g, G : bevćgelse\n"
   12.96 +"  Enter           : chdir eller afspil     Filliste\n"
   12.97 +"  Tab             : filliste/spilleliste   --------\n"
   12.98 +"  n, p            : nćste/forrige nummer   BkSpc : et filkatalog op\n"
   12.99 +"  z, x            : pause/stop fra og til  o, s  : gĺ til, sřg gennemgĺende\n"
  12.100 +"  Venstre, Hřjre,                          a     : tilfřj (valgte) til spilleliste\n"
  12.101 +"  C-f, C-b    : sřg forlćns/baglćns\n"
  12.102 +"  c           : tćller-type                Spilleliste\n"
  12.103 +"  C-s, C-r, / : isřgning                   --------\n"
  12.104 +"  C-g, Esc    : afbryd                     d, D  : fjern (valgte) numre/spillelist\n"
  12.105 +"  1..9, +, -  : lydstyrke                  m, M  : flyt valgte numre efter/fřr\n"
  12.106 +"  v           : PCM eller MASTER volume    r, R  : repeat/Random fra og til\n"
  12.107 +"  C-l, l      : opfrisk, vis liste         s, S  : bland/Sortér spillelist\n"
  12.108 +"  h, q, Q     : hjćlp, afslut?, Afslut!    w, @  : gem spillelist, gĺ til nuvćrende\n"
  12.109 +"\n"
  12.110 +"\n"
  12.111 +"\n"
  12.112 +"  Godbider\n"
  12.113 +"  -------\n"
  12.114 +"  b, '   : sćt/hent filliste-bogmćrke\n"
  12.115 +"  X      : stop spilleiste efter hvert nummer\n"
  12.116 +"  C-a, ^ : gĺ til begyndelsen af det nyvćrende nummer (start om)\n"
  12.117 +"  C-e, $ : gĺ til slutningen af det nuvćrende nummer (ubrugelig?)\n"
  12.118 +"  !      : skal-kommando ($@ = valgte eller nuvćrende)\n"
  12.119 +"  "
  12.120 +
  12.121 +#: cplay:593
  12.122 +msgid "filename"
  12.123 +msgstr "filnavn"
  12.124 +
  12.125 +#: cplay:594
  12.126 +msgid "pathname"
  12.127 +msgstr "stinavn"
  12.128 +
  12.129 +#: cplay:625
  12.130 +msgid "Listing %s"
  12.131 +msgstr "Sććter %s pĺ liste"
  12.132 +
  12.133 +#: cplay:648
  12.134 +msgid "Tag regexp"
  12.135 +msgstr "Vćlg regexp"
  12.136 +
  12.137 +#: cplay:648
  12.138 +msgid "Untag regexp"
  12.139 +msgstr "Fravćlg regexp"
  12.140 +
  12.141 +#: cplay:687
  12.142 +msgid "shell$ "
  12.143 +msgstr "skal$ "
  12.144 +
  12.145 +#: cplay:734
  12.146 +msgid "bookmark"
  12.147 +msgstr "bogmćrke"
  12.148 +
  12.149 +#: cplay:750
  12.150 +msgid "Not found!"
  12.151 +msgstr "Ikke fundet!"
  12.152 +
  12.153 +#: cplay:755
  12.154 +msgid "set bookmark"
  12.155 +msgstr "sćt bogmćrke"
  12.156 +
  12.157 +#: cplay:764
  12.158 +msgid "search"
  12.159 +msgstr "sřg"
  12.160 +
  12.161 +#: cplay:771
  12.162 +msgid "Searching..."
  12.163 +msgstr "Sřger..."
  12.164 +
  12.165 +#: cplay:782
  12.166 +msgid "search results"
  12.167 +msgstr "sřgnings-resultater"
  12.168 +
  12.169 +#: cplay:802
  12.170 +msgid "Filelist: "
  12.171 +msgstr "Filliste: "
  12.172 +
  12.173 +#: cplay:813
  12.174 +msgid "Reading directory..."
  12.175 +msgstr "Lćser filkatalog..."
  12.176 +
  12.177 +#: cplay:868
  12.178 +msgid "goto"
  12.179 +msgstr "gĺ til"
  12.180 +
  12.181 +#: cplay:874
  12.182 +msgid "Not a directory!"
  12.183 +msgstr "Ikke et filkatalog"
  12.184 +
  12.185 +#: cplay:919
  12.186 +msgid "metadata"
  12.187 +msgstr "metadata"
  12.188 +
  12.189 +#: cplay:925
  12.190 +msgid "Playlist %s %s %s"
  12.191 +msgstr "Spilleliste %s %s %s"
  12.192 +
  12.193 +#: cplay:926
  12.194 +msgid "[repeat]"
  12.195 +msgstr "[gentag]"
  12.196 +
  12.197 +#: cplay:927
  12.198 +msgid "[random]"
  12.199 +msgstr "[tilfćldig]"
  12.200 +
  12.201 +#: cplay:928
  12.202 +msgid "[stop]"
  12.203 +msgstr "[stop]"
  12.204 +
  12.205 +#: cplay:981 cplay:1108
  12.206 +msgid "Working..."
  12.207 +msgstr "Arbejder..."
  12.208 +
  12.209 +#: cplay:990
  12.210 +msgid "Added: %s"
  12.211 +msgstr "Tilfřjede: %s"
  12.212 +
  12.213 +#: cplay:1079
  12.214 +msgid "Deleted playlist"
  12.215 +msgstr "Fjernede spilleliste"
  12.216 +
  12.217 +#: cplay:1105
  12.218 +msgid "Shuffled playlist... Oops?"
  12.219 +msgstr "Blandede spilleliste... Hovsa?"
  12.220 +
  12.221 +#: cplay:1112
  12.222 +msgid "Sorted playlist"
  12.223 +msgstr "Sortérede spilleliste"
  12.224 +
  12.225 +#: cplay:1115
  12.226 +msgid "Repeat: %s"
  12.227 +msgstr "Gentag: %s"
  12.228 +
  12.229 +#: cplay:1118
  12.230 +msgid "Random: %s"
  12.231 +msgstr "Tilfćldig: %s"
  12.232 +
  12.233 +#: cplay:1124
  12.234 +msgid "Stop playlist: %s"
  12.235 +msgstr "Stop spilleliste"
  12.236 +
  12.237 +#: cplay:1128
  12.238 +msgid "off"
  12.239 +msgstr "fra"
  12.240 +
  12.241 +#: cplay:1128
  12.242 +msgid "on"
  12.243 +msgstr "til"
  12.244 +
  12.245 +#: cplay:1134
  12.246 +msgid "Save playlist"
  12.247 +msgstr "Gem spilleliste"
  12.248 +
  12.249 +#: cplay:1310
  12.250 +msgid "Stopped: %s"
  12.251 +msgstr "Stoppet: %s"
  12.252 +
  12.253 +#: cplay:1312
  12.254 +msgid "Paused: %s"
  12.255 +msgstr "Sat pĺ pause: %s"
  12.256 +
  12.257 +#: cplay:1314
  12.258 +msgid "Playing: %s"
  12.259 +msgstr "Spiller: %s"
  12.260 +
  12.261 +#: cplay:1491
  12.262 +msgid "Player not found!"
  12.263 +msgstr "Afspiller ikke fundet!"
  12.264 +
  12.265 +#: cplay:1548
  12.266 +msgid "Volume %s%%"
  12.267 +msgstr "Lydstyrke %s%"
  12.268 +
  12.269 +#: cplay:1556
  12.270 +msgid "%s volume %s%%"
  12.271 +msgstr "%s lydstyrke %s%"
  12.272 +
  12.273 +#: cplay:1587
  12.274 +msgid "cancel"
  12.275 +msgstr "afbryd"
  12.276 +
  12.277 +#: cplay:1623
  12.278 +msgid ""
  12.279 +"Usage: %s [-rRv] [ file | dir | playlist ] ...\n"
  12.280 +msgstr ""
  12.281 +"Brug: %s [-rRv] [ fil | filkatalog | spilleliste ] ...\n"
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/po/de.po	Wed Sep 27 09:22:32 2017 +0200
    13.3 @@ -0,0 +1,193 @@
    13.4 +# Copyright (C) 2000 Martin Michlmayr <tbm@cyrius.com>
    13.5 +#
    13.6 +msgid ""
    13.7 +msgstr ""
    13.8 +"Project-Id-Version: 1.0\n"
    13.9 +"PO-Revision-Date: Sat Jul 22 00:21:05 2000\n"
   13.10 +"Last-Translator: Martin Michlmayr <tbm@cyrius.com>\n"
   13.11 +"Language-Team: Martin Michlmayr <tbm@cyrius.com>\n"
   13.12 +"MIME-Version: 1.0\n"
   13.13 +"Content-Type: text/plain; charset=iso-8859-1\n"
   13.14 +"Content-Transfer-Encoding: 8bit\n"
   13.15 +
   13.16 +#: cplay:828
   13.17 +msgid "Almost there..."
   13.18 +msgstr "Fast fertig..."
   13.19 +
   13.20 +#: cplay:834
   13.21 +msgid "Cleared all marks..."
   13.22 +msgstr "Lösche alle Markierungen..."
   13.23 +
   13.24 +#: cplay:746
   13.25 +msgid "IOError"
   13.26 +msgstr "I/O-Fehler"
   13.27 +
   13.28 +#: cplay:882 cplay:888
   13.29 +msgid "off"
   13.30 +msgstr "aus"
   13.31 +
   13.32 +#: cplay:918
   13.33 +msgid "Cannot write playlist!"
   13.34 +msgstr "Kann Playliste nicht schreiben!"
   13.35 +
   13.36 +#: cplay:412
   13.37 +msgid "forward-isearch"
   13.38 +msgstr "Vorwärts-isuche"
   13.39 +
   13.40 +#: cplay:539
   13.41 +msgid "Not found: %s"
   13.42 +msgstr "Nicht gefunden: %s"
   13.43 +
   13.44 +#: cplay:727
   13.45 +msgid "Playlist %s %s"
   13.46 +msgstr "Playliste %s %s"
   13.47 +
   13.48 +#: cplay:411
   13.49 +msgid "backward-isearch"
   13.50 +msgstr "Rückwärts-isuche"
   13.51 +
   13.52 +#: cplay:878
   13.53 +msgid "Sorted playlist..."
   13.54 +msgstr "Sortiere Playliste..."
   13.55 +
   13.56 +#: cplay:205
   13.57 +msgid "Help"
   13.58 +msgstr "Hilfe"
   13.59 +
   13.60 +#: cplay:419 cplay:916
   13.61 +msgid "ok"
   13.62 +msgstr "ok"
   13.63 +
   13.64 +#: cplay:958
   13.65 +msgid "Paused: %s"
   13.66 +msgstr "Pause: %s"
   13.67 +
   13.68 +#: cplay:882
   13.69 +msgid "Repeat %s"
   13.70 +msgstr "Wiederholung %s"
   13.71 +
   13.72 +#: cplay:882 cplay:888
   13.73 +msgid "on"
   13.74 +msgstr "ein"
   13.75 +
   13.76 +#: cplay:742 cplay:749
   13.77 +msgid "Added: %s"
   13.78 +msgstr "Hinzugefügt: %s"
   13.79 +
   13.80 +#: cplay:728 cplay:729
   13.81 +msgid "[random]"
   13.82 +msgstr "[zufällig]"
   13.83 +
   13.84 +#: cplay:872
   13.85 +msgid "Shuffled playlist... Oops?"
   13.86 +msgstr "Durchwühle Playliste... Oops?"
   13.87 +
   13.88 +#: cplay:418 cplay:639 cplay:904
   13.89 +msgid "cancel"
   13.90 +msgstr "Abbruch"
   13.91 +
   13.92 +#: cplay:463
   13.93 +msgid "probably not ok"
   13.94 +msgstr "wahrscheinlich nicht ok"
   13.95 +
   13.96 +#: cplay:575
   13.97 +msgid "Reading directory..."
   13.98 +msgstr "Lese Verzeichnis..."
   13.99 +
  13.100 +#: cplay:1162
  13.101 +msgid "Player not found!"
  13.102 +msgstr "Player nicht gefunden!"
  13.103 +
  13.104 +#: cplay:611
  13.105 +msgid "Filelist: "
  13.106 +msgstr "Dateiliste: "
  13.107 +
  13.108 +#: cplay:1240
  13.109 +msgid "Usage: %s [ filename | playlist.m3u ] ...\n"
  13.110 +msgstr "Verwendung: %s [ datei | playliste.m3u ] ...\n"
  13.111 +
  13.112 +#: cplay:1217
  13.113 +msgid "Volume %d%%"
  13.114 +msgstr "Lautstärke %d%%"
  13.115 +
  13.116 +#: cplay:941 cplay:964
  13.117 +msgid "Playing: %s"
  13.118 +msgstr "Spiele: %s"
  13.119 +
  13.120 +#: cplay:181
  13.121 +msgid ""
  13.122 +"\n"
  13.123 +"  Global                            Filelist\n"
  13.124 +"  ------                            --------\n"
  13.125 +"  Up, C-p, k,                       Space     : add to playlist\n"
  13.126 +"  Down, C-n, j,                     a         : add recursively\n"
  13.127 +"  PgUp, PgDown,                     g         : goto\n"
  13.128 +"  Home, End : movement              Enter     : chdir or play\n"
  13.129 +"  Tab       : filelist/playlist     Backspace : parent dir\n"
  13.130 +"  n,p       : next/prev track       \n"
  13.131 +"  z,x       : toggle pause/stop     Playlist\n"
  13.132 +"  Left, Right,                      --------\n"
  13.133 +"  C-b, C-f  : seek                  Enter : play track\n"
  13.134 +"  C-s       : forward-isearch       Space : mark\n"
  13.135 +"  C-r       : backward-isearch      a,c   : mark/clear all\n"
  13.136 +"  C-g, Esc  : cancel                m     : move marked tracks\n"
  13.137 +"  1..9, +-  : volume                d     : delete marked tracks\n"
  13.138 +"  C-l       : refresh screen        r, R  : toggle repeat/Random mode\n"
  13.139 +"  h         : help                  s, S  : shuffle/Sort playlist\n"
  13.140 +"  q         : quit                  o     : save playlist (to .m3u file)\n"
  13.141 +msgstr ""
  13.142 +"\n"
  13.143 +"  Global                            Dateiliste\n"
  13.144 +"  ------                            ----------\n"
  13.145 +"  Up, C-p, k,                       Space     : Zu Playliste hinzufügen\n"
  13.146 +"  Down, C-n, j,                     a         : Füge rekursiv hinzu\n"
  13.147 +"  PgUp, PgDown,                     g         : Gehzu\n"
  13.148 +"  Home, End : Bewegung              Enter     : Wechsle Verzeichnis oder "
  13.149 +"spiele\n"
  13.150 +"  Tab       : Dateiliste/Spielliste Backspace : Übergeordnetes Verzeichnis\n"
  13.151 +"  n,p       : nächster/vorheriger Track\n"
  13.152 +"  z,x       : schalte Pause/Stop    Playliste\n"
  13.153 +"  Left, Right,                      ---------\n"
  13.154 +"  C-b, C-f  : Suche                 Enter : Spiele Track\n"
  13.155 +"  C-s       : Vorwärts-isuche       Space : markiere\n"
  13.156 +"  C-r       : Rückwärts-isuche      a,c   : markiere/lösche alle\n"
  13.157 +"  C-g, Esc  : Abbruch               m     : verschiebe markierte Tracks\n"
  13.158 +"  1..9, +-  : Lautstärke            d     : lösche markierte Tracks\n"
  13.159 +"  C-l       : stelle Bildschirm her r, R  : schalte widerhole/zufällig "
  13.160 +"Modus\n"
  13.161 +"  h         : Hilfe                 s, S  : durchwühle/ordne Playliste\n"
  13.162 +"  q         : Ende                  o     : speichere Playliste (als .m3u "
  13.163 +"Datei)\n"
  13.164 +
  13.165 +#: cplay:727 cplay:728
  13.166 +msgid "[repeat]"
  13.167 +msgstr "[wiederhole]"
  13.168 +
  13.169 +#: cplay:888
  13.170 +msgid "Random %s"
  13.171 +msgstr "Zufall %s"
  13.172 +
  13.173 +#: cplay:894
  13.174 +msgid "Save playlist"
  13.175 +msgstr "Speichere Playliste"
  13.176 +
  13.177 +#: cplay:629
  13.178 +msgid "goto"
  13.179 +msgstr "Gehzu"
  13.180 +
  13.181 +#: cplay:975
  13.182 +msgid "Stopped: %s"
  13.183 +msgstr "Gestoppt: %s"
  13.184 +
  13.185 +#: cplay:1209 cplay:1219
  13.186 +msgid "Cannot open mixer device %s"
  13.187 +msgstr "Kann Mixer-Device %s nicht öffnen."
  13.188 +
  13.189 +#: cplay:710
  13.190 +msgid "Playlist"
  13.191 +msgstr "Playliste"
  13.192 +
  13.193 +#: cplay:644
  13.194 +msgid "Not a directory!"
  13.195 +msgstr "Kein Verzeichnis!"
  13.196 +
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/po/hu.po	Wed Sep 27 09:22:32 2017 +0200
    14.3 @@ -0,0 +1,189 @@
    14.4 +# Copyright (C) 2001 Gergely Nagy <8@free.bsd.hu>
    14.5 +#
    14.6 +msgid ""
    14.7 +msgstr ""
    14.8 +"Project-Id-Version: 1.0\n"
    14.9 +"PO-Revision-Date: Thu Jun  7 12:29:48 CEST 2001\n"
   14.10 +"Last-Translator: Gergely Nagy <8@free.bsd.hu>\n"
   14.11 +"Language-Team: Gergely Nagy <8@free.bsd.hu>\n"
   14.12 +"MIME-Version: 1.0\n"
   14.13 +"Content-Type: text/plain; charset=iso-8859-1\n"
   14.14 +"Content-Transfer-Encoding: 8bit\n"
   14.15 +
   14.16 +#: cplay:828
   14.17 +msgid "Almost there..."
   14.18 +msgstr "Majdnem kész..."
   14.19 +
   14.20 +#: cplay:834
   14.21 +msgid "Cleared all marks..."
   14.22 +msgstr "Minden jel törölve..."
   14.23 +
   14.24 +#: cplay:746
   14.25 +msgid "IOError"
   14.26 +msgstr "I/O-hiba"
   14.27 +
   14.28 +#: cplay:882 cplay:888
   14.29 +msgid "off"
   14.30 +msgstr "ki"
   14.31 +
   14.32 +#: cplay:918
   14.33 +msgid "Cannot write playlist!"
   14.34 +msgstr "Nem tudom menteni a számlistát!"
   14.35 +
   14.36 +#: cplay:412
   14.37 +msgid "forward-isearch"
   14.38 +msgstr "keresés előre"
   14.39 +
   14.40 +#: cplay:539
   14.41 +msgid "Not found: %s"
   14.42 +msgstr "Nem található: %s"
   14.43 +
   14.44 +#: cplay:727
   14.45 +msgid "Playlist %s %s"
   14.46 +msgstr "Számlista %s %s"
   14.47 +
   14.48 +#: cplay:411
   14.49 +msgid "backward-isearch"
   14.50 +msgstr "keresés hátra"
   14.51 +
   14.52 +#: cplay:878
   14.53 +msgid "Sorted playlist..."
   14.54 +msgstr "Rendezett számlista..."
   14.55 +
   14.56 +#: cplay:205
   14.57 +msgid "Help"
   14.58 +msgstr "Segítség"
   14.59 +
   14.60 +#: cplay:419 cplay:916
   14.61 +msgid "ok"
   14.62 +msgstr "ok"
   14.63 +
   14.64 +#: cplay:958
   14.65 +msgid "Paused: %s"
   14.66 +msgstr "Szünetel: %s"
   14.67 +
   14.68 +#: cplay:882
   14.69 +msgid "Repeat %s"
   14.70 +msgstr "Ismétel %s"
   14.71 +
   14.72 +#: cplay:882 cplay:888
   14.73 +msgid "on"
   14.74 +msgstr "be"
   14.75 +
   14.76 +#: cplay:742 cplay:749
   14.77 +msgid "Added: %s"
   14.78 +msgstr "Hozzáadva: %s"
   14.79 +
   14.80 +#: cplay:728 cplay:729
   14.81 +msgid "[random]"
   14.82 +msgstr "[véletlenszerű]"
   14.83 +
   14.84 +#: cplay:872
   14.85 +msgid "Shuffled playlist... Oops?"
   14.86 +msgstr "Kevert számlista... Hopsz?"
   14.87 +
   14.88 +#: cplay:418 cplay:639 cplay:904
   14.89 +msgid "cancel"
   14.90 +msgstr "mégsem"
   14.91 +
   14.92 +#: cplay:463
   14.93 +msgid "probably not ok"
   14.94 +msgstr "valószinűleg nem jó"
   14.95 +
   14.96 +#: cplay:575
   14.97 +msgid "Reading directory..."
   14.98 +msgstr "Könyvtár olvasása..."
   14.99 +
  14.100 +#: cplay:1162
  14.101 +msgid "Player not found!"
  14.102 +msgstr "Lejátszó nem található!"
  14.103 +
  14.104 +#: cplay:611
  14.105 +msgid "Filelist: "
  14.106 +msgstr "Állománylista: "
  14.107 +
  14.108 +#: cplay:1240
  14.109 +msgid "Usage: %s [ filename | playlist.m3u ] ...\n"
  14.110 +msgstr "Használat: %s [ állományok | számlista.m3u ] ...\n"
  14.111 +
  14.112 +#: cplay:1217
  14.113 +msgid "Volume %d%%"
  14.114 +msgstr "Hangerő %d%%"
  14.115 +
  14.116 +#: cplay:941 cplay:964
  14.117 +msgid "Playing: %s"
  14.118 +msgstr "Lejátszás: %s"
  14.119 +
  14.120 +#: cplay:181
  14.121 +msgid ""
  14.122 +"\n"
  14.123 +"  Global                            Filelist\n"
  14.124 +"  ------                            --------\n"
  14.125 +"  Up, C-p, k,                       Space     : add to playlist\n"
  14.126 +"  Down, C-n, j,                     a         : add recursively\n"
  14.127 +"  PgUp, PgDown,                     g         : goto\n"
  14.128 +"  Home, End : movement              Enter     : chdir or play\n"
  14.129 +"  Tab       : filelist/playlist     Backspace : parent dir\n"
  14.130 +"  n,p       : next/prev track       \n"
  14.131 +"  z,x       : toggle pause/stop     Playlist\n"
  14.132 +"  Left, Right,                      --------\n"
  14.133 +"  C-b, C-f  : seek                  Enter : play track\n"
  14.134 +"  C-s       : forward-isearch       Space : mark\n"
  14.135 +"  C-r       : backward-isearch      a,c   : mark/clear all\n"
  14.136 +"  C-g, Esc  : cancel                m     : move marked tracks\n"
  14.137 +"  1..9, +-  : volume                d     : delete marked tracks\n"
  14.138 +"  C-l       : refresh screen        r, R  : toggle repeat/Random mode\n"
  14.139 +"  h         : help                  s, S  : shuffle/Sort playlist\n"
  14.140 +"  q         : quit                  o     : save playlist (to .m3u file)\n"
  14.141 +msgstr ""
  14.142 +"\n"
  14.143 +"  Általános                         Állománylista\n"
  14.144 +"  ---------                         -------------\n"
  14.145 +"  Fel, C-p, k,                      Szóköz    : hozzáadás a számlistához\n"
  14.146 +"  Le, C-n, j,                       a         : rekurzív hozzáadás\n"
  14.147 +"  PgUp, PgDown,                     g         : ugrás\n"
  14.148 +"  Home, End : mozgás                Enter     : könyvtárváltás vagy lejátszás\n"
  14.149 +"  Tab       : állomány/számlista    Backspace : előző könyvtár\n"
  14.150 +"  n,p       : előző/következp szám  \n"
  14.151 +"  z,x       : szünet/megállítás     Számlista\n"
  14.152 +"  Baéra, Jobbra,                    ---------\n"
  14.153 +"  C-b, C-f  : tekerés               Enter  : szám lejátszása\n"
  14.154 +"  C-s       : keresés előre         Szóköz : megjelőlés\n"
  14.155 +"  C-r       : keresés hátra         a,c    : összes megjelőlése/jelölés törlése\n"
  14.156 +"  C-g, Esc  : mégse                 m      : kijelölt számok mozgatása\n"
  14.157 +"  1..9, +-  : hangerő               d      : kijelölt számok törlése\n"
  14.158 +"  C-l       : képernyő frissítés    r, R   : ismétlő/kevert mód váltása\n"
  14.159 +"  h         : segítség              s, S   : számlista keverése/rendezése\n"
  14.160 +"  q         : kilépés               o      : számlista mentése (.m3u állományba)\n"
  14.161 +
  14.162 +#: cplay:727 cplay:728
  14.163 +msgid "[repeat]"
  14.164 +msgstr "[ismétlés]"
  14.165 +
  14.166 +#: cplay:888
  14.167 +msgid "Random %s"
  14.168 +msgstr "Véletlenszerű %s"
  14.169 +
  14.170 +#: cplay:894
  14.171 +msgid "Save playlist"
  14.172 +msgstr "Számlista mentése"
  14.173 +
  14.174 +#: cplay:629
  14.175 +msgid "goto"
  14.176 +msgstr "ugrás"
  14.177 +
  14.178 +#: cplay:975
  14.179 +msgid "Stopped: %s"
  14.180 +msgstr "Megállítva: %s"
  14.181 +
  14.182 +#: cplay:1209 cplay:1219
  14.183 +msgid "Cannot open mixer device %s"
  14.184 +msgstr "A %s keverő-eszköz nem nyitható meg"
  14.185 +
  14.186 +#: cplay:710
  14.187 +msgid "Playlist"
  14.188 +msgstr "Számlista"
  14.189 +
  14.190 +#: cplay:644
  14.191 +msgid "Not a directory!"
  14.192 +msgstr "Nem könyvtár!"
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/po/pl.po	Wed Sep 27 09:22:32 2017 +0200
    15.3 @@ -0,0 +1,320 @@
    15.4 +# Copyright (C) 2003 Marcin D. Mikielewicz <perry@ry.pl>
    15.5 +#
    15.6 +msgid ""
    15.7 +msgstr ""
    15.8 +"Project-Id-Version: 1.0\n"
    15.9 +"PO-Revision-Date: Mon Apr 14 02:36:52 2003\n"
   15.10 +"Last-Translator: Marcin D. Mikielewicz <perry@ry.pl>\n"
   15.11 +"Language-Team: Marcin D. Mikielewicz <perry@ry.pl>\n"
   15.12 +"MIME-Version: 1.0\n"
   15.13 +"Content-Type: text/plain; charset=iso-8859-2\n"
   15.14 +"Content-Transfer-Encoding: 8bit\n"
   15.15 +
   15.16 +#: cplay:287
   15.17 +msgid "elapsed"
   15.18 +msgstr "odegranego"
   15.19 +
   15.20 +#: cplay:287
   15.21 +msgid "remaining"
   15.22 +msgstr "pozostałego"
   15.23 +
   15.24 +#: cplay:288
   15.25 +msgid "Counting %s time"
   15.26 +msgstr "Licznik %s czasu"
   15.27 +
   15.28 +#: cplay:320
   15.29 +msgid "Quit? (y/N)"
   15.30 +msgstr "Wyj¶ć (y/N)"
   15.31 +
   15.32 +#: cplay:405
   15.33 +msgid "backward-isearch"
   15.34 +msgstr "szukanie-wstecz"
   15.35 +
   15.36 +#: cplay:407
   15.37 +msgid "forward-isearch"
   15.38 +msgstr "szukanie-do przodu"
   15.39 +
   15.40 +#: cplay:496 cplay:657 cplay:748 cplay:760 cplay:1148
   15.41 +msgid "ok"
   15.42 +msgstr "ok"
   15.43 +
   15.44 +#: cplay:505 cplay:517
   15.45 +msgid "Not found: %s "
   15.46 +msgstr "Nie znaleziono: %s"
   15.47 +
   15.48 +#: cplay:525
   15.49 +msgid "Help"
   15.50 +msgstr "Pomoc"
   15.51 +
   15.52 +#: cplay:528
   15.53 +msgid ""
   15.54 +"\n"
   15.55 +"  Global                               Space : tag/untag current\n"
   15.56 +"  ------                               t, T  : tag current/regex\n"
   15.57 +"  Up, Down, k, j, C-p, C-n,            u, U  : untag current/regex\n"
   15.58 +"  PgUp, PgDn, K, J,                    i     : invert tags\n"
   15.59 +"  Home, End, g, G : movement\n"
   15.60 +"  Enter           : chdir or play      Filelist\n"
   15.61 +"  Tab             : filelist/playlist  --------\n"
   15.62 +"  n, p            : next/prev track    BkSpc : parent dir\n"
   15.63 +"  z, x            : toggle pause/stop  o, s  : goto, search recursively\n"
   15.64 +"  Left, Right,                         a     : add (tagged) to playlist\n"
   15.65 +"  C-f, C-b    : seek forward/backward\n"
   15.66 +"  c           : counter mode           Playlist\n"
   15.67 +"  C-s, C-r, / : isearch                --------\n"
   15.68 +"  C-g, Esc    : cancel                 d, D  : delete (tagged) tracks/playlist\n"
   15.69 +"  1..9, +, -  : volume control         m, M  : move tagged tracks after/before\n"
   15.70 +"  v           : PCM or MASTER volume   r, R  : toggle repeat/Random mode\n"
   15.71 +"  C-l, l      : refresh, list mode     s, S  : shuffle/Sort playlist\n"
   15.72 +"  h, q, Q     : help, quit?, Quit!     w, @  : write playlist, jump to current\n"
   15.73 +"\n"
   15.74 +"\n"
   15.75 +"\n"
   15.76 +"  Goodies\n"
   15.77 +"  -------\n"
   15.78 +"  b, '   : set/get filelist bookmark\n"
   15.79 +"  X      : stop playlist after each track\n"
   15.80 +"  C-a, ^ : seek to beginning of current track (restart)\n"
   15.81 +"  C-e, $ : seek to end of current track (useless?)\n"
   15.82 +"  !      : shell command ($@ = tagged or current)\n"
   15.83 +"  "
   15.84 +msgstr ""
   15.85 +"\n"
   15.86 +"  Globalne                             Spacja: zaznacz/odznacz bież±cy\n"
   15.87 +"  ------                               t, T  : zaznacz bież±cy/regex\n"
   15.88 +"  Góra, Dół, k, j, C-p, C-n,           u, U  : odznacz bież±cy/regex\n"
   15.89 +"  PgUp, PgDn, K, J,                    i     : odwróć zaznaczenia\n"
   15.90 +"  Home, End, g, G : poruszanie się\n"
   15.91 +"  Enter           : chdir or play      Lista plików\n"
   15.92 +"  Tab             : filelist/playlist  --------\n"
   15.93 +"  n, p       : następny/poprzedniutwór BkSpc : katalog macierzysty\n"
   15.94 +"  z, x       : przeł±cznik pauza/stop  o, s  : idĽ do, szukaj rekursywnie\n"
   15.95 +"  Lewo, Prawo,                         a     : dodaj (zazanaczone) do listy odgrywania\n"
   15.96 +"  C-f, C-b    : szukaj w przód/wstecz\n"
   15.97 +"  c           : tryb licznika          Lista odgrywania\n"
   15.98 +"  C-s, C-r, / : szukanie               --------\n"
   15.99 +"  C-g, Esc    : anuluj                 d, D  : delete (tagged) tracks/playlist\n"
  15.100 +"  1..9, +, -  : kontrola gło¶no¶ci     m, M  : move tagged tracks after/before\n"
  15.101 +"  v           : gło¶no¶ć PCM lub MASTER r, R  : przeł±cznik trybu powtarzania/losowo¶ci\n"
  15.102 +"  C-l, l      : od¶wież, tryb listy    s, S  : przetasuj/Sortuj listę odtwarzania\n"
  15.103 +"  h, q, Q     : pomoc,wyj¶cie?,Wyj¶cie! w, @  : zapisz listę odtwarzania, skocz do bież±cego\n"
  15.104 +"\n"
  15.105 +"\n"
  15.106 +"\n"
  15.107 +"  Dobrodziejstwa\n"
  15.108 +"  --------------\n"
  15.109 +"  b, '   : ustaw/pobierz listę plików z zakładki\n"
  15.110 +"  X      : zatrzymaj listę utworów po każdym utworze\n"
  15.111 +"  C-a, ^ : szukaj do pocz±tku od bież±cego utworu (restart)\n"
  15.112 +"  C-e, $ : szukaj do końca bież±cego utworu (użyteczne?)\n"
  15.113 +"  !      : komenda powłoki ($@ = zaznaczone lub bież±ce)\n"
  15.114 +"  "
  15.115 +
  15.116 +#: cplay:593
  15.117 +msgid "filename"
  15.118 +msgstr "plików"
  15.119 +
  15.120 +#: cplay:594
  15.121 +msgid "pathname"
  15.122 +msgstr "¶cieżek"
  15.123 +
  15.124 +#: cplay:625
  15.125 +msgid "Listing %s"
  15.126 +msgstr "Lista %s"
  15.127 +
  15.128 +#: cplay:648
  15.129 +msgid "Tag regexp"
  15.130 +msgstr "Zaznacz wyrażenie"
  15.131 +
  15.132 +#: cplay:648
  15.133 +msgid "Untag regexp"
  15.134 +msgstr "Odznacz wyrażenie"
  15.135 +
  15.136 +#: cplay:687
  15.137 +msgid "shell$ "
  15.138 +msgstr "powłoka$ "
  15.139 +
  15.140 +#: cplay:700
  15.141 +msgid "\nshell returned %s, press return!\n"
  15.142 +msgstr "\npowłoka zwróciła %s, wci¶nij enter!\n"
  15.143 +
  15.144 +#: cplay:734
  15.145 +msgid "bookmark"
  15.146 +msgstr "zakładka"
  15.147 +
  15.148 +#: cplay:750
  15.149 +msgid "Not found!"
  15.150 +msgstr "Nie znaleziono!"
  15.151 +
  15.152 +#: cplay:755
  15.153 +msgid "set bookmark"
  15.154 +msgstr "ustaw zakładkę"
  15.155 +
  15.156 +#: cplay:764
  15.157 +msgid "search"
  15.158 +msgstr "szukaj"
  15.159 +
  15.160 +#: cplay:771
  15.161 +msgid "Searching..."
  15.162 +msgstr "Wyszukiwanie..."
  15.163 +
  15.164 +#: cplay:782
  15.165 +msgid "search results"
  15.166 +msgstr "wyniki wyszukiwania"
  15.167 +
  15.168 +#: cplay:802
  15.169 +msgid "Filelist: "
  15.170 +msgstr "Lista plików: "
  15.171 +
  15.172 +#: cplay:813
  15.173 +msgid "Reading directory..."
  15.174 +msgstr "Wczytywanie katalogu..."
  15.175 +
  15.176 +#: cplay:868
  15.177 +msgid "goto"
  15.178 +msgstr "idĽ do"
  15.179 +
  15.180 +#: cplay:874
  15.181 +msgid "Not a directory!"
  15.182 +msgstr "To nie jest katalog!"
  15.183 +
  15.184 +#: cplay:919
  15.185 +msgid "metadata"
  15.186 +msgstr "metadata"
  15.187 +
  15.188 +#: cplay:925
  15.189 +msgid "Playlist %s %s %s"
  15.190 +msgstr "Lista utworów %s %s %s"
  15.191 +
  15.192 +#: cplay:926
  15.193 +msgid "[repeat]"
  15.194 +msgstr "[powtarzanie]"
  15.195 +
  15.196 +#: cplay:927
  15.197 +msgid "[random]"
  15.198 +msgstr "[losowo]"
  15.199 +
  15.200 +#: cplay:928
  15.201 +msgid "[stop]"
  15.202 +msgstr "[zatrzymano]"
  15.203 +
  15.204 +#: cplay:967
  15.205 +msgid "File(\\d+)=(.*)"
  15.206 +msgstr "Plik(\\d+)=(.*)"
  15.207 +
  15.208 +#: cplay:981 cplay:1108
  15.209 +msgid "Working..."
  15.210 +msgstr "Zajety..."
  15.211 +
  15.212 +#: cplay:990
  15.213 +msgid "Added: %s"
  15.214 +msgstr "Dodano: %s"
  15.215 +
  15.216 +#: cplay:990
  15.217 +msgid "Deleted playlist"
  15.218 +msgstr "Skasowano listę utworów"
  15.219 +
  15.220 +#: cplay:1105
  15.221 +msgid "Shuffled playlist... Oops?"
  15.222 +msgstr "Przetasowano listę utworów... Oops?"
  15.223 +
  15.224 +#: cplay:1112
  15.225 +msgid "Sorted playlist"
  15.226 +msgstr "Posortowano listę utworów"
  15.227 +
  15.228 +#: cplay:1115
  15.229 +msgid "repeat"
  15.230 +msgstr "powtarzanie"
  15.231 +
  15.232 +#: cplay:1115
  15.233 +msgid "Repeat: %s"
  15.234 +msgstr "Powtarzanie: %s"
  15.235 +
  15.236 +#: cplay:1118
  15.237 +msgid "random"
  15.238 +msgstr "losowanie"
  15.239 +
  15.240 +#: cplay:1118
  15.241 +msgid "Random: %s"
  15.242 +msgstr "Losowo¶ć: %s"
  15.243 +
  15.244 +#: cplay:1124 cplay:1375
  15.245 +msgid "stop"
  15.246 +msgstr "zatrzymanie"
  15.247 +
  15.248 +#: cplay:1124
  15.249 +msgid "Stop playlist: %s"
  15.250 +msgstr "Zatrzymano listę utworów: %s"
  15.251 +
  15.252 +#: cplay:1128
  15.253 +msgid "on"
  15.254 +msgstr "wł±czone"
  15.255 +
  15.256 +#: cplay:1128
  15.257 +msgid "off"
  15.258 +msgstr "wył±czone"
  15.259 +
  15.260 +#: cplay:1134
  15.261 +msgid "Save playlist"
  15.262 +msgstr "Zapisz listę utworów"
  15.263 +
  15.264 +#: cplay:1310
  15.265 +msgid "Stopped: %s"
  15.266 +msgstr "Zatrzymano: %s"
  15.267 +
  15.268 +#: cplay:1312
  15.269 +msgid "Paused: %s"
  15.270 +msgstr "Pauza: %s"
  15.271 +
  15.272 +#: cplay:1314
  15.273 +msgid "Playing: %s"
  15.274 +msgstr "Odtwarzanie: %s"
  15.275 +
  15.276 +#: cplay:1318
  15.277 +msgid "Time.*\\s(\\d+):(\\d+).*\\[(\\d+):(\\d+)"
  15.278 +msgstr "Czas.*\\s(\\d+):(\\d+).*\\[(\\d+):(\\d+)"
  15.279 +
  15.280 +#: cplay:1369
  15.281 +msgid "pause"
  15.282 +msgstr "pauza"
  15.283 +
  15.284 +#: cplay:1370
  15.285 +msgid "next"
  15.286 +msgstr "następny"
  15.287 +
  15.288 +#: cplay:1371
  15.289 +msgid "prev"
  15.290 +msgstr "poprzedni"
  15.291 +
  15.292 +#: cplay:1372
  15.293 +msgid "forward"
  15.294 +msgstr "do przodu"
  15.295 +
  15.296 +#: cplay:1373
  15.297 +msgid "backward"
  15.298 +msgstr "wstecz"
  15.299 +
  15.300 +#: cplay:1374
  15.301 +msgid "play"
  15.302 +msgstr "odtwarzaj"
  15.303 +
  15.304 +#: cplay:1491
  15.305 +msgid "Player not found!"
  15.306 +msgstr "Nie znaleziono programu odtwarzaj±cego!"
  15.307 +
  15.308 +#: cplay:1548
  15.309 +msgid "Volume %s%%"
  15.310 +msgstr "Gło¶no¶ć %s%%"
  15.311 +
  15.312 +#: cplay:1556
  15.313 +msgid "%s volume %s%%"
  15.314 +msgstr "%s gło¶no¶ć %s%%"
  15.315 +
  15.316 +#: cplay:1587
  15.317 +msgid "cancel"
  15.318 +msgstr "anulowanie"
  15.319 +
  15.320 +#: cplay:1375
  15.321 +msgid "Usage: %s [-rRv] [ file | dir | playlist ] ...\n"
  15.322 +msgstr "Użycie: %s [-rRv] [ plik | katalog | lista utworów ] ...\n"
  15.323 +