kodi.plugin.video.youtube/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py
MoojMidge fca610cb25 Reorganise constants and simplify window property usage
- Remove need for script action for external player switching
2024-06-25 00:09:49 +10:00

477 lines
16 KiB
Python

# -*- coding: utf-8 -*-
"""
Copyright (C) 2014-2016 bromix (plugin.video.youtube)
Copyright (C) 2016-2018 plugin.video.youtube
SPDX-License-Identifier: GPL-2.0-only
See LICENSES/GPL-2.0-only for more information.
"""
from __future__ import absolute_import, division, unicode_literals
import sys
from ..constants import SETTINGS
from ..utils import current_system_version, validate_ip_address
class AbstractSettings(object):
_vars = vars()
for name, value in SETTINGS.__dict__.items():
_vars[name] = value
del _vars
_echo = False
_cache = {}
_check_set = True
@classmethod
def flush(cls, xbmc_addon):
raise NotImplementedError()
def get_bool(self, setting, default=None, echo=None):
raise NotImplementedError()
def set_bool(self, setting, value, echo=None):
raise NotImplementedError()
def get_int(self, setting, default=-1, converter=None, echo=None):
raise NotImplementedError()
def set_int(self, setting, value, echo=None):
raise NotImplementedError()
def get_string(self, setting, default='', echo=None):
raise NotImplementedError()
def set_string(self, setting, value, echo=None):
raise NotImplementedError()
def get_string_list(self, setting, default=None, echo=None):
raise NotImplementedError()
def set_string_list(self, setting, value, echo=None):
raise NotImplementedError()
def open_settings(self):
raise NotImplementedError()
def items_per_page(self, value=None):
if value is not None:
return self.set_int(SETTINGS.ITEMS_PER_PAGE, value)
return self.get_int(SETTINGS.ITEMS_PER_PAGE, 50)
_VIDEO_QUALITY_MAP = {
0: 240,
1: 360,
2: 480, # 576 seems not to work well
3: 720,
4: 1080,
}
def fixed_video_quality(self, value=None):
default = 3
if value is None:
_value = self.get_int(SETTINGS.VIDEO_QUALITY, default)
else:
_value = value
if _value not in self._VIDEO_QUALITY_MAP:
_value = default
if value is not None:
self.set_int(SETTINGS.VIDEO_QUALITY, _value)
return self._VIDEO_QUALITY_MAP[_value]
def ask_for_video_quality(self):
return (self.get_bool(SETTINGS.VIDEO_QUALITY_ASK, False)
or self.get_int(SETTINGS.MPD_STREAM_SELECT) == 4)
def fanart_selection(self):
return self.get_int(SETTINGS.FANART_SELECTION, 2)
def cache_size(self, value=None):
if value is not None:
return self.set_int(SETTINGS.CACHE_SIZE, value)
return self.get_int(SETTINGS.CACHE_SIZE, 20)
def get_search_history_size(self):
return self.get_int(SETTINGS.SEARCH_SIZE, 10)
def setup_wizard_enabled(self, value=None):
# Increment min_required on new release to enable oneshot on first run
min_required = 4
if value is False:
self.set_int(SETTINGS.SETUP_WIZARD_RUNS, min_required)
return self.set_bool(SETTINGS.SETUP_WIZARD, False)
if value is True:
self.set_int(SETTINGS.SETUP_WIZARD_RUNS, 0)
return self.set_bool(SETTINGS.SETUP_WIZARD, True)
forced_runs = self.get_int(SETTINGS.SETUP_WIZARD_RUNS, 0)
if forced_runs < min_required:
self.set_int(SETTINGS.SETUP_WIZARD_RUNS, min_required)
self.set_bool(SETTINGS.SETTINGS_END, True)
return True
return self.get_bool(SETTINGS.SETUP_WIZARD, False)
def support_alternative_player(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.SUPPORT_ALTERNATIVE_PLAYER, value)
return self.get_bool(SETTINGS.SUPPORT_ALTERNATIVE_PLAYER, False)
def default_player_web_urls(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.DEFAULT_PLAYER_WEB_URLS, value)
if self.support_alternative_player():
return False
return self.get_bool(SETTINGS.DEFAULT_PLAYER_WEB_URLS, False)
def alternative_player_web_urls(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.ALTERNATIVE_PLAYER_WEB_URLS, value)
if (self.support_alternative_player()
and not self.alternative_player_adaptive()):
return self.get_bool(SETTINGS.ALTERNATIVE_PLAYER_WEB_URLS, False)
return False
def alternative_player_adaptive(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.ALTERNATIVE_PLAYER_ADAPTIVE, value)
if self.support_alternative_player():
return self.get_bool(SETTINGS.ALTERNATIVE_PLAYER_ADAPTIVE, False)
return False
def use_isa(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.USE_ISA, value)
return self.get_bool(SETTINGS.USE_ISA, False)
def subtitle_download(self):
return self.get_bool(SETTINGS.SUBTITLE_DOWNLOAD, False)
def audio_only(self):
return self.get_bool(SETTINGS.AUDIO_ONLY, False)
def get_subtitle_selection(self):
return self.get_int(SETTINGS.SUBTITLE_SELECTION, 0)
def set_subtitle_selection(self, value):
return self.set_int(SETTINGS.SUBTITLE_SELECTION, value)
def set_subtitle_download(self, value):
return self.set_bool(SETTINGS.SUBTITLE_DOWNLOAD, value)
_THUMB_SIZES = {
0: { # Medium (16:9)
'size': 320 * 180,
'ratio': 320 / 180,
},
1: { # High (4:3)
'size': 480 * 360,
'ratio': 480 / 360,
},
2: { # Best available
'size': 0,
'ratio': 0,
},
}
def get_thumbnail_size(self, value=None):
default = 1
if value is None:
value = self.get_int(SETTINGS.THUMB_SIZE, default)
if value in self._THUMB_SIZES:
return self._THUMB_SIZES[value]
return self._THUMB_SIZES[default]
def safe_search(self):
index = self.get_int(SETTINGS.SAFE_SEARCH, 0)
values = {0: 'moderate', 1: 'none', 2: 'strict'}
return values[index]
def age_gate(self):
return self.get_bool(SETTINGS.AGE_GATE, True)
def verify_ssl(self):
verify = self.get_bool(SETTINGS.VERIFY_SSL, False)
if sys.version_info <= (2, 7, 9):
verify = False
return verify
def get_timeout(self):
connect_timeout = self.get_int(SETTINGS.CONNECT_TIMEOUT, 9) + 0.5
read_timout = self.get_int(SETTINGS.READ_TIMEOUT, 27)
return connect_timeout, read_timout
def allow_dev_keys(self):
return self.get_bool(SETTINGS.ALLOW_DEV_KEYS, False)
def use_mpd_videos(self, value=None):
if self.use_isa():
if value is not None:
return self.set_bool(SETTINGS.MPD_VIDEOS, value)
return self.get_bool(SETTINGS.MPD_VIDEOS, True)
return False
_LIVE_STREAM_TYPES = {
0: 'mpegts',
1: 'hls',
2: 'isa_hls',
3: 'isa_mpd',
}
def live_stream_type(self, value=None):
if self.use_isa():
default = 2
setting = SETTINGS.LIVE_STREAMS + '.1'
else:
default = 0
setting = SETTINGS.LIVE_STREAMS + '.2'
if value is not None:
return self.set_int(setting, value)
value = self.get_int(setting, default)
if value in self._LIVE_STREAM_TYPES:
return self._LIVE_STREAM_TYPES[value]
return self._LIVE_STREAM_TYPES[default]
def use_isa_live_streams(self):
if self.use_isa():
return self.get_int(SETTINGS.LIVE_STREAMS + '.1', 2) > 1
return False
def use_mpd_live_streams(self):
if self.use_isa():
return self.get_int(SETTINGS.LIVE_STREAMS + '.1', 2) == 3
return False
def httpd_port(self, value=None):
default = 50152
if value is None:
port = self.get_int(SETTINGS.HTTPD_PORT, default)
else:
port = value
try:
port = int(port)
except ValueError:
port = default
if value is not None:
return self.set_int(SETTINGS.HTTPD_PORT, port)
return port
def httpd_listen(self, value=None):
default = '0.0.0.0'
if value is None:
ip_address = self.get_string(SETTINGS.HTTPD_LISTEN, default)
else:
ip_address = value
octets = validate_ip_address(ip_address)
ip_address = '.'.join(map(str, octets))
if value is not None:
return self.set_string(SETTINGS.HTTPD_LISTEN, ip_address)
return ip_address
def httpd_whitelist(self):
whitelist = self.get_string(SETTINGS.HTTPD_WHITELIST, '')
whitelist = ''.join(whitelist.split()).split(',')
allow_list = []
for ip_address in whitelist:
octets = validate_ip_address(ip_address)
if not any(octets):
continue
allow_list.append('.'.join(map(str, octets)))
return allow_list
def api_config_page(self):
return self.get_bool(SETTINGS.API_CONFIG_PAGE, False)
def api_id(self, new_id=None):
if new_id is not None:
self.set_string(SETTINGS.API_ID, new_id)
return new_id
return self.get_string(SETTINGS.API_ID)
def api_key(self, new_key=None):
if new_key is not None:
self.set_string(SETTINGS.API_KEY, new_key)
return new_key
return self.get_string(SETTINGS.API_KEY)
def api_secret(self, new_secret=None):
if new_secret is not None:
self.set_string(SETTINGS.API_SECRET, new_secret)
return new_secret
return self.get_string(SETTINGS.API_SECRET)
def get_location(self):
location = self.get_string(SETTINGS.LOCATION, '').replace(' ', '').strip()
coords = location.split(',')
latitude = longitude = None
if len(coords) == 2:
try:
latitude = float(coords[0])
longitude = float(coords[1])
if latitude > 90.0 or latitude < -90.0:
latitude = None
if longitude > 180.0 or longitude < -180.0:
longitude = None
except ValueError:
latitude = longitude = None
if latitude and longitude:
return '{lat},{long}'.format(lat=latitude, long=longitude)
return ''
def set_location(self, value):
self.set_string(SETTINGS.LOCATION, value)
def get_location_radius(self):
return ''.join((self.get_int(SETTINGS.LOCATION_RADIUS, 500, str), 'km'))
def get_play_count_min_percent(self):
return self.get_int(SETTINGS.PLAY_COUNT_MIN_PERCENT, 0)
def use_local_history(self):
return self.get_bool(SETTINGS.USE_LOCAL_HISTORY, False)
def use_remote_history(self):
return self.get_bool(SETTINGS.USE_REMOTE_HISTORY, False)
# Selections based on max width and min height at common (utra-)wide aspect ratios
_QUALITY_SELECTIONS = { # Setting | Resolution
7: {'width': 7680, 'min_height': 3148, 'nom_height': 4320, 'label': '{0}p{1} (8K){2}'}, # 7 | 4320p 8K
6: {'width': 3840, 'min_height': 1080, 'nom_height': 2160, 'label': '{0}p{1} (4K){2}'}, # 6 | 2160p 4K
5: {'width': 2560, 'min_height': 984, 'nom_height': 1440, 'label': '{0}p{1} (QHD){2}'}, # 5 | 1440p 2.5K / QHD
4.1: {'width': 2048, 'min_height': 858, 'nom_height': 1152, 'label': '{0}p{1} (2K){2}'}, # N/A | 1152p 2K / QWXGA
4: {'width': 1920, 'min_height': 787, 'nom_height': 1080, 'label': '{0}p{1} (FHD){2}'}, # 4 | 1080p FHD
3: {'width': 1280, 'min_height': 525, 'nom_height': 720, 'label': '{0}p{1} (HD){2}'}, # 3 | 720p HD
2: {'width': 854, 'min_height': 350, 'nom_height': 480, 'label': '{0}p{1}{2}'}, # 2 | 480p
1: {'width': 640, 'min_height': 263, 'nom_height': 360, 'label': '{0}p{1}{2}'}, # 1 | 360p
0: {'width': 426, 'min_height': 175, 'nom_height': 240, 'label': '{0}p{1}{2}'}, # 0 | 240p
-1: {'width': 256, 'min_height': 105, 'nom_height': 144, 'label': '{0}p{1}{2}'}, # N/A | 144p
-2: {'width': 0, 'min_height': 0, 'nom_height': 0, 'label': '{0}p{1}{2}'}, # N/A | Custom
}
def mpd_video_qualities(self, value=None):
if value is not None:
return self.set_int(SETTINGS.MPD_QUALITY_SELECTION, value)
if not self.use_mpd_videos():
return []
value = self.get_int(SETTINGS.MPD_QUALITY_SELECTION, 4)
return [quality
for key, quality in sorted(self._QUALITY_SELECTIONS.items(),
reverse=True)
if value >= key]
def stream_features(self, value=None):
if value is not None:
return self.set_string_list(SETTINGS.MPD_STREAM_FEATURES, value)
return frozenset(self.get_string_list(SETTINGS.MPD_STREAM_FEATURES))
_STREAM_SELECT = {
1: 'auto',
2: 'list',
3: 'auto+list',
4: 'ask+auto+list',
}
def stream_select(self, value=None):
if value is not None:
return self.set_int(SETTINGS.MPD_STREAM_SELECT, value)
default = 3
value = self.get_int(SETTINGS.MPD_STREAM_SELECT, default)
if value in self._STREAM_SELECT:
return self._STREAM_SELECT[value]
return self._STREAM_SELECT[default]
_DEFAULT_FILTER = {
'shorts': True,
'upcoming': True,
'upcoming_live': True,
'live': True,
'premieres': True,
'completed': True,
'vod': True,
}
def item_filter(self, update=None):
types = dict.fromkeys(self.get_string_list(SETTINGS.HIDE_VIDEOS), False)
types = dict(self._DEFAULT_FILTER, **types)
if update:
if 'live_folder' in update:
if 'live_folder' in types:
types.update(update)
else:
types.update({
'upcoming': True,
'upcoming_live': True,
'live': True,
'premieres': True,
'completed': True,
})
types['vod'] = False
else:
types.update(update)
return types
def client_selection(self, value=None):
if value is not None:
return self.set_int(SETTINGS.CLIENT_SELECTION, value)
return self.get_int(SETTINGS.CLIENT_SELECTION, 0)
def show_detailed_description(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.DETAILED_DESCRIPTION, value)
return self.get_bool(SETTINGS.DETAILED_DESCRIPTION, True)
def show_detailed_labels(self, value=None):
if value is not None:
return self.set_bool(SETTINGS.DETAILED_LABELS, value)
return self.get_bool(SETTINGS.DETAILED_LABELS, True)
def get_language(self):
return self.get_string(SETTINGS.LANGUAGE, 'en_US').replace('_', '-')
def set_language(self, language_id):
return self.set_string(SETTINGS.LANGUAGE, language_id)
def get_region(self):
return self.get_string(SETTINGS.REGION, 'US')
def set_region(self, region_id):
return self.set_string(SETTINGS.REGION, region_id)
def get_watch_later_playlist(self):
return self.get_string(SETTINGS.WATCH_LATER_PLAYLIST, '').strip()
def set_watch_later_playlist(self, value):
return self.set_string(SETTINGS.WATCH_LATER_PLAYLIST, value)
def get_history_playlist(self):
return self.get_string(SETTINGS.HISTORY_PLAYLIST, '').strip()
def set_history_playlist(self, value):
return self.set_string(SETTINGS.HISTORY_PLAYLIST, value)
if current_system_version.compatible(20, 0):
def get_label_color(self, label_part):
setting_name = '.'.join((SETTINGS.LABEL_COLOR, label_part))
return self.get_string(setting_name, 'white')
else:
_COLOR_MAP = {
'commentCount': 'cyan',
'favoriteCount': 'gold',
'likeCount': 'lime',
'viewCount': 'lightblue',
}
def get_label_color(self, label_part):
return self._COLOR_MAP.get(label_part, 'white')
def get_channel_name_aliases(self):
return frozenset(self.get_string_list(SETTINGS.CHANNEL_NAME_ALIASES))