mirror of
https://github.com/anxdpanic/plugin.video.youtube.git
synced 2025-12-06 02:30:50 -08:00
477 lines
16 KiB
Python
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))
|