diff --git a/CHANGELOG b/CHANGELOG
index 53dada5..13857ec 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,35 @@
-Current
+v0.6.2
+- Made sd warn and has-captions overlays optional (and made sd warning False by default due to recently buggy API data).
+- Changed Function menu alt-key from F to u (overlapped with File menu).
+- Shuffled around various settings to lessen a wasteful amount of tabs.
+- Moved datestamp settings to its own Time & Date tab.
+- Update text ellision values to work with the VideoTile redesign.
+- Made config.ini only contain overrides instead of mirroring all defaults.
+- Created (and added to UI) a FontPickerButton which lets users pick fonts and displays the current font in a
+ human readable format on the button.
+- Changed default Fonts from Helvetica to Noto Sans (likely the same font)
+- Made all font read_config entries non-literal eval (i.e. read as string).
+- Added utils function for checking if a string contains unicode.
+- Removed size limit from TextPickleType due to lack of support.
+ - PostgreSQL doesn't seem to support strings with any length limit at all.
+- Added some if coverage in debug tile colouring.
+- ElidedLabel is now aligned to top and added spacer between thumb and title.
+- Removed redundant (and also problematic) layout alignments.
+- Removed maximum height sizing logic from VideoTile.
+- Made Title, Channel and Date text fields optional if cfg lines == 0.
+- Changed VideoTile sizing from fixed size to fixed width and max height.
+- Changed ThumbnailTile sizing from fixed size to minimum width (scaled).
+- Refactor renamed (and moved) VideoTileLabel to ElidedLabel.
+- Changed start with stored videos to True by default.
+- Changed VideoTileLabel font handling from plaintext to QFont.fromString.
+- Added accidentally omitted config hotkey to defaults and sample config.
+- Fixed critical DB creation failure bug introduced in commit 2da05b2 (Caused by IDE refactor move).
+- Made PyYAML dependency in requirements.txt >= instead of ==.
+- Made sample cfg/hotkeys if not exist use the new create_config_file function.
+- Replaced copyfile(sample, cfg) with func that uses DEFAULTS dict directly.
+- Made log_handler automatically create missing dirs and files.
+- Made PyQt5 pip requirements >= instead of forcing specific version.
+- Renamed subs list reload item, so it's harder to confuse with subfeed refresh.
- Hotfix: disabled elision by default and instead explicitly using it during init only, due to VideoTileLabel.width()
changing drastically later on.
- Deleted deprecated debug_functions and cli arg --debug_open_1k_fds that was used for "too many file descriptors"
diff --git a/VERSION b/VERSION
index 7ceb040..b1d7abc 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.1
\ No newline at end of file
+0.6.2
\ No newline at end of file
diff --git a/config.ini.sample b/config.ini.sample
index 385a990..788adba 100644
--- a/config.ini.sample
+++ b/config.ini.sample
@@ -12,7 +12,7 @@ filter_videos_days_old = -1
[Debug]
debug = False
cached_subs = True
-start_with_stored_videos = False
+start_with_stored_videos = True
channels_limit = -1
use_playlistitems = True
disable_tooltips = False
@@ -43,6 +43,12 @@ toolbar_icon_size_modifier = 1
last_style =
last_theme =
+[Fonts]
+video_title_font = Noto Sans,10,-1,0,75,0,0,0,0,0,Bold
+video_channel_font = Noto Sans,10,-1,0,50,0,0,0,0,0,Regular
+video_date_font = Noto Sans,10,-1,0,50,0,0,0,0,0,Regular
+video_thumbnail_overlay_font = Noto Sans,10,-1,0,50,0,0,0,0,0,Regular
+
[GridView]
show_watched = False
show_dismissed = False
diff --git a/sane_yt_subfeed/database/decorators.py b/sane_yt_subfeed/database/decorators.py
index 65edaa5..652ce0d 100644
--- a/sane_yt_subfeed/database/decorators.py
+++ b/sane_yt_subfeed/database/decorators.py
@@ -3,11 +3,8 @@
import sqlalchemy
from sqlalchemy import TypeDecorator
-SIZE = 256
-
-
class TextPickleType(TypeDecorator):
- impl = sqlalchemy.Text(SIZE)
+ impl = sqlalchemy.Text()
def process_bind_param(self, value, dialect):
if value is not None:
diff --git a/sane_yt_subfeed/gui/main_window/main_window.py b/sane_yt_subfeed/gui/main_window/main_window.py
index f6767ca..0ca7b26 100644
--- a/sane_yt_subfeed/gui/main_window/main_window.py
+++ b/sane_yt_subfeed/gui/main_window/main_window.py
@@ -599,16 +599,16 @@ def add_menu_function(self, menubar):
:param menubar:
:return:
"""
- self.add_menu(menubar, '&Function')
+ self.add_menu(menubar, 'F&unction')
# Set function menu triggers
- self.add_submenu('&Function', 'Copy all URLs', self.clipboard_copy_urls,
+ self.add_submenu('F&unction', 'Copy all URLs', self.clipboard_copy_urls,
shortcut=read_config('Global', 'copy_all_urls', custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL),
tooltip='Copy URLs of all currently visible videos to clipboard', icon=COPY_ALL_URLS_ICON)
# refresh_list
- self.toolbar_items['RefreshSubFeed'] = self.add_submenu('&Function', 'Refresh Feed',
+ self.toolbar_items['RefreshSubFeed'] = self.add_submenu('F&unction', 'Refresh Feed',
self.emit_signal_with_set_args,
shortcut=read_config('Global', 'refresh_feed',
custom_ini=HOTKEYS_INI,
@@ -618,53 +618,53 @@ def add_menu_function(self, menubar):
signal=self.main_model.main_window_listener.refreshVideos,
args=(LISTENER_SIGNAL_NORMAL_REFRESH,))
- self.add_submenu('&Function', 'Fetch &List of Subscribed Channels',
+ self.add_submenu('F&unction', 'Fetch &List of Subscribed Channels',
self.main_model.main_window_listener.refreshSubs.emit,
shortcut=read_config('Global', 'reload_subslist', custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL),
tooltip='Fetch a new subscriptions list', icon=RELOAD_SUBS_LIST_ICON)
# FIXME: icon, shortcut(alt/shift as extra modifier to the normal refresh shortcut?)
- self.add_submenu('&Function', 'Deep refresh of feed', self.emit_signal_with_set_args,
+ self.add_submenu('F&unction', 'Deep refresh of feed', self.emit_signal_with_set_args,
shortcut=read_config('Global', 'refresh_feed_deep', custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL),
tooltip='Deep refresh the subscription feed', icon=REFRESH_SUBFEED_DEEP_ICON,
signal=self.main_model.main_window_listener.refreshVideos,
args=(LISTENER_SIGNAL_DEEP_REFRESH,))
- self.add_submenu('&Function', 'Test Channels', self.main_model.main_window_listener.testChannels.emit,
+ self.add_submenu('F&unction', 'Test Channels', self.main_model.main_window_listener.testChannels.emit,
tooltip='Tests the test_pages and miss_limit of channels', icon=RERUN_TEST_ICON)
if self.main_model.yt_dir_listener is not None:
- self.add_submenu('&Function', 'Manual dir search', self.main_model.yt_dir_listener.manualCheck.emit,
+ self.add_submenu('F&unction', 'Manual dir search', self.main_model.yt_dir_listener.manualCheck.emit,
tooltip='Starts a manual search for new videos in youtube directory',
icon=MANUAL_DIR_SEARCH_ICON)
thumb_tooltip = 'Starts a manual download of thumbnails for videos currently in play view and sub feed'
- self.add_submenu('&Function', 'Manual thumbnail download',
+ self.add_submenu('F&unction', 'Manual thumbnail download',
self.download_thumbnails_manually,
tooltip=thumb_tooltip, icon=MANUAL_THUMBS_DOWNLOAD_ICON)
- self.add_submenu('&Function', 'Manual DB grab', self.update_from_db,
+ self.add_submenu('F&unction', 'Manual DB grab', self.update_from_db,
tooltip='Starts a manual grab of data for the model', icon=DATABASE_ICON,
shortcut=read_config('Global', 'manual_db_grab', custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL))
# FIXME: icon, look more related to action
- self.add_submenu('&Function', 'Toggle sort-by: ascending date', self.toggle_sort_by_ascending,
+ self.add_submenu('F&unction', 'Toggle sort-by: ascending date', self.toggle_sort_by_ascending,
tooltip='Toggles the ascending date config option, and does a manual re-grab',
icon=SORT_BY_ASC_DATE_ICON, shortcut=read_config('Playback', 'ascending_sort_toggle',
custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL))
- self.add_submenu('&Function', 'Toggle sort-by: channel', self.toggle_sort_by_channel,
+ self.add_submenu('F&unction', 'Toggle sort-by: channel', self.toggle_sort_by_channel,
tooltip='Toggles the ascending date config option, and does a manual re-grab',
icon=SORT_BY_CHANNEL_ICON, shortcut=read_config('Playback', 'by_channel_sort_toggle',
custom_ini=HOTKEYS_INI,
literal_eval=HOTKEYS_EVAL))
- self.add_submenu('&Function', 'Log History 2.0', self.history_log,
+ self.add_submenu('F&unction', 'Log History 2.0', self.history_log,
tooltip='Send entire history to logger')
- self.add_submenu('&Function', 'Undo', self.history_undo,
+ self.add_submenu('F&unction', 'Undo', self.history_undo,
tooltip='Undo previous action (if possible)',
icon=UNDO_ICON, shortcut=read_config('Global', 'history_undo_action',
custom_ini=HOTKEYS_INI, literal_eval=HOTKEYS_EVAL))
diff --git a/sane_yt_subfeed/gui/views/config_view/config_items/font_picker_button.py b/sane_yt_subfeed/gui/views/config_view/config_items/font_picker_button.py
new file mode 100644
index 0000000..0933460
--- /dev/null
+++ b/sane_yt_subfeed/gui/views/config_view/config_items/font_picker_button.py
@@ -0,0 +1,161 @@
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import QPushButton, QFontDialog
+
+from sane_yt_subfeed import create_logger
+from sane_yt_subfeed.handlers.config_handler import read_config, set_config
+
+FONT_WEIGHT_MAP = {'0': 'Thin', '12': 'ExtraLight', '18': 'Thi', '25': 'Light', '50': 'Regular', '54': 'Medium',
+ '62': 'ExtraCondensed', '63': 'DemiBold', '75': 'Bold', '81': 'ExtraBold', '87': 'Black'}
+HUMAN_READABLE_KEYS = {'underline': 'Underline', 'strikeout': 'Strike out', 'fixed_pitch': 'Fixed pitch',
+ 'raw_mode': 'RAW mode'}
+
+
+class FontPickerButton(QPushButton):
+ def __init__(self, parent, cfg_section, cfg_option, tooltip=None, actions=None, actions_kwargs=None):
+ """
+ A custom QPushButton that launches a QFontDialog.
+ :param parent: Parent ptr.
+ :param cfg_section: Config section.
+ :param cfg_option: Config Option.
+ :param tooltip: String to show on tooltip.
+ :param actions: Function to call when font is selected.
+ :param actions_kwargs: Keyword arguments (dict) to send in checked action calls.
+ """
+ super(QPushButton, self).__init__(parent=parent)
+ self.parent = parent
+ self.logger = create_logger(__name__)
+ self.tooltip = tooltip
+ self.cfg_section = cfg_section
+ self.cfg_option = cfg_option
+ self.current_font = QFont()
+ self.current_font.fromString(read_config(self.cfg_section, self.cfg_option, literal_eval=False))
+ self.update_info()
+
+ self.clicked.connect(self.open_font_dialog)
+
+ def weight_map_lookup(self, weight):
+ """
+ Checks if a weight exists in the font weight map,
+ if not then it reports it and returns a fallback string.
+ :param weight:
+ :return:
+ """
+ if str(weight) in FONT_WEIGHT_MAP:
+ return FONT_WEIGHT_MAP[str(weight)]
+ else:
+ self.logger.error("Umatched font weight: {}!".format(weight))
+ return "UNDEFINED({})".format(weight)
+
+ def map_font_str(self, font_str):
+ """
+ Maps a comma separated QFont.toString to a much more sensible dict, which it then returns.
+
+ QFont.toString composition (comma separated, always in the following order):
+ Family: Font family name.
+ Point size: Point size of the font.
+ -1 if the font size was specified in pixels.
+ Pixel size: Pixel size of the font if it was set with pixel size.
+ -1 if the size was set with point size.
+ Style hint: Affects the {QFont}{font matching} algorithm. See QFont.StyleHint and QFont constants.
+ Style hints are used by the {QFont}{font matching} algorithm to find an appropriate default family
+ if a selected font family is not available.
+
+ AnyStyle leaves the font matching algorithm to choose the family. This is the default.
+
+ 0 seems to be equivalent to Helvetica and 5 is AnyStyle.
+ Weight: Weight of the font (0-99), usually it's a predefined enum.
+ Qt uses a weighting scale from 0 to 99 similar to, but not the same as, the scales used in Windows or
+ CSS. A weight of 0 is ultralight, whilst 99 will be an extremely black.
+ Style: Style of the font (enum of the different styles of glyphs that are used to display text).
+ Only covers the following types: Normal = 0, Italic = 1, Oblique = 2
+ Underline: Self-explanatory.
+ 0 if False, 1 if True.
+ Strikeout: Self-explanatory.
+ 0 if False, 1 if True.
+ Fixed pitch: Fixed pitch value of the matched window system font.
+ 0 if False, 1 if True.
+
+ Useful url:
+ shorthand: https://bit.ly/2XIjI5B
+ raw (with linebreaks): https://cep.xray.aps.anl.gov/software/qt4-x11-4.2.2-browser/
+ d7/da1/class_q_font.html#5ab046d742a8538c2e2e50c2e03733ea
+
+ :param font_str: A string in the form of QFont.toString().
+ :return:
+ """
+ font_info = font_str.split(',')
+ font_info_map = {'family': str(font_info[0]), 'point_size': int(font_info[1]),
+ 'pixel_size': float(font_info[2]), 'style_hint': int(font_info[3]),
+ 'weight': self.weight_map_lookup(font_info[4]), 'style': int(font_info[5]),
+ 'underline': bool(int(font_info[6])), 'strikeout': bool(int(font_info[7])),
+ 'fixed_pitch': bool(int(font_info[8])), 'raw_mode': bool(int(font_info[9])),
+ 'font_style': str(font_info[10])}
+
+ return font_info_map
+
+ def format_qfont_str(self, font_str):
+ """
+ Formats a QFont.toString to a more human readable string.
+ :param font_str:
+ :return:
+ """
+ # Map the string to a font info map/dict.
+ f: dict = self.map_font_str(font_str)
+
+ # Make a list of enabled setting keys.
+ enabled_bools = []
+ for key, value in f.items():
+ if value is True:
+ enabled_bools.append(HUMAN_READABLE_KEYS[key])
+
+ # It is guaranteed to be either pt or px, so ternary works fine for this.
+ pt_or_px = "{}pt.".format(f['point_size']) if f['point_size'] != -1 else "{}px.".format(f['pixel_size'])
+
+ # Determine the enabled (boolean) settings:
+ bools = []
+ for boolean in enabled_bools:
+ bools.append(boolean)
+ bools.append(", ")
+
+ if len(bools) > 0:
+ # Strip trailing runaway comma-space delimiter
+ bools = bools[:-1]
+
+ # Wrap in parenthesis
+ bools.insert(0, '(')
+ bools.append(')')
+
+ # Retval for weight (optional): " (" + f['weight'] + ")" if f['weight'] != f['font_style'] else "",
+
+ return "{} ({}) {} {}".format(f['family'], f['font_style'], pt_or_px,
+ "".join(bools) if not len(bools) == 0 else "")
+
+ def update_info(self):
+ """
+ Update info (various text/labels etc).
+ :return:
+ """
+ self.setText(self.format_qfont_str(self.current_font.toString()))
+ if self.tooltip:
+ self.setToolTip(self.tooltip)
+
+ def open_font_dialog(self):
+ """
+ Opens a QFontDialog and updates current font and config with the choice (if any).
+ :return:
+ """
+ font = QFont()
+ font.fromString(read_config(self.cfg_section, self.cfg_option, literal_eval=False))
+ font, ok = QFontDialog.getFont()
+
+ # If user selected a font.
+ if ok:
+ # Update current font ref.
+ self.current_font = font
+
+ # Save selected font to config.
+ set_config(self.cfg_section, self.cfg_option, font.toString())
+
+ # Make button text reflect changes.
+ self.update_info()
+
diff --git a/sane_yt_subfeed/gui/views/config_view/config_view_tabs.py b/sane_yt_subfeed/gui/views/config_view/config_view_tabs.py
index 2a39213..d1d578b 100644
--- a/sane_yt_subfeed/gui/views/config_view/config_view_tabs.py
+++ b/sane_yt_subfeed/gui/views/config_view/config_view_tabs.py
@@ -6,8 +6,7 @@
from sane_yt_subfeed.gui.views.config_view.config_scroll_area import ConfigScrollArea
from sane_yt_subfeed.gui.views.config_view.views.config_view import ConfigViewWidget
-CONFIG_TABS = ["GUI", "Views", "Model", "Requests", "Thumbnails", "Threading", "Download", "Media player",
- "Default Application", "Logging", "Debug", "Advanced"]
+CONFIG_TABS = ["GUI", "Views", "Download", "Apps && Players", "Time && Date", "Logging", "Advanced", "Debug"]
class ConfigViewTabs(QTabWidget):
diff --git a/sane_yt_subfeed/gui/views/config_view/input_super.py b/sane_yt_subfeed/gui/views/config_view/input_super.py
index 6edbb6d..e9d74cc 100644
--- a/sane_yt_subfeed/gui/views/config_view/input_super.py
+++ b/sane_yt_subfeed/gui/views/config_view/input_super.py
@@ -1,7 +1,8 @@
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
-from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
+from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel, QFontDialog
+from sane_yt_subfeed.gui.views.config_view.config_items.font_picker_button import FontPickerButton
from sane_yt_subfeed.handlers.config_handler import read_config, set_config
from sane_yt_subfeed.gui.views.config_view.config_items.button import GenericConfigPushButton
from sane_yt_subfeed.gui.views.config_view.config_items.checkbox import GenericConfigCheckBox
@@ -104,7 +105,7 @@ def add_option_line_edit(self, description, cfg_section, cfg_option, cfg_validat
:param cfg_section:
:param description:
:param actions: Function to call when line gets edited.
- :param actions_kwargs Keyword arguments (dict) to send in checked action calls.
+ :param actions_kwargs: Keyword arguments (dict) to send in checked action calls.
:return:
"""
if restart_check and actions is None:
@@ -120,6 +121,32 @@ def add_option_line_edit(self, description, cfg_section, cfg_option, cfg_validat
return value # Needed for connected listeners etc
+ def add_option_fontpicker(self, description, cfg_section, cfg_option, disabled=False, tooltip=None,
+ actions=None, actions_kwargs=None, restart_check=True):
+ """
+ Add an option w/ value to the ConfigView layout and increment the grid offset.
+ :param description: Description of the option.
+ :param cfg_section: Config section.
+ :param cfg_option: Config option.
+ :param disabled: Sets disabled status if True.
+ :param tooltip: String to show on tooltip.
+ :param actions: Function to call when font is selected.
+ :param actions_kwargs: Keyword arguments (dict) to send in checked action calls.
+ :param restart_check: If set to false, don't check if a restart (may) be required for this option.
+ :return:
+ """
+ if restart_check and actions is None:
+ description = "{} {}".format(description, RESTART_REQUIRED_SIGNIFIER)
+ option = QLabel(description)
+ value = FontPickerButton(self, cfg_section, cfg_option, tooltip=tooltip, actions=actions,
+ actions_kwargs=actions_kwargs)
+
+ if disabled:
+ value.setDisabled(True)
+ self.layout.addWidget(option, self.offset, 0)
+ self.layout.addWidget(value, self.offset, 1)
+ self.offset += 1
+
def add_option_inactive(self, description, cfg_section, cfg_option):
"""
Add an option w/ UNEDITABLE value to the ConfigView layout and increment the grid offset.
diff --git a/sane_yt_subfeed/gui/views/config_view/views/config_view.py b/sane_yt_subfeed/gui/views/config_view/views/config_view.py
index f858ac2..95e53d9 100644
--- a/sane_yt_subfeed/gui/views/config_view/views/config_view.py
+++ b/sane_yt_subfeed/gui/views/config_view/views/config_view.py
@@ -38,20 +38,12 @@ def __init__(self, config_view_tabs, parent, root, tab_id):
self.add_config_tab_views()
elif self.tab_id == 'Debug' and read_config('Debug', 'debug'):
self.add_config_tab_debug()
- elif self.tab_id == 'Model':
- self.add_config_tab_model()
- elif self.tab_id == 'Requests':
- self.add_config_tab_requests()
- elif self.tab_id == 'Thumbnails':
- self.add_config_tab_thumbnails()
- elif self.tab_id == 'Threading':
- self.add_config_tab_threading()
elif self.tab_id == 'Download' and read_config('Play', 'enabled'):
self.add_config_tab_download()
- elif self.tab_id == 'Media player':
- self.add_config_tab_mediaplayer()
- elif self.tab_id == 'Default Application':
- self.add_config_tab_default_apps()
+ elif self.tab_id == 'Apps && Players':
+ self.add_config_tab_apps()
+ elif self.tab_id == "Time && Date":
+ self.add_config_tab_datetime()
elif self.tab_id == 'Logging':
self.add_config_tab_logging()
elif self.tab_id == 'Advanced':
@@ -67,6 +59,8 @@ def init_ui(self):
self.logger.info("Initializing UI: ConfigViewWidget: {}".format(self.tab_id))
def add_config_tab_gui(self):
+ self.add_option_line_edit('Videos to load by default', 'Model', 'loaded_videos',
+ cfg_validator=QIntValidator())
self.add_option_checkbox('Grey background on old (1d+) videos', 'Gui', 'grey_old_videos')
self.add_option_line_edit('Grid tile height (px)', 'Gui', 'tile_pref_height', cfg_validator=QIntValidator())
self.add_option_line_edit('Grid tile width (px)', 'Gui', 'tile_pref_width', cfg_validator=QIntValidator())
@@ -102,6 +96,9 @@ def add_config_tab_views(self):
self.add_section('{}Grid Views{}'.format(self.deco_l, self.deco_r))
self.add_option_checkbox('Show watched videos', 'GridView', 'show_watched')
self.add_option_checkbox('Show dismissed videos', 'GridView', 'show_dismissed')
+ self.add_option_checkbox('Warn if video is SD quality', 'GridView', 'show_sd_warning', restart_check=False)
+ self.add_option_checkbox('Show if video has captions available', 'GridView', 'show_has_captions',
+ restart_check=False)
self.add_option_checkbox('Enable Playback view (and download support)', 'Play', 'enabled',
checked_actions=[self.config_view_tabs.add_tab,
self.root.respawn_menubar_and_toolbar,
@@ -115,18 +112,20 @@ def add_config_tab_views(self):
self.root.del_central_widget_download,
self.root.setup_views],
unchecked_kwargs=[{'tab': 'Download'}, None, None, None, None])
+ self.add_option_fontpicker('Thumbnail overlay font', 'Fonts', 'video_thumbnail_overlay_font')
+ self.add_option_fontpicker('Title font', 'Fonts', 'video_title_font')
self.add_option_line_edit('Title elided text multiplier',
'GridView', 'elided_text_modifier_title', cfg_validator=QDoubleValidator())
self.add_option_line_edit('Title lines to display', 'GridView', 'tile_title_lines',
cfg_validator=QIntValidator())
- self.add_option_combobox('Title text font weight', 'GridView', 'title_tile_font_weight',
- TILE_TITLE_FONT_WEIGHTS)
self.add_option_info(None, None) # Line spacer
+ self.add_option_fontpicker('Channel font', 'Fonts', 'video_channel_font')
self.add_option_line_edit('Channel elided text multiplier',
'GridView', 'elided_text_modifier_channel', cfg_validator=QDoubleValidator())
self.add_option_line_edit('Channel Title lines to display', 'GridView', 'tile_channel_lines',
cfg_validator=QIntValidator())
self.add_option_info(None, None) # Line spacer
+ self.add_option_fontpicker('Date font', 'Fonts', 'video_date_font')
self.add_option_line_edit('Date elided text multiplier',
'GridView', 'elided_text_modifier_date', cfg_validator=QDoubleValidator())
self.add_option_line_edit('Date lines to display', 'GridView', 'tile_date_lines',
@@ -141,35 +140,6 @@ def add_config_tab_views(self):
'(Set this to 0 if using phantomstyle)', 'GridView',
'elided_text_unicode_weight_modifier', cfg_validator=QDoubleValidator())
self.add_option_info(None, None) # Line spacer
- self.add_option_line_edit('Date format for: videos uploaded'
- ' less than a day ago', 'GridView', 'timedelta_format')
- self.add_option_line_edit('Date format for: videos uploaded'
- ' a day ago', 'GridView', 'timedelta_format_days')
- self.add_option_line_edit('Date format for: videos uploaded'
- ' a month ago', 'GridView', 'timedelta_format_months')
- self.add_option_line_edit('Date format for: videos uploaded'
- ' a year ago', 'GridView', 'timedelta_format_years')
- self.add_option_line_edit('Date format for: videos uploaded'
- ' a decade ago', 'GridView', 'timedelta_format_decades')
-
- self.add_option_info('$decadesdecades', 'Decades as a zero-padded decimal number.')
- self.add_option_info('$decades', 'Decades as a decimal number.')
- self.add_option_info('$ydyd', 'Yearsdelta as a zero-padded decimal number.')
- self.add_option_info('$yd', 'Yearsdelta as a decimal number.')
- self.add_option_info('$mm', 'Months as a zero-padded decimal number.')
- self.add_option_info('$m', 'Months as a decimal number.')
- self.add_option_info('$dd', 'Days of the month as a zero-padded decimal number.')
- self.add_option_info('$d', 'Days of the month as a decimal number.')
- self.add_option_info('$HH', 'Hours (24-hour clock) as a zero-padded decimal number.')
- self.add_option_info('$H', 'Hours (24-hour clock) as a decimal number.')
- self.add_option_info('$MM', 'Minutes as a zero-padded decimal number.')
- self.add_option_info('$M', 'Minutes as a decimal number.')
- self.add_option_info('$SS', 'Seconds as a zero-padded decimal number.')
- self.add_option_info('$S', 'Seconds as a decimal number.')
- self.add_option_info('$f', 'Microseconds as a decimal number, zero-padded on the left.')
- self.add_option_info('$%', 'A literal \'%\' character.')
- self.add_option_info('', '')
- self.add_option_info('Valid delimters:', '$, ${}')
# Section [SubFeed]
self.add_section('{}Subscription feed{}'.format(self.deco_l, self.deco_r))
@@ -193,8 +163,6 @@ def add_config_tab_views(self):
# Section [Play]
if read_config('Play', 'enabled'):
self.add_section('{}Playback feed{}'.format(self.deco_l, self.deco_r))
- self.add_option_line_edit('YouTube video directory', 'Play', 'yt_file_path', restart_check=False)
- self.add_option_checkbox('Disable directory listener (inotify)', 'Play', 'disable_dir_listener')
self.add_option_line_edit('Default watch priority', 'Play', 'default_watch_prio',
cfg_validator=QIntValidator(), restart_check=False)
# Section [PlaySort]
@@ -209,38 +177,6 @@ def add_config_tab_debug(self):
self.add_option_checkbox('Color video tile elements', 'Debug', 'color_tile_elements')
self.add_option_info_restart_required()
- def add_config_tab_model(self):
- self.add_option_line_edit('Videos to load by default', 'Model', 'loaded_videos',
- cfg_validator=QIntValidator())
- self.add_option_info_restart_required()
-
- def add_config_tab_requests(self):
- self.add_option_checkbox('Use tests', 'Requests', 'use_tests', restart_check=False)
- self.add_option_line_edit('Missed video limit', 'Requests', 'miss_limit',
- cfg_validator=QIntValidator(), restart_check=False)
- self.add_option_line_edit('Test pages', 'Requests', 'test_pages',
- cfg_validator=QIntValidator(), restart_check=False)
- self.add_option_line_edit('Additional list pages', 'Requests', 'extra_list_pages',
- cfg_validator=QIntValidator(), restart_check=False)
- self.add_option_line_edit('Deep search API quota limit per request (in K)', 'Requests',
- 'deep_search_quota_k',
- cfg_validator=QIntValidator(), restart_check=False)
- self.add_option_line_edit('Filter videos older than (days)', 'Requests', 'filter_videos_days_old',
- cfg_validator=QIntValidator(), restart_check=False)
-
- def add_config_tab_thumbnails(self):
- self.add_option_checkbox('Force download best quality, based on prioritised list',
- 'Thumbnails', 'force_download_best', restart_check=False)
- self.add_option_combobox('1. Priority', 'Thumbnails', '0', THUMBNAIL_QUALITIES, restart_check=False)
- self.add_option_combobox('2. Priority', 'Thumbnails', '1', THUMBNAIL_QUALITIES, restart_check=False)
- self.add_option_combobox('3. Priority', 'Thumbnails', '2', THUMBNAIL_QUALITIES, restart_check=False)
- self.add_option_combobox('4. Priority', 'Thumbnails', '3', THUMBNAIL_QUALITIES, restart_check=False)
- self.add_option_combobox('5. Priority', 'Thumbnails', '4', THUMBNAIL_QUALITIES, restart_check=False)
-
- def add_config_tab_threading(self):
- self.add_option_line_edit('Image/thumbnail download thread limit', 'Threading', 'img_threads',
- cfg_validator=QIntValidator(), restart_check=False)
-
def add_config_tab_download(self):
# Section [Youtube-dl]
if 'youtube_dl' in sys.modules:
@@ -272,12 +208,12 @@ def add_config_tab_download(self):
if 'youtube_dl' not in sys.modules:
self.add_option_info_restart_required()
- def add_config_tab_mediaplayer(self):
- # Section [Player]
+ self.add_option_line_edit('YouTube video directory', 'Play', 'yt_file_path', restart_check=False)
+ self.add_option_checkbox('Disable directory listener (inotify)', 'Play', 'disable_dir_listener')
+
+ def add_config_tab_apps(self):
+ self.add_section('{}Media Players{}'.format(self.deco_l, self.deco_r))
self.add_option_line_edit('Default Media Player', 'Player', 'default_player', restart_check=False)
- self.add_option_line_edit('Default Web Browser
'
- '(Uses system default if none specified)', 'Player', 'url_player',
- restart_check=False)
_counter = 1
for alt_player in get_options('Player'):
# if _counter == 1: # Skip default player
@@ -288,9 +224,43 @@ def add_config_tab_mediaplayer(self):
restart_check=False)
_counter += 1
- def add_config_tab_default_apps(self):
+ self.add_section('{}Default Applications{}'.format(self.deco_l, self.deco_r))
+ self.add_option_line_edit('Default Web Browser
'
+ '(Uses system default if none specified)', 'Player', 'url_player',
+ restart_check=False)
self.add_option_line_edit('Image viewer', 'DefaultApp', 'Image', restart_check=False)
+ def add_config_tab_datetime(self):
+ self.add_option_line_edit('Date format for: videos uploaded'
+ ' less than a day ago', 'GridView', 'timedelta_format')
+ self.add_option_line_edit('Date format for: videos uploaded'
+ ' a day ago', 'GridView', 'timedelta_format_days')
+ self.add_option_line_edit('Date format for: videos uploaded'
+ ' a month ago', 'GridView', 'timedelta_format_months')
+ self.add_option_line_edit('Date format for: videos uploaded'
+ ' a year ago', 'GridView', 'timedelta_format_years')
+ self.add_option_line_edit('Date format for: videos uploaded'
+ ' a decade ago', 'GridView', 'timedelta_format_decades')
+
+ self.add_option_info('$decadesdecades', 'Decades as a zero-padded decimal number.')
+ self.add_option_info('$decades', 'Decades as a decimal number.')
+ self.add_option_info('$ydyd', 'Yearsdelta as a zero-padded decimal number.')
+ self.add_option_info('$yd', 'Yearsdelta as a decimal number.')
+ self.add_option_info('$mm', 'Months as a zero-padded decimal number.')
+ self.add_option_info('$m', 'Months as a decimal number.')
+ self.add_option_info('$dd', 'Days of the month as a zero-padded decimal number.')
+ self.add_option_info('$d', 'Days of the month as a decimal number.')
+ self.add_option_info('$HH', 'Hours (24-hour clock) as a zero-padded decimal number.')
+ self.add_option_info('$H', 'Hours (24-hour clock) as a decimal number.')
+ self.add_option_info('$MM', 'Minutes as a zero-padded decimal number.')
+ self.add_option_info('$M', 'Minutes as a decimal number.')
+ self.add_option_info('$SS', 'Seconds as a zero-padded decimal number.')
+ self.add_option_info('$S', 'Seconds as a decimal number.')
+ self.add_option_info('$f', 'Microseconds as a decimal number, zero-padded on the left.')
+ self.add_option_info('$%', 'A literal \'%\' character.')
+ self.add_option_info('', '')
+ self.add_option_info('Valid delimters:', '$, ${}')
+
def add_config_tab_logging(self):
self.add_option_checkbox('Use socket instead of file', 'Logging', 'use_socket_log')
self.add_option_info('Value\t Level', None)
@@ -324,4 +294,30 @@ def add_config_tab_advanced(self):
self.add_option_line_edit('Grid view X', 'Gui', 'grid_view_x', cfg_validator=QIntValidator())
self.add_option_line_edit('Grid view Y', 'Gui', 'grid_view_y', cfg_validator=QIntValidator())
self.add_option_line_edit('Database URL', 'Database', 'url')
+
+ self.add_section('{}Thumbnails{}'.format(self.deco_l, self.deco_r))
+ self.add_option_checkbox('Force download best quality, based on prioritised list',
+ 'Thumbnails', 'force_download_best', restart_check=False)
+ self.add_option_combobox('1. Priority', 'Thumbnails', '0', THUMBNAIL_QUALITIES, restart_check=False)
+ self.add_option_combobox('2. Priority', 'Thumbnails', '1', THUMBNAIL_QUALITIES, restart_check=False)
+ self.add_option_combobox('3. Priority', 'Thumbnails', '2', THUMBNAIL_QUALITIES, restart_check=False)
+ self.add_option_combobox('4. Priority', 'Thumbnails', '3', THUMBNAIL_QUALITIES, restart_check=False)
+ self.add_option_combobox('5. Priority', 'Thumbnails', '4', THUMBNAIL_QUALITIES, restart_check=False)
+ self.add_option_line_edit('Image/thumbnail download thread limit', 'Threading', 'img_threads',
+ cfg_validator=QIntValidator(), restart_check=False)
+
+ self.add_section('{}YouTube requests{}'.format(self.deco_l, self.deco_r))
+ self.add_option_checkbox('Use tests', 'Requests', 'use_tests', restart_check=False)
+ self.add_option_line_edit('Missed video limit', 'Requests', 'miss_limit',
+ cfg_validator=QIntValidator(), restart_check=False)
+ self.add_option_line_edit('Test pages', 'Requests', 'test_pages',
+ cfg_validator=QIntValidator(), restart_check=False)
+ self.add_option_line_edit('Additional list pages', 'Requests', 'extra_list_pages',
+ cfg_validator=QIntValidator(), restart_check=False)
+ self.add_option_line_edit('Deep search API quota limit per request (in K)', 'Requests',
+ 'deep_search_quota_k',
+ cfg_validator=QIntValidator(), restart_check=False)
+ self.add_option_line_edit('Filter videos older than (days)', 'Requests', 'filter_videos_days_old',
+ cfg_validator=QIntValidator(), restart_check=False)
+
self.add_option_info_restart_required()
diff --git a/sane_yt_subfeed/gui/views/grid_view/labels/channel_label.py b/sane_yt_subfeed/gui/views/grid_view/labels/channel_label.py
index 88175b2..b849a13 100644
--- a/sane_yt_subfeed/gui/views/grid_view/labels/channel_label.py
+++ b/sane_yt_subfeed/gui/views/grid_view/labels/channel_label.py
@@ -1,12 +1,17 @@
-from sane_yt_subfeed.gui.views.grid_view.labels.video_tile_label import VideoTileLabel
+from sane_yt_subfeed.gui.views.grid_view.labels.elided_label import ElidedLabel
+from sane_yt_subfeed.handlers.config_handler import read_config
+from PyQt5.QtGui import QFont
CFG_LINES_ENTRY = ['GridView', 'tile_channel_lines']
CFG_ELIDED_MOD_ENTRY = ['GridView', 'elided_text_modifier_channel']
-class ChannelLabel(VideoTileLabel):
+class ChannelLabel(ElidedLabel):
def __init__(self, text, parent):
- VideoTileLabel.__init__(self, text, parent, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY)
+ font = QFont()
+ font.fromString(read_config("Fonts", "video_channel_font", literal_eval=False))
+
+ ElidedLabel.__init__(self, text, parent, font, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY)
diff --git a/sane_yt_subfeed/gui/views/grid_view/labels/date_label.py b/sane_yt_subfeed/gui/views/grid_view/labels/date_label.py
index 0d777f6..c70800a 100644
--- a/sane_yt_subfeed/gui/views/grid_view/labels/date_label.py
+++ b/sane_yt_subfeed/gui/views/grid_view/labels/date_label.py
@@ -1,11 +1,15 @@
-from sane_yt_subfeed.gui.views.grid_view.labels.video_tile_label import VideoTileLabel
+from sane_yt_subfeed.gui.views.grid_view.labels.elided_label import ElidedLabel
+from sane_yt_subfeed.handlers.config_handler import read_config
+from PyQt5.QtGui import QFont
CFG_LINES_ENTRY = ['GridView', 'tile_date_lines']
CFG_ELIDED_MOD_ENTRY = ['GridView', 'elided_text_modifier_date']
-class DateLabel(VideoTileLabel):
+class DateLabel(ElidedLabel):
def __init__(self, text, parent):
- VideoTileLabel.__init__(self, text, parent, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY)
+ font = QFont()
+ font.fromString(read_config("Fonts", "video_date_font", literal_eval=False))
+ ElidedLabel.__init__(self, text, parent, font, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY)
diff --git a/sane_yt_subfeed/gui/views/grid_view/labels/video_tile_label.py b/sane_yt_subfeed/gui/views/grid_view/labels/elided_label.py
similarity index 76%
rename from sane_yt_subfeed/gui/views/grid_view/labels/video_tile_label.py
rename to sane_yt_subfeed/gui/views/grid_view/labels/elided_label.py
index 64c1aff..54b74dd 100644
--- a/sane_yt_subfeed/gui/views/grid_view/labels/video_tile_label.py
+++ b/sane_yt_subfeed/gui/views/grid_view/labels/elided_label.py
@@ -5,27 +5,24 @@
from sane_yt_subfeed.handlers.config_handler import read_config
from sane_yt_subfeed.utils import get_unicode_weight
-from sane_yt_subfeed.gui.views.config_view.config_item_types import TILE_TITLE_FONT_WEIGHTS_MAP
-class VideoTileLabel(QLabel):
+class ElidedLabel(QLabel):
- def __init__(self, text, parent, cfg_lines_entry, cfg_elided_mod_entry, cfg_font_weight=None):
+ def __init__(self, text, parent, font: QFont, cfg_lines_entry, cfg_elided_mod_entry):
"""
- Video tile label (superclass).
+ Elided label (superclass).
:param text: String to put on QLabel.
:param parent: Parent ptr.
- :param cfg_lines_entry: Config [section, option] entry for lines of text to show.
- :param cfg_elided_mod_entry: Config [section, option] entry for elided text modifier.
- :param cfg_font_weight: Config [section, option] entry for font weight (optional).
+ :param cfg_lines_entry: QFont font to use.
"""
QLabel.__init__(self, text)
self.parent = parent
+ self.setFont(font)
# Set label type independent config entries
self.cfg_lines_entry: list = cfg_lines_entry
self.cfg_elided_mod_entry: list = cfg_elided_mod_entry
- self.cfg_font_weight: list = cfg_font_weight
# Elided overwrites the original, so we need to keep a copy.
self.original_text = text
@@ -33,13 +30,6 @@ def __init__(self, text, parent, cfg_lines_entry, cfg_elided_mod_entry, cfg_font
# Get font metrics/info.
metrics = QFontMetrics(self.font())
- # Set up font.
- t_font: QFont = self.font()
- t_font.setStyleHint(QFont.Helvetica) # FIXME: Make font configurable
- if cfg_font_weight:
- t_font.setWeight(TILE_TITLE_FONT_WEIGHTS_MAP[read_config(*cfg_font_weight)])
- t_font.setFixedPitch(True)
-
# Lines of text to show (determines height of title text item).
lines = read_config(*self.cfg_lines_entry)
@@ -47,7 +37,7 @@ def __init__(self, text, parent, cfg_lines_entry, cfg_elided_mod_entry, cfg_font
#
# If set to 2 there will be 1px clearing beneath unicode,
# but ASCII will show 1px of its supposedly cut-off next line.
- unicode_height_offset = read_config('GridView', 'tile_unicode_line_height_offset') # = 1.99
+ unicode_height_offset = read_config('GridView', 'tile_unicode_line_height_offset')
# Set height equal to lines and add some newline spacing for unicode.
self.setFixedHeight((metrics.height() * lines) + (unicode_height_offset * lines))
@@ -56,9 +46,6 @@ def __init__(self, text, parent, cfg_lines_entry, cfg_elided_mod_entry, cfg_font
self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
self.setWordWrap(True)
- # Apply modified font.
- self.setFont(t_font)
-
# Finally, set the text string.
self.setText(text, elided=True)
diff --git a/sane_yt_subfeed/gui/views/grid_view/labels/title_label.py b/sane_yt_subfeed/gui/views/grid_view/labels/title_label.py
index a770f4c..41ffb13 100644
--- a/sane_yt_subfeed/gui/views/grid_view/labels/title_label.py
+++ b/sane_yt_subfeed/gui/views/grid_view/labels/title_label.py
@@ -1,12 +1,15 @@
-from sane_yt_subfeed.gui.views.grid_view.labels.video_tile_label import VideoTileLabel
+from sane_yt_subfeed.gui.views.grid_view.labels.elided_label import ElidedLabel
+from sane_yt_subfeed.handlers.config_handler import read_config
+from PyQt5.QtGui import QFont
CFG_LINES_ENTRY = ['GridView', 'tile_title_lines']
CFG_ELIDED_MOD_ENTRY = ['GridView', 'elided_text_modifier_title']
-CFG_FONT_WEIGHT = ['GridView', 'title_tile_font_weight']
-class TitleLabel(VideoTileLabel):
+class TitleLabel(ElidedLabel):
def __init__(self, text, parent):
- VideoTileLabel.__init__(self, text, parent, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY,
- cfg_font_weight=CFG_FONT_WEIGHT)
+ font = QFont()
+ font.fromString(read_config("Fonts", "video_title_font", literal_eval=False))
+
+ ElidedLabel.__init__(self, text, parent, font, CFG_LINES_ENTRY, CFG_ELIDED_MOD_ENTRY)
diff --git a/sane_yt_subfeed/gui/views/grid_view/thumbnail_tile.py b/sane_yt_subfeed/gui/views/grid_view/thumbnail_tile.py
index 58700fa..6cb3bc0 100644
--- a/sane_yt_subfeed/gui/views/grid_view/thumbnail_tile.py
+++ b/sane_yt_subfeed/gui/views/grid_view/thumbnail_tile.py
@@ -1,5 +1,5 @@
from PyQt5.QtCore import Qt, QSize, QPoint, QRect
-from PyQt5.QtGui import QPainter, QPixmap, QBrush, QColor, QPen, QFont
+from PyQt5.QtGui import QPainter, QPixmap, QBrush, QColor, QPen, QFont, QFontMetrics
from PyQt5.QtWidgets import QLabel
from sane_yt_subfeed.absolute_paths import THUMBNAIL_NA_PATH
@@ -14,8 +14,7 @@ def __init__(self, parent):
self.parent = parent
self.logger = create_logger(__name__ + ".ThumbnailTitle")
- margins = self.parent.layout.getContentsMargins()
- self.setFixedSize(self.parent.width() - margins[0] - margins[2], (self.parent.height() - 4 * margins[3]) * 0.6)
+ self.setMinimumWidth(self.parent.width())
def setPixmap(self, p):
"""
@@ -37,38 +36,88 @@ def paintEvent(self, event):
painter = QPainter(self)
if read_config('Gui', 'keep_thumb_ar'):
- thumb = self.p.scaled(self.width(), self.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ thumb = self.p.scaledToWidth(self.width(), Qt.SmoothTransformation)
+ self.setMinimumHeight(thumb.height())
painter.drawPixmap(0, 0, thumb)
else:
thumb = self
painter.drawPixmap(thumb.rect(), thumb.p)
# Overlay video duration on thumbnail
+ font = QFont()
+ font.fromString(read_config("Fonts", "video_thumbnail_overlay_font", literal_eval=False))
+
pen = QPen(Qt.white)
painter.setPen(pen)
- point = QPoint(thumb.width() * 0.70, thumb.height() * 0.85)
- rect = QRect(point, QSize(thumb.width() * 0.28, thumb.height() * 0.12))
+ painter.setFont(font)
+
+ duration_right_padding = 4
+ duration_bottom_padding = 8
+
+ point = QPoint(
+ thumb.width() - duration_right_padding,
+ thumb.height() - duration_bottom_padding
+ )
+ metrics = QFontMetrics(font)
+ duration_string = format(self.parent.video.duration)
+ # Find out the width of the text
+ text_width = metrics.width(duration_string)
+ # Work out the max height the text can be
+ text_height = metrics.descent() + metrics.ascent()
+ # Add a padding of 8px (4px on left, 4px on right) for width
+ rect_width = text_width + 8
+ # Add a padding of 4px (2px on top, 2px on bottom) for height
+ rect_height = text_height + 4
+ # Create a rectangle
+ # point starts at the bottom right so we need to use negative sizes
+ # because we need to move closer to 0,0 again
+ rect = QRect(point, QSize(-rect_width, -rect_height))
painter.fillRect(rect, QBrush(QColor(0, 0, 0, 180)))
- painter.drawText(rect, Qt.AlignCenter, format(self.parent.video.duration))
+ painter.drawText(rect, Qt.AlignCenter, duration_string)
# Overlay captions (if any) on thumbnail # FIXME: Replace with something better like a small icon
- if self.parent.video.has_caption:
+ if self.parent.video.has_caption and read_config('GridView', 'show_has_captions'):
pen = QPen(Qt.white)
painter.setPen(pen)
- point = QPoint(thumb.width() * 0.03, thumb.height() * 0.85)
- rect = QRect(point, QSize(thumb.width() * 0.28, thumb.height() * 0.12))
+ painter.setFont(font)
+
+ captions_left_padding = 4
+ captions_bottom_padding = 8
+
+ point = QPoint(
+ captions_left_padding,
+ thumb.height() - captions_bottom_padding
+ )
+ metrics = QFontMetrics(font)
+ text_width = metrics.width("captions")
+ text_height = metrics.descent() + metrics.ascent()
+ rect_width = text_width + 8
+ rect_height = text_height + 4
+
+ rect = QRect(point, QSize(rect_width, -rect_height))
painter.fillRect(rect, QBrush(QColor(0, 0, 0, 180)))
painter.drawText(rect, Qt.AlignCenter, "captions")
- if self.parent.video.definition == "sd":
+ if self.parent.video.definition == "sd" and read_config('GridView', 'show_sd_warning'):
pen = QPen(Qt.red)
painter.setPen(pen)
- point = QPoint(thumb.width() * 0.02, thumb.height() * 0.02)
- rect = QRect(point, QSize(thumb.width() * 0.16, thumb.height() * 0.20))
+ painter.setFont(font)
+
+ sd_left_padding = 4
+ sd_top_padding = 4
+
+ point = QPoint(
+ sd_left_padding,
+ sd_top_padding
+ )
+ metrics = QFontMetrics(font)
+ text_width = metrics.width("SD")
+ text_height = metrics.descent() + metrics.ascent()
+ rect_width = text_width + 4
+ rect_height = text_height + 4
+
+ rect = QRect(point, QSize(rect_width, rect_height))
painter.fillRect(rect, QBrush(QColor(0, 0, 0, 180)))
- enlarged_font = QFont(painter.font())
- enlarged_font.setPointSize(14)
- painter.setFont(enlarged_font)
painter.drawText(rect, Qt.AlignCenter, "SD")
self.add_overlay(painter, thumb)
diff --git a/sane_yt_subfeed/gui/views/grid_view/video_tile.py b/sane_yt_subfeed/gui/views/grid_view/video_tile.py
index a3628bf..708555a 100644
--- a/sane_yt_subfeed/gui/views/grid_view/video_tile.py
+++ b/sane_yt_subfeed/gui/views/grid_view/video_tile.py
@@ -42,43 +42,55 @@ def __init__(self, parent, video, vid_id, clipboard, status_bar):
self.pref_height = read_config('Gui', 'tile_pref_height')
self.pref_width = read_config('Gui', 'tile_pref_width')
- self.setFixedSize(self.pref_width, self.pref_height)
self.layout = QGridLayout()
self.layout.setSpacing(0) # Don't use Qt's "global padding" spacing.
- self.layout.setAlignment(Qt.AlignTop)
+
# Make sure layout items don't overlap
self.layout.setContentsMargins(0, 0, 0, 0)
-
+
self.thumbnail_label = self.init_thumbnail_tile()
- self.title_label = TitleLabel(video.title, self)
- self.channel_label = ChannelLabel(video.channel_title, self)
- self.date_label = DateLabel('', self)
+ if read_config('GridView', 'tile_title_lines') != 0:
+ self.title_label = TitleLabel(video.title, self)
+ if read_config('GridView', 'tile_channel_lines') != 0:
+ self.channel_label = ChannelLabel(video.channel_title, self)
+ if read_config('GridView', 'tile_date_lines') != 0:
+ self.date_label = DateLabel('', self)
+
+ self.setFixedWidth(self.pref_width)
# Use a blank QLabel as spacer item for increased control of spacing (avoids global padding).
spacer_label = QLabel()
spacer_label.setFixedHeight(read_config('GridView', 'tile_line_spacing'))
+ if read_config('Debug', 'color_tile_elements'):
+ self.color_palette(Qt.green)
+ self.thumbnail_label.setStyleSheet("QLabel { background-color : darkMagenta}")
+ # spacer_label.setStyleSheet("QLabel { background-color : cyan}")
+ if read_config('GridView', 'tile_channel_lines') != 0:
+ self.title_label.setStyleSheet("QLabel { background-color : crimson}")
+ if read_config('GridView', 'tile_title_lines') != 0:
+ self.channel_label.setStyleSheet("QLabel { background-color : darkGreen}")
+ if read_config('GridView', 'tile_date_lines') != 0:
+ self.date_label.setStyleSheet("QLabel { background-color : gray}")
+
# Add labels to layout
self.layout.addWidget(self.thumbnail_label)
- self.layout.addWidget(self.title_label)
self.layout.addWidget(spacer_label)
- self.layout.addWidget(self.channel_label)
- self.layout.addWidget(spacer_label)
- self.layout.addWidget(self.date_label)
+ if read_config('GridView', 'tile_title_lines') != 0:
+ self.layout.addWidget(self.title_label)
+ self.layout.addWidget(spacer_label)
+ if read_config('GridView', 'tile_channel_lines') != 0:
+ self.layout.addWidget(self.channel_label)
+ self.layout.addWidget(spacer_label)
+ if read_config('GridView', 'tile_date_lines') != 0:
+ self.layout.addWidget(self.date_label)
self.setLayout(self.layout)
# Add video on the layout/tile.
self.set_video(video)
- if read_config('Debug', 'color_tile_elements'):
- self.color_palette(Qt.green)
- self.thumbnail_label.setStyleSheet("QLabel { background-color : darkMagenta}")
- self.title_label.setStyleSheet("QLabel { background-color : crimson}")
- self.channel_label.setStyleSheet("QLabel { background-color : darkGreen}")
- self.date_label.setStyleSheet("QLabel { background-color : gray}")
-
def init_thumbnail_tile(self):
raise ValueError("ThumbnailTile initialised from VideoTile, not subclass!")
@@ -94,9 +106,10 @@ def set_video(self, video):
self.video = video
self.set_tool_tip()
- self.channel_label.setText(self.video.channel_title)
-
- self.date_label.setText(self.strf_delta(self.video.date_published))
+ if read_config('GridView', 'tile_channel_lines') != 0:
+ self.channel_label.setText(self.video.channel_title)
+ if read_config('GridView', 'tile_date_lines') != 0:
+ self.date_label.setText(self.strf_delta(self.video.date_published))
self.color_old_video(self.video.date_published)
self.color_live_video()
@@ -234,7 +247,6 @@ def strf_delta(date_published, fmt=None):
def color_palette(self, color, role=QPalette.Window, log_facility=None, log_msg=""):
"""
Colors a given palette.
- :param palette:
:param color: A Qt color integer
:param role: Which QPalette role to apply color to (default: background)
:param log_facility: if set, log to this facility
diff --git a/sane_yt_subfeed/handlers/config_handler.py b/sane_yt_subfeed/handlers/config_handler.py
index b6914f4..d2adda2 100644
--- a/sane_yt_subfeed/handlers/config_handler.py
+++ b/sane_yt_subfeed/handlers/config_handler.py
@@ -2,8 +2,6 @@
import copy
import os
from configparser import ConfigParser, NoSectionError, NoOptionError
-from shutil import copyfile
-
from sane_yt_subfeed.absolute_paths import CONFIG_PATH, SAMPLE_PATH, CONFIG_HOTKEYS_PATH, \
SAMPLE_HOTKEYS_PATH, DATABASE_PATH
@@ -28,7 +26,7 @@
'Debug': {
'debug': 'False',
'cached_subs': 'True',
- 'start_with_stored_videos': 'False',
+ 'start_with_stored_videos': 'True',
'channels_limit': '-1',
'use_playlistitems': 'True',
'disable_tooltips': 'False',
@@ -59,22 +57,30 @@
'last_style': "",
'last_theme': ""
},
+ 'Fonts': {
+ 'video_title_font': 'Noto Sans,10,-1,0,75,0,0,0,0,0,Bold',
+ 'video_channel_font': 'Noto Sans,10,-1,0,50,0,0,0,0,0,Regular',
+ 'video_date_font': 'Noto Sans,10,-1,0,50,0,0,0,0,0,Regular',
+ 'video_thumbnail_overlay_font': 'Noto Sans,10,-1,0,50,0,0,0,0,0,Regular'
+ },
'GridView': {
'show_watched': 'False',
'show_dismissed': 'False',
- 'elided_text_modifier_title': '0.28',
- 'elided_text_modifier_channel': '0.28',
- 'elided_text_modifier_date': '0.28',
+ 'show_sd_warning': 'False',
+ 'show_has_captions': 'True',
+ 'elided_text_modifier_title': '1',
+ 'elided_text_modifier_channel': '1',
+ 'elided_text_modifier_date': '1',
'elided_text_unicode_weight_modifier': '0.0075',
'tile_unicode_line_height_offset': '1.99',
- 'tile_line_spacing': '7',
+ 'tile_line_spacing': '5',
'tile_title_lines': '2',
'tile_channel_lines': '1',
'tile_date_lines': '1',
'title_tile_font_weight': 'Bold',
'timedelta_format': '$HH:$MM:$SS ago',
'timedelta_format_days': '$d days, $HH:$MM:$SS ago',
- 'timedelta_format_months': '$m months, $d d, $HH:$MM:$SS ago',
+ 'timedelta_format_months': '$m months, ${d}d, $HH:$MM:$SS ago',
'timedelta_format_years': '${yd}y, ${m}m, ${d}d, $HH:$MM:$SS ago',
'timedelta_format_decades': '${decades}dc, ${yd}y, ${m}m, ${d}d, $HH:$MM:$SS ago'
},
@@ -244,25 +250,18 @@ def read_config(section, option, literal_eval=True, custom_ini=None):
:param custom_ini: if set, use given custom config
:return:
"""
- config_path = CONFIG_PATH
- sample_path = SAMPLE_PATH
defaults = DEFAULTS
parser = default_parser
# Support multiple configs
if custom_ini is not None:
# logger.debug("Reading custom config: {}".format(custom_ini))
if custom_ini == "hotkeys":
- config_path = CONFIG_HOTKEYS_PATH
- sample_path = SAMPLE_HOTKEYS_PATH
defaults = DEFAULTS_HOTKEYS
parser = hotkeys_parser
else:
- # logger.critical("Custom config '{}' is not defined in handler!!".format(custom_ini))
raise ValueError("Custom config '{}' is not defined in handler!!".format(custom_ini))
if literal_eval:
- if not os.path.exists(config_path):
- create_config_file(config_path, DEFAULTS)
try:
value = parser.get(section, option)
except (NoSectionError, NoOptionError):
@@ -275,8 +274,6 @@ def read_config(section, option, literal_eval=True, custom_ini=None):
else:
return ast.literal_eval(defaults[section][option])
else:
- if not os.path.exists(config_path):
- create_config_file(config_path, DEFAULTS)
try:
value = parser.get(section, option)
except (NoSectionError, NoOptionError):
diff --git a/sane_yt_subfeed/utils.py b/sane_yt_subfeed/utils.py
index 1de9cfd..7a0594a 100644
--- a/sane_yt_subfeed/utils.py
+++ b/sane_yt_subfeed/utils.py
@@ -15,3 +15,16 @@ def get_unicode_weight(text, unicode_weight_modifier):
unicode_weight += unicode_weight_modifier
return unicode_weight
+
+
+def text_has_unicode(text):
+ """
+ Determine if a text contains unicode characters.
+ :param text:
+ :return:
+ """
+ for c in text:
+ if ord(c) > 128:
+ return True
+
+ return False