diff --git a/Changelog b/Changelog
index 6a13f504..d53b449a 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,10 @@
+2019-05-27 s-n-g
+ * Version 0.7.6
+ * Added "e" option to change station's encoding.
+ * Implemented playlist backup and recovery, to address saving errors.
+ * Inform user when playlist not found and of playlist recovery result.
+ * Parameter -s - Check if file is supported (ends with .csv)
+
2019-04-18 s-n-g
* Version 0.7.5
* Minimum python version supported changed. Now it's 2.7+/3.5+
diff --git a/README.html b/README.html
index 2ad4ecb0..9fee13d4 100644
--- a/README.html
+++ b/README.html
@@ -154,6 +154,7 @@
Managing playlists (within PyRadio)
Once PyRadio has been loaded, one can perform a series of actions on the current playlist and set of playlists saved in its configuration directory.
Currently, the following actions are available:
One thing you may want to do is remove a station from a playlist, e.g. when found that it not longer works. You can do that by pressing “DEL” or “x”.
+Pressing “e” will enable you to change the encoding of the selected station. If the station is currently playing, playback will be restarted so that the encoding’s change takes effect (hopefully correctly displaying the station/song title).
Then, when this is done, you can either save the modified playlist, by pressing “s”, or reload the playlist from disk, by pressing “R”. A modified playlist will automatically be saved when PyRadio exits (or Ctrl-C is pressed).
Finally, opening another playlist is also possible. Just press “o” and you will be presented with a list of saved playlists to choose from. These playlists must be saved beforehand in PyRadio’s configuration directory.
While executing any of the previous actions, you may get confirmation messages (when opening a playlist while the current one is modified but not saved, for example) or error messages (when an action fails). Just follow the on screen information, keeping in mind that a capital letter as an answer will save this answer in PyRadio’s configuration file for future reference.
@@ -180,7 +181,7 @@ Station by station encoding dec
Finally, we insert the new station to the playlist:
Station1,Station1_URL,
Station2,Station2_URL,iso-8859-7
-Note: Using the -a command line option will save you all this trouble, as it will automatically take care of creating a valid CSV file.
+Note: Using the -a command line option will save you all this trouble, as it will automatically take care of creating a valid CSV file. Alternatively, you can change the selected station’s encoding by pressing “e” while in PyRadio.
Global encoding declaration
PyRadio’s configuration file contains the parameter default_encoding, which by default is set to utf-8.
Setting this parameter to a different encoding, will permit PyRadio to successfully decode such stations.
diff --git a/README.md b/README.md
index c7b13db3..05cadded 100644
--- a/README.md
+++ b/README.md
@@ -177,6 +177,8 @@ Currently, the following actions are available:
One thing you may want to do is remove a station from a playlist, e.g. when found that it not longer works. You can do that by pressing "***DEL***" or "***x***".
+Pressing "***e***" will enable you to change the encoding of the selected station. If the station is currently playing, playback will be restarted so that the encoding's change takes effect (hopefully correctly displaying the station/song title).
+
Then, when this is done, you can either save the modified playlist, by pressing "***s***", or reload the playlist from disk, by pressing "***R***". A modified playlist will automatically be saved when **PyRadio** exits (or Ctrl-C is pressed).
Finally, opening another playlist is also possible. Just press "***o***" and you will be presented with a list of saved playlists to choose from. These playlists must be saved beforehand in **PyRadio**'s configuration directory.
@@ -237,7 +239,7 @@ Station2,Station2_URL,iso-8859-7
```
**Note:**
-Using the ***-a*** command line option will save you all this trouble, as it will automatically take care of creating a valid ***CSV*** file.
+Using the ***-a*** command line option will save you all this trouble, as it will automatically take care of creating a valid ***CSV*** file. Alternatively, you can change the selected station's encoding by pressing "***e***" while in **PyRadio**.
### Global encoding declaration
diff --git a/pyradio.1 b/pyradio.1
index d4588c83..15f25bbf 100644
--- a/pyradio.1
+++ b/pyradio.1
@@ -1,7 +1,7 @@
.\" Copyright (C) 2011 Ben Dowling
.\" This manual is freely distributable under the terms of the GPL.
.\"
-.TH PYRADIO 1 "April 2019"
+.TH PYRADIO 1 "June 2019"
.SH NAME
.PP
@@ -178,6 +178,8 @@ Currently, the following actions are available:
One thing you may want to do is remove a station from a playlist, e.g. when found that it not longer works. You can do that by pressing "\fIDEL\fR" or "\fIx\fR".
+Pressing "\fIe\fR" will enable you to change the encoding of the selected station. If the station is currently playing, playback will be restarted so that the encoding's change takes effect (hopefully correctly displaying the station/song title).
+
Then, when this is done, you can either save the modified playlist, by pressing "\fIs\fR", or reload the playlist from disk, by pressing "\fIR\fR". A modified playlist will \fIautomatically\fR be saved when \fBpyradio\fR exits (or Ctrl-C is pressed).
Finally, opening another playlist is also possible. Just press "\fIo\fR" and you will be presented with a list of saved playists to choose from. These playlists must be saved beforehand in \fBpyradio\fR's configuration directory.
@@ -248,7 +250,8 @@ Finally, we insert the new station to the playlist:
Station2\fB,\fIStation2_URL\fB,\fIiso-8859-7
.IP \fBNote\fR
-Using the \fB-a\fR command line option will save you all this trouble, as it will automatically take care of creating a valid \fBCSV\fR file.
+Using the \fB-a\fR command line option will save you all this trouble, as it will automatically take care of creating a valid \fBCSV\fR file. Alternatively, you can change the selected station's encoding by pressing "\fIe\fR" while in \fBpyradio\fR.
+
.PP
.B
diff --git a/pyradio/__init__.py b/pyradio/__init__.py
index 0c36a77d..fe04baca 100644
--- a/pyradio/__init__.py
+++ b/pyradio/__init__.py
@@ -1,6 +1,6 @@
" pyradio -- Console radio player. "
-version_info = (0, 7, 5)
+version_info = (0, 7, 6)
__version__ = version = '.'.join(map(str, version_info))
__project__ = __name__
diff --git a/pyradio/common.py b/pyradio/common.py
index 480e195c..9ece042f 100644
--- a/pyradio/common.py
+++ b/pyradio/common.py
@@ -10,9 +10,11 @@
SELECT_ENCODING_MODE = 6
SELECT_PLAYLIST_MODE = 7
SELECT_STATION_MODE = 8
+SELECT_STATION_ENCODING_MODE = 9
REMOVE_STATION_MODE = 50
SAVE_PLAYLIST_MODE = 51
-ASK_TO_SAVE_PLAYLIST_MODE = 52
+ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE = 52
+ASK_TO_SAVE_PLAYLIST_WHEN_EXITING_MODE = 53
MAIN_HELP_MODE = 100
PLAYLIST_HELP_MODE = 101
CONFIG_HELP_MODE = 102
@@ -21,13 +23,15 @@
SELECT_ENCODING_HELP_MODE = 105
SELECT_PLAYLIST_HELP_MODE = 106
SELECT_STATION_HELP_MODE = 107
-PLAYLIST_LOAD_ERROR_MODE = 200
-PLAYLIST_RELOAD_ERROR_MODE = 201
-PLAYLIST_RELOAD_CONFIRM_MODE = 202
-PLAYLIST_DIRTY_RELOAD_CONFIRM_MODE = 203
-PLAYLIST_SCAN_ERROR_MODE = 204
-SAVE_PLAYLIST_ERROR_1_MODE = 205
-SAVE_PLAYLIST_ERROR_2_MODE = 206
+PLAYLIST_RECOVERY_ERROR_MODE = 200
+PLAYLIST_NOT_FOUND_ERROR_MODE = 201
+PLAYLIST_LOAD_ERROR_MODE = 202
+PLAYLIST_RELOAD_ERROR_MODE = 203
+PLAYLIST_RELOAD_CONFIRM_MODE = 204
+PLAYLIST_DIRTY_RELOAD_CONFIRM_MODE = 205
+PLAYLIST_SCAN_ERROR_MODE = 206
+SAVE_PLAYLIST_ERROR_1_MODE = 207
+SAVE_PLAYLIST_ERROR_2_MODE = 208
FOREIGN_PLAYLIST_ASK_MODE = 300
FOREIGN_PLAYLIST_MESSAGE_MODE = 301
FOREIGN_PLAYLIST_COPY_ERROR_MODE = 302
diff --git a/pyradio/config.py b/pyradio/config.py
index 94a406c6..619f44c7 100644
--- a/pyradio/config.py
+++ b/pyradio/config.py
@@ -5,7 +5,7 @@
import glob
import curses
import collections
-from os import path, getenv, makedirs, remove
+from os import path, getenv, makedirs, remove, rename
from time import ctime
from datetime import datetime
from shutil import copyfile, move
@@ -43,6 +43,8 @@ class PyRadioStations(object):
dirty_playlist = False
+ playlist_recovery_result = 0
+
def __init__(self, stationFile=''):
if sys.platform.startswith('win'):
@@ -149,6 +151,7 @@ def _get_playlist_abspath_from_data(self, stationFile=''):
-2 - playlist not found
-3 - negative number specified
-4 - number not found
+ -8 - file type not supported
"""
ret = -1
orig_input = stationFile
@@ -158,6 +161,8 @@ def _get_playlist_abspath_from_data(self, stationFile=''):
stationFile = path.abspath(stationFile)
else:
""" try to find it in config dir """
+ if path.exists(stationFile):
+ return '', -8
stationFile += '.csv'
stationFile = path.join(self.stations_dir, stationFile)
if path.exists(stationFile) and path.isfile(stationFile):
@@ -196,16 +201,24 @@ def read_playlist_file(self, stationFile=''):
x - number of stations or
-1 - playlist is malformed
-2 - playlist not found
+ -7 - playlist recovery failed
+ -8 - file not supported
"""
- prev_file = self.stations_file
- prev_format = self.new_format
- self.new_format = False
ret = 0
stationFile, ret = self._get_playlist_abspath_from_data(stationFile)
if ret < 0:
+ # returns -2, -3, -4 or -8
return ret
+ self.playlist_recovery_result = self._recover_backed_up_playlist(stationFile)
+ if self.playlist_recovery_result > 0:
+ # playlist recovery failed
+ # reason in cnf.playlist_recovery_result
+ return -7
+ prev_file = self.stations_file
+ prev_format = self.new_format
+ self.new_format = False
self._reading_stations = []
with open(stationFile, 'r') as cfgfile:
try:
@@ -214,10 +227,10 @@ def read_playlist_file(self, stationFile=''):
continue
try:
name, url = [s.strip() for s in row]
- self._reading_stations.append((name, url, ''))
+ self._reading_stations.append([name, url, ''])
except:
name, url, enc = [s.strip() for s in row]
- self._reading_stations.append((name, url, enc))
+ self._reading_stations.append([name, url, enc])
self.new_format = True
except:
self._reading_stations = []
@@ -238,6 +251,46 @@ def read_playlist_file(self, stationFile=''):
logger.debug('Playlist is in old format')
return self.number_of_stations
+ def _recover_backed_up_playlist(self, stationFile):
+ """ If a playlist backup file exists (.txt file), try to
+ recover it (rename it to .csv)
+
+ Return:
+ -1: playlist recovered
+ 0: no back up file found
+ 1: remove (empty) csv file failed
+ 2: rename txt to csv failed """
+ backup_stationFile = stationFile.replace('.csv', '.txt')
+ if path.isfile(backup_stationFile):
+ if logging:
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('Trying to recover backed up playlist: "{}"'.format(backup_stationFile))
+ else:
+ print('Trying to recover backed up playlist:\n "{}"'.format(backup_stationFile))
+
+
+ if path.isfile(stationFile):
+ try:
+ remove(stationFile)
+ except:
+ # remove failed
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('Playlist recovery failed: Cannot remove CSV file')
+ return 1
+ try:
+ rename(backup_stationFile, stationFile)
+ except:
+ # rename failed
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('Playlist recovery failed: Cannot rename TXT file to CSV')
+ return 2
+ # playlist recovered
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('Playlist recovery successful!!!')
+ return -1
+ # no playlist back up found
+ return 0
+
def _playlist_format_changed(self):
""" Check if we have new or old format
and report if format has changed
diff --git a/pyradio/main.py b/pyradio/main.py
index 7f37e290..d7cc16ac 100644
--- a/pyradio/main.py
+++ b/pyradio/main.py
@@ -198,6 +198,26 @@ def print_playlist_selection_error(a_selection, cnf, ret, exit_if_malformed=True
elif ret == -6:
print('Error: Failed to rename playlist')
sys.exit(1)
+ elif ret == -7:
+ print('Error: Playlist recovery failed!\n')
+ if cnf.playlist_recovery_result == 1:
+ msg = """Both a playlist file (CSV) and a playlist backup file (TXT)
+exist for the selected playlist. In this case, PyRadio would
+try to delete the CSV file, and then rename the TXT file to CSV.\n
+Unfortunately, deleting the CSV file has failed, so you have to
+manually address the issue."""
+ else:
+ msg = """A playlist backup file (TXT) has been found for the selected
+playlist. In this case, PyRadio would try to rename this file
+to CSV.\n
+Unfortunately, renaming this file has failed, so you have to
+manually address the issue."""
+ print(msg)
+ #open_conf_dir(cnf)
+ sys.exit(1)
+ elif ret == -8:
+ print('File type not supported')
+ sys.exit(1)
def open_conf_dir(cnf):
import subprocess
diff --git a/pyradio/radio.py b/pyradio/radio.py
index 917bc19b..fda9c159 100644
--- a/pyradio/radio.py
+++ b/pyradio/radio.py
@@ -8,6 +8,7 @@
# Spiros Georgaras - 2018
import curses
+import threading
import logging
import os
import random
@@ -87,6 +88,8 @@ class PyRadio(object):
_old_config_encoding = ''
+ #_recovery_lock = threading.Lock()
+
def __init__(self, pyradio_config, play=False, req_player='', theme=''):
self._cnf = pyradio_config
if theme:
@@ -268,11 +271,7 @@ def initBody(self):
# if logger.isEnabledFor(logging.DEBUG):
# logger.debug('MODE: THEME_HELP_MODE => THEME_MODE')
# make sure selected is visible
- max_lines = self.maxY - 4
- if self.number_of_items < max_lines:
- self.startPos = 0
- elif not self.startPos <= self.selection < self.startPos + max_lines:
- self._put_selection_in_the_middle()
+ self._put_selection_in_the_middle()
self.refreshBody()
def initFooter(self):
@@ -289,6 +288,7 @@ def refreshBody(self):
self.operation_mode == SELECT_PLAYER_HELP_MODE:
self._player_select_win.refresh_and_resize(self.bodyMaxY, self.bodyMaxX)
elif self.operation_mode == SELECT_ENCODING_MODE or \
+ self.operation_mode == SELECT_STATION_ENCODING_MODE or \
self.operation_mode == SELECT_ENCODING_HELP_MODE:
self._encoding_select_win.refresh_and_resize(self.bodyMaxY, self.bodyMaxX)
elif self.operation_mode == SELECT_PLAYLIST_MODE or \
@@ -352,7 +352,8 @@ def _print_body_header(self):
self.bodyWin.addstr(']',curses.color_pair(5))
elif cur_mode == PLAYLIST_MODE or \
- self.operation_mode == PLAYLIST_LOAD_ERROR_MODE:
+ self.operation_mode == PLAYLIST_LOAD_ERROR_MODE or \
+ self.operation_mode == PLAYLIST_NOT_FOUND_ERROR_MODE:
""" display playlists header """
w_header = ' Select playlist to open '
self.bodyWin.addstr(0,
@@ -375,7 +376,8 @@ def __displayBodyLine(self, lineNum, pad, station):
self.bodyWin.hline(lineNum + 1, 1, ' ', body_width, col)
if self.operation_mode == PLAYLIST_MODE or \
- self.operation_mode == PLAYLIST_LOAD_ERROR_MODE:
+ self.operation_mode == PLAYLIST_LOAD_ERROR_MODE or \
+ self.operation_mode == PLAYLIST_NOT_FOUND_ERROR_MODE:
line = self._format_playlist_line(lineNum, pad, station)
else:
line = "{0}. {1}".format(str(lineNum + self.startPos + 1).rjust(pad), station[0])
@@ -469,10 +471,10 @@ def _goto_playing_station(self, changing_playlist=False):
def _put_selection_in_the_middle(self, force=False):
max_lines = self.bodyMaxY - 2
- if self.number_of_items < max_lines:
+ if self.number_of_items < max_lines or self.selection < max_lines:
self.startPos = 0
elif force or self.selection < self.startPos or \
- self.selection > self.startPos + max_lines:
+ self.selection >= self.startPos + max_lines:
if logger.isEnabledFor(logging.INFO):
logger.info('=== _put:adjusting startPos')
if self.selection < max_lines:
@@ -484,7 +486,7 @@ def _put_selection_in_the_middle(self, force=False):
else:
self.startPos = int(self.selection+1/max_lines) - int(max_lines/2)
if logger.isEnabledFor(logging.INFO):
- logger.info('===== _put:startPos = {0}, force = {1}'.format(self.startPos, force))
+ logger.info('===== _put:startPos -> {0}, force = {1}'.format(self.startPos, force))
def setStation(self, number):
""" Select the given station number """
@@ -688,8 +690,10 @@ def _show_help(self, txt,
inner_width = self._get_message_width_from_list(lines) + 4
outer_height = inner_height + 2
outer_width = inner_width + 2
- if self.window_mode == CONFIG_MODE and \
- self.operation_mode > CONFIG_HELP_MODE:
+ if (self.window_mode == CONFIG_MODE and \
+ self.operation_mode > CONFIG_HELP_MODE) or \
+ (self.window_mode == NORMAL_MODE and \
+ self.operation_mode == SELECT_ENCODING_HELP_MODE):
use_empty_win = True
height_to_use = outer_height
width_to_use = outer_width
@@ -821,6 +825,7 @@ def _print_help(self):
self._show_help(txt, mode_to_set=PLAYLIST_HELP_MODE, caption=' Playlist Help ')
if logger.isEnabledFor(logging.DEBUG):
logger.debug('MODE = PLAYLIST_HELP_MODE')
+
elif self.window_mode == THEME_MODE:
self._show_theme_help()
@@ -843,6 +848,10 @@ def _print_help(self):
else:
self._show_config_help()
+ elif self.operation_mode == SELECT_STATION_ENCODING_MODE or \
+ self.operation_mode == SELECT_ENCODING_HELP_MODE:
+ self._show_config_encoding_help()
+
elif self.window_mode == NORMAL_MODE:
txt = """Up|/|j|/|PgUp
Down|/|k|/|PgDown |Change station selection.
@@ -854,6 +863,7 @@ def _print_help(self):
-|/|+| or |,|/|. |Change volume.
m v ||M|ute player / |S|ave volume (not in vlc).
o s R ||O|pen / |S|ave / |R|eload playlist.
+ e |Change station's encoding.
DEL|,|x |Delete selected station.
t T |Load |t|heme / |T|oggle transparency.
c |Open Configuration window.
@@ -862,6 +872,20 @@ def _print_help(self):
if logger.isEnabledFor(logging.DEBUG):
logger.debug('MODE = MAIN_HELP_MODE')
+ def _show_playlist_recovered(self):
+ txt = 'Playlist recoverd!'
+ self._show_help(txt, mode_to_set=self.operation_mode, caption='',
+ prompt='', is_message=True)
+ # start 750 ms counter
+ th = threading.Timer(0.75, self.closeRecoveryNotification)
+ th.start()
+
+ def closeRecoveryNotification(self, *arg, **karg):
+ #arg[1].acquire()
+ self._cnf.playlist_recovery_result = 0
+ #arg[1].release()
+ self.refreshBody()
+
def _show_theme_help(self):
txt = """Up|/|j|/|PgUp
Down|/|k|/|PgDown |Change theme selection.
@@ -1006,8 +1030,41 @@ def _print_foreign_playlist_copy_error(self):
prompt = ' Press any key ',
is_message=True)
+ def _print_playlist_recovery_error(self):
+ if self._cnf.playlist_recovery_result == 1:
+ txt = """Both a playlist file (CSV) and a playlist backup
+file (TXT) exist for the selected playlist. In
+this case, PyRadio would try to delete the CSV
+file, and then rename the TXT file to CSV.\n
+Unfortunately, deleting the CSV file has failed,
+so you have to manually address the issue.
+"""
+ else:
+ txt = """A playlist backup file (TXT) has been found for
+the selected playlist. In this case, PyRadio would
+try to rename this file to CSV.\n
+Unfortunately, renaming this file has failed, so
+you have to manually address the issue.
+"""
+ self._show_help(txt, PLAYLIST_RECOVERY_ERROR_MODE,
+ caption = ' Error ',
+ prompt = ' Press any key ',
+ is_message=True)
+
+ def _print_playlist_not_found_error(self):
+ txt = """Playlist |not| found!
+
+ This means that the playlist file was deleted
+ (or renamed) some time after you opened the
+ Playlist Selection window.
+ """
+ self._show_help(txt, PLAYLIST_NOT_FOUND_ERROR_MODE,
+ caption = ' Error ',
+ prompt = ' Press any key ',
+ is_message=True)
+
def _print_playlist_load_error(self):
- txt ="""Playlist loading |failed|!
+ txt = """Playlist loading |failed|!
This means that either the file is corrupt,
or you are not permitted to access it.
@@ -1052,7 +1109,7 @@ def _print_playlist_dirty_reload_confirmation(self):
prompt = ' ',
is_message=True)
- def _print_save_modified_playlist(self):
+ def _print_save_modified_playlist(self, mode):
txt ='''This playlist has been modified within
PyRadio. Do you want to save it?
@@ -1062,12 +1119,15 @@ def _print_save_modified_playlist(self):
Press "|y|" to confirm, "|Y|" to confirm and not
be asked again, "|n|" to reject, or "|q|" or
"|ESCAPE|" to cancel'''
- self._show_help(txt, ASK_TO_SAVE_PLAYLIST_MODE,
+ self._show_help(txt, mode,
caption = ' Playlist Modified ',
prompt = ' ',
is_message=True)
if logger.isEnabledFor(logging.DEBUG):
- logger.debug('MODE = ASK_TO_SAVE_PLAYLIST_MODE')
+ if mode == ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE:
+ logger.debug('MODE = ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE')
+ else:
+ logger.debug('MODE = ASK_TO_SAVE_PLAYLIST_WHEN_EXITING_MODE')
def _print_save_playlist_error_1(self):
txt = '''Saving current playlist |failed|!
@@ -1086,6 +1146,9 @@ def _print_save_playlist_error_2(self):
You will find a copy of the saved playlist in
"|{}|"
+
+ Pyradio will open this file when the playlist
+ is opened in the future.
'''
self._show_help(txt.format(self._cnf.stations_file.replace('.csv', '.txt')),
mode_to_set = SAVE_PLAYLIST_ERROR_2_MODE,
@@ -1282,19 +1345,30 @@ def get_active_encoding(self, an_encoding):
return self._cnf.default_encoding
def _redisplay_transient_window(self):
+ if self._cnf.playlist_recovery_result == -1:
+ self._show_playlist_recovered()
+ return
if self.operation_mode == MAIN_HELP_MODE or \
self.operation_mode == PLAYLIST_HELP_MODE or \
self.operation_mode == THEME_HELP_MODE or \
self.operation_mode == CONFIG_HELP_MODE or \
self.operation_mode == SELECT_PLAYER_HELP_MODE or \
- self.operation_mode == SELECT_ENCODING_HELP_MODE or \
self.operation_mode == SELECT_PLAYLIST_HELP_MODE or \
self.operation_mode == SELECT_STATION_HELP_MODE:
self._print_help()
+ elif self.operation_mode == SELECT_ENCODING_HELP_MODE:
+ if self.window_mode == NORMAL_MODE:
+ self._encoding_select_win.refresh_and_resize(self.bodyMaxY, self.bodyMaxX)
+ self._print_help()
+ if self.operation_mode == SELECT_STATION_ENCODING_MODE:
+ self._encoding_select_win.refresh_and_resize(self.bodyMaxY, self.bodyMaxX)
+ elif self.operation_mode == PLAYLIST_NOT_FOUND_ERROR_MODE:
+ self._print_playlist_not_found_error()
elif self.operation_mode == PLAYLIST_LOAD_ERROR_MODE:
self._print_playlist_load_error()
- elif self.operation_mode == ASK_TO_SAVE_PLAYLIST_MODE:
- self._print_save_modified_playlist()
+ elif self.operation_mode == ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE or \
+ self.operation_mode == ASK_TO_SAVE_PLAYLIST_WHEN_EXITING_MODE:
+ self._print_save_modified_playlist(self.operation_mode)
elif self.operation_mode == PLAYLIST_RELOAD_CONFIRM_MODE:
self._print_playlist_reload_confirmation()
elif self.operation_mode == PLAYLIST_DIRTY_RELOAD_CONFIRM_MODE:
@@ -1319,6 +1393,8 @@ def _redisplay_transient_window(self):
elif self.operation_mode == THEME_MODE:
self._theme_slector.parent = self.bodyWin
self._show_theme_selector()
+ elif self.operation_mode == PLAYLIST_RECOVERY_ERROR_MODE:
+ self._print_playlist_recovery_error()
def play_random(self):
# Pick a random radio station
@@ -1549,7 +1625,37 @@ def keypress(self, char):
self._config_win.refresh_config_win()
return
+ elif self.operation_mode == SELECT_STATION_ENCODING_MODE:
+ """ select station's encoding from main window """
+ if char not in (ord('m'), ord('v'), ord('.'),
+ ord(','), ord('+'), ord('-'),
+ ord('?'), ord('#'), curses.KEY_RESIZE):
+ ret, ret_encoding = self._encoding_select_win.keypress(char)
+ if ret >= 0:
+ if ret == 0:
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('new station encoding = {}'.format(ret_encoding))
+ """ save encoding and playlist """
+ if self._old_station_encoding == 'utf-8':
+ self._old_station_encoding = ''
+ if ret_encoding == 'utf-8':
+ ret_encoding = ''
+ if self._old_station_encoding != ret_encoding:
+ self._cnf.dirty_playlist = True
+ logger.info('self.stations[self.selection] = {}'.format(self.stations[self.selection]))
+ self.stations[self.selection][2] = ret_encoding
+ if self.player.isPlaying():
+ curses.ungetch('l')
+ #self._config_win._config_options['default_encoding'][1] = ret_encoding
+ self.operation_mode = self.window_mode = NORMAL_MODE
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('MODE: SELECT_STATION_ENCODING_MODE => NORMAL_MODE')
+ self.refreshBody()
+ self._encoding_select_win = None
+ return
+
elif self.operation_mode == SELECT_ENCODING_MODE:
+ """ select global encoding from config window """
if char not in (ord('m'), ord('v'), ord('.'),
ord(','), ord('+'), ord('-'),
ord('?'), ord('#'), curses.KEY_RESIZE):
@@ -1800,10 +1906,18 @@ def keypress(self, char):
return
elif self.operation_mode == PLAYLIST_SCAN_ERROR_MODE:
- """ exit """
+ """ exit due to scan error """
self.stopPlayer()
return -1
+ elif self.operation_mode == PLAYLIST_RECOVERY_ERROR_MODE:
+ self._cnf.playlist_recovery_result = 0
+ self.operation_mode = PLAYLIST_MODE
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('MODE: PLAYLIST_RECOVERY_ERROR_MODE -> PLAYLIST_MODE')
+ self.refreshBody()
+ return
+
elif self.operation_mode == MAIN_HELP_MODE:
""" Main help in on, just update """
self.helpWinContainer = None
@@ -1855,11 +1969,16 @@ def keypress(self, char):
""" Main help in on, just update """
self.helpWinContainer = None
self.helpWin = None
- self.operation_mode = SELECT_ENCODING_MODE
+ if self.window_mode == NORMAL_MODE:
+ self.operation_mode = SELECT_STATION_ENCODING_MODE
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('MODE: SELECT_ENCODING_HELP_MODE -> SELECT_STATION_ENCODING_MODE')
+ else:
+ self.operation_mode = SELECT_ENCODING_MODE
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('MODE: SELECT_ENCODING_HELP_MODE -> SELECT_ENCODING_MODE')
#self.setupAndDrawScreen()
self.refreshBody()
- if logger.isEnabledFor(logging.DEBUG):
- logger.debug('MODE: SELECT_ENCODING_HELP_MODE -> SELECT_ENCODING_MODE')
return
elif self.operation_mode == SELECT_PLAYLIST_HELP_MODE:
@@ -1884,7 +2003,29 @@ def keypress(self, char):
logger.debug('MODE: SELECT_STATION_HELP_MODE -> SELECT_STATION_MODE')
return
- elif self.operation_mode == ASK_TO_SAVE_PLAYLIST_MODE:
+ elif self.operation_mode == ASK_TO_SAVE_PLAYLIST_WHEN_EXITING_MODE:
+ if char in (ord('y'), ord('Y')):
+ if char == ord('Y'):
+ self._cnf.auto_save_playlist = True
+ ret = self.saveCurrentPlaylist()
+ if ret == -1:
+ # do not exit
+ return
+ # exit program
+ return -1
+ elif char in (ord('n'), ):
+ # exit program
+ return -1
+ elif char in (curses.KEY_EXIT, ord('q'), 27):
+ self.bodyWin.nodelay(True)
+ char = self.bodyWin.getch()
+ self.bodyWin.nodelay(False)
+ if char == -1:
+ """ ESCAPE """
+ return -1
+ return
+
+ elif self.operation_mode == ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE:
if char in (ord('y'), ord('Y')):
if char == ord('Y'):
self._cnf.auto_save_playlist = True
@@ -1900,7 +2041,7 @@ def keypress(self, char):
if char == -1:
""" ESCAPE """
if logger.isEnabledFor(logging.DEBUG):
- logger.debug('MODE: Cancel ASK_TO_SAVE_PLAYLIST_MODE -> NORMAL_MODE')
+ logger.debug('MODE: Cancel ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE -> NORMAL_MODE')
self.operation_mode = self.window_mode = NORMAL_MODE
self.refreshBody()
return
@@ -1948,15 +2089,18 @@ def keypress(self, char):
return
elif self.operation_mode == PLAYLIST_HELP_MODE or \
- self.operation_mode == PLAYLIST_LOAD_ERROR_MODE:
+ self.operation_mode == PLAYLIST_LOAD_ERROR_MODE or \
+ self.operation_mode == PLAYLIST_NOT_FOUND_ERROR_MODE:
""" close playlist help """
self.operation_mode = self.window_mode = PLAYLIST_MODE
self.refreshBody()
if logger.isEnabledFor(logging.DEBUG):
if self.operation_mode == PLAYLIST_HELP_MODE:
logger.debug('MODE: PLAYLIST_HELP_MODE -> PLAYLIST_MODE')
- else:
+ elif self.operation_mode == PLAYLIST_LOAD_ERROR_MODE:
logger.debug('MODE: PLAYLIST_LOAD_ERROR_MODE -> PLAYLIST_MODE')
+ else:
+ logger.debug('MODE: PLAYLIST_NOT_FOUND_ERROR_MODE -> PLAYLIST_MODE')
return
elif self.operation_mode == SAVE_PLAYLIST_ERROR_1_MODE or \
@@ -2102,7 +2246,20 @@ def keypress(self, char):
""" exit """
if self.player:
self.stopPlayer()
- self.ctrl_c_handler(0,0)
+ if self._cnf.dirty_playlist:
+ if self._cnf.auto_save_playlist:
+ # save playlist and exit
+ ret = self.saveCurrentPlaylist()
+ #if ret == -1:
+ # # do not exit program
+ # return
+ else:
+ # ask to save playlist
+ self._print_save_modified_playlist(ASK_TO_SAVE_PLAYLIST_WHEN_EXITING_MODE)
+ return
+ #else:
+ # self._open_playlist()
+ # self.ctrl_c_handler(0,0)
return -1
else:
return
@@ -2159,6 +2316,20 @@ def keypress(self, char):
self._show_config_window()
return
+ elif char in (ord('e'), ):
+ self._old_station_encoding = self.stations[self.selection][2]
+ if self._old_station_encoding == '':
+ self._old_station_encoding = 'utf-8'
+ logger.info('encoding = {}'.format(self._old_station_encoding))
+ self.operation_mode = SELECT_STATION_ENCODING_MODE
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('MODE: NORMAL_MODE => SELECT_STATION_ENCODING_MODE')
+ self._encoding_select_win = PyRadioSelectEncodings(self.bodyMaxY,
+ self.bodyMaxX, self._old_station_encoding)
+ self._encoding_select_win.init_window()
+ self._encoding_select_win.refresh_win()
+ self._encoding_select_win.setEncoding(self._old_station_encoding)
+
elif char in (ord('o'), ):
self.jumpnr = ''
self._random_requested = False
@@ -2171,7 +2342,7 @@ def keypress(self, char):
self._open_playlist()
else:
# ask to save playlist
- self._print_save_modified_playlist()
+ self._print_save_modified_playlist(ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE)
else:
self._open_playlist()
return
@@ -2253,6 +2424,15 @@ def keypress(self, char):
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Error loading playlist: "{}"'.format(self.stations[self.selection][-1]))
return
+ elif ret == -2:
+ self.stations = self._cnf.playlists
+ self._print_playlist_not_found_error()
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('Playlist not found: "{}"'.format(self.stations[self.selection][-1]))
+ return
+ elif ret == -7:
+ self._print_playlist_recovery_error()
+ return
else:
self.number_of_items = ret
self.selections[self.operation_mode] = [self.selection, self.startPos, self.playing, self._cnf.playlists]
diff --git a/pyradio/themes.py b/pyradio/themes.py
index 8bb2f8b3..e6051fec 100644
--- a/pyradio/themes.py
+++ b/pyradio/themes.py
@@ -110,7 +110,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 8
self._colors['Name'] = 'dark'
- self._colors['Path'] = 'dark'
+ self._colors['Path'] = ''
elif a_theme == 'dark_16_colors':
self._colors['Stations'] = [ 15, 8 ]
@@ -128,7 +128,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 16
self._colors['Name'] = 'dark_16_colors'
- self._colors['Path'] = 'dark_16_colors'
+ self._colors['Path'] = ''
elif a_theme == 'light':
self._colors['Stations'] = [ curses.COLOR_BLACK, curses.COLOR_WHITE ]
@@ -146,7 +146,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 8
self._colors['Name'] = 'dark'
- self._colors['Path'] = 'dark'
+ self._colors['Path'] = ''
elif a_theme == 'light_16_colors':
self._colors['Stations'] = [ 8, 15 ]
@@ -164,7 +164,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 16
self._colors['Name'] = 'light_16_colors'
- self._colors['Path'] = 'light_16_colors'
+ self._colors['Path'] = ''
elif a_theme == 'black_on_white' or a_theme == 'bow':
self._colors['Stations'] = [ 245, 15 ]
@@ -182,7 +182,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 256
self._colors['Name'] = 'black_on_white'
- self._colors['Path'] = 'black_on_white'
+ self._colors['Path'] = ''
elif a_theme == 'white_on_black' or a_theme == 'wob':
self._colors['Stations'] = [ 247, 235 ]
@@ -200,7 +200,7 @@ def open_theme(self, a_theme = ''):
# info
self._colors['Colors'] = 256
self._colors['Name'] = 'white_on_black'
- self._colors['Path'] = 'white_on_black'
+ self._colors['Path'] = ''
else:
# TODO: read a theme from disk