diff --git a/addon.xml b/addon.xml
index 3b82c5b..58f335e 100644
--- a/addon.xml
+++ b/addon.xml
@@ -2,20 +2,25 @@
-
+
executable
- Language Preference Manager
- Sets the audio and subtitle track according to your language preferences
- For bugs, requests or general questions visit the Language Preference Manager thread on the Kodi forum.
+ Language Preference Manager
+ Sets the audio and subtitle track according to your language preferences
+ For bugs, requests or general questions visit the Language Preference Manager thread on the Kodi forum.
all
+ Updated with changes required for Kodi 19 / Python 3
+
+ resources/icon.png
+
+
diff --git a/changelog.txt b/changelog.txt
index 4156f2a..535948e 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,12 +1,3 @@
---- Version 0.0.8
-
-- Fix compatibility with leia
-- Added support for forced subtitles (ability to choose between normal or forced, in UI and conditional settings
-
---- Version 0.0.7
-
-- ?
-
--- Version 0.0.6
- Fix compatibility with jarvis and krypton
diff --git a/default.py b/default.py
index b5c0bcb..6d563b0 100644
--- a/default.py
+++ b/default.py
@@ -1,8 +1,5 @@
-# -*- coding: utf-8 -*-
-# Your code goes below this line
-
import os, sys, re
-import xbmc, xbmcaddon
+import xbmc, xbmcaddon, xbmcvfs
import json as simplejson
@@ -11,8 +8,8 @@
__addonid__ = __addon__.getAddonInfo('id')
__addonname__ = __addon__.getAddonInfo('name')
__addonPath__ = __addon__.getAddonInfo('path')
-__addonResourcePath__ = xbmc.translatePath(os.path.join(__addonPath__, 'resources', 'lib'))
-__addonIconFile__ = xbmc.translatePath(os.path.join(__addonPath__, 'icon.png'))
+__addonResourcePath__ = xbmcvfs.translatePath(os.path.join(__addonPath__, 'resources', 'lib'))
+__addonIconFile__ = xbmcvfs.translatePath(os.path.join(__addonPath__, 'icon.png'))
sys.path.append(__addonResourcePath__)
from langcodes import *
@@ -33,11 +30,7 @@ def log(level, msg):
l = xbmc.LOGINFO
elif level == LOG_DEBUG:
l = xbmc.LOGDEBUG
-
- if isinstance(msg, dict):
- xbmc.log("[Language Preference Manager]: " + str(msg), l)
- else:
- xbmc.log('[Language Preference Manager]: ' + msg.encode('ascii','replace'), l)
+ xbmc.log("[Language Preference Manager]: " + str(msg), l)
class LangPref_Monitor( xbmc.Monitor ):
@@ -62,9 +55,9 @@ def _init_vars( self ):
self.Player = LangPrefMan_Player()
def _daemon( self ):
- while (not xbmc.abortRequested):
- xbmc.sleep(500)
-
+ while (not self.Monitor.abortRequested()):
+ self.Monitor.waitForAbort(1)
+
class LangPrefMan_Player(xbmc.Player) :
@@ -109,8 +102,7 @@ def evalPrefs(self):
if settings.turn_subs_off:
log(LOG_INFO, 'Subtitle: disabling subs' )
self.showSubtitles(False)
-
- #import web_pdb; web_pdb.set_trace()
+
if settings.audio_prefs_on and not fa:
if settings.custom_audio_prefs_on:
trackIndex = self.evalAudioPrefs(settings.custom_audio)
@@ -199,11 +191,11 @@ def evalAudioPrefs(self, audio_prefs):
name, codes = pref
codes = codes.split(r',')
for code in codes:
- if (code == 'non'):
+ if (code is None):
log(LOG_DEBUG,'continue')
continue
if (self.selected_audio_stream and
- self.selected_audio_stream.has_key('language') and
+ 'language' in self.selected_audio_stream and
(code == self.selected_audio_stream['language'] or name == self.selected_audio_stream['language'])):
log(LOG_INFO, 'Selected audio language matches preference {0} ({1})'.format(i, name) )
return -1
@@ -230,18 +222,20 @@ def evalSubPrefs(self, sub_prefs):
name, codes, forced = pref
codes = codes.split(r',')
for code in codes:
- if (code == 'non'):
+ if (code is None):
log(LOG_DEBUG,'continue')
continue
-
- for sub in sorted(self.subtitles, key=lambda subElt: (self.isExternalSub(subElt['name']))):
- log(LOG_DEBUG, u'Wanted name={0}, wanted forced={1}, stream sub index={2} lang={3} name={4}, for iteration {5}'.format(name, forced, sub['index'], sub['language'], sub['name'], i))
- if ((code == sub['language']) or (name == sub['language'])):
- if (self.testForcedFlag(forced, sub['name'])):
- log(LOG_INFO, u'Subtitle language of subtitle {0} matches preference {1} ({2}) forced {3}'.format(sub['index'], i, name, forced) )
+ if (self.selected_sub and
+ 'language' in self.selected_sub and
+ ((code == self.selected_sub['language'] or name == self.selected_sub['language']) and self.testForcedFlag(forced, self.selected_sub['name']))):
+ log(LOG_INFO, 'Selected subtitle language matches preference {0} ({1})'.format(i, name) )
+ return -1
+ else:
+ for sub in self.subtitles:
+ if ((code == sub['language'] or name == sub['language']) and self.testForcedFlag(forced, sub['name'])):
+ log(LOG_INFO, 'Subtitle language of subtitle {0} matches preference {1} ({2})'.format(sub['index'], i, name) )
return sub['index']
-
- log(LOG_INFO, u'Subtitle: preference {0} ({1}:{2}) not available'.format(i, name, code) )
+ log(LOG_INFO, 'Subtitle: preference {0} ({1}:{2}) not available'.format(i, name, code) )
return -2
def evalCondSubPrefs(self, condsub_prefs):
@@ -263,40 +257,40 @@ def evalCondSubPrefs(self, condsub_prefs):
log(LOG_INFO,'Cond Sub: genre/tag preference {0} met with intersection {1}'.format(g_t, (self.genres_and_tags & g_t)))
for pref in preferences:
audio_name, audio_code, sub_name, sub_code, forced = pref
- if (audio_code == 'non'):
+ if (audio_code is None):
log(LOG_DEBUG,'continue')
continue
if (self.selected_audio_stream and
- self.selected_audio_stream.has_key('language') and
+ 'language' in self.selected_audio_stream and
(audio_code == self.selected_audio_stream['language'] or audio_name == self.selected_audio_stream['language'])):
- log(LOG_INFO, u'Selected audio language matches conditional preference {0} ({1}:{2}), force tag is {3}'.format(i, audio_name, sub_name, forced) )
- if (sub_code == 'non'):
+ log(LOG_INFO, 'Selected audio language matches conditional preference {0} ({1}:{2}), force tag is {3}'.format(i, audio_name, sub_name, forced) )
+ if (sub_code is None):
return -1
else:
for sub in self.subtitles:
if ((sub_code == sub['language']) or (sub_name == sub['language'])):
if (self.testForcedFlag(forced, sub['name'])):
- log(LOG_INFO, u'Language of subtitle {0} matches conditional preference {1} ({2}:{3}) forced {4}'.format(sub['index'], i, audio_name, sub_name, forced) )
+ log(LOG_INFO, 'Language of subtitle {0} matches conditional preference {1} ({2}:{3}) forced {4}'.format(sub['index'], i, audio_name, sub_name, forced) )
return sub['index']
- log(LOG_INFO, u'Conditional subtitle: no match found for preference {0} ({1}:{2})'.format(i, audio_name, sub_name) )
+ log(LOG_INFO, 'Conditional subtitle: no match found for preference {0} ({1}:{2})'.format(i, audio_name, sub_name) )
return -2
-
+
def testForcedFlag(self, forced, subName):
test = subName.lower()
- matches = [u'forced', u'forcés']
+ matches = ['forced', 'forcés']
found = any(x in test for x in matches)
return ((forced == 'false') and not found) or ((forced == 'true') and found)
def isExternalSub(self, subName):
test = subName.lower()
- matches = [u'ext']
+ matches = ['ext']
return any(x in test for x in matches)
-
+
def getDetails(self):
activePlayers ='{"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}'
json_query = xbmc.executeJSONRPC(activePlayers)
- json_query = unicode(json_query, 'utf-8', errors='ignore')
+ #json_query = unicode(json_query, 'utf-8', errors='ignore')
json_response = simplejson.loads(json_query)
activePlayerID = json_response['result'][0]['playerid']
details_query_dict = { "jsonrpc": "2.0",
@@ -308,10 +302,10 @@ def getDetails(self):
"id": 1}
details_query_string = simplejson.dumps(details_query_dict)
json_query = xbmc.executeJSONRPC(details_query_string)
- json_query = unicode(json_query, 'utf-8', errors='ignore')
+ #json_query = unicode(json_query, 'utf-8', errors='ignore')
json_response = simplejson.loads(json_query)
- if json_response.has_key('result') and json_response['result'] != None:
+ if 'result' in json_response and json_response['result'] != None:
self.selected_audio_stream = json_response['result']['currentaudiostream']
self.selected_sub = json_response['result']['currentsubtitle']
self.selected_sub_enabled = json_response['result']['subtitleenabled']
@@ -326,13 +320,13 @@ def getDetails(self):
"id": 1}
genre_tags_query_string = simplejson.dumps(genre_tags_query_dict)
json_query = xbmc.executeJSONRPC(genre_tags_query_string)
- json_query = unicode(json_query, 'utf-8', errors='ignore')
+ #json_query = unicode(json_query, 'utf-8', errors='ignore')
json_response = simplejson.loads(json_query)
- if json_response.has_key('result') and json_response['result'] != None:
+ if 'result' in json_response and json_response['result'] != None:
gt = []
- if json_response['result']['item'].has_key('genre'):
+ if 'genre' in json_response['result']['item']:
gt = json_response['result']['item']['genre']
- if json_response['result']['item'].has_key('tag'):
+ if 'tag' in json_response['result']['item']:
gt.extend(json_response['result']['item']['tag'])
self.genres_and_tags = set(map(lambda x:x.lower(), gt))
log(LOG_DEBUG, 'Video tags/genres: {0}'.format(self.genres_and_tags))
diff --git a/resources/icon.png b/resources/icon.png
new file mode 100644
index 0000000..c312af5
Binary files /dev/null and b/resources/icon.png differ
diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po
new file mode 100644
index 0000000..b0089c3
--- /dev/null
+++ b/resources/language/resource.language.en_gb/strings.po
@@ -0,0 +1,290 @@
+# Kodi Media Center language file
+# Addon Name: Language Preference Manager
+# Addon id: service.languagepreferencemanager
+# Addon Provider: ace20022
+msgid ""
+msgstr ""
+"Project-Id-Version: Kodi Addons\n"
+"Report-Msgid-Bugs-To: https://forum.kodi.tv/\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
+"Language-Team: Team-Kodi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en_GB\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "#30100"
+msgid "Languages"
+msgstr ""
+
+msgctxt "#30101"
+msgid "Primary Language"
+msgstr ""
+
+msgctxt "#30102"
+msgid "Secondary Language"
+msgstr ""
+
+msgctxt "#30103"
+msgid "Third Language"
+msgstr ""
+
+msgctxt "#30104"
+msgid "Audio Preferences"
+msgstr ""
+
+msgctxt "#30105"
+msgid "Subtitle Preferences"
+msgstr ""
+
+msgctxt "#30106"
+msgid "Conditional Subtitle Preferences"
+msgstr ""
+
+msgctxt "#30107"
+msgid "Enable Preference"
+msgstr ""
+
+msgctxt "#30108"
+msgid "Enable Language Preference Manager"
+msgstr ""
+
+msgctxt "#30109"
+msgid "Pause while evaluating preferences"
+msgstr ""
+
+msgctxt "#30110"
+msgid "If audio language is "
+msgstr ""
+
+msgctxt "#30111"
+msgid "then display subtitles with language"
+msgstr ""
+
+msgctxt "#30112"
+msgid "Delay the evaluation by the following value [ms]"
+msgstr ""
+
+msgctxt "#30113"
+msgid "Turn on subtitles if a subtitle prefrence matched"
+msgstr ""
+
+msgctxt "#30114"
+msgid "Turn off subtitles if no subtitle prefrence matched"
+msgstr ""
+
+msgctxt "#30115"
+msgid "Advanced/Custom settings"
+msgstr ""
+
+msgctxt "#30116"
+msgid "Try to set the audio and/or subtitle track from the filename first"
+msgstr ""
+
+msgctxt "#30117"
+msgid "Regular expression for filename based preferences"
+msgstr ""
+
+msgctxt "#30118"
+msgid "Use custom pref, e.g. eng>ger>fin :"
+msgstr ""
+
+msgctxt "#30119"
+msgid "Use custom pref, e.g. eng>ger>fin :"
+msgstr ""
+
+msgctxt "#30120"
+msgid "Forced"
+msgstr ""
+
+msgctxt "#30121"
+msgid "Use custom pref, e.g. jpn:eng>eng:ger :"
+msgstr ""
+
+msgctxt "#30122"
+msgid "General"
+msgstr ""
+
+msgctxt "#30201"
+msgid "Albanian"
+msgstr ""
+
+msgctxt "#30202"
+msgid "Arabic"
+msgstr ""
+
+msgctxt "#30203"
+msgid "Belarusian"
+msgstr ""
+
+msgctxt "#30204"
+msgid "Bosnian (Latin)"
+msgstr ""
+
+msgctxt "#30205"
+msgid "Bulgarian"
+msgstr ""
+
+msgctxt "#30206"
+msgid "Catalan"
+msgstr ""
+
+msgctxt "#30207"
+msgid "Chinese"
+msgstr ""
+
+msgctxt "#30208"
+msgid "Croatian"
+msgstr ""
+
+msgctxt "#30209"
+msgid "Czech"
+msgstr ""
+
+msgctxt "#30210"
+msgid "Danish"
+msgstr ""
+
+msgctxt "#30211"
+msgid "Dutch"
+msgstr ""
+
+msgctxt "#30212"
+msgid "English"
+msgstr ""
+
+msgctxt "#30213"
+msgid "Estonian"
+msgstr ""
+
+msgctxt "#30214"
+msgid "Finnish"
+msgstr ""
+
+msgctxt "#30215"
+msgid "French"
+msgstr ""
+
+msgctxt "#30216"
+msgid "German"
+msgstr ""
+
+msgctxt "#30217"
+msgid "Greek"
+msgstr ""
+
+msgctxt "#30218"
+msgid "Hebrew"
+msgstr ""
+
+msgctxt "#30219"
+msgid "Hindi"
+msgstr ""
+
+msgctxt "#30220"
+msgid "Hungarian"
+msgstr ""
+
+msgctxt "#30221"
+msgid "Icelandic"
+msgstr ""
+
+msgctxt "#30222"
+msgid "Indonesian"
+msgstr ""
+
+msgctxt "#30224"
+msgid "Italian"
+msgstr ""
+
+msgctxt "#30225"
+msgid "Japanese"
+msgstr ""
+
+msgctxt "#30226"
+msgid "Korean"
+msgstr ""
+
+msgctxt "#30227"
+msgid "Latvian"
+msgstr ""
+
+msgctxt "#30228"
+msgid "Lithuanian"
+msgstr ""
+
+msgctxt "#30229"
+msgid "Macedonian"
+msgstr ""
+
+msgctxt "#30230"
+msgid "Norwegian"
+msgstr ""
+
+msgctxt "#30232"
+msgid "Polish"
+msgstr ""
+
+msgctxt "#30233"
+msgid "Portuguese"
+msgstr ""
+
+msgctxt "#30234"
+msgid "Portuguese (Brazil)"
+msgstr ""
+
+msgctxt "#30235"
+msgid "Romanian"
+msgstr ""
+
+msgctxt "#30236"
+msgid "Russian"
+msgstr ""
+
+msgctxt "#30237"
+msgid "SerbianLatin"
+msgstr ""
+
+msgctxt "#30238"
+msgid "Slovak"
+msgstr ""
+
+msgctxt "#30239"
+msgid "Slovenian"
+msgstr ""
+
+msgctxt "#30240"
+msgid "Spanish"
+msgstr ""
+
+msgctxt "#30242"
+msgid "Swedish"
+msgstr ""
+
+msgctxt "#30243"
+msgid "Thai"
+msgstr ""
+
+msgctxt "#30244"
+msgid "Turkish"
+msgstr ""
+
+msgctxt "#30245"
+msgid "Ukrainian"
+msgstr ""
+
+msgctxt "#30246"
+msgid "Vietnamese"
+msgstr ""
+
+msgctxt "#30247"
+msgid "Farsi"
+msgstr ""
+
+msgctxt "#30248"
+msgid "None"
+msgstr ""
+
diff --git a/resources/lib/prefparser.py b/resources/lib/prefparser.py
index 98c766c..f6ec180 100644
--- a/resources/lib/prefparser.py
+++ b/resources/lib/prefparser.py
@@ -31,7 +31,6 @@ def __init__( self ):
self.custom_g_t_pref_delim = r'#'
self.custom_g_t_delim = r','
self.custom_condSub_delim = r':'
- self.custom_condType_delim = r'/'
def parsePrefString(self, pref_string):
preferences = []
@@ -80,30 +79,13 @@ def parsePref(self, prefs):
self.log(LOG_INFO, 'Custom cond subs prefs parse error: {0}'.format(pref))
else:
temp_a = (languageTranslate(pref[0], 3, 0), pref[0])
- tmp_s = pref[1].split(self.custom_condType_delim)
- if len(tmp_s) != 2:
- self.log(LOG_INFO, 'Custom cond type prefs parse error: {0}'.format(tmp_s))
+ temp_s = (languageTranslate(pref[1], 3, 0), pref[1])
+ if (temp_a[0] and temp_a[1] and temp_s[0] and temp_s[1]):
+ lang_prefs.append((temp_a[0], temp_a[1], temp_s[0], temp_s[1]))
else:
- temp_s = (languageTranslate(tmp_s[0], 3, 0), tmp_s[0])
- temp_t = tmp_s[1]
- if (temp_a[0] and temp_a[1] and temp_s[0] and temp_s[1] and temp_t):
- lang_prefs.append((temp_a[0], temp_a[1], temp_s[0], temp_s[1], temp_t))
- else:
- self.log(LOG_INFO, 'Custom cond sub prefs: lang code not found in db!'\
- ' Please report this: {0}:{1}'.format(temp_a, temp_s))
- # custom sub pref
- elif (pref.find(self.custom_condType_delim) > 0):
- tmp_pref = pref.split(self.custom_condType_delim)
- if len(tmp_pref) != 2:
- self.log(LOG_INFO, 'Custom sub prefs parse error: {0}'.format(tmp_pref))
- temp_pref = (languageTranslate(tmp_pref[0], 3, 0), tmp_pref[0])
- temp_t = tmp_pref[1]
- if (temp_pref[0] and temp_t):
- lang_prefs.append(temp_pref, temp_t)
- else:
- self.log(LOG_INFO, 'Custom sub prefs: lang code {0} not found in db!'\
- ' Please report this'.format(pref))
- # custom audio pref
+ self.log(LOG_INFO, 'Custom cond sub prefs: lang code not found in db!'\
+ ' Please report this: {0}:{1}'.format(temp_a, temp_s))
+ # custom audio or subtitle pref
else:
temp_pref = (languageTranslate(pref, 3, 0), pref)
if temp_pref[0]:
diff --git a/resources/settings.xml b/resources/settings.xml
index 6a051eb..428aa8e 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -1,59 +1,829 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ false
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+ false
+
+
+
+ false
+
+
+
+ audiostream[_|.|-]*\d+|subtitle[_|.|-]*\d+
+
+ false
+
+
+ 30117
+
+
+
+ 300
+
+ 30112
+
+
+
+ false
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ 30118
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+ 30119
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 46
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+ 30121
+
+
+
+
+