Enigma2 plugin to to play various online streams (mostly Latvian).

subtitles.py 221KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938
  1. # -*- coding: UTF-8 -*-
  2. #################################################################################
  3. #
  4. # SubsSupport 1.2.0 for Enigma2
  5. # Coded by mx3L (c) 2014
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. #################################################################################
  18. from Queue import Queue
  19. from datetime import datetime
  20. import json
  21. from os import path as os_path, listdir
  22. import os
  23. from re import compile as re_compile
  24. import re
  25. import sys
  26. from threading import Thread
  27. import traceback
  28. from twisted.internet.defer import Deferred
  29. from twisted.web import client
  30. from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap
  31. from Components.ConfigList import ConfigListScreen
  32. from Components.GUIComponent import GUIComponent
  33. from Components.Harddisk import harddiskmanager
  34. from Components.Label import Label
  35. from Components.Language import language
  36. from Components.MenuList import MenuList
  37. from Components.MultiContent import MultiContentEntryText, \
  38. MultiContentEntryPixmapAlphaTest
  39. from Components.Renderer.Renderer import Renderer
  40. from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
  41. from Components.Sources.Boolean import Boolean
  42. from Components.Sources.List import List
  43. from Components.Sources.StaticText import StaticText
  44. from Components.config import ConfigSubsection, ConfigSelection, ConfigYesNo, \
  45. configfile, getConfigListEntry, config, ConfigText, ConfigDirectory, ConfigOnOff, \
  46. ConfigNothing, ConfigInteger, NoSave, KEY_DELETE, KEY_BACKSPACE, \
  47. KEY_TIMEOUT, KEY_ASCII
  48. from Screens.ChoiceBox import ChoiceBox
  49. from Screens.HelpMenu import HelpableScreen
  50. from Screens.InfoBarGenerics import InfoBarSeek, InfoBarNotifications
  51. from Screens.LocationBox import LocationBox
  52. from Screens.MessageBox import MessageBox
  53. from Screens.Screen import Screen
  54. from Tools import Notifications
  55. from Tools.Directories import SCOPE_SKIN_IMAGE, SCOPE_SKIN, resolveFilename, \
  56. fileExists
  57. from Tools.ISO639 import LanguageCodes
  58. from Tools.LoadPixmap import LoadPixmap
  59. from compat import eTimer, FileList
  60. from e2_utils import messageCB, E2SettingsProvider, MyLanguageSelection, unrar, \
  61. ConfigFinalText, Captcha, DelayMessageBox, MyConfigList, getFps, fps_float, \
  62. getFonts, BaseMenuScreen
  63. from enigma import RT_HALIGN_RIGHT, RT_VALIGN_TOP, eSize, ePoint, RT_HALIGN_LEFT, \
  64. RT_HALIGN_RIGHT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, eListboxPythonMultiContent, \
  65. gFont, getDesktop, eServiceCenter, iServiceInformation, eServiceReference, \
  66. iSeekableService, iPlayableService, iPlayableServicePtr, addFont, gFont, \
  67. gRGB, loadPNG, ePythonMessagePump, eConsoleAppContainer, eLabel
  68. from parsers import SubRipParser, MicroDVDParser
  69. from process import SubsLoader, DecodeError, ParseError, ParserNotFoundError, \
  70. LoadError
  71. from searchsubs import Messages
  72. from seek import SubsSeeker, SubtitlesDownloadError, SubtitlesErrors
  73. from seekers.utilities import detectSearchParams, languageTranslate
  74. from skin import parseColor, parsePosition, parseFont
  75. from utils import toString, SimpleLogger, toUnicode
  76. from . import _, __author__, __version__, __email__
  77. try:
  78. from xml.etree.cElementTree import parse as parse_xml
  79. except ImportError:
  80. from xml.etree.ElementTree import parse as parse_xml
  81. try:
  82. from Screens.AudioSelection import QuickSubtitlesConfigMenu
  83. except ImportError:
  84. QuickSubtitlesConfigMenu = None
  85. # localization function
  86. def warningMessage(session, text):
  87. session.open(MessageBox, text, type=MessageBox.TYPE_WARNING, timeout=5)
  88. def debug(text, *args):
  89. if DEBUG:
  90. if len(args) == 1 and isinstance(args[0], tuple):
  91. text = text % args[0]
  92. else:
  93. text = text % (args)
  94. print "[SubsSupport]", toString('utf-8')
  95. # set the name of plugin in which this library belongs
  96. # PLUGIN_NAME = 'mediaplayer2'
  97. # set debug mode
  98. DEBUG = False
  99. # set supported encodings, you have to make sure, that you have corresponding python
  100. # libraries in %PYTHON_PATH%/encodings/ (ie. iso-8859-2 requires iso_8859_2.searchsubs library)
  101. # to choose encodings for region you want, visit:
  102. # http://docs.python.org/release/2.4.4/lib/standard-encodings.html
  103. # Common encodings for all languages
  104. ALL_LANGUAGES_ENCODINGS = ['utf-8', 'utf-16']
  105. # other encodings, sorted according usage
  106. CENTRAL_EASTERN_EUROPE_ENCODINGS = ['windows-1250', 'iso-8859-2', 'maclatin2', 'IBM852']
  107. WESTERN_EUROPE_ENCODINGS = ['windows-1252', 'iso-8859-15', 'macroman', 'ibm1140', 'IBM850']
  108. RUSSIAN_ENCODINGS = ['windows-1251', 'cyrillic', 'maccyrillic', 'koi8_r', 'IBM866']
  109. ARABIC_ENCODINGS = ['windows-1256', 'iso-8859-6', 'IBM864']
  110. TURKISH_ENCODINGS = ['windows-1254', 'iso-8859-9', 'latin5', 'macturkish', 'ibm1026', 'IBM857']
  111. GREEK_ENCODINGS = ['windows-1253', 'iso-8859-7', 'macgreek']
  112. ENCODINGS = {("Central and Eastern Europe") : CENTRAL_EASTERN_EUROPE_ENCODINGS,
  113. ("Western Europe"):WESTERN_EUROPE_ENCODINGS,
  114. ("Russia"):RUSSIAN_ENCODINGS,
  115. ("Arabic"): ARABIC_ENCODINGS,
  116. ("Turkish"):TURKISH_ENCODINGS,
  117. ("Greek"):GREEK_ENCODINGS}
  118. # initializing parsers
  119. PARSERS = [SubRipParser, MicroDVDParser]
  120. def getDefaultFont(fontType):
  121. ubuntu = None
  122. openpli = None
  123. for f in getFonts():
  124. if fontType == "regular":
  125. if f == "Subs":
  126. openpli = f
  127. elif f== "Ubuntu-M":
  128. ubuntu = f
  129. elif fontType == "italic":
  130. if f == "Subsi":
  131. openpli = f
  132. elif f=="Ubuntu-MI":
  133. ubuntu = f
  134. elif fontType == "bold":
  135. if f == "Subsb":
  136. openpli = f
  137. elif f =="Ubuntu-B":
  138. ubuntu = f
  139. if ubuntu:
  140. return ubuntu
  141. if openpli:
  142. return openpli
  143. return "Regular"
  144. def getEmbeddedFontSizeCfg(defaultFontSizeCfg):
  145. CONFIG_SUBTITLES_OPENPLI = "subtitles"
  146. CONFIG_FONTSIZE_OPENPLI = "subtitle_fontsize"
  147. CONFIG_SUBTITLES_VTI = "subtitle"
  148. CONFIG_FONTSIZE_VTI = "subtitlefontsize"
  149. try:
  150. subtitles_pli_cfg = getattr(config, CONFIG_SUBTITLES_OPENPLI)
  151. except KeyError:
  152. subtitles_pli_cfg = None
  153. if subtitles_pli_cfg is not None:
  154. try:
  155. return getattr(subtitles_pli_cfg, CONFIG_FONTSIZE_OPENPLI)
  156. except KeyError:
  157. pass
  158. try:
  159. subtitles_vti_cfg = getattr(config, CONFIG_SUBTITLES_VTI)
  160. except KeyError:
  161. subtitles_vti_cfg = None
  162. if subtitles_vti_cfg is not None:
  163. try:
  164. return getattr(subtitles_vti_cfg, CONFIG_FONTSIZE_VTI)
  165. except KeyError:
  166. pass
  167. return defaultFontSizeCfg
  168. GLOBAL_CONFIG_INIT = False
  169. fontChoiceList = [f for f in getFonts()]
  170. fontSizeChoiceList = [("%d" % i, "%d px" % i) for i in range(10, 60, 1)]
  171. positionChoiceList = [("0", _("top"))]
  172. positionChoiceList.extend([("%d" % i, "%d %%" % i) for i in range(1, 100, 1)])
  173. positionChoiceList.append(("100", _("bottom")))
  174. shadowSizeChoiceList = [("%d" % i, "%d px" % i) for i in range(1, 8, 1)]
  175. shadowOffsetChoiceList = [("%d" % i, "%d px" % i) for i in range(-8, -1, 1)]
  176. backgroundOffsetChoiceList = [("%d" % i, "%d px" % i) for i in range(5, 100, 1)]
  177. colorChoiceList = []
  178. colorChoiceList.append(("ff0000", _("red")))
  179. colorChoiceList.append(("DCDCDC", _("grey")))
  180. colorChoiceList.append(("00ff00", _("green")))
  181. colorChoiceList.append(("ff00ff", _("purple")))
  182. colorChoiceList.append(("ffff00", _("yellow")))
  183. colorChoiceList.append(("ffffff", _("white")))
  184. colorChoiceList.append(("00ffff", _("blue")))
  185. colorChoiceList.append(("000000", _("black")))
  186. COLORFILE = os.path.join(os.path.dirname(__file__), 'colors.txt')
  187. print '[SubsSupport] looking for custom colors in', COLORFILE
  188. try:
  189. with open(COLORFILE, 'r') as f:
  190. for line in f:
  191. color = re.search('^(\w+)\s+([0-9A-Fa-f]{6})$', line)
  192. if color is not None:
  193. alias = color.group(1)
  194. hex_color = color.group(2)
  195. print '[SubsSupport] adding custom color', alias
  196. colorChoiceList.append((hex_color, alias))
  197. except IOError as e:
  198. print '[SubsSupport] error while loading custom colors', str(e)
  199. alphaChoiceList = [("00", _("opaque"))]
  200. alphaChoiceList.extend([("%02x" % val, "%d %%"%(int(percent * 100 / float(32)))) for percent, val in enumerate(xrange(0, 256, 8)) if val != 0])
  201. alphaChoiceList.append(("ff", _("transparent")))
  202. def initGeneralSettings(configsubsection):
  203. configsubsection.pauseVideoOnSubtitlesMenu = ConfigYesNo(default=True)
  204. configsubsection.encodingsGroup = ConfigSelection(default="Central and Eastern Europe", choices=[(e, _(e)) for e in ENCODINGS.keys()])
  205. def initExternalSettings(configsubsection):
  206. configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList)
  207. configsubsection.font = ConfigSubsection()
  208. configsubsection.font.regular = ConfigSubsection()
  209. configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList)
  210. configsubsection.font.regular.alpha = ConfigSelection(default="00", choices=alphaChoiceList)
  211. configsubsection.font.regular.color = ConfigSelection(default="ffffff", choices=colorChoiceList)
  212. configsubsection.font.italic = ConfigSubsection()
  213. configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList)
  214. configsubsection.font.italic.alpha = ConfigSelection(default="00", choices=alphaChoiceList)
  215. configsubsection.font.italic.color = ConfigSelection(default="ffffff", choices=colorChoiceList)
  216. configsubsection.font.bold = ConfigSubsection()
  217. configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList)
  218. configsubsection.font.bold.alpha = ConfigSelection(default="00", choices=alphaChoiceList)
  219. configsubsection.font.bold.color = ConfigSelection(default="ffffff", choices=colorChoiceList)
  220. configsubsection.font.size = ConfigSelection(default="43", choices=fontSizeChoiceList)
  221. configsubsection.shadow = ConfigSubsection()
  222. configsubsection.shadow.enabled = ConfigOnOff(default=True)
  223. configsubsection.shadow.type = ConfigSelection(default="border", choices=[("offset", _("offset")), ("border", _('border'))])
  224. configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList)
  225. configsubsection.shadow.size = ConfigSelection(default="2", choices=shadowSizeChoiceList)
  226. configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  227. configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  228. configsubsection.background = ConfigSubsection()
  229. configsubsection.background.enabled = ConfigOnOff(default=True)
  230. configsubsection.background.type = ConfigSelection(default="dynamic", choices=[("dynamic", _("dynamic")), ("static", _("static"))])
  231. configsubsection.background.xOffset = ConfigSelection(default="10", choices=backgroundOffsetChoiceList)
  232. configsubsection.background.yOffset = ConfigSelection(default="10", choices=backgroundOffsetChoiceList)
  233. configsubsection.background.color = ConfigSelection(default="000000", choices=colorChoiceList)
  234. configsubsection.background.alpha = ConfigSelection(default="80", choices=alphaChoiceList)
  235. def initEmbeddedSettings(configsubsection):
  236. configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList)
  237. configsubsection.font = ConfigSubsection()
  238. configsubsection.font.regular = ConfigSubsection()
  239. configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList)
  240. configsubsection.font.italic = ConfigSubsection()
  241. configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList)
  242. configsubsection.font.bold = ConfigSubsection()
  243. configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList)
  244. configsubsection.font.size = ConfigSelection(default="34", choices=fontSizeChoiceList)
  245. configsubsection.color = ConfigSelection(default="ffffff", choices=colorChoiceList)
  246. configsubsection.shadow = ConfigSubsection()
  247. configsubsection.shadow.size = ConfigSelection(default="3", choices=shadowSizeChoiceList)
  248. configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList)
  249. configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  250. configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  251. def initEngineSettings(configsubsection):
  252. configsubsection.expert = ConfigSubsection()
  253. configsubsection.expert.show = NoSave(ConfigYesNo(default=False))
  254. configsubsection.expert.playerDelay = ConfigSelection(default="0", choices=[("%d" % i, "%d ms" % i) for i in range(0, 20000, 200)])
  255. configsubsection.expert.startDelay = ConfigSelection(default="1200", choices=[("%d" % i, "%d ms" % i) for i in range(0, 100, 1500)])
  256. configsubsection.expert.hideDelay = ConfigSelection(default="200", choices=[("%d" % i, "%d ms" % i) for i in range(0, 1000, 50)])
  257. configsubsection.expert.ptsDelayCheck = ConfigSelection(default="200", choices=[("%d" % i, "%d ms" % i) for i in range(100, 1000, 100)])
  258. configsubsection.expert.syncDelay = ConfigSelection(default="300", choices=[("%d" % i, "%d ms" % i) for i in range(100, 1000, 100)])
  259. configsubsection.expert.refreshDelay = ConfigSelection(default="1000", choices=[("%d" % i, "%d ms" % i) for i in range(200, 3000, 200)])
  260. def initSearchSettings(configsubsection):
  261. configsubsection.downloadHistory = ConfigSubsection()
  262. configsubsection.downloadHistory.enabled = ConfigYesNo(default=True)
  263. configsubsection.downloadHistory.limit = ConfigInteger(default=50, limits=(2, 200))
  264. configsubsection.downloadHistory.path = ConfigDirectory(default = os.path.dirname(__file__), visible_width=30)
  265. configsubsection.downloadHistory.removeAction = ConfigSelection(default='list', choices=[('list', _("List")), ('file', _("List + File"))])
  266. configsubsection.downloadHistory.removeActionAsk = ConfigYesNo(default = True)
  267. configsubsection.downloadPath = ConfigDirectory(default="/tmp/")
  268. configsubsection.tmpPath = ConfigDirectory(default="/tmp/")
  269. configsubsection.lang1 = ConfigFinalText(default=language.getLanguage()[:2])
  270. configsubsection.lang2 = ConfigFinalText(default=language.getLanguage()[:2])
  271. configsubsection.lang3 = ConfigFinalText(default=language.getLanguage()[:2])
  272. configsubsection.timeout = ConfigSelection(default="10", choices=[(("%d" % i, "%d s" % i)) for i in range(5, 20)])
  273. configsubsection.history = ConfigText(default="")
  274. configsubsection.movieProvider = ConfigSelection(default="all", choices=[("all", _("All")), ])
  275. configsubsection.tvshowProvider = ConfigSelection(default="all", choices=[("all", _("All")), ])
  276. configsubsection.manualSearch = ConfigYesNo(default=False)
  277. configsubsection.defaultSort = ConfigSelection(default='lang', choices=[('lang', _("Language")), ('provider', _("Provider"))])
  278. configsubsection.saveAs = ConfigSelection(default='version', choices=[('default', _("Default")), ('version', _("Release")), ('video', _("Video filename"))])
  279. configsubsection.saveAsFallback = ConfigSelection(default='version', choices=[('default', _("Default")), ('version', _("Release"))])
  280. configsubsection.saveTo = ConfigSelection(default='custom', choices=[('custom', _('User defined')), ('video', _('Next to video'))])
  281. configsubsection.addLangToSubsFilename = ConfigYesNo(default=False)
  282. configsubsection.askOverwriteExistingSubs = ConfigYesNo(default=True)
  283. configsubsection.loadSubtitlesAfterDownload = ConfigYesNo(default=True)
  284. configsubsection.openParamsDialogOnSearch = ConfigYesNo(default=True)
  285. configsubsection.showProvidersErrorMessage = ConfigYesNo(default=True)
  286. # session settings
  287. configsubsection.title = ConfigTextWithSuggestionsAndHistory(configsubsection.history, default="", fixed_size=False)
  288. configsubsection.type = ConfigSelection(default="movie", choices=[("tv_show", _("TV show")), ("movie", _("Movie"))])
  289. configsubsection.year = ConfigInteger(default=0, limits=(0, 2100))
  290. configsubsection.season = ConfigInteger(default=0, limits=(0, 100))
  291. configsubsection.episode = ConfigInteger(default=0, limits=(0, 100))
  292. configsubsection.provider= ConfigSelection(default="all", choices=[("all", _("All")), ])
  293. configsubsection.useFilePath = ConfigYesNo(default=True)
  294. def initSubsSettings(configSubsection=None):
  295. global GLOBAL_CONFIG_INIT
  296. if configSubsection:
  297. print '[SubsSupport] using provided ConfigSubsection to store config'
  298. subtitles_settings = configSubsection
  299. elif 'PLUGIN_NAME' in globals():
  300. print '[SubsSupport] using config.plugins.%s.%s to store config' % (PLUGIN_NAME, 'subtitles')
  301. plugin_settings = getattr(config.plugins, PLUGIN_NAME)
  302. setattr(plugin_settings, 'subtitles', ConfigSubsection())
  303. subtitles_settings = getattr(plugin_settings, 'subtitles')
  304. elif GLOBAL_CONFIG_INIT:
  305. print "[SubsSupport] using global config (already initialized)"
  306. return config.plugins.subtitlesSupport
  307. else:
  308. print "[SubsSupport] using global config"
  309. config.plugins.subtitlesSupport = ConfigSubsection()
  310. subtitles_settings = config.plugins.subtitlesSupport
  311. GLOBAL_CONFIG_INIT = True
  312. initGeneralSettings(subtitles_settings)
  313. subtitles_settings.external = ConfigSubsection()
  314. initExternalSettings(subtitles_settings.external)
  315. subtitles_settings.embedded = ConfigSubsection()
  316. initEmbeddedSettings(subtitles_settings.embedded)
  317. subtitles_settings.engine = ConfigSubsection()
  318. initEngineSettings(subtitles_settings.engine)
  319. subtitles_settings.search = ConfigSubsection()
  320. initSearchSettings(subtitles_settings.search)
  321. return subtitles_settings
  322. class SubsStatusScreen(Screen, HelpableScreen):
  323. def __init__(self, session, setSubsDelay, getSubsDelay, subscribeDelay, unsubscribeDelay, toNextSub, toPrevSub, setSubsFps, getSubsFps, subsDelayStepInMs=200, showDelayInMs=False):
  324. desktopWidth = getDesktop(0).size().width()
  325. offset = 50
  326. self.skin = """
  327. <screen position="%d,20" size="%d,90" zPosition="5" backgroundColor="transparent" flags="wfNoBorder">
  328. <widget source="delay" render="Label" position="0,0" size="%d,70" valign="center" halign="left" font="Regular;22" transparent="1" foregroundColor="#ffffff" shadowColor="#40101010" shadowOffset="2,2" />
  329. <widget source="fps" render="Label" position="%d,0" size="%d,70" valign="center" halign="right" font="Regular;22" transparent="1" foregroundColor="#6F9EF5" shadowColor="#40101010" shadowOffset="2,2" />
  330. </screen>""" % (offset, desktopWidth-(2*offset), (desktopWidth - (2*offset))/2 - 5, (desktopWidth-(2*offset))/2 + 5, (desktopWidth-(2*offset))/2 - 5)
  331. Screen.__init__(self, session)
  332. HelpableScreen.__init__(self)
  333. self.setSubsDelay = setSubsDelay
  334. self.getSubsDelay = getSubsDelay
  335. self.subscribeDelay = subscribeDelay
  336. self.unsubscribeDelay = unsubscribeDelay
  337. self.toNextSub = toNextSub
  338. self.toPrevSub = toPrevSub
  339. self.setSubsFps = setSubsFps
  340. self.getSubsFps = getSubsFps
  341. self.subsDelayStep = subsDelayStepInMs
  342. self.showDelayInMs = showDelayInMs
  343. self.fpsChoices = ["23.976", "23.980", "24.000", "25.000", "29.970", "30.000"]
  344. self['fps'] = StaticText()
  345. self['delay'] = StaticText()
  346. self['SubsArrowActions'] = HelpableActionMap(self, "DirectionActions",
  347. {
  348. 'right': (self.nextSubDelay, _("jump to next subtitle")),
  349. 'left':(self.prevSubDelay, _("jump to previous subtitle")),
  350. 'up':(self.incSubDelay, _("increase subtitles delay")),
  351. 'down':(self.decSubDelay, _("decrease subtitles delay")),
  352. })
  353. self['SubsColorActions'] = HelpableActionMap(self, "ColorActions",
  354. {
  355. 'red': (self.reset, _("reset subtitles delay/fps")),
  356. 'blue': (self.changeFps, _("change subtitles fps")),
  357. })
  358. self['OkCancelActions'] = HelpableActionMap(self, "OkCancelActions",
  359. {
  360. 'ok': (self.showHelp, _("displays this menu")),
  361. 'cancel':(self.close, _("exit"))
  362. })
  363. self._subsDelay = None
  364. self.onLayoutFinish.append(self._subscribeDelay)
  365. self.onLayoutFinish.append(self.updateSubsFps)
  366. self.onLayoutFinish.append(self.updateSubsDelay)
  367. self.onClose.append(self._unsubscribeDelay)
  368. def _subscribeDelay(self):
  369. self.subscribeDelay(self._setSubsDelayAndUpdate)
  370. def _unsubscribeDelay(self):
  371. self.unsubscribeDelay(self._setSubsDelayAndUpdate)
  372. def _setSubsDelayAndUpdate(self, delay):
  373. self._subsDelay = delay
  374. self.updateSubsDelay()
  375. def _getSubsDelay(self):
  376. if self._subsDelay is None:
  377. self._subsDelay = self.getSubsDelay()
  378. return self._subsDelay
  379. def updateSubsFps(self):
  380. subsFps = self.getSubsFps()
  381. videoFps = getFps(self.session, True)
  382. if subsFps is None or videoFps is None:
  383. self['fps'].text = "%s: %s"%(_("Subtitles FPS"), _("unknown"))
  384. return
  385. if subsFps == videoFps:
  386. self['fps'].text = "%s: %s"%(_("Subtitles FPS"), _("original"))
  387. else:
  388. self['fps'].text = "%s: %s"%(_("Subtitles FPS"), str(subsFps))
  389. def updateSubsDelay(self):
  390. subsDelay = self._getSubsDelay()
  391. if self.showDelayInMs:
  392. if subsDelay > 0:
  393. self["delay"].text = "%s: +%dms"%(_("Subtitles Delay"), subsDelay)
  394. else:
  395. self["delay"].text = "%s: %dms"%(_("Subtitles Delay"), subsDelay)
  396. else:
  397. if subsDelay > 0:
  398. self["delay"].text = "%s: +%.2fs"%(_("Subtitles Delay"), subsDelay/float(1000))
  399. else:
  400. self["delay"].text = "%s: %.2fs"%(_("Subtitles Delay"), subsDelay/float(1000))
  401. def nextSubDelay(self):
  402. self.toNextSub()
  403. def prevSubDelay(self):
  404. self.toPrevSub()
  405. def incSubDelay(self):
  406. self.setSubsDelay(self._getSubsDelay() + self.subsDelayStep)
  407. def decSubDelay(self):
  408. self.setSubsDelay(self._getSubsDelay() - self.subsDelayStep)
  409. def changeFps(self):
  410. subsFps = self.getSubsFps()
  411. if subsFps is None:
  412. return
  413. currIdx = self.fpsChoices.index(str(subsFps))
  414. if currIdx == len(self.fpsChoices) -1:
  415. nextIdx = 0
  416. else:
  417. nextIdx = currIdx+1
  418. self.setSubsFps(fps_float(self.fpsChoices[nextIdx]))
  419. self.updateSubsFps()
  420. def reset(self):
  421. self.setSubsFps(getFps(self.session, True))
  422. self.setSubsDelay(0)
  423. self.updateSubsFps()
  424. class SubsSupportStatus(object):
  425. def __init__(self, delayStepInMs=200, showDelayInMs=False, statusScreen=None):
  426. assert isinstance(self, SubsSupport), "not derived from SubsSupport!"
  427. self.__delayStepInMs = delayStepInMs
  428. self.__showDelayInMs = showDelayInMs
  429. self.__statusScreen = statusScreen
  430. self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
  431. {
  432. iPlayableService.evStart: self.__serviceChanged,
  433. iPlayableService.evEnd: self.__serviceChanged,
  434. })
  435. self["SubsStatusActions"] = HelpableActionMap(self, "SubtitlesActions",
  436. {
  437. "subtitlesStatus": (self.subsStatus, _("change external subtitles status")),
  438. } , -5)
  439. self.onClose.append(self.__closeSubsStatusScreen)
  440. def __serviceChanged(self):
  441. self.__closeSubsStatusScreen()
  442. def __closeSubsStatusScreen(self):
  443. try:
  444. self.__subsStatusScreen.close()
  445. except Exception:
  446. pass
  447. def subsStatus(self):
  448. setDelay = self.setSubsDelay
  449. getDelay = self.getSubsDelay
  450. subscribe = self.subscribeOnSubsDelayChanged
  451. unsubscribe = self.unsubscribeOnSubsDelayChanged
  452. toNextSub = self.setSubsDelayToNextSubtitle
  453. toPrevSub = self.setSubsDelayToPrevSubtitle
  454. getFps = self.getSubsFps
  455. setFps = self.setSubsFps
  456. if self.isSubsLoaded():
  457. self.__subsStatusScreen = self.session.open(SubsStatusScreen,
  458. setDelay, getDelay, subscribe, unsubscribe, toNextSub, toPrevSub, setFps, getFps,
  459. self.__delayStepInMs, self.__showDelayInMs)
  460. else:
  461. if self.__statusScreen is not None:
  462. self.__statusScreen.setStatus(_("No external subtitles are loaded"))
  463. elif isinstance(self, InfoBarNotifications):
  464. Notifications.AddNotification(MessageBox, _("No external subtitles are loaded"), type=MessageBox.TYPE_INFO, timeout=2)
  465. class SubsSupportEmbedded(object):
  466. def __init__(self, embeddedSupport, preferEmbedded):
  467. self.embeddedSupport = embeddedSupport
  468. self.preferEmbedded = preferEmbedded
  469. self.__subStyles = {}
  470. self.selected_subtitle = None
  471. self.subtitle_window = self.session.instantiateDialog(SubsEmbeddedScreen, self.subsSettings.embedded)
  472. self.subtitle_window.hide()
  473. if isinstance(self, InfoBarBase):
  474. self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
  475. {
  476. iPlayableService.evStart: self.__serviceChanged,
  477. iPlayableService.evEnd: self.__serviceChanged,
  478. # iPlayableService.evUpdatedInfo: self.__updatedInfo
  479. })
  480. self.onClose.append(self.exitEmbeddedSubs)
  481. def __isEmbeddedEnabled(self):
  482. return self.subtitle_window.shown
  483. embeddedEnabled = property(__isEmbeddedEnabled)
  484. def getCurrentServiceSubtitle(self):
  485. service = self.session.nav.getCurrentService()
  486. return service and service.subtitle()
  487. def __serviceChanged(self):
  488. if self.selected_subtitle:
  489. self.selected_subtitle = None
  490. self.subtitle_window.hide()
  491. def __updatedInfo(self):
  492. if not self.selected_subtitle:
  493. subtitle = self.getCurrentServiceSubtitle()
  494. cachedsubtitle = subtitle.getCachedSubtitle()
  495. if cachedsubtitle:
  496. self.enableSubtitle(cachedsubtitle)
  497. def enableSubtitle(self, selectedSubtitle):
  498. print '[SubsSupportEmbedded] enableSubtitle', selectedSubtitle
  499. subtitle = self.getCurrentServiceSubtitle()
  500. self.selected_subtitle = selectedSubtitle
  501. if subtitle and self.selected_subtitle:
  502. self.resetEmbeddedSubs()
  503. subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
  504. self.subtitle_window.show()
  505. print '[SubsSupportEmbedded] enable embedded subtitles'
  506. else:
  507. print '[SubsSupportEmbedded] disable embedded subtitles'
  508. if subtitle:
  509. subtitle.disableSubtitles(self.subtitle_window.instance)
  510. self.subtitle_window.hide()
  511. def restartSubtitle(self):
  512. if self.selected_subtitle:
  513. print '[SubsSupportEmbedded] restart embedded subtitles'
  514. self.enableSubtitle(self.selected_subtitle)
  515. def resetEmbeddedSubs(self, reloadScreen=False):
  516. if QuickSubtitlesConfigMenu:
  517. return
  518. print '[SubsSupportEmbedded] updating embedded screen'
  519. from enigma import eWidget, eSubtitleWidget
  520. scale = ((1, 1), (1, 1))
  521. embeddedSettings = self.subsSettings.embedded
  522. fontSize = embeddedSettings.font.size.value
  523. fontTypeR = embeddedSettings.font.regular.type.value
  524. fontTypeI = embeddedSettings.font.italic.type.value
  525. fontTypeB = embeddedSettings.font.bold.type.value
  526. foregroundColor = "#" + embeddedSettings.color.value
  527. foregroundColor = parseColor(foregroundColor)
  528. borderColor = "#" + embeddedSettings.shadow.color.value
  529. borderColor = parseColor(borderColor)
  530. borderWidth = int(embeddedSettings.shadow.size.value)
  531. offset = "%s,%s" % (embeddedSettings.shadow.xOffset.value,
  532. embeddedSettings.shadow.yOffset.value)
  533. shadowOffset = parsePosition(offset, scale)
  534. fontRegular = parseFont("%s;%s" % (fontTypeR, fontSize), scale)
  535. fontItalic = parseFont("%s;%s" % (fontTypeI, fontSize), scale)
  536. fontBold = parseFont("%s;%s" % (fontTypeB, fontSize), scale)
  537. self._loadEmbeddedStyle({"Subtitle_Regular":(fontRegular, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset),
  538. "Subtitle_Italic":(fontItalic, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset),
  539. "Subtitle_Bold":(fontBold, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset)})
  540. if reloadScreen:
  541. print '[SubsSupportEmbedded] reloading embedded screen'
  542. subtitle = self.getCurrentServiceSubtitle()
  543. if subtitle:
  544. subtitle.disableSubtitles(self.subtitle_window.instance)
  545. self.session.deleteDialog(self.subtitle_window)
  546. self.subtitle_window = None
  547. self.subtitle_window = self.session.instantiateDialog(SubsEmbeddedScreen, self.subsSettings.embedded)
  548. self.subtitle_window.hide()
  549. self.restartSubtitle()
  550. def _parseEmbeddedStyles(self, filename):
  551. if filename in self.__subStyles:
  552. return self.defaultStyles[filename]
  553. skin = parse_xml(filename).getroot()
  554. for c in skin.findall("subtitles"):
  555. scale = ((1, 1), (1, 1))
  556. substyles = {}
  557. for substyle in c.findall("sub"):
  558. get_attr = substyle.attrib.get
  559. font = parseFont(get_attr("font"), scale)
  560. col = get_attr("foregroundColor")
  561. if col:
  562. foregroundColor = parseColor(col)
  563. haveColor = 1
  564. else:
  565. foregroundColor = gRGB(0xFFFFFF)
  566. haveColor = 0
  567. col = get_attr("borderColor")
  568. if col:
  569. borderColor = parseColor(col)
  570. else:
  571. borderColor = gRGB(0)
  572. borderwidth = get_attr("borderWidth")
  573. if borderwidth is None:
  574. # default: use a subtitle border
  575. borderWidth = 3
  576. else:
  577. borderWidth = int(borderwidth)
  578. col = get_attr("shadowColor")
  579. if col:
  580. shadowColor = parseColor(col)
  581. else:
  582. shadowColor = gRGB(0)
  583. col = get_attr("shadowOffset")
  584. if col:
  585. shadowOffset = get_attr("shadowOffset")
  586. else:
  587. shadowOffset = "-3,-3"
  588. shadowOffset = parsePosition(shadowOffset, scale)
  589. substyles[get_attr("name")] = (font, haveColor, foregroundColor, borderColor, borderWidth, shadowColor, shadowOffset)
  590. self.__subStyles[filename] = substyle
  591. return substyles
  592. def _loadEmbeddedStyle(self, substyles):
  593. from enigma import eWidget, eSubtitleWidget
  594. for faceName in substyles.keys():
  595. s = substyles[faceName]
  596. face = eSubtitleWidget.__dict__[faceName]
  597. font, haveColor, foregroundColor, borderColor, borderWidth, shadowColor, shadowOffset = s[0], s[1], s[2], s[3], s[4], s[5], s[6]
  598. try:
  599. eSubtitleWidget.setFontStyle(face, font , haveColor, foregroundColor, borderColor, borderWidth)
  600. except TypeError:
  601. eSubtitleWidget.setFontStyle(face, font, haveColor, foregroundColor, shadowColor, shadowOffset)
  602. def resetEmbeddedDefaults(self):
  603. userSkin = resolveFilename(SCOPE_SKIN, 'skin_user.xml')
  604. defaultSkin = resolveFilename(SCOPE_SKIN, 'skin_default.xml')
  605. skinSubtitles = resolveFilename(SCOPE_SKIN, 'skin_subtitles.xml')
  606. skinPaths = [userSkin, skinSubtitles, defaultSkin]
  607. for skinPath in skinPaths:
  608. if fileExists(skinPath):
  609. styles = self._parseEmbeddedStyles(skinPath)
  610. if styles:
  611. print "[SubsEmbeddedSupport] reseting defaults from", skinPath
  612. self._loadEmbeddedStyle(styles)
  613. break
  614. def exitEmbeddedSubs(self):
  615. if self.subtitle_window is not None:
  616. self.session.deleteDialog(self.subtitle_window)
  617. self.subtitle_window = None
  618. if not QuickSubtitlesConfigMenu:
  619. self.resetEmbeddedDefaults()
  620. class SubsSupport(SubsSupportEmbedded):
  621. """Client class for subtitles
  622. If this class is not subclass of InfoBarBase you should use public function of this class to
  623. to connect your media player (resume,pause,exit,after seeking, subtitles setup)
  624. functions with subtitles
  625. @param session: set active session
  626. @param subsPath: set path for subtitles to load
  627. @param defaultPath: set default path when choosing external subtitles
  628. @param forceDefaultPath: always use default path when choosing external subtitles
  629. @param autoLoad: tries to auto load subtitles according to name of played file
  630. @param embeddedSupport: added support for embedded subtitles
  631. """
  632. def __init__(self, session=None, subsPath=None, defaultPath=None, forceDefaultPath=False, autoLoad=True,
  633. showGUIInfoMessages=True, embeddedSupport=False, preferEmbedded=False, searchSupport=False, configEntry=None):
  634. if session is not None:
  635. self.session = session
  636. self.searchSupport = searchSupport
  637. self.subsSettings = initSubsSettings(configEntry)
  638. SubsSupportEmbedded.__init__(self, embeddedSupport, preferEmbedded)
  639. self.__subsScreen = self.session.instantiateDialog(SubsScreen, self.subsSettings.external)
  640. self.__subsScreen.hide()
  641. self.__subsEngine = SubsEngine(self.session, self.subsSettings.engine, self.__subsScreen)
  642. self.__subsLoader = SubsLoader(PARSERS, ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()])
  643. self.__subsLoader.set_row_parsing(False)
  644. self.__loaded = False
  645. self.__working = False
  646. self.__firstStart = True
  647. self.__autoLoad = autoLoad
  648. self.__subsPath = None
  649. self.__subsDir = None
  650. self.__subsEnc = None
  651. self.__playerDelay = 0
  652. self.__startDelay = int(self.subsSettings.engine.expert.startDelay.value)
  653. self.__defaultPath = None
  654. self.__isServiceSet = False
  655. self.__subclassOfInfobarBase = isinstance(self, InfoBarBase)
  656. self.__forceDefaultPath = forceDefaultPath
  657. self.__showGUIInfoMessages = showGUIInfoMessages
  658. self.__checkTimer = eTimer()
  659. self.__starTimer = eTimer()
  660. self.__starTimer.callback.append(self.__updateSubs)
  661. try:
  662. from Screens.InfoBar import InfoBar
  663. InfoBar.instance.subtitle_window.hide()
  664. except Exception:
  665. pass
  666. if self.__subclassOfInfobarBase:
  667. self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
  668. {
  669. iPlayableService.evStart: self.__serviceStarted,
  670. iPlayableService.evEnd: self.__serviceStopped,
  671. iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
  672. })
  673. self["SubsActions"] = HelpableActionMap(self, "SubtitlesActions",
  674. {
  675. "subtitles": (self.subsMenu, _("show subtitles menu")),
  676. } , -5)
  677. self.onClose.append(self.exitSubs)
  678. if defaultPath is not None and os.path.isdir(toString(defaultPath)):
  679. self.__defaultPath = toString(defaultPath)
  680. self.__subsDir = toString(defaultPath)
  681. if subsPath is not None and self.__autoLoad:
  682. self.loadSubs(subsPath)
  683. def loadSubs(self, subsPath, newService=True):
  684. """loads subtitles from subsPath
  685. @param subsPath: path to subtitles (http url supported)
  686. @param newService: set False if service remains the same
  687. @return: True if subtitles was successfully loaded
  688. @return: False if subtitles wasnt successfully loaded
  689. """
  690. self.__working = True
  691. self.__subsPath = None
  692. if self.__defaultPath is not None:
  693. self.__subsDir = self.__defaultPath
  694. else:
  695. self.__subsDir = None
  696. if subsPath is not None:
  697. subsPath = toString(subsPath)
  698. if not subsPath.startswith('http'):
  699. if self.__defaultPath is not None and self.__forceDefaultPath:
  700. self.__subsDir = self.__defaultPath
  701. else:
  702. if os.path.isdir(os.path.dirname(subsPath)):
  703. self.__subsDir = os.path.dirname(subsPath)
  704. else:
  705. self.__subsDir = self.__defaultPath
  706. if not os.path.isfile(subsPath):
  707. print '[Subtitles] trying to load not existing path:', subsPath
  708. subsPath = None
  709. if subsPath is not None:
  710. subsList, self.__subsEnc = self.__processSubs(subsPath, self.__subsEnc)
  711. if subsList is not None:
  712. self.__subsPath = subsPath
  713. if newService:
  714. self.__subsEngine.reset()
  715. self.__subsEngine.pause()
  716. self.__subsEngine.setPlayerDelay(self.__playerDelay)
  717. self.__subsEngine.setSubsList(subsList)
  718. self.__loaded = True
  719. self.__working = False
  720. return True
  721. else:
  722. self.__subsEnc = None
  723. self.__subsPath = None
  724. self.__working = False
  725. return False
  726. def startSubs(self, time):
  727. """If subtitles are loaded then start to play them after time set in ms"""
  728. def wrapped():
  729. self.__startTimer.start(time, True)
  730. if self.__working or self.__loaded:
  731. self.__afterWork(wrapped)
  732. def isSubsLoaded(self):
  733. return self.__loaded
  734. def getSubsFileFromSref(self):
  735. ref = self.session.nav.getCurrentlyPlayingServiceReference()
  736. if os.path.isdir(os.path.dirname(ref.getPath())):
  737. self.__subsDir = os.path.dirname(ref.getPath())
  738. for parser in PARSERS:
  739. for ext in parser.parsing:
  740. subsPath = os.path.splitext(ref.getPath())[0] + ext
  741. if os.path.isfile(subsPath):
  742. return subsPath
  743. return None
  744. def resumeSubs(self):
  745. if self.__loaded:
  746. print '[Subtitles] resuming subtitles'
  747. self.showSubsDialog()
  748. self.__subsEngine.resume()
  749. def pauseSubs(self):
  750. if self.__loaded:
  751. print '[Subtitles] pausing subtitles'
  752. self.__subsEngine.pause()
  753. def playAfterSeek(self):
  754. if self.__loaded:
  755. self.showSubsDialog()
  756. self.__subsEngine.sync()
  757. def showSubsDialog(self):
  758. if self.__loaded:
  759. print '[Subtitles] show dialog'
  760. self.__subsScreen.show()
  761. def hideSubsDialog(self):
  762. if self.__loaded:
  763. print '[Subtitles] hide dialog'
  764. if self.__subsScreen:
  765. self.__subsScreen.hide()
  766. def setPlayerDelay(self, delayInMs):
  767. self.__playerDelay = delayInMs
  768. def subscribeOnSubsDelayChanged(self, fnc):
  769. if fnc not in self.__subsEngine.onSubsDelayChanged:
  770. self.__subsEngine.onSubsDelayChanged.append(fnc)
  771. def unsubscribeOnSubsDelayChanged(self, fnc):
  772. if fnc in self.__subsEngine.onSubsDelayChanged:
  773. self.__subsEngine.onSubsDelayChanged.remove(fnc)
  774. def setSubsDelay(self, delayInMs):
  775. if self.__loaded:
  776. self.__subsEngine.setSubsDelay(delayInMs)
  777. def setSubsDelayToNextSubtitle(self):
  778. if self.__loaded:
  779. self.__subsEngine.setSubsDelayToNextSubtitle()
  780. def setSubsDelayToPrevSubtitle(self):
  781. if self.__loaded:
  782. self.__subsEngine.setSubsDelayToPrevSubtitle()
  783. def getSubsDelay(self):
  784. if self.__loaded:
  785. return self.__subsEngine.getSubsDelay()
  786. def subscribeOnSubsFpsChanged(self, fnc):
  787. if fnc not in self.__subsEngine.onFpsChanged:
  788. self.__subsEngine.onFpsChanged.append(fnc)
  789. def unsubscribeOnSubsFpsChanged(self, fnc):
  790. if fnc in self.__subsEngine.onFpsChanged:
  791. self.__subsEngine.onFpsChanged.remove(fnc)
  792. def setSubsFps(self, fps):
  793. if self.__loaded:
  794. return self.__subsEngine.setSubsFps(fps)
  795. def getSubsFps(self):
  796. if self.__loaded:
  797. return self.__subsEngine.getSubsFps()
  798. def getSubsPath(self):
  799. return self.__subsPath
  800. def subsMenu(self):
  801. if not self.__working and not (self.__subclassOfInfobarBase and not self.__isServiceSet):
  802. self.__alreadyPausedVideo = False
  803. if self.subsSettings.pauseVideoOnSubtitlesMenu.value:
  804. print '[SubsSupport] stopVideoOnSubtitlesMenu: True'
  805. if isinstance(self, InfoBarSeek):
  806. if self.seekstate == InfoBarSeek.SEEK_STATE_PLAY:
  807. print '[SubsSupport] pausing video'
  808. self.setSeekState(InfoBarSeek.SEEK_STATE_PAUSE)
  809. else:
  810. print '[SubsSupport] video is already paused'
  811. self.__alreadyPausedVideo = True
  812. else:
  813. print '[SubsSupport] not subclass of InfobarSeek'
  814. self.session.openWithCallback(self.__subsMenuCB, SubsMenu, self,
  815. self.__subsPath, self.__subsDir, self.__subsEnc, self.embeddedSupport, self.embeddedEnabled, self.searchSupport)
  816. def resetSubs(self, resetEnc=True, resetEngine=True, newSubsScreen=False, newService=True):
  817. """
  818. Resets subtitle state -> stops engine, reload encodings, reset paths..
  819. @param resetEnc : start trying encodings from beginning of current encodings-group list
  820. @param resetEngine: clean active subtitle, subtitle list, reset engine vars
  821. @param newSubsSCreen: recreates subtitles screen
  822. @param newService: set to True if new servicereference is in use
  823. """
  824. # start trying encodings from beginning of encodings_group list
  825. if resetEnc:
  826. self.__subsEnc = None
  827. self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()])
  828. self.__subsEngine.pause()
  829. # stop subtitles, clean active subtitle, subtitles list, reset delay
  830. # if new service -> remove service
  831. if resetEngine:
  832. self.__subsEngine.setSubsDelay(0)
  833. self.__subsEngine.reset()
  834. if newService:
  835. self.__firstStart = False
  836. # hide subtitles, reload screen with new settings
  837. # if newSubsScreen, remove current subscreen and create new one
  838. self.__resetSubsScreen(newSubsScreen)
  839. self.__subsPath = None
  840. self.__loaded = False
  841. def __resetSubsScreen(self, newSubsScreen=False):
  842. self.__subsScreen.hide()
  843. if newSubsScreen:
  844. self.__subsEngine.setRenderer(None)
  845. self.session.deleteDialog(self.__subsScreen)
  846. self.__subsScreen = self.session.instantiateDialog(self._getSubsScreenCls(), self.subsSettings.external)
  847. self.__subsEngine.setRenderer(self.__subsScreen)
  848. else:
  849. self.__subsScreen.reloadSettings()
  850. self.__subsScreen.show()
  851. def exitSubs(self):
  852. """This method should be called at the end of usage of this class"""
  853. self.hideSubsDialog()
  854. if self.__subsEngine:
  855. self.__subsEngine.exit()
  856. self.__subsEngine = None
  857. if self.__subsScreen:
  858. self.session.deleteDialog(self.__subsScreen)
  859. self.__subsScreen = None
  860. self.__starTimer.stop()
  861. self.__starTimer = None
  862. self.__checkTimer.stop()
  863. self.__checkTimer = None
  864. print '[SubsSupport] closing subtitleDisplay'
  865. def __subsMenuCB(self, subsPath, subsEmbedded, settingsChanged, changeEncoding,
  866. changedEncodingGroup, changedShadowType, reloadEmbeddedScreen, turnOff, forceReload=False):
  867. if self.embeddedEnabled and self.embeddedSupport and not subsEmbedded and not turnOff and not subsPath:
  868. print "embedded settings changed"
  869. self.resetEmbeddedSubs(reloadEmbeddedScreen)
  870. elif turnOff:
  871. print '[SubsSupport] turn off'
  872. if self.embeddedSupport and self.embeddedEnabled:
  873. self.enableSubtitle(None)
  874. if self.__loaded:
  875. self.resetSubs(newService=False)
  876. elif self.embeddedSupport and subsEmbedded:
  877. print '[SubsSupport] loading embedded subtitles'
  878. if self.__loaded:
  879. self.resetSubs()
  880. self.__subsScreen.hide()
  881. self.enableSubtitle(subsEmbedded)
  882. elif subsPath is not None:
  883. if self.embeddedEnabled:
  884. self.enableSubtitle(None)
  885. newScreen = changedShadowType
  886. self.__subsScreen.show()
  887. if self.__subsPath == subsPath:
  888. if not settingsChanged and not ((changeEncoding or changedEncodingGroup) or newScreen or forceReload):
  889. print '[SubsSupport] no changes made'
  890. elif settingsChanged and not (newScreen or changedEncodingGroup or forceReload):
  891. print '[SubSupport] reloading SubScreen'
  892. self.__subsEngine.pause()
  893. self.__resetSubsScreen()
  894. self.__subsEngine.resume()
  895. else:
  896. self.__subsEngine.pause()
  897. if changedEncodingGroup or (changedShadowType and not changeEncoding) or forceReload:
  898. self.__subsEnc = None
  899. if changedEncodingGroup:
  900. self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()])
  901. if newScreen:
  902. self.__resetSubsScreen(newSubsScreen=True)
  903. self.__subsEngine.reset(position=False)
  904. if self.loadSubs(subsPath, newService=False):
  905. self.__subsEngine.refresh()
  906. else:
  907. self.pauseSubs()
  908. self.__subsEnc = None
  909. if changedEncodingGroup:
  910. self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()])
  911. if newScreen:
  912. self.__resetSubsScreen(newSubsScreen=True)
  913. self.__subsEngine.reset()
  914. if self.__loaded:
  915. if self.loadSubs(subsPath, newService=False):
  916. self.__subsEngine.refresh()
  917. else:
  918. if self.loadSubs(subsPath, newService=False):
  919. self.__subsEngine.resume()
  920. if not self.__alreadyPausedVideo and isinstance(self, InfoBarSeek):
  921. print '[SubsSupport] unpausing video'
  922. del self.__alreadyPausedVideo
  923. self.setSeekState(InfoBarSeek.SEEK_STATE_PLAY)
  924. def __processSubs(self, subsPath, subsEnc):
  925. showMessages = self.__showGUIInfoMessages and not (self.__firstStart and self.__subclassOfInfobarBase)
  926. try:
  927. return self.__subsLoader.load(subsPath, subsEnc, getFps(self.session))
  928. except LoadError:
  929. if showMessages:
  930. warningMessage(self.session, _("Cannot load subtitles. Invalid path"))
  931. return None, None
  932. except DecodeError:
  933. if showMessages:
  934. warningMessage(self.session, _("Cannot decode subtitles. Try another encoding group"))
  935. return None, None
  936. except ParserNotFoundError:
  937. if showMessages:
  938. warningMessage(self.session, _("Cannot parse subtitles. Not supported subtitles format"))
  939. return None, None
  940. except ParseError:
  941. if showMessages:
  942. warningMessage(self.session, _("Cannot parse subtitles. Invalid subtitles format"))
  943. return None, None
  944. finally:
  945. self.__firstStart = False
  946. def __updateSubs(self):
  947. if self.__loaded:
  948. self.resumeSubs()
  949. return
  950. subsPath = self.getSubsFileFromSref()
  951. if subsPath is not None:
  952. if self.loadSubs(subsPath):
  953. self.resumeSubs()
  954. self.__working = False
  955. def __afterWork(self, fnc):
  956. def checkWorking():
  957. if self.__working:
  958. print 'check working..'
  959. self.__checkTimer.start(200, True)
  960. else:
  961. self.__checkTimer.stop()
  962. fnc()
  963. self.__checkTimer.stop()
  964. self.__starTimer.stop()
  965. if self.__working:
  966. del self.__checkTimer.callback[:]
  967. self.__checkTimer.callback.append(checkWorking)
  968. self.__checkTimer.start(200, True)
  969. else:
  970. fnc()
  971. ############ Methods triggered by videoEvents when SubsSupport is subclass of Screen ################
  972. def __serviceStarted(self):
  973. print '[SubsSupport] Service Started'
  974. def startSubs():
  975. self.__starTimer.start(self.__startDelay, True)
  976. self.__isServiceSet = True
  977. # subtitles are loading or already loaded
  978. if self.__working or self.__loaded:
  979. self.__afterWork(startSubs)
  980. else:
  981. self.resetSubs(True)
  982. if self.__subsPath is None and self.__autoLoad:
  983. startSubs()
  984. def __serviceStopped(self):
  985. self.resetSubs(True)
  986. self.__isServiceSet = False
  987. def __seekableStatusChanged(self):
  988. if not hasattr(self, 'seekstate'):
  989. return
  990. if self.seekstate == self.SEEK_STATE_PLAY:
  991. self.pauseSubs()
  992. elif self.seekstate == self.SEEK_STATE_PAUSE:
  993. self.resumeSubs()
  994. elif self.seekstate == self.SEEK_STATE_EOF:
  995. self.resetSubs(True)
  996. ########### Methods which extends InfobarSeek seek methods
  997. def doSeekRelative(self, pts):
  998. if self.__loaded:
  999. # self.__subsEngine.preSeek(pts)
  1000. super(SubsSupport, self).doSeekRelative(pts)
  1001. self.playAfterSeek()
  1002. else:
  1003. super(SubsSupport, self).doSeekRelative(pts)
  1004. def doSeek(self, pts):
  1005. if self.__loaded:
  1006. super(SubsSupport, self).doSeek(pts)
  1007. self.playAfterSeek()
  1008. else:
  1009. super(SubsSupport, self).doSeek(pts)
  1010. ############################################################
  1011. class SubsEmbeddedScreen(Screen):
  1012. """
  1013. Pli defaults
  1014. <fonts>
  1015. <font filename="nmsbd.ttf" name="Subs" scale="100" />
  1016. </fonts>
  1017. <subtitles>
  1018. <sub name="Subtitle_TTX" font="Subs;34" borderColor="#000000" borderWidth="3" />
  1019. <sub name="Subtitle_Regular" font="Subs;34" foregroundColor="#ffffff" borderColor="#000000" borderWidth="3" />
  1020. <sub name="Subtitle_Bold" font="Subs;34" foregroundColor="#ffffff" borderColor="#000000" borderWidth="3" />
  1021. <sub name="Subtitle_Italic" font="Subs;34" foregroundColor="#ffffff" borderColor="#000000" borderWidth="3" />
  1022. </subtitles>
  1023. """
  1024. def __init__(self, session, embeddedSettings):
  1025. desktop = getDesktop(0)
  1026. size = desktop.size()
  1027. vSizeOrig = size.height()
  1028. hSizeOrig = size.width()
  1029. if QuickSubtitlesConfigMenu:
  1030. vPosition = 0
  1031. vSize = vSizeOrig
  1032. else:
  1033. vPositionPercent = int(embeddedSettings.position.value)
  1034. fontSize = int(getEmbeddedFontSizeCfg(embeddedSettings.font.size).value)
  1035. vSize = fontSize * 4 + 10
  1036. vPosition = int(vPositionPercent * float((vSizeOrig - vSize) / 100))
  1037. vPosition = vPosition if vPosition > 0 else 0
  1038. vSize = vSizeOrig - vPosition
  1039. self.skin = """<screen position="0,%s" size="%s,%s" zPosition="-1" backgroundColor="transparent" flags="wfNoBorder" />""" % (vPosition, hSizeOrig, vSize)
  1040. Screen.__init__(self, session)
  1041. class SubtitlesWidget(GUIComponent):
  1042. STATE_NO_BACKGROUND, STATE_BACKGROUND = range(2)
  1043. def __init__(self, boundDynamic=True, boundXOffset=10, boundYOffset=10, boundSize=None, fontSize=25, positionPercent=94):
  1044. GUIComponent.__init__(self)
  1045. self.state = self.STATE_BACKGROUND
  1046. self.boundDynamic = boundDynamic
  1047. self.boundXOffset = boundXOffset
  1048. self.boundYOffset = boundYOffset
  1049. self.font = (gFont("Regular", fontSize), fontSize)
  1050. self.positionPercent = positionPercent
  1051. desktopSize = getDesktop(0).size()
  1052. self.desktopSize = (desktopSize.width(), desktopSize.height())
  1053. self.boundSize = boundSize or (self.desktopSize[0], self.calcWidgetHeight())
  1054. def GUIcreate(self, parent):
  1055. self.instance = eLabel(parent)
  1056. self.instance2 = eLabel(parent)
  1057. self.postWidgetCreate()
  1058. def GUIdelete(self):
  1059. self.preWidgetRemove()
  1060. self.instance = None
  1061. self.instance2 = None
  1062. def postWidgetCreate(self):
  1063. self.instance2.hide()
  1064. self.update()
  1065. def preWidgetRemove(self):
  1066. pass
  1067. def calcWidgetYPosition(self):
  1068. return int((self.desktopSize[1] - self.calcWidgetHeight() - self.boundYOffset) / float(100) * self.positionPercent)
  1069. def calcWidgetHeight(self):
  1070. return int(4 * self.font[1] + 15)
  1071. def update(self):
  1072. ds = self.desktopSize
  1073. bs = self.boundSize = (self.desktopSize[0], self.calcWidgetHeight())
  1074. self.instance2.resize(eSize(int(ds[0]), int(bs[1])))
  1075. self.instance2.move(ePoint(int(0), int(self.calcWidgetYPosition())))
  1076. self.instance2.setHAlign(self.instance2.alignCenter)
  1077. self.instance2.setVAlign(self.instance2.alignCenter)
  1078. self.instance2.setFont(self.font[0])
  1079. self.instance2.setTransparent(True)
  1080. if not self.boundDynamic:
  1081. self.instance.resize(eSize(int(bs[0]), int(bs[1])))
  1082. self.instance.move(ePoint(int(ds[0] / 2 - bs[0] / 2), int(self.calcWidgetYPosition())))
  1083. self.instance.setFont(self.font[0])
  1084. self.instance.setHAlign(self.instance.alignCenter)
  1085. self.instance.setVAlign(self.instance.alignCenter)
  1086. def setText(self, text):
  1087. if self.instance and self.instance2:
  1088. if self.state == self.STATE_NO_BACKGROUND:
  1089. self.instance.hide()
  1090. self.instance2.setText(text)
  1091. self.instance2.show()
  1092. elif self.state == self.STATE_BACKGROUND:
  1093. self.instance2.hide()
  1094. if not text:
  1095. self.instance.hide()
  1096. return
  1097. if self.boundDynamic:
  1098. # hack so empty spaces are part of calculateSize calculation
  1099. self.instance2.setText(text.replace(' ','.'))
  1100. ds = self.desktopSize
  1101. bs = self.boundSize
  1102. ws = self.instance2.calculateSize()
  1103. ws = (ws.width() + self.boundXOffset * 2, ws.height() + self.boundYOffset * 2)
  1104. wp = self.instance2.position()
  1105. wp = (wp.x(), wp.y())
  1106. wpy = wp[1] + (bs[1] - ws[1]) / 2
  1107. wpx = ds[0] / 2 - ws[0] / 2
  1108. self.instance.resize(eSize(int(ws[0]), int(ws[1])))
  1109. self.instance.move(ePoint(int(wpx), int(wpy)))
  1110. else:
  1111. bs = self.boundSize
  1112. ds = self.desktopSize
  1113. self.instance.resize(eSize(int(bs[0]), int(bs[1])))
  1114. self.instance.move(ePoint(int(ds[0] / 2 - bs[0] / 2), int(self.calcWidgetYPosition())))
  1115. self.instance.setHAlign(self.instance.alignCenter)
  1116. self.instance.setVAlign(self.instance.alignCenter)
  1117. self.instance.setText(text)
  1118. self.instance.show()
  1119. def setPosition(self, percent):
  1120. self.positionPercent = percent
  1121. self.update()
  1122. def setBoundDynamic(self, value):
  1123. self.boundDynamic = value
  1124. self.update()
  1125. def setBoundOffset(self, offsetX, offsetY):
  1126. self.boundXOffset = int(offsetX)
  1127. self.boundYOffset = int(offsetY)
  1128. self.update()
  1129. def setForegroundColor(self, color):
  1130. self.instance.setForegroundColor(parseColor(color))
  1131. self.instance2.setForegroundColor(parseColor(color))
  1132. def setBackgroundColor(self, color):
  1133. if color[1:3] == "ff":
  1134. self.state = self.STATE_NO_BACKGROUND
  1135. else:
  1136. self.state = self.STATE_BACKGROUND
  1137. self.instance.setBackgroundColor(parseColor(color))
  1138. def setBorderColor(self, color):
  1139. self.instance.setBorderColor(parseColor(color))
  1140. self.instance2.setBorderColor(parseColor(color))
  1141. def setBorderWidth(self, width):
  1142. self.instance.setBorderWidth(int(width))
  1143. self.instance2.setBorderWidth(int(width))
  1144. def setShadowColor(self, color):
  1145. self.instance.setShadowColor(parseColor(color))
  1146. self.instance2.setShadowColor(parseColor(color))
  1147. def setShadowOffset(self, offset, scale):
  1148. self.instance.setShadowOffset(parsePosition(offset, scale))
  1149. self.instance2.setShadowOffset(parsePosition(offset, scale))
  1150. def setFont(self, font):
  1151. if self.font[1] == font[1]:
  1152. self.font = font
  1153. self.instance.setFont(font[0])
  1154. self.instance2.setFont(font[0])
  1155. else:
  1156. self.font = font
  1157. self.update()
  1158. class SubsScreen(Screen):
  1159. def __init__(self, session, externalSettings):
  1160. self.subShown = False
  1161. self.__shadowType = 'border'
  1162. self.__eLabelHasBorderParams = False
  1163. self.externalSettings = externalSettings
  1164. fontSize = int(externalSettings.font.size.getValue())
  1165. self.font = {
  1166. "regular":{
  1167. 'gfont':(gFont(externalSettings.font.regular.type.value, fontSize), fontSize),
  1168. 'color':externalSettings.font.regular.alpha.value + externalSettings.font.regular.color.value
  1169. },
  1170. "italic":{
  1171. 'gfont':(gFont(externalSettings.font.italic.type.value, fontSize), fontSize),
  1172. 'color':externalSettings.font.italic.alpha.value + externalSettings.font.italic.color.value
  1173. },
  1174. "bold":{
  1175. 'gfont':(gFont(externalSettings.font.bold.type.value, fontSize), fontSize),
  1176. 'color':externalSettings.font.bold.alpha.value + externalSettings.font.bold.type.value
  1177. }
  1178. }
  1179. self.selectedFont = "regular"
  1180. self.currentColor = externalSettings.font.regular.color.value
  1181. self.skin = """
  1182. <screen position="0,0" size="%d,%d" zPosition="-1" backgroundColor="transparent" flags="wfNoBorder">
  1183. <widget name="subtitles" />
  1184. </screen>""" % (getDesktop(0).size().width(), getDesktop(0).size().height())
  1185. Screen.__init__(self, session)
  1186. self.stand_alone = True
  1187. self["subtitles"] = SubtitlesWidget()
  1188. self.onLayoutFinish.append(self.__checkElabelCaps)
  1189. self.onLayoutFinish.append(self.reloadSettings)
  1190. def __checkElabelCaps(self):
  1191. if hasattr(self["subtitles"].instance, 'setBorderWidth') and hasattr(self["subtitles"].instance, 'setBorderColor'):
  1192. self.__eLabelHasBorderParams = True
  1193. elif self.__shadowType == 'border':
  1194. self.__shadowType = 'offset'
  1195. def setShadowType(self, type):
  1196. if type == 'border' and self.__eLabelHasBorderParams:
  1197. self.__shadowType = 'border'
  1198. else:
  1199. self.__shadowType = 'offset'
  1200. def setShadow(self, type, color, size=None, xOffset=None, yOffset=None):
  1201. self.setShadowType(type)
  1202. if self.__shadowType == 'border':
  1203. self["subtitles"].setBorderColor("#" + color)
  1204. elif self.__shadowType == 'offset':
  1205. self["subtitles"].setShadowColor("#" + color)
  1206. if self.__shadowType == 'border' and size is not None:
  1207. self["subtitles"].setBorderWidth(size)
  1208. elif self.__shadowType == 'offset' and (xOffset is not None and yOffset is not None):
  1209. self["subtitles"].setShadowOffset(str(-xOffset) + ',' + str(-yOffset), self.scale)
  1210. def setBackground(self, type, alpha, color, xOffset=None, yOffset=None):
  1211. if type == 'dynamic':
  1212. self["subtitles"].setBoundDynamic(True)
  1213. self["subtitles"].setBoundOffset(xOffset, yOffset)
  1214. else:
  1215. self["subtitles"].setBoundDynamic(False)
  1216. color = "#" + alpha + color
  1217. self["subtitles"].setBackgroundColor(color)
  1218. def setColor(self, color):
  1219. self.currentColor = color
  1220. color = "#" + color
  1221. self["subtitles"].setForegroundColor(color)
  1222. def setPosition(self, position):
  1223. self["subtitles"].setPosition(position)
  1224. def setFonts(self, font):
  1225. self.font = font
  1226. self['subtitles'].setFont(self.font['regular']['gfont'])
  1227. def reloadSettings(self):
  1228. shadowType = self.externalSettings.shadow.type.getValue()
  1229. shadowColor = self.externalSettings.shadow.color.getValue()
  1230. shadowSize = int(self.externalSettings.shadow.size.getValue())
  1231. shadowXOffset = int(self.externalSettings.shadow.xOffset.getValue())
  1232. shadowYOffset = int(self.externalSettings.shadow.yOffset.getValue())
  1233. shadowEnabled = int(self.externalSettings.shadow.enabled.getValue())
  1234. if not shadowEnabled:
  1235. shadowXOffset = shadowYOffset = shadowSize = 0
  1236. backgroundType = self.externalSettings.background.type.getValue()
  1237. backgroundAlpha = self.externalSettings.background.alpha.getValue()
  1238. backgroundColor = self.externalSettings.background.color.getValue()
  1239. backgroundXOffset = self.externalSettings.background.xOffset.getValue()
  1240. backgroundYOffset = self.externalSettings.background.yOffset.getValue()
  1241. backgroundEnabled = self.externalSettings.background.enabled.getValue()
  1242. if not backgroundEnabled:
  1243. backgroundAlpha = "ff"
  1244. backgroundColor = "ffffff"
  1245. fontSize = int(self.externalSettings.font.size.getValue())
  1246. position = int(self.externalSettings.position.getValue())
  1247. self.setPosition(position)
  1248. self.setShadow(shadowType, shadowColor, shadowSize, shadowXOffset, shadowYOffset)
  1249. self.setBackground(backgroundType, backgroundAlpha, backgroundColor, backgroundXOffset, backgroundYOffset)
  1250. externalSettings = self.externalSettings
  1251. self.setFonts({
  1252. "regular":{
  1253. 'gfont':(gFont(externalSettings.font.regular.type.value, fontSize), fontSize),
  1254. 'color':externalSettings.font.regular.alpha.value + externalSettings.font.regular.color.value
  1255. },
  1256. "italic":{
  1257. 'gfont':(gFont(externalSettings.font.italic.type.value, fontSize), fontSize),
  1258. 'color':externalSettings.font.italic.alpha.value + externalSettings.font.italic.color.value
  1259. },
  1260. "bold":{
  1261. 'gfont':(gFont(externalSettings.font.bold.type.value, fontSize), fontSize),
  1262. 'color':externalSettings.font.bold.alpha.value + externalSettings.font.bold.color.value
  1263. }
  1264. })
  1265. def setSubtitle(self, sub):
  1266. if sub['style'] != self.selectedFont:
  1267. self.selectedFont = sub['style']
  1268. self['subtitles'].setFont(self.font[sub['style']]['gfont'])
  1269. color = sub['color']
  1270. if color == "default":
  1271. color = self.font[sub['style']]['color']
  1272. self.setColor(color)
  1273. self["subtitles"].setText(toString(sub['text']))
  1274. self.subShown = True
  1275. def hideSubtitle(self):
  1276. if self.subShown:
  1277. self["subtitles"].setText("")
  1278. self.subShown = False
  1279. class SubsEngine(object):
  1280. def __init__(self, session, engineSettings, renderer):
  1281. self.session = session
  1282. self.engineSettings = engineSettings
  1283. self.renderer = renderer
  1284. self.subsList = None
  1285. self.position = 0
  1286. self.sub = None
  1287. self.subsFpsRatio = 1
  1288. self.onSubsFpsChanged = []
  1289. self.subsDelay = 0
  1290. self.onSubsDelayChanged = []
  1291. self.playerDelay = 0
  1292. self.syncDelay = 300
  1293. self.hideInterval = 200 * 90
  1294. self.__seek = None
  1295. self.__pts = None
  1296. self.__ptsDelay = None
  1297. self.__callbackPts = None
  1298. self.preDoPlay = [self.updateSubPosition]
  1299. self.refreshTimer = eTimer()
  1300. self.refreshTimer.callback.append(self.play)
  1301. self.refreshTimerDelay = 1000
  1302. self.hideTimer = eTimer()
  1303. self.hideTimer.callback.append(self.checkHideSub)
  1304. self.hideTimer.callback.append(self.incSubPosition)
  1305. self.hideTimer.callback.append(self.doPlay)
  1306. self.getPlayPtsTimer = eTimer()
  1307. self.getPlayPtsTimer.callback.append(self.getPts)
  1308. self.getPlayPtsTimer.callback.append(self.validPts)
  1309. self.getPlayPtsTimer.callback.append(self.callbackPts)
  1310. self.getPlayPtsTimerDelay = 200
  1311. self.resume = self.play
  1312. self.addNotifiers()
  1313. def addNotifiers(self):
  1314. def hideInterval(configElement):
  1315. self.hideInterval = int(configElement.value) * 90
  1316. def playerDelay(configElement):
  1317. self.playerDelay = int(configElement.value) * 90
  1318. def syncDelay(configElement):
  1319. self.syncDelay = int(configElement.value)
  1320. def getPlayPtsTimerDelay(configElement):
  1321. self.getPlayPtsTimerDelay = int(configElement.value)
  1322. def refreshTimerDelay(configElement):
  1323. self.refreshTimerDelay = int(configElement.value)
  1324. self.engineSettings.expert.hideDelay.addNotifier(hideInterval)
  1325. self.engineSettings.expert.playerDelay.addNotifier(playerDelay)
  1326. self.engineSettings.expert.syncDelay.addNotifier(syncDelay)
  1327. self.engineSettings.expert.ptsDelayCheck.addNotifier(getPlayPtsTimerDelay)
  1328. self.engineSettings.expert.refreshDelay.addNotifier(refreshTimerDelay)
  1329. def removeNotifiers(self):
  1330. del self.engineSettings.expert.hideDelay.notifiers[:]
  1331. del self.engineSettings.expert.playerDelay.notifiers[:]
  1332. del self.engineSettings.expert.syncDelay.notifiers[:]
  1333. del self.engineSettings.expert.ptsDelayCheck.notifiers[:]
  1334. del self.engineSettings.expert.refreshDelay.notifiers[:]
  1335. def getPlayPts(self, callback, delay=None):
  1336. self.getPlayPtsTimer.stop()
  1337. self.__callbackPts = callback
  1338. self.__ptsDelay = delay
  1339. self.__pts = None
  1340. if delay is None:
  1341. delay = 1
  1342. self.getPlayPtsTimer.start(delay, True)
  1343. def getPts(self):
  1344. try:
  1345. if not self.__seek:
  1346. service = self.session.nav.getCurrentService()
  1347. self.__seek = service.seek()
  1348. except Exception:
  1349. return
  1350. r = self.__seek.getPlayPosition()
  1351. if r[0]:
  1352. self.__pts = None
  1353. else:
  1354. self.__pts = long(r[1]) + self.playerDelay
  1355. def validPts(self):
  1356. pass
  1357. def callbackPts(self):
  1358. if self.__pts is not None:
  1359. self.getPlayPtsTimer.stop()
  1360. self.__callbackPts()
  1361. else:
  1362. delay = self.getPlayPtsTimerDelay
  1363. if self.__ptsDelay is not None:
  1364. delay = self.__ptsDelay
  1365. self.getPlayPtsTimer.start(delay)
  1366. def setSubsList(self, subslist):
  1367. self.subsList = subslist
  1368. def setRenderer(self, renderer):
  1369. self.renderer = renderer
  1370. def setPlayerDelay(self, playerDelay):
  1371. self.pause()
  1372. self.playerDelay = playerDelay * 90
  1373. self.resume()
  1374. def setSubsFps(self, subsFps):
  1375. print "[SubsEngine] setSubsFps - setting fps to %s"% str(subsFps)
  1376. videoFps = getFps(self.session, True)
  1377. if videoFps is None:
  1378. print "[SubsEngine] setSubsFps - cannot get video fps!"
  1379. else:
  1380. self.pause()
  1381. self.subsFpsRatio = subsFps/ float(videoFps)
  1382. for f in self.onSubsFpsChanged:
  1383. f(self.getSubsFps())
  1384. self.resume()
  1385. def getSubsFps(self):
  1386. videoFps = getFps(self.session, True)
  1387. if videoFps is None:
  1388. return None
  1389. return fps_float(self.subsFpsRatio * videoFps)
  1390. def setSubsDelay(self, delayInMs):
  1391. print "[SubsEngine] setSubsDelay - setting delay to %sms"% str(delayInMs)
  1392. self.pause()
  1393. self.subsDelay = int(delayInMs) * 90
  1394. for f in self.onSubsDelayChanged:
  1395. f(self.getSubsDelay())
  1396. self.resume()
  1397. def getSubsDelay(self):
  1398. return self.subsDelay / 90
  1399. def getSubsPosition(self):
  1400. return self.position
  1401. def setSubsDelayToNextSubtitle(self):
  1402. def setDelay():
  1403. if not self.renderer.subShown:
  1404. print '[SubsEngine] setDelayToNextSubtitle - next pos: %d of %d'%(self.position, len(self.subsList))
  1405. toSub = self.subsList[self.position]
  1406. # position is incremented right after subtitle is hidden so we don't do anything
  1407. elif self.renderer.subShown and self.position != len(self.subsList) -1:
  1408. print '[SubsEngine] setDelayToNextSubtitle - next pos: %d of %d'%(self.position+1, len(self.subsList))
  1409. toSub = self.subsList[self.position+1]
  1410. else:
  1411. print '[SubsEngine] setDelayToNextSubtitle - we are on last subtitle'
  1412. return
  1413. toSubDelay = (self.__pts - (toSub['start'] * self.subsFpsRatio))/90
  1414. self.setSubsDelay(toSubDelay)
  1415. self.stopTimers()
  1416. self.getPlayPts(setDelay)
  1417. def setSubsDelayToPrevSubtitle(self):
  1418. def setDelay():
  1419. if not self.renderer.subShown:
  1420. print '[SubsEngine] setDelayToPrevSubtitle - skipping to start of current sub'
  1421. # position is incremented right after subtitle is hidden so we have to go one back
  1422. toSub = self.subsList[self.position-1]
  1423. elif self.renderer.subShown and self.position != 0:
  1424. print '[SubsEngine] setDelayToPrevSubtitle - prev pos: %d of %d'%(self.position-1, len(self.subsList))
  1425. toSub = self.subsList[self.position -1]
  1426. else:
  1427. print '[SubsEngine] setDelayToPrevSubtitle - we are on first subtitle'
  1428. return
  1429. toSubDelay = (self.__pts - (toSub['start'] * self.subsFpsRatio))/90
  1430. self.setSubsDelay(toSubDelay)
  1431. self.stopTimers()
  1432. self.getPlayPts(setDelay)
  1433. def reset(self, position=True):
  1434. self.stopTimers()
  1435. self.hideSub()
  1436. if position:
  1437. self.position = 0
  1438. self.__seek = None
  1439. self.__pts = None
  1440. self.__callbackPts = None
  1441. self.sub = None
  1442. self.subsDelay = 0
  1443. self.subsFpsRatio = 1
  1444. def refresh(self):
  1445. self.stopTimers()
  1446. self.hideSub()
  1447. self.refreshTimer.start(self.refreshTimerDelay, True)
  1448. def pause(self):
  1449. self.stopTimers()
  1450. self.hideSub()
  1451. def play(self):
  1452. self.stopTimers()
  1453. self.hideSub()
  1454. self.getPlayPts(self.prePlay)
  1455. def sync(self):
  1456. self._oldPts = None
  1457. def checkPts():
  1458. if self._oldPts is None:
  1459. self._oldPts = self.__pts
  1460. self.getPlayPts(checkPts, self.syncDelay)
  1461. # video is frozen no progress made
  1462. elif self._oldPts == self.__pts:
  1463. self._oldPts = None
  1464. self.getPlayPts(checkPts, self.syncDelay)
  1465. # abnormal pts
  1466. elif (self.__pts > self._oldPts + self.syncDelay * 90 + (200 * 90)) or (
  1467. self.__pts < self._oldPts + self.syncDelay * 90 - (200 * 90)):
  1468. self._oldPts = None
  1469. self.getPlayPts(checkPts, self.syncDelay)
  1470. # normal playback
  1471. else:
  1472. del self._oldPts
  1473. self.updateSubPosition()
  1474. self.doPlay()
  1475. self.stopTimers()
  1476. self.hideSub()
  1477. self.getPlayPts(checkPts, self.syncDelay)
  1478. def prePlay(self):
  1479. for f in self.preDoPlay:
  1480. f()
  1481. self.doPlay()
  1482. def doPlay(self):
  1483. if self.position == len(self.subsList):
  1484. print '[SubsEngine] reached end of subtitle list'
  1485. self.position = len(self.subsList) - 1
  1486. self.stopTimers()
  1487. else:
  1488. self.sub = self.subsList[self.position]
  1489. self.getPlayPts(self.doWait)
  1490. def doWait(self):
  1491. subStartPts = int(self.sub['start'] * self.subsFpsRatio) + self.subsDelay
  1492. if self.__pts < subStartPts:
  1493. diffPts = subStartPts - self.__pts
  1494. diffMs = diffPts / 90
  1495. if diffMs > 50:
  1496. self.getPlayPts(self.doWait, diffMs)
  1497. else:
  1498. print '[SubsEngine] sub shown sooner by %dms' % diffMs
  1499. self.renderSub()
  1500. else:
  1501. subsEndPts = (self.sub['end'] * self.subsFpsRatio) + self.subsDelay
  1502. if subsEndPts - self.__pts < 0:
  1503. #print '[SubsEngine] sub should be already shown - %dms, skipping...'%((subsEndPts - self.__pts)/90)
  1504. self.getPlayPts(self.skipSubs, 100)
  1505. else:
  1506. print '[SubsEngine] sub shown later by %dms' % ((self.__pts - subStartPts)/90)
  1507. self.renderSub()
  1508. def skipSubs(self):
  1509. if self.position == len(self.subsList) - 1:
  1510. self.incSubPosition()
  1511. else:
  1512. self.updateSubPosition()
  1513. self.doPlay()
  1514. def renderSub(self):
  1515. duration = int(self.sub['duration'] * self.subsFpsRatio)
  1516. self.renderer.setSubtitle(self.sub)
  1517. self.hideTimer.start(duration, True)
  1518. def checkHideSub(self):
  1519. if self.subsList[-1] == self.sub:
  1520. self.hideSub()
  1521. elif (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.hideInterval < (self.subsList[self.position + 1]['start'] * self.subsFpsRatio):
  1522. self.hideSub()
  1523. def hideSub(self):
  1524. self.renderer.hideSubtitle()
  1525. def incSubPosition(self):
  1526. self.position += 1
  1527. def updateSubPosition(self):
  1528. playPts = self.__pts
  1529. print '[SubsEngine] pre-update sub position:', self.position
  1530. subStartPts = (self.subsList[self.position]['start'] * self.subsFpsRatio) + self.subsDelay
  1531. subStartEndPts = (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.subsDelay
  1532. # seek backwards
  1533. if subStartPts > playPts:
  1534. subPrevEndPts = (self.subsList[self.position -1]['end'] * self.subsFpsRatio) + self.subsDelay
  1535. while self.position > 0 and subPrevEndPts > playPts:
  1536. self.position -= 1
  1537. subPrevEndPts = (self.subsList[self.position - 1]['end'] * self.subsFpsRatio) + self.subsDelay
  1538. # seek forward
  1539. elif subStartPts < playPts and subStartEndPts < playPts:
  1540. while self.position < len(self.subsList) - 1 and subStartPts < playPts and subStartEndPts < playPts:
  1541. self.position += 1
  1542. subStartPts = (self.subsList[self.position]['start'] * self.subsFpsRatio) + self.subsDelay
  1543. subStartEndPts = (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.subsDelay
  1544. print '[SubsEngine] post-update sub position:', self.position
  1545. def showDialog(self):
  1546. self.renderer.show()
  1547. def hideSubtitlesDialog(self):
  1548. self.renderer.hide()
  1549. def stopTimers(self):
  1550. if self.refreshTimer is not None:
  1551. self.refreshTimer.stop()
  1552. if self.getPlayPtsTimer is not None:
  1553. self.getPlayPtsTimer.stop()
  1554. if self.hideTimer is not None:
  1555. self.hideTimer.stop()
  1556. def exit(self):
  1557. self.hideTimer = None
  1558. self.refreshTimer = None
  1559. self.getPlayPtsTimer = None
  1560. del self.onSubsDelayChanged[:]
  1561. del self.onSubsFpsChanged[:]
  1562. self.removeNotifiers()
  1563. class PanelList(MenuList):
  1564. def __init__(self, list, height=30):
  1565. MenuList.__init__(self, list, False, eListboxPythonMultiContent)
  1566. self.l.setItemHeight(height)
  1567. self.l.setFont(0, gFont("Regular", 20))
  1568. self.l.setFont(1, gFont("Regular", 17))
  1569. def PanelListEntry(name, mode):
  1570. res = [(name, mode)]
  1571. res.append(MultiContentEntryText(pos=(5, 5), size=(330, 25), font=0, flags=RT_VALIGN_CENTER, text=name))
  1572. return res
  1573. def PanelColorListEntry(name, value, colorName, colorValue, sizePanelX):
  1574. res = [(name)]
  1575. res.append(MultiContentEntryText(pos=(0, 5), size=(sizePanelX, 30), font=1, flags=RT_HALIGN_LEFT, text=name, color=colorName))
  1576. res.append(MultiContentEntryText(pos=(0, 5), size=(sizePanelX, 30), font=1, flags=RT_HALIGN_RIGHT, text=value, color=colorValue))
  1577. return res
  1578. class SubsMenu(Screen):
  1579. skin = """
  1580. <screen position="center,center" size="500,400" zPosition="1" >
  1581. <widget name="title_label" position="0,5" size="500,35" valign="center" halign="center" font="Regular;25" transparent="1" foregroundColor="white" />
  1582. <widget name="subfile_label" position="0,50" size="500,50" valign="center" halign="center" font="Regular;22" transparent="1" foregroundColor="#DAA520" />
  1583. <widget name="subfile_list" position="center,100" size="300,30" transparent="1" />
  1584. <eLabel position="5,135" size="490,1" backgroundColor="#999999" />
  1585. <widget name="menu_list" position="0,140" size="500,235" transparent="1" scrollbarMode="showOnDemand" />
  1586. <widget name="copyright" position="10,375" size="480,20" valign="center" halign="center" font="Regular;15" transparent="1" foregroundColor="white" />
  1587. </screen>"""
  1588. def __init__(self, session, infobar, subfile=None, subdir=None, encoding=None, embeddedSupport=False, embeddedEnabled=False, searchSupport=False):
  1589. Screen.__init__(self, session)
  1590. self.infobar = infobar
  1591. self.subfile = subfile
  1592. self.subdir = subdir
  1593. self.encoding = encoding
  1594. self.embeddedSupport = embeddedSupport
  1595. self.embeddedEnabled = embeddedEnabled
  1596. self.searchSupport = searchSupport
  1597. self.embeddedSubtitle = None
  1598. self.newSelection = False
  1599. self.changeEncoding = False
  1600. self.changedEncodingGroup = False
  1601. self.changedShadowType = False
  1602. self.changedSettings = False
  1603. self.reloadEmbeddedScreen = False
  1604. self.turnOff = False
  1605. self.forceReload = False
  1606. self["title_label"] = Label(_("Currently choosed subtitles"))
  1607. self["subfile_label"] = Label("")
  1608. self["subfile_list"] = PanelList([], 25)
  1609. self["menu_list"] = PanelList([], 28)
  1610. self["copyright"] = Label("")
  1611. # self["copyright"] = Label("created by %s <%s>"%(__author__,__email__))
  1612. self["actions"] = ActionMap(["SetupActions", "DirectionActions"],
  1613. {
  1614. "ok": self.ok,
  1615. "cancel": self.cancel,
  1616. }, -2)
  1617. self.onLayoutFinish.append(self.initTitle)
  1618. self.onLayoutFinish.append(self.initGUI)
  1619. self.onLayoutFinish.append(self.disableSelection)
  1620. def disableSelection(self):
  1621. self["subfile_list"].selectionEnabled(False)
  1622. def initTitle(self):
  1623. self.setTitle("SubsSupport %s" % __version__)
  1624. def initGUI(self):
  1625. self.initSubInfo()
  1626. self.initMenu()
  1627. def initSubInfo(self):
  1628. subInfo = []
  1629. if self.embeddedEnabled or self.embeddedSubtitle:
  1630. self["subfile_label"].setText(_("Embedded Subtitles"))
  1631. self["subfile_label"].instance.setForegroundColor(parseColor("#ffff00"))
  1632. elif self.subfile is not None:
  1633. self["subfile_label"].setText(toString(os.path.split(self.subfile)[1]))
  1634. self["subfile_label"].instance.setForegroundColor(parseColor("#DAA520"))
  1635. if self.newSelection:
  1636. pass
  1637. # subInfo.append(PanelColorListEntry(_("State:"),_("not loaded"), 0xDAA520, 0xffff00, 300))
  1638. elif self.encoding and not self.newSelection:
  1639. # subInfo.append(PanelColorListEntry(_("State:"),_("loaded"), 0xDAA520, 0x00ff00, 300))
  1640. subInfo.append(PanelColorListEntry(_("Encoding:"), self.encoding, 0xDAA520, 0xffffff, 300))
  1641. elif not self.encoding and not self.newSelection:
  1642. # subInfo.append(PanelColorListEntry(_("State:"),_("not loaded"), 0xDAA520, 0xffff00, 300))
  1643. subInfo.append(PanelColorListEntry(_("Encoding:"), _("cannot decode"), 0xDAA520, 0xffffff, 300))
  1644. else:
  1645. self["subfile_label"].setText(_("None"))
  1646. self["subfile_label"].instance.setForegroundColor(parseColor("#DAA520"))
  1647. self["subfile_list"].setList(subInfo)
  1648. def initMenu(self):
  1649. self.menu = [(_('Choose subtitles'), 'choose')]
  1650. if self.searchSupport:
  1651. self.menu.append((_("Search subtitles"), 'search'))
  1652. if not self.embeddedEnabled:
  1653. if self.subfile is not None and not self.newSelection:
  1654. self.menu.append((_('Change encoding'), 'encoding'))
  1655. self.menu.append((_('Subtitles settings'), 'settings'))
  1656. if self.embeddedEnabled and QuickSubtitlesConfigMenu:
  1657. self.menu.append((_('Subtitles settings (embedded)'), 'settings_embedded_pli'))
  1658. if self.embeddedEnabled and not QuickSubtitlesConfigMenu:
  1659. self.menu.append((_('Subtitles settings (embedded)'), 'settings_embedded'))
  1660. if self.subfile is not None or self.embeddedEnabled:
  1661. self.menu.append((_('Turn off subtitles'), 'subsoff'))
  1662. list = [PanelListEntry(x, y) for x, y in self.menu]
  1663. self["menu_list"].setList(list)
  1664. def ok(self):
  1665. mode = self["menu_list"].getCurrent()[0][1]
  1666. if mode == 'choose':
  1667. self.session.openWithCallback(self.subsChooserCB, SubsChooser, self.infobar.subsSettings, self.subdir, self.embeddedSupport, False, True)
  1668. elif mode =='search':
  1669. self.searchSubs()
  1670. elif mode == 'settings':
  1671. self.session.openWithCallback(self.subsSetupCB, SubsSetupMainMisc, self.infobar.subsSettings)
  1672. elif mode == 'settings_embedded':
  1673. self.session.openWithCallback(self.subsSetupEmbeddedCB, SubsSetupEmbedded, self.infobar.subsSettings.embedded)
  1674. elif mode == 'settings_embedded_pli':
  1675. self.session.open(QuickSubtitlesConfigMenu, self.infobar)
  1676. elif mode == 'encoding':
  1677. self.changeEncoding = True
  1678. self.cancel()
  1679. elif mode == 'subsoff':
  1680. self.turnOff = True
  1681. self.cancel()
  1682. def getSearchTitleList(self, sName, sPath):
  1683. searchTitles = []
  1684. if sName:
  1685. searchTitles.append(sName)
  1686. if sPath:
  1687. dirname = os.path.basename(os.path.dirname(sPath))
  1688. dirnameFix = dirname.replace('.', ' ').replace('_', ' ').replace('-', ' ')
  1689. filename = os.path.splitext(os.path.basename(sPath))[0]
  1690. filenameFix = filename.replace('.', ' ').replace('_', ' ').replace('-', ' ')
  1691. if filename not in searchTitles:
  1692. searchTitles.append(filename)
  1693. if filenameFix not in searchTitles:
  1694. searchTitles.append(filenameFix)
  1695. if dirname not in searchTitles:
  1696. searchTitles.append(dirname)
  1697. if dirnameFix not in searchTitles:
  1698. searchTitles.append(dirnameFix)
  1699. return searchTitles
  1700. def searchSubs(self):
  1701. def checkDownloadedSubsSelection(downloadedSubtitle=None):
  1702. if downloadedSubtitle:
  1703. self.subsChooserCB(downloadedSubtitle, False, True)
  1704. def paramsDialogCB(callback=None):
  1705. if callback:
  1706. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList, resetSearchParams=False)
  1707. def showProvidersErrorCB(callback):
  1708. if not callback:
  1709. subsSettings.search.showProvidersErrorMessage.value = False
  1710. if subsSettings.search.openParamsDialogOnSearch.value:
  1711. self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, titleList, enabledList=False)
  1712. else:
  1713. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList)
  1714. if self.searchSupport:
  1715. ref = self.session.nav.getCurrentlyPlayingServiceReference()
  1716. try:
  1717. sPath = ref.getPath()
  1718. except Exception:
  1719. sPath = None
  1720. try:
  1721. sName = ref.getName()
  1722. except Exception:
  1723. sName = None
  1724. titleList = self.getSearchTitleList(sName, sPath)
  1725. subsSettings = self.infobar.subsSettings
  1726. seeker = E2SubsSeeker(self.session, subsSettings.search, debug=True)
  1727. if seeker.providers_error and subsSettings.search.showProvidersErrorMessage.value:
  1728. msg = _("Some subtitles providers are not working") + ".\n"
  1729. msg += _("For more details please check search settings") + "."
  1730. msg += "\n\n"
  1731. msg += _("Do you want to show this message again?")
  1732. self.session.openWithCallback(showProvidersErrorCB, MessageBox, msg, type=MessageBox.TYPE_YESNO)
  1733. elif subsSettings.search.openParamsDialogOnSearch.value:
  1734. self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, titleList, enabledList=False)
  1735. else:
  1736. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList)
  1737. def subsChooserCB(self, subfile=None, embeddedSubtitle=None, forceReload=False):
  1738. if subfile is not None and self.subfile != subfile:
  1739. self.subfile = subfile
  1740. self.subdir = os.path.dirname(self.subfile)
  1741. self.newSelection = True
  1742. self.embeddedEnabled = False
  1743. self.cancel()
  1744. elif subfile is not None and self.subfile == subfile and forceReload:
  1745. self.forceReload = True
  1746. self.cancel()
  1747. elif embeddedSubtitle and embeddedSubtitle != self.infobar.selected_subtitle:
  1748. self.embeddedSubtitle = embeddedSubtitle
  1749. self.cancel()
  1750. # self.initGUI()
  1751. def subsSetupCB(self, changedSettings=False, changedEncodingGroup=False,
  1752. changedShadowType=False):
  1753. self.changedSettings = changedSettings
  1754. self.changedEncodingGroup = changedEncodingGroup
  1755. self.changedShadowType = changedShadowType
  1756. def subsSetupEmbeddedCB(self, reloadEmbeddedScreen=False):
  1757. self.reloadEmbeddedScreen = reloadEmbeddedScreen
  1758. def cancel(self):
  1759. self.close(self.subfile, self.embeddedSubtitle, self.changedSettings, self.changeEncoding,
  1760. self.changedEncodingGroup, self.changedShadowType, self.reloadEmbeddedScreen, self.turnOff, self.forceReload)
  1761. # rework
  1762. class SubsSetupExternal(BaseMenuScreen):
  1763. @staticmethod
  1764. def getConfigList(externalSettings):
  1765. configList = []
  1766. shadowType = externalSettings.shadow.type.getValue()
  1767. shadowEnabled = externalSettings.shadow.enabled.getValue()
  1768. backgroundType = externalSettings.background.type.getValue()
  1769. backgroundEnabled = externalSettings.background.enabled.getValue()
  1770. configList.append(getConfigListEntry(_("Font type (Regular)"), externalSettings.font.regular.type))
  1771. configList.append(getConfigListEntry(_("Font color (Regular)"), externalSettings.font.regular.color))
  1772. configList.append(getConfigListEntry(_("Font transparency (Regular)"), externalSettings.font.regular.alpha))
  1773. configList.append(getConfigListEntry(_("Font type (Italic)"), externalSettings.font.italic.type))
  1774. configList.append(getConfigListEntry(_("Font color (Italic"), externalSettings.font.italic.color))
  1775. configList.append(getConfigListEntry(_("Font transparency (Italic)"), externalSettings.font.italic.alpha))
  1776. configList.append(getConfigListEntry(_("Font type (Bold)"), externalSettings.font.bold.type))
  1777. configList.append(getConfigListEntry(_("Font color (Bold)"), externalSettings.font.bold.color))
  1778. configList.append(getConfigListEntry(_("Font transparency (Bold)"), externalSettings.font.bold.alpha))
  1779. configList.append(getConfigListEntry(_("Font size"), externalSettings.font.size))
  1780. configList.append(getConfigListEntry(_("Position"), externalSettings.position))
  1781. configList.append(getConfigListEntry(_("Shadow"), externalSettings.shadow.enabled))
  1782. if shadowEnabled:
  1783. configList.append(getConfigListEntry(_("Shadow type"), externalSettings.shadow.type))
  1784. if shadowType == 'offset':
  1785. configList.append(getConfigListEntry(_("Shadow X-offset"), externalSettings.shadow.xOffset))
  1786. configList.append(getConfigListEntry(_("Shadow Y-offset"), externalSettings.shadow.yOffset))
  1787. else:
  1788. configList.append(getConfigListEntry(_("Shadow size"), externalSettings.shadow.size))
  1789. configList.append(getConfigListEntry(_("Shadow color"), externalSettings.shadow.color))
  1790. configList.append(getConfigListEntry(_("Background"), externalSettings.background.enabled))
  1791. if backgroundEnabled:
  1792. configList.append(getConfigListEntry(_("Background type"), externalSettings.background.type))
  1793. if backgroundType == 'dynamic':
  1794. configList.append(getConfigListEntry(_("Background X-offset"), externalSettings.background.xOffset))
  1795. configList.append(getConfigListEntry(_("Background Y-offset"), externalSettings.background.yOffset))
  1796. configList.append(getConfigListEntry(_("Background color"), externalSettings.background.color))
  1797. configList.append(getConfigListEntry(_("Background transparency"), externalSettings.background.alpha))
  1798. return configList
  1799. def __init__(self, session, externalSettings):
  1800. BaseMenuScreen.__init__(self, session, _("External Subtitles settings"))
  1801. self.externalSettings = externalSettings
  1802. def buildMenu(self):
  1803. self["config"].setList(self.getConfigList(self.externalSettings))
  1804. def keySave(self):
  1805. changedShadowType = self.externalSettings.shadow.type.isChanged()
  1806. for x in self["config"].list:
  1807. x[1].save()
  1808. configfile.save()
  1809. self.close(True, changedShadowType)
  1810. def keyLeft(self):
  1811. ConfigListScreen.keyLeft(self)
  1812. current = self["config"].getCurrent()[1]
  1813. if current in [self.externalSettings.shadow.type,
  1814. self.externalSettings.shadow.enabled,
  1815. self.externalSettings.background.enabled,
  1816. self.externalSettings.background.type]:
  1817. self.buildMenu()
  1818. def keyRight(self):
  1819. ConfigListScreen.keyRight(self)
  1820. current = self["config"].getCurrent()[1]
  1821. if current in [self.externalSettings.shadow.type,
  1822. self.externalSettings.shadow.enabled,
  1823. self.externalSettings.background.enabled,
  1824. self.externalSettings.background.type]:
  1825. self.buildMenu()
  1826. class SubsSetupMainMisc(BaseMenuScreen):
  1827. def __init__(self, session, subsSettings):
  1828. BaseMenuScreen.__init__(self, session, _("Subtitles setting"))
  1829. self.subsSettings = subsSettings
  1830. self.showExpertSettings = ConfigYesNo(default=False)
  1831. def buildMenu(self):
  1832. configList = []
  1833. configList.append(getConfigListEntry(_("Pause video on opening subtitles menu"), self.subsSettings.pauseVideoOnSubtitlesMenu))
  1834. configList.append(getConfigListEntry("-"*200, ConfigNothing()))
  1835. configList.extend(SubsSetupExternal.getConfigList(self.subsSettings.external))
  1836. configList.append(getConfigListEntry(_("Encoding"), self.subsSettings.encodingsGroup))
  1837. configList.append(getConfigListEntry(_("Show expert settings"), self.showExpertSettings))
  1838. if self.showExpertSettings.value:
  1839. engineSettings = self.subsSettings.engine
  1840. configList.append(getConfigListEntry(_("Hide delay"), engineSettings.expert.hideDelay))
  1841. configList.append(getConfigListEntry(_("Sync delay"), engineSettings.expert.syncDelay))
  1842. configList.append(getConfigListEntry(_("Player delay"), engineSettings.expert.playerDelay))
  1843. configList.append(getConfigListEntry(_("Refresh delay"), engineSettings.expert.refreshDelay))
  1844. configList.append(getConfigListEntry(_("PTS check delay"), engineSettings.expert.ptsDelayCheck))
  1845. self["config"].setList(configList)
  1846. def keySave(self):
  1847. changedEncodingGroup = self.subsSettings.encodingsGroup.isChanged()
  1848. changedShadowType = self.subsSettings.external.shadow.type.isChanged()
  1849. for x in self["config"].list:
  1850. x[1].save()
  1851. configfile.save()
  1852. self.close(True, changedEncodingGroup, changedShadowType)
  1853. def keyLeft(self):
  1854. ConfigListScreen.keyLeft(self)
  1855. current = self["config"].getCurrent()[1]
  1856. if current in [self.subsSettings.external.shadow.type,
  1857. self.subsSettings.external.shadow.enabled,
  1858. self.showExpertSettings,
  1859. self.subsSettings.external.background.enabled,
  1860. self.subsSettings.external.background.type]:
  1861. self.buildMenu()
  1862. def keyRight(self):
  1863. ConfigListScreen.keyRight(self)
  1864. current = self["config"].getCurrent()[1]
  1865. if current in [self.subsSettings.external.shadow.type,
  1866. self.subsSettings.external.shadow.enabled,
  1867. self.showExpertSettings,
  1868. self.subsSettings.external.background.enabled,
  1869. self.subsSettings.external.background.type]:
  1870. self.buildMenu()
  1871. class SubsSetupEmbedded(BaseMenuScreen):
  1872. @staticmethod
  1873. def initConfig(configsubsection):
  1874. configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList)
  1875. configsubsection.font = ConfigSubsection()
  1876. configsubsection.font.regular = ConfigSubsection()
  1877. configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList)
  1878. configsubsection.font.italic = ConfigSubsection()
  1879. configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList)
  1880. configsubsection.font.bold = ConfigSubsection()
  1881. configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList)
  1882. configsubsection.font.size = ConfigSelection(default="34", choices=fontSizeChoiceList)
  1883. configsubsection.color = ConfigSelection(default="ffffff", choices=colorChoiceList)
  1884. configsubsection.shadow = ConfigSubsection()
  1885. configsubsection.shadow.size = ConfigSelection(default="3", choices=shadowSizeChoiceList)
  1886. configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList)
  1887. configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  1888. configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList)
  1889. @staticmethod
  1890. def getConfigList(embeddedSettings):
  1891. fontSizeCfg = getEmbeddedFontSizeCfg(embeddedSettings.font.size)
  1892. configList = []
  1893. configList.append(getConfigListEntry(_("Font type (Regular)"), embeddedSettings.font.regular.type))
  1894. configList.append(getConfigListEntry(_("Font type (Italic)"), embeddedSettings.font.italic.type))
  1895. configList.append(getConfigListEntry(_("Font type (Bold)"), embeddedSettings.font.bold.type))
  1896. configList.append(getConfigListEntry(_("Font size"), fontSizeCfg))
  1897. configList.append(getConfigListEntry(_("Position"), embeddedSettings.position))
  1898. configList.append(getConfigListEntry(_("Color"), embeddedSettings.color))
  1899. configList.append(getConfigListEntry(_("Shadow X-offset"), embeddedSettings.shadow.xOffset))
  1900. configList.append(getConfigListEntry(_("Shadow Y-offset"), embeddedSettings.shadow.yOffset))
  1901. return configList
  1902. def __init__(self, session, embeddedSettings):
  1903. BaseMenuScreen.__init__(self, session, _("Embedded subtitles settings"))
  1904. self.embeddedSettings = embeddedSettings
  1905. def buildMenu(self):
  1906. self["config"].setList(self.getConfigList((self.embeddedSettings)))
  1907. def keySave(self):
  1908. reloadEmbeddedScreen = (self.embeddedSettings.position.isChanged() or
  1909. getEmbeddedFontSizeCfg(self.embeddedSettings.font.size).isChanged())
  1910. for x in self["config"].list:
  1911. x[1].save()
  1912. configfile.save()
  1913. self.close(reloadEmbeddedScreen)
  1914. class SubsSetupGeneral(BaseMenuScreen):
  1915. def __init__(self, session, generalSettings):
  1916. BaseMenuScreen.__init__(self, session, _("General settings"))
  1917. self.generalSettings = generalSettings
  1918. def buildMenu(self):
  1919. self["config"].setList([
  1920. getConfigListEntry(_("Pause video on opening subtitles menu"), self.generalSettings.pauseVideoOnSubtitlesMenu),
  1921. getConfigListEntry(_("Encoding"), self.generalSettings.encodingsGroup),
  1922. ])
  1923. def FileEntryComponent(name, absolute=None, isDir=False):
  1924. res = [ (absolute, isDir) ]
  1925. res.append((eListboxPythonMultiContent.TYPE_TEXT, 35, 1, 470, 20, 0, RT_HALIGN_LEFT, toString(name)))
  1926. if isDir:
  1927. png = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "extensions/directory.png"))
  1928. else:
  1929. png = LoadPixmap(os.path.join(os.path.dirname(__file__), 'img', 'subtitles.png'))
  1930. if png is not None:
  1931. res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 10, 2, 20, 20, png))
  1932. return res
  1933. class SubFileList(FileList):
  1934. def __init__(self, defaultDir):
  1935. extensions = []
  1936. for parser in PARSERS:
  1937. extensions += list(parser.parsing)
  1938. FileList.__init__(self, defaultDir, matchingPattern="(?i)^.*\." + '(' + '|'.join(ext[1:] for ext in extensions) + ')', useServiceRef=False)
  1939. def changeDir(self, directory, select = None):
  1940. self.list = []
  1941. # if we are just entering from the list of mount points:
  1942. if self.current_directory is None:
  1943. if directory and self.showMountpoints:
  1944. self.current_mountpoint = self.getMountpointLink(directory)
  1945. else:
  1946. self.current_mountpoint = None
  1947. self.current_directory = directory
  1948. directories = []
  1949. files = []
  1950. if directory is None and self.showMountpoints: # present available mountpoints
  1951. for p in harddiskmanager.getMountedPartitions():
  1952. path = os.path.join(p.mountpoint, "")
  1953. if path not in self.inhibitMounts and not self.inParentDirs(path, self.inhibitDirs):
  1954. self.list.append(FileEntryComponent(name = p.description, absolute = path, isDir = True))
  1955. files = [ ]
  1956. directories = [ ]
  1957. elif directory is None:
  1958. files = [ ]
  1959. directories = [ ]
  1960. elif self.useServiceRef:
  1961. # we should not use the 'eServiceReference(string)' constructor, because it doesn't allow ':' in the directoryname
  1962. root = eServiceReference(2, 0, directory)
  1963. if self.additional_extensions:
  1964. root.setName(self.additional_extensions)
  1965. serviceHandler = eServiceCenter.getInstance()
  1966. list = serviceHandler.list(root)
  1967. while 1:
  1968. s = list.getNext()
  1969. if not s.valid():
  1970. del list
  1971. break
  1972. if s.flags & s.mustDescent:
  1973. directories.append(s.getPath())
  1974. else:
  1975. files.append(s)
  1976. directories.sort()
  1977. files.sort()
  1978. else:
  1979. if fileExists(directory):
  1980. try:
  1981. files = os.listdir(directory)
  1982. except:
  1983. files = []
  1984. files.sort()
  1985. tmpfiles = files[:]
  1986. for x in tmpfiles:
  1987. if os.path.isdir(directory + x):
  1988. directories.append(directory + x + "/")
  1989. files.remove(x)
  1990. if self.showDirectories:
  1991. if directory:
  1992. if self.showMountpoints and directory == self.current_mountpoint:
  1993. self.list.append(FileEntryComponent(name = "<" +_("List of storage devices") + ">", absolute = None, isDir = True))
  1994. elif (directory != self.topDirectory) and not (self.inhibitMounts and self.getMountpoint(directory) in self.inhibitMounts):
  1995. self.list.append(FileEntryComponent(name = "<" +_("Parent directory") + ">", absolute = '/'.join(directory.split('/')[:-2]) + '/', isDir = True))
  1996. for x in directories:
  1997. if not (self.inhibitMounts and self.getMountpoint(x) in self.inhibitMounts) and not self.inParentDirs(x, self.inhibitDirs):
  1998. name = x.split('/')[-2]
  1999. self.list.append(FileEntryComponent(name = name, absolute = x, isDir = True))
  2000. if self.showFiles:
  2001. for x in files:
  2002. if self.useServiceRef:
  2003. path = x.getPath()
  2004. name = path.split('/')[-1]
  2005. else:
  2006. path = directory + x
  2007. name = x
  2008. if (self.matchingPattern is None) or self.matchingPattern.search(path):
  2009. self.list.append(FileEntryComponent(name = name, absolute = x , isDir = False))
  2010. if self.showMountpoints and len(self.list) == 0:
  2011. self.list.append(FileEntryComponent(name = _("nothing connected"), absolute = None, isDir = False))
  2012. self.l.setList(self.list)
  2013. if select is not None:
  2014. i = 0
  2015. self.moveToIndex(0)
  2016. for x in self.list:
  2017. p = x[0][0]
  2018. if isinstance(p, eServiceReference):
  2019. p = p.getPath()
  2020. if p == select:
  2021. self.moveToIndex(i)
  2022. i += 1
  2023. class SubsChooserMenuList(MenuList):
  2024. def __init__(self, embeddedAvailable=False, searchSupport=False, historySupport=False):
  2025. MenuList.__init__(self, [], False, eListboxPythonMultiContent)
  2026. self.l.setItemHeight(30)
  2027. self.l.setFont(0, gFont("Regular", 20))
  2028. menulist = []
  2029. if embeddedAvailable:
  2030. res = [('embedded')]
  2031. res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_red.png'))))
  2032. res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from embedded subtitles")))
  2033. menulist.append(res)
  2034. if historySupport:
  2035. res = [('downloaded')]
  2036. res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_yellow.png'))))
  2037. res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from downloaded subtitles")))
  2038. menulist.append(res)
  2039. if searchSupport:
  2040. res = [('search')]
  2041. res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_blue.png'))))
  2042. res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from web subtitles")))
  2043. menulist.append(res)
  2044. if embeddedAvailable or historySupport or searchSupport:
  2045. self.l.setList(menulist)
  2046. class E2SubsSeeker(SubsSeeker):
  2047. def __init__(self, session, searchSettings, debug=False):
  2048. self.session = session
  2049. self.download_thread = None
  2050. self.search_settings = searchSettings
  2051. download_path = searchSettings.downloadPath.value
  2052. tmp_path = searchSettings.tmpPath.value
  2053. class SubsSearchSettingsProvider(E2SettingsProvider):
  2054. def __init__(self, providerName, defaults, configSubSection):
  2055. E2SettingsProvider.__init__(self, providerName, configSubSection, defaults)
  2056. SubsSeeker.__init__(self, download_path, tmp_path,
  2057. captcha_cb=self.captcha_cb,
  2058. delay_cb=self.delay_cb,
  2059. message_cb=messageCB,
  2060. settings_provider_cls=SubsSearchSettingsProvider,
  2061. settings_provider_args=searchSettings,
  2062. debug=debug)
  2063. self.providers_error = False
  2064. for p in self.seekers:
  2065. if p.error is not None:
  2066. self.providers_error = True
  2067. def downloadSubtitle(self, success_cb, error_cb, cancel_cb, selected_subtitle, subtitles_dict, settings, path=None, fname=None):
  2068. download_fnc = super(E2SubsSeeker, self).downloadSubtitle
  2069. if settings.get('ask_overwrite', True):
  2070. overwrite_cb = self.overwrite_cb
  2071. else:
  2072. overwrite_cb = None
  2073. params = (selected_subtitle, subtitles_dict, self.choice_cb, path, fname, overwrite_cb, settings)
  2074. download_thread = SubsDownloadThread(self.session, download_fnc, params, success_cb, error_cb, cancel_cb)
  2075. self.download_thread = download_thread
  2076. self.download_thread.start()
  2077. def _unpack_rarsub(self, rar_path, dest_dir):
  2078. assert self.download_thread is not None
  2079. assert self.download_thread.is_alive()
  2080. files = self.download_thread.getUnrar(rar_path, dest_dir)
  2081. return filter(lambda x:os.path.splitext(x)[1] in ('.srt', '.sub', 'txt'), files)
  2082. def overwrite_cb(self, subfile):
  2083. assert self.download_thread is not None
  2084. assert self.download_thread.is_alive()
  2085. return self.download_thread.getOverwrite(subfile)
  2086. def choice_cb(self, subfiles):
  2087. assert self.download_thread is not None
  2088. assert self.download_thread.is_alive()
  2089. return self.download_thread.getChoice(subfiles)
  2090. def captcha_cb(self, image_path):
  2091. assert self.download_thread is not None
  2092. assert self.download_thread.is_alive()
  2093. return self.download_thread.getCaptcha(image_path)
  2094. def delay_cb(self, seconds):
  2095. assert self.download_thread is not None
  2096. assert self.download_thread.is_alive()
  2097. message = _("Subtitles will be downloaded in") + " " + str(seconds) + " " + _("seconds")
  2098. self.download_thread.getDelay(seconds, message)
  2099. class SubsChooser(Screen):
  2100. skin = """
  2101. <screen position="center,center" size="610,460" zPosition="3" >
  2102. <!-- <widget source="filename" render="Label" position="10, 10" size="590,50" valign="center" halign="center" font="Regular;21" /> -->
  2103. <!-- <eLabel position="5,65" size="600,1" backgroundColor="#999999" /> -->
  2104. <widget name="file_list" position="0,30" size="610,330" scrollbarMode="showOnDemand" />
  2105. <eLabel position="5,370" size="600,1" backgroundColor="#999999" />
  2106. <widget name="menu_list" position="0,380" size="610,80" scrollbarMode="showOnDemand" />
  2107. </screen>
  2108. """
  2109. def __init__(self, session, subsSettings, subdir=None, embeddedSupport=False, searchSupport=False, historySupport=False, titleList=None):
  2110. Screen.__init__(self, session)
  2111. self.session = session
  2112. self.subsSettings = subsSettings
  2113. defaultDir = subdir
  2114. if subdir is not None and not subdir.endswith('/'):
  2115. defaultDir = subdir + '/'
  2116. self.embeddedList = None
  2117. self.embeddedSubtitle = None
  2118. if embeddedSupport:
  2119. service = self.session.nav.getCurrentService()
  2120. subtitle = service and service.subtitle()
  2121. self.embeddedList = subtitle and subtitle.getSubtitleList()
  2122. self.searchSupport = searchSupport
  2123. self.historySupport = historySupport
  2124. self.titleList = titleList
  2125. ref = self.session.nav.getCurrentlyPlayingServiceReference()
  2126. videoPath = ref and ref.getPath()
  2127. if videoPath and os.path.isfile(videoPath):
  2128. self.videoPath = videoPath
  2129. else:
  2130. self.videoPath = None
  2131. videoName = ref and os.path.split(ref.getPath())[1]
  2132. self["filename"] = StaticText(videoName)
  2133. self["file_list"] = SubFileList(defaultDir)
  2134. self["menu_list"] = SubsChooserMenuList(self.embeddedList, searchSupport, historySupport)
  2135. self["actions"] = NumberActionMap(["OkCancelActions", "ColorActions"],
  2136. {
  2137. "ok": self.ok,
  2138. "cancel": self.close,
  2139. "red": self.embeddedSubsSelection,
  2140. "yellow":self.downloadedSubsSelection,
  2141. "blue": self.webSubsSelection,
  2142. }, -2)
  2143. self.onLayoutFinish.append(self.updateTitle)
  2144. self.onLayoutFinish.append(self.disableMenuList)
  2145. def updateTitle(self):
  2146. self.setTitle(_("Choose Subtitles"))
  2147. def disableMenuList(self):
  2148. self["menu_list"].selectionEnabled(False)
  2149. def ok(self):
  2150. if self['file_list'].canDescent():
  2151. self['file_list'].descent()
  2152. else:
  2153. filePath = os.path.join(self['file_list'].current_directory, self['file_list'].getFilename())
  2154. print '[SubsFileChooser]' , filePath
  2155. self.close(filePath, False)
  2156. def checkEmbeddedSubsSelection(self, embeddedSubtitle=None):
  2157. if embeddedSubtitle:
  2158. self.close(None, embeddedSubtitle)
  2159. def embeddedSubsSelection(self):
  2160. if self.embeddedList:
  2161. self.session.openWithCallback(self.checkEmbeddedSubsSelection, SubsEmbeddedSelection)
  2162. def webSubsSelection(self):
  2163. def checkDownloadedSubsSelection(downloadedSubtitle=None):
  2164. if downloadedSubtitle:
  2165. self.close(downloadedSubtitle, False, True)
  2166. def paramsDialogCB(callback=None):
  2167. if callback:
  2168. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList, resetSearchParams=False)
  2169. def showProvidersErrorCB(callback):
  2170. if not callback:
  2171. subsSettings.search.showProvidersErrorMessage.value = False
  2172. if subsSettings.search.openParamsDialogOnSearch.value:
  2173. self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, self.titleList, enabledList=False)
  2174. else:
  2175. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList)
  2176. subsSettings = self.subsSettings
  2177. if not self.searchSupport:
  2178. return
  2179. seeker = E2SubsSeeker(self.session, subsSettings.search, debug=True)
  2180. if seeker.providers_error and subsSettings.search.showProvidersErrorMessage.value:
  2181. msg = _("Some subtitles providers are not working") + ".\n"
  2182. msg += _("For more details please check search settings") + "."
  2183. msg += "\n\n"
  2184. msg += _("Do you want to show this message again?")
  2185. self.session.openWithCallback(showProvidersErrorCB, MessageBox, msg, type=MessageBox.TYPE_YESNO)
  2186. elif subsSettings.search.openParamsDialogOnSearch.value:
  2187. self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, self.titleList, enabledList=False)
  2188. else:
  2189. self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList)
  2190. def downloadedSubsSelectionCB(self, subtitles, downloadedSubtitle=None):
  2191. fpath = os.path.join(self.subsSettings.search.downloadHistory.path.value,'hsubtitles.json')
  2192. try:
  2193. json.dump(subtitles, open(fpath,"w"))
  2194. except Exception as e:
  2195. print '[SubsFileChooser] downloadedSubsSelectionCB - %s'% str(e)
  2196. if downloadedSubtitle:
  2197. self.close(downloadedSubtitle, False, True)
  2198. def downloadedSubsSelection(self):
  2199. if not self.historySupport:
  2200. return
  2201. fpath = os.path.join(self.subsSettings.search.downloadHistory.path.value,'hsubtitles.json')
  2202. try:
  2203. subtitles = json.load(open(fpath, "r"))
  2204. except Exception as e:
  2205. print '[SubsFileChooser] downloadedSubsSelection - %s'% str(e)
  2206. subtitles = []
  2207. self.session.openWithCallback(self.downloadedSubsSelectionCB, SubsDownloadedSelection, subtitles, self.subsSettings.search.downloadHistory)
  2208. class SubsDownloadedSelection(Screen):
  2209. class InfoScreen(Screen):
  2210. skin = """
  2211. <screen position = "center,center" size="650,200" zPosition="4" flags="wfNoBorder" backgroundColor="#333333">
  2212. <widget source="path" render="Label" position="5,5" size="640,190" valign="center" halign="center" font="Regular;20"/>
  2213. </screen>
  2214. """
  2215. def __init__(self, session, subtitle):
  2216. Screen.__init__(self, session)
  2217. self["path"] = StaticText(_(toString(subtitle['fpath'])))
  2218. skin = """
  2219. <screen position="center,center" size="700,520" zPosition="3">
  2220. <widget source="header_name" render="Label" position = "5,10" size="360,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  2221. <widget source="header_provider" render="Label" position = "380,10" size="165,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  2222. <widget source="header_date" render="Label" position = "525, 10" size="170,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  2223. <eLabel position="5,45" size="690,1" backgroundColor="#999999" />
  2224. <widget source="subtitles" render="Listbox" scrollbarMode="showOnDemand" position="5,55" size="690,355" zPosition="3" transparent="1" >
  2225. <convert type="TemplatedMultiContent">
  2226. {"templates":
  2227. {"default": (50, [
  2228. MultiContentEntryPixmapAlphaBlend(pos = (0, 15), size = (24, 24), png=0), # key,
  2229. MultiContentEntryText(pos = (30, 0), size = (325, 50), font = 0, flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text=1, color=0xFF000004), # name,
  2230. MultiContentEntryText(pos = (375, 0), size = (165, 50), font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 2, color=0xFF000004), # provider,
  2231. MultiContentEntryText(pos = (520, 0), size = (170, 50), font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3, color=0xFF000004), # date,
  2232. ], True, "showOnDemand"),
  2233. },
  2234. "fonts": [gFont("Regular", 19), gFont("Regular", 16)],
  2235. "itemHeight": 50
  2236. }
  2237. </convert>
  2238. </widget>
  2239. <eLabel position="5,430" size="690,1" backgroundColor="#999999" />
  2240. <widget source="entries_sum" render="Label" position = "10, 440" size="300,25" font="Regular;18" halign="left" foregroundColor="white" />
  2241. <eLabel position="5,470" size="690,1" backgroundColor="#999999" />
  2242. <ePixmap pixmap="skin_default/buttons/key_info.png" position="10,485" size="35,25" transparent="1" alphatest="on" />
  2243. <ePixmap pixmap="skin_default/buttons/key_red.png" position="50,485" size="35,25" transparent="1" alphatest="on" />
  2244. <widget source="key_red" render="Label" position = "90, 485" size="230,25" font="Regular;20" halign="left" foregroundColor="white" />
  2245. <ePixmap pixmap="skin_default/buttons/key_blue.png" position="500,485" size="35,25" transparent="1" alphatest="on" />
  2246. <widget source="key_blue" render="Label" position = "540, 485" size="130,25" font="Regular;20" halign="left" foregroundColor="white" />
  2247. </screen> """
  2248. def __init__(self, session, subtitles, historySettings, marked=None):
  2249. Screen.__init__(self, session)
  2250. self["header_name"] = StaticText(_("Name"))
  2251. self["header_provider"] = StaticText(_("Provider"))
  2252. self["header_date"] = StaticText(_("Download date"))
  2253. self["subtitles"] = List()
  2254. self["entries_sum"] = StaticText()
  2255. self["key_red"] = StaticText(_("Remove (file)"))
  2256. self["key_blue"] = StaticText(_("Settings"))
  2257. self["actions"] = ActionMap(["ColorActions", "OkCancelActions", "InfoActions"],
  2258. {
  2259. "ok": self.ok,
  2260. "cancel": self.cancel,
  2261. "info":self.showInfo,
  2262. "red":self.removeEntry,
  2263. "blue":self.openSettings,
  2264. }, -2)
  2265. self["infoActions"] = ActionMap(["ColorActions", "OkCancelActions", "DirectionActions", "InfoActions"],
  2266. {
  2267. "ok": self.closeInfoDialog,
  2268. "cancel": self.closeInfoDialog,
  2269. "info": self.closeInfoDialog,
  2270. "red": self.closeInfoDialog,
  2271. "green":self.closeInfoDialog,
  2272. "blue": self.closeInfoDialog,
  2273. "up": self.closeInfoDialog,
  2274. "upUp":self.closeInfoDialog,
  2275. "down":self.closeInfoDialog,
  2276. "downUp": self.closeInfoDialog,
  2277. "right":self.closeInfoDialog,
  2278. "rightUp":self.closeInfoDialog,
  2279. "left":self.closeInfoDialog,
  2280. "leftUp":self.closeInfoDialog,
  2281. } )
  2282. self["infoActions"].setEnabled(False)
  2283. self.subtitles = subtitles
  2284. self.historySettings = historySettings
  2285. self.marked = marked or []
  2286. self.onLayoutFinish.append(self.updateWindowTitle)
  2287. self.onLayoutFinish.append(self.updateSubsList)
  2288. self.onLayoutFinish.append(self.updateEntriesSum)
  2289. self.onLayoutFinish.append(self.updateRemoveAction)
  2290. def updateWindowTitle(self):
  2291. self.setTitle(_("Downloaded Subtitles"))
  2292. def updateSubsList(self):
  2293. imgDict = {'unk':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'countries', 'UNK.png'))}
  2294. subtitleListGUI = []
  2295. for sub in self.subtitles[:]:
  2296. fpath = toString(sub['fpath'])
  2297. if not os.path.isfile(fpath):
  2298. self.subtitles.remove(sub)
  2299. continue
  2300. if sub.get('country','unk') not in imgDict:
  2301. countryImgPath = os.path.join(os.path.dirname(__file__), 'img', 'countries', sub['country'] + '.png')
  2302. if os.path.isfile(countryImgPath):
  2303. imgDict[sub['country']] = loadPNG(toString(countryImgPath))
  2304. countryPng = imgDict[sub['country']]
  2305. else:
  2306. countryPng = imgDict['unk']
  2307. if sub in self.marked:
  2308. color = 0x00ff00
  2309. else:
  2310. color = 0xffffff
  2311. date = datetime.fromtimestamp(os.path.getctime(fpath)).strftime("%d-%m-%Y %H:%M")
  2312. name = os.path.splitext(os.path.basename(fpath))[0]
  2313. subtitleListGUI.append((countryPng,toString(name), toString(sub['provider']), date, color),)
  2314. imgDict = None
  2315. self['subtitles'].list = subtitleListGUI
  2316. def updateEntriesSum(self):
  2317. limit = int(self.historySettings.limit.value)
  2318. self["entries_sum"].text = _("Entries count:") + " " + str(len(self.subtitles)) + " / " + str(limit)
  2319. def updateRemoveAction(self):
  2320. if self.historySettings.removeAction.value == 'file':
  2321. self["key_red"].text = _("Remove Entry (List+File)")
  2322. else:
  2323. self["key_red"].text = _("Remove Entry (List)")
  2324. def removeEntry(self):
  2325. def removeEntryCB(doRemove=False):
  2326. if doRemove:
  2327. if self.historySettings.removeAction.value == 'file':
  2328. try:
  2329. os.unlink(subtitle['fpath'])
  2330. except OSError as e:
  2331. print "[SubsDownloadedSelection] cannot remove - %s"%(str(e))
  2332. self.session.open(MessageBox, _("There was an error while removing subtitle, please check log"), type=MessageBox.TYPE_ERROR)
  2333. else:
  2334. self.subtitles.remove(subtitle)
  2335. curridx = self['subtitles'].index
  2336. self.updateSubsList()
  2337. self['subtitles'].index = curridx -1
  2338. self.updateEntriesSum()
  2339. else:
  2340. self.subtitles.remove(subtitle)
  2341. curridx = self['subtitles'].index
  2342. self.updateSubsList()
  2343. self['subtitles'].index = curridx -1
  2344. self.updateEntriesSum()
  2345. if self["subtitles"].count() > 0:
  2346. subtitle = self.subtitles[self["subtitles"].index]
  2347. if self.historySettings.removeAction.value == 'file':
  2348. if self.historySettings.removeActionAsk.value:
  2349. message = _("Subtitle") + " '" + toString(subtitle['name'])+ "' " + _("will be removed from file system")
  2350. message += "\n\n" + _("Do you want to proceed?")
  2351. self.session.openWithCallback(removeEntryCB, MessageBox, message, type=MessageBox.TYPE_YESNO)
  2352. else:
  2353. removeEntryCB(True)
  2354. else:
  2355. if self.historySettings.removeActionAsk.value:
  2356. message = _("Subtitle") + " '" + toString(subtitle['name'])+ "' " + _("will be removed from list")
  2357. message += "\n\n" + _("Do you want to proceed?")
  2358. self.session.openWithCallback(removeEntryCB, MessageBox, message, type=MessageBox.TYPE_YESNO)
  2359. else:
  2360. removeEntryCB(True)
  2361. def openSettings(self):
  2362. def menuCB(callback=None):
  2363. self.updateEntriesSum()
  2364. self.updateRemoveAction()
  2365. self.session.openWithCallback(menuCB, SubsDownloadedSubtitlesMenu, self.historySettings)
  2366. def showInfo(self):
  2367. if self["subtitles"].count() > 0:
  2368. subtitle = self.subtitles[self["subtitles"].index]
  2369. self["actions"].setEnabled(False)
  2370. self["infoActions"].setEnabled(True)
  2371. self.__infoScreen = self.session.instantiateDialog(self.InfoScreen, subtitle)
  2372. self.__infoScreen.show()
  2373. def closeInfoDialog(self):
  2374. self.session.deleteDialog(self.__infoScreen)
  2375. self["infoActions"].setEnabled(False)
  2376. self["actions"].setEnabled(True)
  2377. def ok(self):
  2378. if self["subtitles"].count() > 0:
  2379. subtitle = self.subtitles[self["subtitles"].index]
  2380. self.close(self.subtitles, subtitle['fpath'])
  2381. self.close(self.subtitles, None)
  2382. def cancel(self):
  2383. self.close(self.subtitles, None)
  2384. class SubsDownloadedSubtitlesMenu(Screen, ConfigListScreen):
  2385. skin = """
  2386. <screen position="center,center" size="610,435" >
  2387. <widget name="key_red" position="10,5" zPosition="1" size="140,45" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" shadowOffset="-2,-2" shadowColor="black" />
  2388. <widget name="key_green" position="160,5" zPosition="1" size="140,45" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" shadowOffset="-2,-2" shadowColor="black" />
  2389. <widget name="key_yellow" position="310,5" zPosition="1" size="140,45" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" shadowOffset="-2,-2" shadowColor="black" />
  2390. <widget name="key_blue" position="460,5" zPosition="1" size="140,45" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" shadowOffset="-2,-2" shadowColor="black" />
  2391. <eLabel position="-1,55" size="612,1" backgroundColor="#999999" />
  2392. <widget name="config" position="0,75" size="610,360" scrollbarMode="showOnDemand" />
  2393. </screen>"""
  2394. def __init__(self, session, historySettings):
  2395. Screen.__init__(self, session)
  2396. ConfigListScreen.__init__(self, [], session=session)
  2397. self["actions"] = ActionMap(["SetupActions", "ColorActions"],
  2398. {
  2399. "cancel": self.keyCancel,
  2400. "green": self.keySave,
  2401. "red": self.keyCancel,
  2402. "blue": self.resetDefaults,
  2403. }, -2)
  2404. self["key_green"] = Label(_("Save"))
  2405. self["key_red"] = Label(_("Cancel"))
  2406. self["key_blue"] = Label(_("Reset Defaults"))
  2407. self["key_yellow"] = Label("")
  2408. self.historySettings = historySettings
  2409. self.buildMenu()
  2410. self.onLayoutFinish.append(self.updateTitle)
  2411. def updateTitle(self):
  2412. self.setTitle(_("Downloaded Subtitles - Settings"))
  2413. def buildMenu(self):
  2414. menuList = []
  2415. menuList.append(getConfigListEntry(_("Max history entries"), self.historySettings.limit))
  2416. menuList.append(getConfigListEntry(_("Remove action") , self.historySettings.removeAction))
  2417. menuList.append(getConfigListEntry(_("Ask on remove action"), self.historySettings.removeActionAsk))
  2418. self["config"].setList(menuList)
  2419. def resetDefaults(self):
  2420. for x in self["config"].list:
  2421. x[1].value = x[1].default
  2422. self.buildMenu()
  2423. def keyOK(self):
  2424. if self["config"].getCurrent()[1] == self.historySettings.path:
  2425. self.session.openWithCallback(self.changeDir, LocationBox,
  2426. _("Select Directory"), currDir=self.historySettings.path.value)
  2427. def keySave(self):
  2428. for x in self["config"].list:
  2429. x[1].save()
  2430. configfile.save()
  2431. self.close(True)
  2432. def keyCancel(self):
  2433. for x in self["config"].list:
  2434. x[1].cancel()
  2435. self.close()
  2436. def keyLeft(self):
  2437. ConfigListScreen.keyLeft(self)
  2438. def keyRight(self):
  2439. ConfigListScreen.keyRight(self)
  2440. # source from openpli
  2441. class SubsEmbeddedSelection(Screen):
  2442. skin = """<screen name="SubsEmbeddedSelection" position="center,center" size="485,220">
  2443. <widget source="streams" render="Listbox" scrollbarMode="showOnDemand" position="10,40" size="465,180" zPosition="3" transparent="1" >
  2444. <convert type="TemplatedMultiContent">
  2445. {"templates":
  2446. {"default": (25, [
  2447. MultiContentEntryText(pos = (0, 0), size = (35, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # key,
  2448. MultiContentEntryText(pos = (40, 0), size = (60, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # number,
  2449. MultiContentEntryText(pos = (110, 0), size = (120, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # description,
  2450. MultiContentEntryText(pos = (240, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 4), # language,
  2451. ], True, "showNever"),
  2452. },
  2453. "fonts": [gFont("Regular", 20), gFont("Regular", 16)],
  2454. "itemHeight": 25
  2455. }
  2456. </convert>
  2457. </widget>
  2458. </screen>"""
  2459. def __init__(self, session):
  2460. Screen.__init__(self, session)
  2461. self["streams"] = List([], enableWrapAround=True)
  2462. self["actions"] = ActionMap(["SetupActions", "DirectionActions", "MenuActions"],
  2463. {
  2464. "ok": self.keyOk,
  2465. "cancel": self.cancel,
  2466. }, -2)
  2467. self.onLayoutFinish.append(self.updateTitle)
  2468. self.onLayoutFinish.append(self.fillList)
  2469. def updateTitle(self):
  2470. self.setTitle(_("Choose subtitles"))
  2471. def fillList(self):
  2472. idx = 0
  2473. streams = []
  2474. subtitlelist = self.getSubtitleList()
  2475. for x in subtitlelist:
  2476. number = str(x[1])
  2477. description = "?"
  2478. language = ""
  2479. try:
  2480. if x[4] != "und":
  2481. if LanguageCodes.has_key(x[4]):
  2482. language = LanguageCodes[x[4]][0]
  2483. else:
  2484. language = x[4]
  2485. except:
  2486. language = ""
  2487. if x[0] == 0:
  2488. description = "DVB"
  2489. number = "%x" % (x[1])
  2490. elif x[0] == 1:
  2491. description = "teletext"
  2492. number = "%x%02x" % (x[3] and x[3] or 8, x[2])
  2493. elif x[0] == 2:
  2494. types = ("unknown", "embedded", "SSA file", "ASS file",
  2495. "SRT file", "VOB file", "PGS file")
  2496. try:
  2497. description = types[x[2]]
  2498. except:
  2499. description = _("unknown") + ": %s" % x[2]
  2500. number = str(int(number) + 1)
  2501. print x, number, description, language
  2502. streams.append((x, "", number, description, language))
  2503. idx += 1
  2504. self["streams"].list = streams
  2505. def getSubtitleList(self):
  2506. service = self.session.nav.getCurrentService()
  2507. subtitle = service and service.subtitle()
  2508. subtitlelist = subtitle and subtitle.getSubtitleList()
  2509. embeddedlist = []
  2510. for x in subtitlelist:
  2511. if x[0] == 2:
  2512. types = ("unknown", "embedded", "SSA file", "ASS file",
  2513. "SRT file", "VOB file", "PGS file")
  2514. # filter embedded subtitles
  2515. if x[2] not in [1, 2, 3, 4, 5, 6]:
  2516. continue
  2517. embeddedlist.append(x)
  2518. return embeddedlist
  2519. self.selectedSubtitle = None
  2520. return subtitlelist
  2521. def cancel(self):
  2522. self.close()
  2523. def keyOk(self):
  2524. cur = self["streams"].getCurrent()
  2525. self.close(cur[0][:4])
  2526. class SimpleObserverList(list):
  2527. def __init__(self, *args):
  2528. list.__init__(self, *args)
  2529. self.observer_fncs = list()
  2530. def add_observer(self, observer_fnc):
  2531. self.observer_fncs.append(observer_fnc)
  2532. def remove_observer(self, observer_fnc):
  2533. self.observer_fncs.remove(observer_fnc)
  2534. def append (self, value):
  2535. list.append(self, value)
  2536. for f in self.observer_fncs:
  2537. f()
  2538. def remove(self, value):
  2539. list.remove(self, value)
  2540. for f in self.observer_fncs:
  2541. f()
  2542. class SubsDownloadThread(Thread):
  2543. THREADS = SimpleObserverList()
  2544. CAPTCHA_REQUEST = 0
  2545. DELAY_REQUEST = 1
  2546. FINISH_REQUEST_SUCCESS = 2
  2547. FINISH_REQUEST_ERROR = 3
  2548. OVERWRITE_REQUEST = 4
  2549. CHOICE_REQUEST = 5
  2550. UNRAR_REQUEST = 6
  2551. def __init__(self, session, fnc, params, callback, errorback, cancelback):
  2552. Thread.__init__(self)
  2553. self.session = session
  2554. self.fnc = fnc
  2555. self.params = params
  2556. self.callback = callback
  2557. self.errorback = errorback
  2558. self.cancelback = cancelback
  2559. self.cancelled = False
  2560. self.messageIn = Queue()
  2561. self.messageOut = Queue()
  2562. self.messagePump = ePythonMessagePump()
  2563. self.messagePump.recv_msg.get().append(self._runInMainThread)
  2564. def start(self):
  2565. SubsDownloadThread.THREADS.append(self)
  2566. Thread.start(self)
  2567. def run(self):
  2568. try:
  2569. ret = self.fnc(*self.params)
  2570. self.messageOut.put((self.FINISH_REQUEST_SUCCESS, ret))
  2571. self.messagePump.send(0)
  2572. except Exception:
  2573. exc_value, exc_traceback = sys.exc_info()[1:]
  2574. exc_value.tb = exc_traceback
  2575. self.messageOut.put((self.FINISH_REQUEST_ERROR, exc_value))
  2576. self.messagePump.send(0)
  2577. def _runInMainThread(self, val):
  2578. ret = self.messageOut.get()
  2579. request = ret[0]
  2580. if request == self.CAPTCHA_REQUEST:
  2581. imagePath = ret[1]
  2582. Captcha(self.session, self.getCaptchaCB, imagePath)
  2583. elif request == self.DELAY_REQUEST:
  2584. seconds, message = ret[1], ret[2]
  2585. self.session.openWithCallback(self.getDelayCB, DelayMessageBox, seconds, message)
  2586. elif request == self.FINISH_REQUEST_SUCCESS:
  2587. subFile = ret[1]
  2588. if self.cancelled:
  2589. self.cancelback()
  2590. else:
  2591. self.callback(subFile)
  2592. SubsDownloadThread.THREADS.remove(self)
  2593. elif request == self.FINISH_REQUEST_ERROR:
  2594. error = ret[1]
  2595. self.errorback(error)
  2596. SubsDownloadThread.THREADS.remove(self)
  2597. elif request == self.CHOICE_REQUEST:
  2598. subFiles = ret[1]
  2599. choiceTitle = _("There are more subtitles in unpacked archive\n please select which one do you want to use")
  2600. choiceList = [(os.path.basename(subfile), subfile) for subfile in subFiles]
  2601. self.session.openWithCallback(self.getChoiceCB, ChoiceBox, choiceTitle, choiceList)
  2602. elif request == self.OVERWRITE_REQUEST:
  2603. overwriteText = _("Subtitles with this name already exist\nDo you want to overwrite them") + "?"
  2604. self.session.openWithCallback(self.getOverwriteCB, MessageBox, overwriteText, MessageBox.TYPE_YESNO)
  2605. elif request == self.UNRAR_REQUEST:
  2606. rarPath = ret[1]
  2607. destDir = ret[2]
  2608. unrar(rarPath, destDir, self.getUnrarCB, self.getUnrarCB)
  2609. def getUnrar(self, subFile, destPath):
  2610. self.messageOut.put((self.UNRAR_REQUEST, subFile, destPath))
  2611. self.messagePump.send(0)
  2612. ret = self.messageIn.get()
  2613. if isinstance(ret, str):
  2614. raise Exception(ret)
  2615. return ret
  2616. def getUnrarCB(self, callback):
  2617. self.messageIn.put(callback)
  2618. def getOverwrite(self, subFile):
  2619. self.messageOut.put((self.OVERWRITE_REQUEST, subFile))
  2620. self.messagePump.send(0)
  2621. return self.messageIn.get()
  2622. def getOverwriteCB(self, callback):
  2623. self.messageIn.put(callback)
  2624. def getChoice(self, files):
  2625. self.messageOut.put((self.CHOICE_REQUEST, files))
  2626. self.messagePump.send(0)
  2627. return self.messageIn.get()
  2628. def getChoiceCB(self, selfile):
  2629. if selfile:
  2630. selfile = selfile[1]
  2631. if selfile is None:
  2632. self.cancelled = True
  2633. return self.messageIn.put(selfile)
  2634. def getCaptcha(self, imagePath):
  2635. self.messageOut.put((self.CAPTCHA_REQUEST, imagePath))
  2636. self.messagePump.send(0)
  2637. return self.messageIn.get()
  2638. def getCaptchaCB(self, word):
  2639. word = word or ""
  2640. self.messageIn.put(word)
  2641. def getDelay(self, seconds, message):
  2642. self.messageOut.put((self.DELAY_REQUEST, seconds, message))
  2643. self.messagePump.send(0)
  2644. return self.messageIn.get()
  2645. def getDelayCB(self, callback=None):
  2646. return self.messageIn.put(None)
  2647. class SubsSearchProcess(object):
  2648. processes = []
  2649. process_path = os.path.join(os.path.dirname(__file__), 'searchsubs.py')
  2650. def __init__(self):
  2651. self.log = SimpleLogger('SubsSearchProcess', SimpleLogger.LOG_INFO)
  2652. self.toRead = None
  2653. self.pPayload = None
  2654. self.data = ""
  2655. self.__stopping = False
  2656. self.appContainer = eConsoleAppContainer()
  2657. self.appContainer.stdoutAvail.append(self.dataOutCB)
  2658. self.appContainer.stderrAvail.append(self.dataErrCB)
  2659. self.appContainer.appClosed.append(self.finishedCB)
  2660. def recieveMessages(self, data):
  2661. def getMessage(data):
  2662. mSize = int(data[:7])
  2663. mPayload = data[7:mSize]
  2664. mPart = mSize > len(data)
  2665. return mSize, mPayload, mPart
  2666. def readMessage(payload):
  2667. try:
  2668. message = json.loads(payload)
  2669. except EOFError:
  2670. pass
  2671. except Exception:
  2672. self.log.debug('data is not in JSON format! - %s' % str(payload))
  2673. else:
  2674. self.log.debug('message successfully recieved')
  2675. self.toRead = None
  2676. self.pPayload = None
  2677. self.handleMessage(message)
  2678. def readStart(data):
  2679. mSize, mPayload, mPart = getMessage(data)
  2680. if not mPart:
  2681. data = data[mSize:]
  2682. readMessage(mPayload)
  2683. if len(data) > 0:
  2684. readStart(data)
  2685. else:
  2686. self.toRead = mSize - len(data)
  2687. self.pPayload = mPayload
  2688. def readContinue(data):
  2689. nextdata = data[:self.toRead]
  2690. self.pPayload += nextdata
  2691. data = data[len(nextdata):]
  2692. self.toRead -= len(nextdata)
  2693. if self.toRead == 0:
  2694. readMessage(self.pPayload)
  2695. if len(data) > 0:
  2696. readStart(data)
  2697. if self.pPayload is not None:
  2698. readContinue(data)
  2699. else:
  2700. readStart(data)
  2701. def handleMessage(self, data):
  2702. self.log.debug('handleMessage "%s"', data)
  2703. if data['message'] == Messages.MESSAGE_UPDATE_CALLBACK:
  2704. self.updateCB(data['value'])
  2705. if data['message'] == Messages.MESSAGE_FINISHED_SCRIPT:
  2706. self.successCB(data['value'])
  2707. if data['message'] == Messages.MESSAGE_CANCELLED_SCRIPT:
  2708. print 'script successfully cancelled'
  2709. if data['message'] == Messages.MESSAGE_ERROR_SCRIPT:
  2710. self.errorCB(data['value'])
  2711. def start(self, params, updateCB, successCB, errorCB):
  2712. self.processes.append(self)
  2713. self.updateCB = updateCB
  2714. self.successCB = successCB
  2715. self.errorCB = errorCB
  2716. cmd = "python %s" % self.process_path
  2717. self.log.debug("start - '%s'", cmd)
  2718. self.appContainer.execute(cmd)
  2719. self.write(params)
  2720. def running(self):
  2721. return self.appContainer.running()
  2722. def stop(self):
  2723. def check_stopped():
  2724. if not self.appContainer.running():
  2725. timer.stop()
  2726. del self.__i
  2727. return
  2728. if self.__i == 0:
  2729. self.__i += 1
  2730. self.log.debug('2. sending SIGKILL')
  2731. self.appContainer.kill()
  2732. elif self.__i == 1:
  2733. timer.stop()
  2734. raise Exception("cannot kill process")
  2735. if self.__stopping:
  2736. self.log.debug('already stopping..')
  2737. return
  2738. self.__stopping = True
  2739. self.log.debug('stopping process..')
  2740. self.__i = 0
  2741. if self.appContainer.running():
  2742. self.log.debug('1. sending SIGINT')
  2743. self.appContainer.sendCtrlC()
  2744. timer = eTimer()
  2745. timer.callback.append(check_stopped)
  2746. timer.start(2000, False)
  2747. else:
  2748. self.log.debug('process is already stopped')
  2749. def write(self, data):
  2750. dump = json.dumps(data)
  2751. dump = "%07d%s" % (len(dump), dump)
  2752. self.appContainer.write(dump)
  2753. def dataErrCB(self, data):
  2754. self.log.debug("dataErrCB: '%s'", data)
  2755. self.error = data
  2756. def dataOutCB(self, data):
  2757. self.log.debug("dataOutCB: '%s", data)
  2758. self.recieveMessages(data)
  2759. def finishedCB(self, retval):
  2760. self.processes.remove(self)
  2761. self.log.debug('process finished, retval:%d', retval)
  2762. class Suggestions(object):
  2763. def __init__(self):
  2764. self._cancelled = False
  2765. def __str__(self):
  2766. return self.__class__.__name__
  2767. def cancel(self):
  2768. self._cancelled = True
  2769. def getSuggestions(self, queryString, successCB, errorCB):
  2770. if queryString is not None:
  2771. d = self._getSuggestions(queryString)
  2772. self.successCB = successCB
  2773. self.errorCB = errorCB
  2774. d.addCallbacks(self.getSuggestionsSuccess, self.getSuggestionsError)
  2775. def getSuggestionsSuccess(self, data):
  2776. if not self._cancelled:
  2777. self.successCB(self._processResult(data))
  2778. def getSuggestionsError(self, failure):
  2779. if not self._cancelled:
  2780. failure.printTraceback()
  2781. self.errorCB(failure)
  2782. def _getSuggestions(self):
  2783. return Deferred()
  2784. def _processResult(self, data):
  2785. return data
  2786. class OpenSubtitlesSuggestions(Suggestions):
  2787. def _getSuggestions(self, queryString):
  2788. query = "http://www.opensubtitles.org/libs/suggest.php?format=json2&SubLanguageID=null&MovieName=" + queryString
  2789. return client.getPage(query, timeout=6)
  2790. def _processResult(self, data):
  2791. return json.loads(data)['result']
  2792. class HistorySuggestions(Suggestions):
  2793. def __init__(self, historyCfg):
  2794. Suggestions.__init__(self)
  2795. self.historyCfg = historyCfg
  2796. def _getSuggestions(self, queryString):
  2797. def getHistory(queryString):
  2798. historyList = self.historyCfg.value.split(',')
  2799. historyList = [{'name':name, 'total':len(historyList) - idx} for idx, name in enumerate(historyList)]
  2800. d.callback(historyList)
  2801. d = Deferred()
  2802. getHistory(queryString)
  2803. return d
  2804. class SuggestionsListScreen(Screen):
  2805. s = getDesktop(0).size()
  2806. desktopSize = (s.width(), s.height())
  2807. windowSize = (int(0.35 * desktopSize[0]), 160)
  2808. skin = """
  2809. <screen name="SuggestionsListScreen" position="%d, 20" zPosition="6" size="%d,160" flags="wfNoBorder" backgroundColor="#33202020" >
  2810. <widget source="suggestionstitle" render="Label" position = "0,0" size="%d, 25" foregroundColor="#00ff00" font="Regular;20" halign="center" valign="center" transparent="0" />
  2811. <widget source="suggestionslist" render="Listbox" position="%d,35" size="%d,115" scrollbarMode="showOnDemand" transparent="1" >
  2812. <convert type="TemplatedMultiContent">
  2813. {"templates":
  2814. {"default": (23, [
  2815. MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title,
  2816. ], True, "showOnDemand"),
  2817. "notselected": (23, [
  2818. MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title,
  2819. ], False, "showOnDemand")
  2820. },
  2821. "fonts": [gFont("Regular", 18), gFont("Regular", 16)],
  2822. "itemHeight": 23
  2823. }
  2824. </convert>
  2825. </widget>
  2826. </screen>""" % (int(0.6 * desktopSize[0]), windowSize[0],
  2827. windowSize[0],
  2828. int(0.05 * windowSize[0]), int(0.9 * windowSize[0]))
  2829. def __init__(self, session, title, configTextWithSuggestions):
  2830. Screen.__init__(self, session)
  2831. self.activeState = False
  2832. self.list = []
  2833. self.suggestlist = []
  2834. self["suggestionslist"] = List(self.list)
  2835. self["suggestionstitle"] = StaticText(toString(title))
  2836. self.configTextWithSuggestion = configTextWithSuggestions
  2837. def update(self, suggestions):
  2838. if suggestions and len(suggestions) > 0:
  2839. if not self.shown:
  2840. self.show()
  2841. suggestions.sort(key=lambda x: int(x['total']))
  2842. suggestions.reverse()
  2843. if len(suggestions):
  2844. self.list = []
  2845. for s in suggestions:
  2846. self.list.append((toString(s['name']),))
  2847. self["suggestionslist"].setList(self.list)
  2848. self["suggestionslist"].setIndex(0)
  2849. else:
  2850. self.hide()
  2851. def getlistlenght(self):
  2852. return len(self.list)
  2853. def up(self):
  2854. if self.list and len(self.list) > 0:
  2855. self["suggestionslist"].selectPrevious()
  2856. return self.getSelection()
  2857. def down(self):
  2858. if self.list and len(self.list) > 0:
  2859. self["suggestionslist"].selectNext()
  2860. return self.getSelection()
  2861. def pageUp(self):
  2862. if self.list and len(self.list) > 0:
  2863. self["suggestionslist"].selectPrevious()
  2864. return self.getSelection()
  2865. def pageDown(self):
  2866. if self.list and len(self.list) > 0:
  2867. self["suggestionslist"].selectNext()
  2868. return self.getSelection()
  2869. def activate(self):
  2870. self.enableSelection(True)
  2871. return self.getSelection()
  2872. def deactivate(self):
  2873. self.enableSelection(False)
  2874. return self.getSelection()
  2875. def getSelection(self):
  2876. if not self["suggestionslist"].getCurrent():
  2877. return None
  2878. return self["suggestionslist"].getCurrent()[0]
  2879. def enableSelection(self, value):
  2880. if value:
  2881. self['suggestionslist'].style = 'default'
  2882. else:
  2883. self['suggestionslist'].style = 'notselected'
  2884. class HistoryListScreen(SuggestionsListScreen):
  2885. s = getDesktop(0).size()
  2886. desktopSize = (s.width(), s.height())
  2887. windowSize = (int(0.35 * desktopSize[0]), 160)
  2888. skin = """
  2889. <screen name="HistoryListScreen" position="%d, 20" zPosition="6" size="%d,160" flags="wfNoBorder" backgroundColor="#33202020" >
  2890. <widget source="suggestionstitle" render="Label" position = "0,0" size="%d, 25" foregroundColor="#ff0000" font="Regular;20" halign="center" valign="center" transparent="0" />
  2891. <widget source="suggestionslist" render="Listbox" position="%d,35" size="%d,115" scrollbarMode="showOnDemand" transparent="1" >
  2892. <convert type="TemplatedMultiContent">
  2893. {"templates":
  2894. {"default": (23, [
  2895. MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title,
  2896. ], True, "showOnDemand"),
  2897. "notselected": (23, [
  2898. MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title,
  2899. ], False, "showOnDemand")
  2900. },
  2901. "fonts": [gFont("Regular", 18), gFont("Regular", 16)],
  2902. "itemHeight": 23
  2903. }
  2904. </convert>
  2905. </widget>
  2906. </screen>""" % (int(0.05 * desktopSize[0]), windowSize[0],
  2907. windowSize[0],
  2908. int(0.05 * windowSize[0]), int(0.9 * windowSize[0]))
  2909. def __init__(self, session, title, configTextWithSuggestions):
  2910. SuggestionsListScreen.__init__(self, session, title, configTextWithSuggestions)
  2911. class ConfigTextWithSuggestionsAndHistory(ConfigText):
  2912. def __init__(self, historyCfg, default="", fixed_size=True, visible_width=False):
  2913. ConfigText.__init__(self, default, fixed_size, visible_width)
  2914. self.historyCfg = historyCfg
  2915. self.historyClass = HistorySuggestions
  2916. self.historyWindow = None
  2917. self.__history = None
  2918. self.suggestionsClass = OpenSubtitlesSuggestions
  2919. self.suggestionsWindow = None
  2920. self.__suggestions = None
  2921. self.currentWindow = None
  2922. def handleKey(self, key):
  2923. ConfigText.handleKey(self, key)
  2924. if key in [KEY_DELETE, KEY_BACKSPACE, KEY_ASCII, KEY_TIMEOUT]:
  2925. self.getSuggestions()
  2926. def onSelect(self, session):
  2927. ConfigText.onSelect(self, session)
  2928. if session is not None:
  2929. if self.suggestionsWindow is None:
  2930. suggestionsWindowTitle = _("Suggestions") + " (" + _("press green") + ")"
  2931. self.suggestionsWindow = session.instantiateDialog(SuggestionsListScreen, suggestionsWindowTitle , self)
  2932. self.suggestionsWindow.deactivate()
  2933. self.suggestionsWindow.show()
  2934. historyWindowTitle = _("History") + " (" + _("press red") + ")"
  2935. if self.historyWindow is None:
  2936. self.historyWindow = session.instantiateDialog(HistoryListScreen, historyWindowTitle, self)
  2937. self.historyWindow.deactivate()
  2938. self.historyWindow.show()
  2939. self.getSuggestions()
  2940. self.getHistory()
  2941. def onDeselect(self, session):
  2942. self.cancelGetSuggestions()
  2943. self.cancelGetHistory()
  2944. ConfigText.onDeselect(self, session)
  2945. if self.suggestionsWindow is not None:
  2946. self.suggestionsWindow.hide()
  2947. if self.historyWindow is not None:
  2948. self.historyWindow.hide()
  2949. def getCurrentSelection(self):
  2950. if self.currentWindow.getlistlenght() > 0:
  2951. return self.currentWindow.getSelection()
  2952. def currentListUp(self):
  2953. if self.currentWindow.getlistlenght() > 0:
  2954. self.value = self.currentWindow.up()
  2955. def currentListDown(self):
  2956. if self.currentWindow.getlistlenght() > 0:
  2957. self.value = self.currentWindow.down()
  2958. def currentListPageDown(self):
  2959. if self.currentWindow.getlistlenght() > 0:
  2960. self.value = self.currentWindow.pageDown()
  2961. def currentListPageUp(self):
  2962. if self.currentWindow.getlistlenght() > 0:
  2963. self.value = self.currentWindow.pageUp()
  2964. def propagateSuggestions(self, suggestionsList):
  2965. self.cancelGetSuggestions()
  2966. if self.suggestionsWindow:
  2967. self.suggestionsWindow.update(suggestionsList)
  2968. def propagateHistory(self, historyList):
  2969. self.cancelGetHistory()
  2970. if self.historyWindow:
  2971. self.historyWindow.update(historyList)
  2972. def enableSuggestions(self, value):
  2973. if value:
  2974. if self.suggestionsWindow:
  2975. self.tmpValue = self.value
  2976. selection = self.suggestionsWindow.activate()
  2977. if selection is None:
  2978. print 'empty suggesstions list'
  2979. return False
  2980. self.value = selection
  2981. self.currentWindow = self.suggestionsWindow
  2982. return True
  2983. else:
  2984. print 'Error - suggestionsWindow no longer exists'
  2985. return False
  2986. else:
  2987. self.cancelGetSuggestions()
  2988. if self.suggestionsWindow:
  2989. self.suggestionsWindow.deactivate()
  2990. self.currentWindow = None
  2991. self.getSuggestions()
  2992. return True
  2993. else:
  2994. print 'Error - suggestionsWindow no longer exists'
  2995. return False
  2996. def enableHistory(self, value):
  2997. if value:
  2998. if self.historyWindow:
  2999. self.tmpValue = self.value
  3000. selection = self.historyWindow.activate()
  3001. if selection is None:
  3002. print "Error - empty history list"
  3003. return False
  3004. self.value = selection
  3005. self.currentWindow = self.historyWindow
  3006. return True
  3007. else:
  3008. print 'Error - historyWindow no longer exists'
  3009. return False
  3010. else:
  3011. self.cancelGetHistory()
  3012. if self.historyWindow:
  3013. self.historyWindow.deactivate()
  3014. self.currentWindow = None
  3015. self.getHistory()
  3016. return True
  3017. else:
  3018. print 'Error - historyWindow no longer exists'
  3019. return False
  3020. def cancelGetSuggestions(self):
  3021. if self.__suggestions is not None:
  3022. self.__suggestions.cancel()
  3023. def cancelGetHistory(self):
  3024. if self.__history is not None:
  3025. self.__history.cancel()
  3026. def gotSuggestionsError(self, val):
  3027. print "[ConfigTextWithSuggestions] gotSuggestionsError:", val
  3028. def gotHistoryError(self, val):
  3029. print "[ConfigTextWithSuggestions] gotHistoryError:", val
  3030. def getSuggestions(self):
  3031. self.__suggestions = self.suggestionsClass().getSuggestions(self.value, self.propagateSuggestions, self.gotSuggestionsError)
  3032. def getHistory(self):
  3033. self.__history = self.historyClass(self.historyCfg).getSuggestions(self.value, self.propagateHistory, self.gotHistoryError)
  3034. def cancelSuggestions(self):
  3035. self.value = self.tmpValue
  3036. self.enableSuggestions(False)
  3037. self.enableHistory(False)
  3038. class Message(object):
  3039. def __init__(self, infowidget, errorwidget):
  3040. self.infowidget = infowidget
  3041. self.errorwidget = errorwidget
  3042. self.timer = eTimer()
  3043. self.timer.callback.append(self.hide)
  3044. def info(self, text, timeout=None):
  3045. self.timer.stop()
  3046. self.errorwidget.hide()
  3047. self.infowidget.setText(text)
  3048. self.infowidget.show()
  3049. if timeout:
  3050. self.timer.start(timeout, True)
  3051. def error(self, text, timeout=None):
  3052. self.timer.stop()
  3053. self.infowidget.hide()
  3054. self.errorwidget.setText(text)
  3055. self.errorwidget.show()
  3056. if timeout:
  3057. self.timer.start(timeout, True)
  3058. def hide(self):
  3059. self.timer.stop()
  3060. self.errorwidget.hide()
  3061. self.infowidget.hide()
  3062. class SearchParamsHelper(object):
  3063. def __init__(self, seeker, searchSettings):
  3064. self.seeker = seeker
  3065. self.searchSettings = searchSettings
  3066. self.searchTitle = searchSettings.title
  3067. self.searchType = searchSettings.type
  3068. self.searchYear = searchSettings.year
  3069. self.searchSeason = searchSettings.season
  3070. self.searchEpisode = searchSettings.episode
  3071. self.searchProvider = searchSettings.provider
  3072. self.searchUseFilePath = searchSettings.useFilePath
  3073. def resetSearchParams(self):
  3074. self.searchType.value = self.searchType.default
  3075. self.searchType.save()
  3076. self.searchTitle.value = self.searchTitle.default
  3077. self.searchTitle.save()
  3078. self.searchSeason.value = self.searchSeason.default
  3079. self.searchSeason.save()
  3080. self.searchEpisode.value = self.searchEpisode.default
  3081. self.searchEpisode.save()
  3082. self.searchYear.value = self.searchYear.default
  3083. self.searchYear.save()
  3084. self.searchProvider.value = self.searchProvider.default
  3085. self.searchProvider.save()
  3086. self.searchUseFilePath.value = self.searchUseFilePath.default
  3087. def detectSearchParams(self, searchExpression):
  3088. self.resetSearchParams()
  3089. params = detectSearchParams(searchExpression)
  3090. if params[2]:
  3091. self.searchType.value = "tv_show"
  3092. self.searchTitle.value = params[2]
  3093. self.searchSeason.value = params[3] and int(params[3]) or 0
  3094. self.searchEpisode.value = params[4] and int(params[4]) or 0
  3095. else:
  3096. self.searchType.value = "movie"
  3097. self.searchTitle.value = params[0]
  3098. self.searchYear.value = params[1] and int(params[1]) or 0
  3099. self.updateProviders()
  3100. def getSearchParams(self):
  3101. langs = [self.searchSettings.lang1.value,
  3102. self.searchSettings.lang2.value,
  3103. self.searchSettings.lang3.value]
  3104. provider = self.searchProvider.value
  3105. title = self.searchTitle.value
  3106. tvshow = self.searchType.value == "tv_show" and title or ""
  3107. year = self.searchYear.value and not tvshow and str(self.searchYear.value) or ""
  3108. season = self.searchSeason.value and tvshow and str(self.searchSeason.value) or ""
  3109. episode = self.searchEpisode.value and tvshow and str(self.searchEpisode.value) or ""
  3110. return provider, langs, title, year, tvshow, season, episode
  3111. def updateProviders(self):
  3112. tvshow = self.searchType.value == "tv_show"
  3113. providers = self.seeker.getProviders([self.searchSettings.lang1.value,
  3114. self.searchSettings.lang2.value,
  3115. self.searchSettings.lang3.value], not tvshow, tvshow)
  3116. choiceList = []
  3117. choiceList.append(("all", _("All")))
  3118. choiceList.extend((p.id, p.provider_name) for p in providers)
  3119. self.searchProvider.setChoices(choiceList)
  3120. if self.searchProvider.value not in [p.id for p in providers]:
  3121. if tvshow:
  3122. self.searchProvider.value = self.searchSettings.tvshowProvider.value
  3123. else:
  3124. self.searchProvider.value = self.searchSettings.movieProvider.value
  3125. tvshowProviders = self.seeker.getProviders(movie=False, tvshow=True)
  3126. choiceList = []
  3127. choiceList.append(("all", _("All")))
  3128. choiceList.extend((p.id, p.provider_name) for p in tvshowProviders)
  3129. self.searchSettings.tvshowProvider.setChoices(choiceList)
  3130. movieProviders = self.seeker.getProviders(movie=True, tvshow=False)
  3131. choiceList = []
  3132. choiceList.append(("all", _("All")))
  3133. choiceList.extend((p.id, p.provider_name) for p in movieProviders)
  3134. self.searchSettings.movieProvider.setChoices(choiceList)
  3135. class SubsSearchDownloadOptions(Screen, ConfigListScreen):
  3136. skin="""
  3137. <screen position="center,center" size="490,350" zPosition="5" >
  3138. <widget name="config" position="10, 10" size="470,110" zPosition="1" />
  3139. <eLabel position="8,128" size="474,79" backgroundColor="#ff0000" />
  3140. <widget source="fname" render="Label" position="10,130" size="470,75" valign="center" halign="center" font="Regular;19" foregroundColor="#ffffff" zPosition="1" />
  3141. <eLabel position="8,220" size="474,79" backgroundColor="#00ff00" />
  3142. <widget source="dpath" render="Label" position="10,222" size="470,75" valign="center" halign="center" font="Regular;19" foregroundColor="#ffffff" zPosition="1" />
  3143. <eLabel position="5,305" size="480,1" backgroundColor="#999999" />
  3144. <ePixmap pixmap="skin_default/buttons/key_red.png" position="10,315" size="35,25" transparent="1" alphatest="on" />
  3145. <widget source="key_red" render="Label" position = "50, 315" size="110,25" font="Regular;20" halign="left" foregroundColor="white" />
  3146. <ePixmap pixmap="skin_default/buttons/key_green.png" position="170,315" size="35,25" transparent="1" alphatest="on" />
  3147. <widget source="key_green" render="Label" position = "210, 315" size="110,25" font="Regular;20" halign="left" foregroundColor="white" />
  3148. <ePixmap pixmap="skin_default/buttons/key_blue.png" position="330,315" size="35,25" transparent="1" alphatest="on" />
  3149. <widget source="key_blue" render="Label" position = "370, 315" size="480,25" font="Regular;20" halign="left" foregroundColor="white" />
  3150. </screen>
  3151. """
  3152. def __init__(self, session, subtitle, saveAs, saveTo, addLang, dPath, vPath=None):
  3153. Screen.__init__(self, session)
  3154. saveAsOptions = []
  3155. saveAsOptions.append(('version', _("Release")))
  3156. if vPath is not None and os.path.isfile(vPath):
  3157. saveAsOptions.append(('video', _("Video filename")))
  3158. saveAsOptions.append(('custom', _("User defined")))
  3159. saveToOptions = []
  3160. saveToOptions.append(('custom', _('User defined')))
  3161. if vPath is not None and os.path.isfile(vPath):
  3162. saveToOptions.append(('video', _('Next to video')))
  3163. if saveAs == 'default':
  3164. # we don't know what the default filename will be
  3165. saveAs = 'version'
  3166. elif saveAs == 'video' and vPath is None:
  3167. saveAs = 'version'
  3168. if saveTo == 'video' and vPath is None:
  3169. saveTo = 'custom'
  3170. self.configSaveAs = ConfigSelection(default=saveAs, choices=saveAsOptions)
  3171. self.configSaveTo = ConfigSelection(default=saveTo, choices=saveToOptions)
  3172. self.configAddLang = ConfigYesNo(default=addLang)
  3173. configList = [
  3174. getConfigListEntry(_("Save to"), self.configSaveTo),
  3175. getConfigListEntry(_("Save as"), self.configSaveAs),
  3176. getConfigListEntry(_("Append language to filename"), self.configAddLang),
  3177. ]
  3178. ConfigListScreen.__init__(self, configList, session)
  3179. self.subtitle = subtitle
  3180. self.dPath = dPath
  3181. self.vPath = vPath
  3182. self["fname"] = StaticText()
  3183. self["dpath"] = StaticText()
  3184. self["key_red"] = StaticText(_("Filename"))
  3185. self["key_green"] = StaticText(_("Path"))
  3186. self["key_blue"] = StaticText(_("Reset"))
  3187. self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "ColorActions"],
  3188. {
  3189. "right": self.keyRight,
  3190. "left":self.keyLeft,
  3191. "ok":self.confirm,
  3192. "cancel":self.cancel,
  3193. "red": self.editFName,
  3194. "green": self.editDPath,
  3195. "blue":self.resetDefaults
  3196. }, -2)
  3197. self.onLayoutFinish.append(self.updateWindowTitle)
  3198. self.onLayoutFinish.append(self.updateFName)
  3199. self.onLayoutFinish.append(self.updateDPath)
  3200. def buildMenu(self):
  3201. configList = []
  3202. configList.append(getConfigListEntry(_("Save to"), self.configSaveTo))
  3203. configList.append(getConfigListEntry(_("Save as"), self.configSaveAs))
  3204. if self.configSaveAs.value != "custom":
  3205. configList.append(getConfigListEntry(_("Append language to filename"), self.configAddLang))
  3206. self["config"].setList(configList)
  3207. def updateWindowTitle(self):
  3208. self.setTitle(_("Download options"))
  3209. def updateFName(self):
  3210. fname = None
  3211. if self.configSaveAs.value == "video":
  3212. fname = os.path.splitext(os.path.basename(self.vPath))[0]
  3213. elif self.configSaveAs.value == "version":
  3214. fname = os.path.splitext(self.subtitle['filename'])[0]
  3215. if self.configAddLang.value and not self.configSaveAs.value == "custom":
  3216. fname = "%s.%s"%(fname, languageTranslate(self.subtitle['language_name'], 0, 2))
  3217. if fname:
  3218. self["fname"].text = toString(fname)
  3219. def updateDPath(self):
  3220. dpath = None
  3221. if self.configSaveTo.value == "video":
  3222. dpath = os.path.dirname(self.vPath)
  3223. elif self.configSaveTo.value == "custom":
  3224. dpath = self.dPath
  3225. if dpath:
  3226. self["dpath"].text = toString(dpath)
  3227. def resetDefaults(self):
  3228. for x in self["config"].list:
  3229. x[1].value = x[1].default
  3230. self.buildMenu()
  3231. self.updateFName()
  3232. self.updateDPath()
  3233. def editFName(self):
  3234. def editFnameCB(callback=None):
  3235. if callback is not None and len(callback):
  3236. self["fname"].text = callback
  3237. self.configSaveAs.value = "custom"
  3238. self.buildMenu()
  3239. self.updateFName()
  3240. from Screens.VirtualKeyBoard import VirtualKeyBoard
  3241. self.session.openWithCallback(editFnameCB, VirtualKeyBoard, _("Edit Filename"), text= toString(self["fname"].text.strip()))
  3242. def editDPath(self):
  3243. def editDPathCB(callback=None):
  3244. if callback is not None and len(callback):
  3245. self["dpath"].text = callback
  3246. self.configSaveTo.value = "custom"
  3247. self["config"].invalidate(self.configSaveTo)
  3248. self.session.openWithCallback(editDPathCB, LocationBox, _("Edit download path"), currDir = toString(self["dpath"].text.strip()))
  3249. def confirm(self):
  3250. fname = self["fname"].text.strip()
  3251. if len(fname) == "":
  3252. self.session.open(MessageBox, _("Filename cannot be empty!"), type=MessageBox.TYPE_WARNING)
  3253. return
  3254. dpath = self["dpath"].text.strip()
  3255. if not os.path.isdir(dpath):
  3256. self.session.open(MessageBox, _("Path doesn't exist!"), type=MessageBox.TYPE_WARNING)
  3257. return
  3258. self.close(dpath, fname)
  3259. def cancel(self):
  3260. self.close(None, None)
  3261. def keyRight(self):
  3262. saveAsConfig = False
  3263. if self['config'].getCurrent()[1] == self.configSaveAs:
  3264. saveAsConfig = True
  3265. currIdx = self.configSaveAs.choices.index(self.configSaveAs.value)
  3266. if currIdx == len(self.configSaveAs.choices) -1:
  3267. nextChoice = self.configSaveAs.choices[0]
  3268. else:
  3269. nextChoice = self.configSaveAs.choices[currIdx+1]
  3270. if nextChoice == 'custom':
  3271. self.configSaveAs.value = nextChoice
  3272. ConfigListScreen.keyRight(self)
  3273. if saveAsConfig:
  3274. self.buildMenu()
  3275. if self['config'].getCurrent()[1] in (self.configSaveAs, self.configAddLang):
  3276. self.updateFName()
  3277. elif self['config'].getCurrent()[1] in (self.configSaveTo,):
  3278. self.updateDPath()
  3279. def keyLeft(self):
  3280. saveAsConfig = False
  3281. if self['config'].getCurrent()[1] == self.configSaveAs:
  3282. saveAsConfig = True
  3283. currIdx = self.configSaveAs.choices.index(self.configSaveAs.value)
  3284. if currIdx == 0:
  3285. nextChoice = self.configSaveAs.choices[len(self.configSaveAs.choices)-1]
  3286. else:
  3287. nextChoice = self.configSaveAs.choices[currIdx-1]
  3288. if nextChoice == 'custom':
  3289. self.configSaveAs.value = nextChoice
  3290. ConfigListScreen.keyLeft(self)
  3291. if saveAsConfig:
  3292. self.buildMenu()
  3293. if self['config'].getCurrent()[1] in (self.configSaveAs, self.configAddLang):
  3294. self.updateFName()
  3295. elif self['config'].getCurrent()[1] in (self.configSaveTo,):
  3296. self.updateDPath()
  3297. class SubsSearchContextMenu(Screen):
  3298. skin = """
  3299. <screen position="center,center" size="400,350" zPosition="5" flags="wfNoBorder">
  3300. <eLabel position="0,0" size="400,350" backgroundColor="#999999" zPosition="0" />
  3301. <widget source="subtitle_release" render="Label" position="5,5" size="390,50" valign="center" halign="center" font="Regular;19" foregroundColor="#66BFFF" zPosition="1" />
  3302. <widget source="context_menu" render="Listbox" position="5,60" size="390,285" scrollbarMode="showNever" zPosition="1">
  3303. <convert type="TemplatedMultiContent">
  3304. {"templates":
  3305. {"default": (22, [
  3306. MultiContentEntryText(pos = (5, 0), size = (380, 22), font = 0, color = 0xffffff, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 0), # langname,
  3307. ], True, "showOnDemand"),
  3308. },
  3309. "fonts": [gFont("Regular", 19)],
  3310. "itemHeight":22,
  3311. }
  3312. </convert>
  3313. </widget>
  3314. </screen>
  3315. """
  3316. def __init__(self, session):
  3317. Screen.__init__(self, session)
  3318. self.options = []
  3319. self["subtitle_release"] = StaticText()
  3320. self["context_menu"] = List()
  3321. def up(self):
  3322. self["context_menu"].selectNext()
  3323. def down(self):
  3324. self["context_menu"].selectPrevious()
  3325. def right(self):
  3326. self["context_menu"].selectNext()
  3327. def left(self):
  3328. self["context_menu"].selectPrevious()
  3329. def updateGUI(self, subtitle, options):
  3330. self["subtitle_release"].text = toString(subtitle['filename'])
  3331. self["context_menu"].list = [(o[0],) for o in options]
  3332. self.options = options
  3333. def getSelection(self):
  3334. return self.options[self["context_menu"].index][1]
  3335. class SubsSearch(Screen):
  3336. skin = """
  3337. <screen name="SubsSearch" position="center,center" size="700,520" zPosition="3" >
  3338. <widget source="search_info" render="Listbox" position="10,10" size="680,150" zPosition="3" scrollbarMode="showNever" transparent="1" >
  3339. <convert type="TemplatedMultiContent">
  3340. {"templates":
  3341. {"default": (22, [
  3342. MultiContentEntryText(pos = (0, 0), size = (200, 22), font = 0, color = 0xDAA520, flags = RT_HALIGN_LEFT, text = 0), # langname,
  3343. MultiContentEntryText(pos = (205, 0), size = (400, 22), font = 0, flags = RT_HALIGN_LEFT, text = 1)
  3344. ], False, "showNever"),
  3345. },
  3346. "fonts": [gFont("Regular", 18)],
  3347. "itemHeight":22,
  3348. }
  3349. </convert>
  3350. </widget>
  3351. <widget source="header_country" render="Label" position = "5,175" size="120,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3352. <widget source="header_release" render="Label" position = "145,175" size="335,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3353. <widget source="header_provider" render="Label" position = "505, 175" size="135,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3354. <widget source="header_sync" render="Label" position = "650, 175" size="20,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3355. <eLabel position="5,205" size="690,1" backgroundColor="#999999" />
  3356. <widget name="loadmessage" position="5,210" size="690,260" valign="center" halign="center" font="Regular;19" foregroundColor="#ffffff" zPosition="4" />
  3357. <widget name="errormessage" position="5,210" size="690,260" valign="center" halign="center" font="Regular;19" foregroundColor="#ff0000" zPosition="5" />
  3358. <widget source="subtitles" render="Listbox" scrollbarMode="showOnDemand" position="5,210" size="690,260" zPosition="3" transparent="1" >
  3359. <convert type="TemplatedMultiContent">
  3360. {"templates":
  3361. {"default": (23, [
  3362. MultiContentEntryPixmapAlphaBlend(pos = (0, 0), size = (24, 24), png=0), # key,
  3363. MultiContentEntryText(pos = (30, 0), size = (100, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # language,
  3364. MultiContentEntryText(pos = (140, 0), size = (335, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # filename,
  3365. MultiContentEntryText(pos = (500, 0), size = (135, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # size,
  3366. MultiContentEntryPixmapAlphaBlend(pos = (645, 0), size = (24, 24), png=4), # syncPng,
  3367. ], True, "showOnDemand"),
  3368. "old": (23, [
  3369. MultiContentEntryPixmapAlphaBlend(pos = (0, 0), size = (24, 24), png=0), # key,
  3370. MultiContentEntryText(pos = (30, 0), size = (60, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # language,
  3371. MultiContentEntryText(pos = (100, 0), size = (335, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # filename,
  3372. MultiContentEntryText(pos = (445, 0), size = (130, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # size,
  3373. MultiContentEntryText(pos = (585, 0), size = (95, 25), font = 0, flags = RT_HALIGN_LEFT, text = 4), # sync,
  3374. ], True, "showOnDemand"),
  3375. },
  3376. "fonts": [gFont("Regular", 18), gFont("Regular", 16)],
  3377. "itemHeight": 23
  3378. }
  3379. </convert>
  3380. </widget>
  3381. <eLabel position="5,475" size="690,1" backgroundColor="#999999" />
  3382. <widget source="key_menu_img" render="Pixmap" pixmap="skin_default/buttons/key_menu.png" position="3,485" size="35,25" transparent="1" alphatest="on" >
  3383. <convert type="ConditionalShowHide" />
  3384. </widget>
  3385. <ePixmap pixmap="skin_default/buttons/key_red.png" position="40,485" size="35,25" transparent="1" alphatest="on" />
  3386. <widget source="key_red" render="Label" position = "80, 485" size="120,25" font="Regular;20" halign="left" foregroundColor="white" />
  3387. <ePixmap pixmap="skin_default/buttons/key_green.png" position="205,485" size="35,25" transparent="1" alphatest="on" />
  3388. <widget source="key_green" render="Label" position = "245, 485" size="110,25" font="Regular;20" halign="left" foregroundColor="white" />
  3389. <ePixmap pixmap="skin_default/buttons/key_yellow.png" position="365,485" size="35,25" transparent="1" alphatest="on" />
  3390. <widget source="key_yellow" render="Label" position = "405, 485" size="110,25" font="Regular;20" halign="left" foregroundColor="white" />
  3391. <ePixmap pixmap="skin_default/buttons/key_blue.png" position="525,485" size="35,25" transparent="1" alphatest="on" />
  3392. <widget source="key_blue" render="Label" position = "565, 485" size="110,25" font="Regular;20" halign="left" foregroundColor="white" />
  3393. </screen> """
  3394. def __init__(self, session, seeker, searchSettings, filepath=None, searchTitles=None, resetSearchParams=True, standAlone=False):
  3395. Screen.__init__(self, session)
  3396. self.searchSettings = searchSettings
  3397. self.standAlone = standAlone
  3398. searchTitles = searchTitles or [""]
  3399. self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings)
  3400. self.seeker = seeker
  3401. self.searchExpression = searchTitles[0]
  3402. self.searchTitles = searchTitles
  3403. self.filepath = filepath
  3404. self.isLocalFilepath = filepath and os.path.isfile(filepath) or False
  3405. self.searchTitle = searchSettings.title
  3406. self.searchType = searchSettings.type
  3407. self.searchYear = searchSettings.year
  3408. self.searchSeason = searchSettings.season
  3409. self.searchEpisode = searchSettings.episode
  3410. self.searchProvider = searchSettings.provider
  3411. self.searchUseFilePath = searchSettings.useFilePath
  3412. self.__downloadedSubtitles = []
  3413. self.__downloading = False
  3414. self.__searching = False
  3415. self["loadmessage"] = Label("")
  3416. self["errormessage"] = Label("")
  3417. self["search_info"] = List([])
  3418. self["header_country"] = StaticText(_("Language"))
  3419. self["header_release"] = StaticText(_("Release"))
  3420. self["header_provider"] = StaticText(_("Provider"))
  3421. self["header_sync"] = StaticText(_("S"))
  3422. self["subtitles"] = List([])
  3423. self["key_menu_img"] = Boolean()
  3424. self["key_red"] = StaticText(_("Update"))
  3425. self["key_green"] = StaticText(_("Search"))
  3426. self["key_yellow"] = StaticText(_("History"))
  3427. self["key_blue"] = StaticText(_("Settings"))
  3428. self["okCancelActions"] = ActionMap(["OkCancelActions"],
  3429. {
  3430. "ok": self.keyOk,
  3431. "cancel": self.keyCancel,
  3432. })
  3433. self["menuActions"] = ActionMap(["ColorActions", "MenuActions"],
  3434. {
  3435. "red":self.updateSearchParams,
  3436. "green":self.searchSubs,
  3437. "yellow":self.openDownloadHistory,
  3438. "blue":self.openSettings,
  3439. "menu":self.openContextMenu,
  3440. })
  3441. self["listActions"] = ActionMap(["DirectionActions"],
  3442. {
  3443. "up": self.keyUp,
  3444. "upRepeated": self.keyUp,
  3445. "down": self.keyDown,
  3446. "downRepeated": self.keyDown,
  3447. "right":self.keyRight,
  3448. "rightRepeated":self.keyRight,
  3449. "left":self.keyLeft,
  3450. "leftRepeated":self.keyLeft,
  3451. }, -2)
  3452. self["searchActions"] = ActionMap(["OkCancelActions"],
  3453. {
  3454. "ok":self.cancelSearchSubs,
  3455. "cancel":self.close,
  3456. })
  3457. self["searchActions"].setEnabled(False)
  3458. self.__contextMenu = self.session.instantiateDialog(SubsSearchContextMenu)
  3459. self.__contextMenu.hide()
  3460. self["contextMenuActions"] = ActionMap(["DirectionActions", "OkCancelActions", "MenuActions"],
  3461. {
  3462. "up":self.__contextMenu.up,
  3463. "down":self.__contextMenu.down,
  3464. "right":self.__contextMenu.right,
  3465. "left": self.__contextMenu.left,
  3466. "ok": self.contextMenuOk,
  3467. "cancel":self.contextMenuCancel,
  3468. "menu":self.contextMenuCancel,
  3469. })
  3470. self["contextMenuActions"].setEnabled(False)
  3471. self.message = Message(self['loadmessage'], self['errormessage'])
  3472. self.onLayoutFinish.append(self.updateTitle)
  3473. self.onLayoutFinish.append(self.__getSubtitlesRenderer)
  3474. if resetSearchParams:
  3475. self.onLayoutFinish.append(self.detectSearchParams)
  3476. self.onLayoutFinish.append(self.searchParamsHelper.updateProviders)
  3477. self.onLayoutFinish.append(self.updateSearchInfoList)
  3478. self.onLayoutFinish.append(self.updateBottomMenu)
  3479. if not searchSettings.manualSearch.value and not self.standAlone:
  3480. self.onLayoutFinish.append(self.searchSubs)
  3481. else:
  3482. self.onLayoutFinish.append(self.searchMessage)
  3483. self.onClose.append(self.__contextMenu.hide)
  3484. self.onClose.append(self.__contextMenu.doClose)
  3485. self.onClose.append(self.message.hide)
  3486. self.onClose.append(self.searchParamsHelper.resetSearchParams)
  3487. self.onClose.append(self.stopSearchSubs)
  3488. self.onClose.append(self.closeSeekers)
  3489. def __getSubtitlesRenderer(self):
  3490. from Components.Sources.Source import Source
  3491. from Components.Renderer.Listbox import Listbox
  3492. for r in self.renderer:
  3493. if isinstance(r, Listbox):
  3494. s = r
  3495. while not isinstance(s,Source):
  3496. s = s.source
  3497. if s == self['subtitles']:
  3498. self.__listboxRenderer = r
  3499. break
  3500. def updateTitle(self):
  3501. self.title = _("Subtitles search")
  3502. def updateSearchInfoList(self):
  3503. searchInfoList = []
  3504. lang1 = self.searchSettings.lang1.value
  3505. lang2 = self.searchSettings.lang2.value
  3506. lang3 = self.searchSettings.lang3.value
  3507. lang1 = lang1 in LanguageCodes and LanguageCodes[lang1][0] or lang1
  3508. lang2 = lang2 in LanguageCodes and LanguageCodes[lang2][0] or lang2
  3509. lang3 = lang3 in LanguageCodes and LanguageCodes[lang3][0] or lang3
  3510. langs = [lang1]
  3511. if lang2 not in langs:
  3512. langs.append(lang2)
  3513. if lang3 not in langs:
  3514. langs.append(lang3)
  3515. languages = ", ".join(_(lang) for lang in langs)
  3516. year = self.searchYear.value and str(self.searchYear.value) or ""
  3517. season = self.searchSeason.value and str(self.searchSeason.value) or ""
  3518. episode = self.searchEpisode.value and str(self.searchEpisode.value) or ""
  3519. useFilePathStr = self.searchUseFilePath.value and _("yes") or _("no")
  3520. searchInfoList.append((_("Title") + ":", self.searchTitle.value))
  3521. searchInfoList.append((_("Type") + ":", self.searchType.getText()))
  3522. if self.searchType.value == "movie":
  3523. searchInfoList.append((_("Year") + ":", year))
  3524. else:
  3525. searchInfoList.append((_("Season") + ":", season))
  3526. searchInfoList.append((_("Episode") + ":", episode))
  3527. searchInfoList.append((_("Provider") + ":", self.searchProvider.getText()))
  3528. searchInfoList.append((_("Preferred languages") + ":", languages))
  3529. searchInfoList.append((_("Use File path") + ":", useFilePathStr))
  3530. self['search_info'].list = searchInfoList
  3531. def updateSubsList(self):
  3532. imgDict = {
  3533. 'sync':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'check.png')),
  3534. 'unk':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'countries', 'UNK.png'))
  3535. }
  3536. subtitleListGUI = []
  3537. for sub in self.subtitlesList:
  3538. sync = 'sync' in sub and sub['sync'] or False
  3539. if sub['country'] not in imgDict:
  3540. countryImgPath = os.path.join(os.path.dirname(__file__), 'img', 'countries', sub['country'] + '.png')
  3541. if os.path.isfile(countryImgPath):
  3542. countryPng = loadPNG(countryImgPath)
  3543. imgDict[sub['country']] = countryPng
  3544. else:
  3545. countryPng = imgDict['unk']
  3546. syncPng = sync and imgDict['sync'] or None
  3547. subtitleListGUI.append((countryPng, _(toString(sub['language_name'])),
  3548. toString(sub['filename']), toString(sub['provider']), syncPng),)
  3549. imgDict = None
  3550. self['subtitles'].list = subtitleListGUI
  3551. def updateBottomMenu(self):
  3552. if self.__searching:
  3553. self["key_red"].text = ""
  3554. self["key_green"].text = ""
  3555. self["key_yellow"].text = ""
  3556. self["key_blue"].text = ""
  3557. self["key_menu_img"].boolean = False
  3558. elif self.__downloading:
  3559. self["key_red"].text = ""
  3560. self["key_green"].text = ""
  3561. self["key_yellow"].text = ""
  3562. self["key_blue"].text = ""
  3563. self["key_menu_img"].boolean = False
  3564. else:
  3565. self["key_red"].text = (_("Update"))
  3566. self["key_green"].text = (_("Search"))
  3567. self["key_yellow"].text = (_("History"))
  3568. self["key_blue"].text = (_("Settings"))
  3569. if self["subtitles"].count() > 0:
  3570. self["key_menu_img"].boolean = True
  3571. def updateActionMaps(self):
  3572. if self.__searching:
  3573. self["okCancelActions"].setEnabled(False)
  3574. self["listActions"].setEnabled(False)
  3575. self["menuActions"].setEnabled(False)
  3576. self["searchActions"].setEnabled(True)
  3577. self["contextMenuActions"].setEnabled(False)
  3578. elif self.__downloading:
  3579. self["okCancelActions"].setEnabled(False)
  3580. self["listActions"].setEnabled(False)
  3581. self["menuActions"].setEnabled(False)
  3582. self["searchActions"].setEnabled(False)
  3583. self["contextMenuActions"].setEnabled(False)
  3584. elif self.__contextMenu.shown:
  3585. self["okCancelActions"].setEnabled(False)
  3586. self["listActions"].setEnabled(False)
  3587. self["menuActions"].setEnabled(False)
  3588. self["searchActions"].setEnabled(False)
  3589. self["contextMenuActions"].setEnabled(True)
  3590. else:
  3591. self["okCancelActions"].setEnabled(True)
  3592. self["listActions"].setEnabled(True)
  3593. self["menuActions"].setEnabled(True)
  3594. self["searchActions"].setEnabled(False)
  3595. self["contextMenuActions"].setEnabled(False)
  3596. def detectSearchParams(self):
  3597. self.searchParamsHelper.detectSearchParams(self.searchExpression)
  3598. def closeSeekers(self):
  3599. for seeker in self.seeker.seekers:
  3600. seeker.close()
  3601. def keyOk(self):
  3602. if self['subtitles'].count():
  3603. self.downloadSubs(self.subtitlesList[self["subtitles"].index])
  3604. def keyCancel(self):
  3605. self.close()
  3606. def keyUp(self):
  3607. if self['subtitles'].count():
  3608. self.message.hide()
  3609. self['subtitles'].selectPrevious()
  3610. def keyDown(self):
  3611. if self['subtitles'].count():
  3612. self.message.hide()
  3613. self['subtitles'].selectNext()
  3614. def keyRight(self):
  3615. if self['subtitles'].count():
  3616. self.message.hide()
  3617. self.__listboxRenderer.move(self.__listboxRenderer.instance.pageDown)
  3618. def keyLeft(self):
  3619. if self['subtitles'].count():
  3620. self.message.hide()
  3621. self.__listboxRenderer.move(self.__listboxRenderer.instance.pageUp)
  3622. def searchMessage(self):
  3623. self.message.info(_("Update search parameters if not correct\n and press green button for search"))
  3624. def searchSubs(self):
  3625. def searchSubsUpdate(args):
  3626. pfinished, status, value = args
  3627. if status:
  3628. self.__finished[pfinished] = value
  3629. else:
  3630. self.__finished[pfinished] = {'list':[], 'status':status,'message':str(value)}
  3631. progressMessage = "%s - %d%%" % (_("loading subtitles list"), int(len(self.__finished.keys()) / float(len(provider)) * 100))
  3632. progressMessage +="\n" + _("subtitles found") + " (%d)"%(sum(len(self.__finished[p]['list']) for p in self.__finished.keys()))
  3633. progressMessage +="\n\n" + _("Press OK to Stop")
  3634. self.message.info(progressMessage)
  3635. self.stopSearchSubs()
  3636. self.subtitlesList = []
  3637. self.subtitlesDict = {}
  3638. self.__finished = {}
  3639. p = self.searchParamsHelper.getSearchParams()
  3640. langs, title, year, tvshow, season, episode = p[1], p[2], p[3], p[4], p[5], p[6]
  3641. providers = self.seeker.getProviders(langs, not tvshow, tvshow)
  3642. if self.searchProvider.value == "all":
  3643. provider = providers
  3644. else:
  3645. provider = [p for p in providers if p.id == self.searchProvider.value]
  3646. filepath = self.searchUseFilePath.value and self.filepath or None
  3647. timeout = float(self.searchSettings.timeout.value)
  3648. params = {
  3649. 'search':{
  3650. 'providers':[p.id for p in provider],
  3651. 'title':title,
  3652. 'filepath':filepath,
  3653. 'langs':langs,
  3654. 'year': year,
  3655. 'tvshow': tvshow,
  3656. 'season': season,
  3657. 'episode': episode,
  3658. 'timeout': timeout
  3659. },
  3660. 'settings': dict((s.id, s.settings_provider.getSettingsDict()) for s in self.seeker.seekers)
  3661. }
  3662. progressMessage = "%s - %d%%" % (_("loading subtitles list"), 0)
  3663. progressMessage +="\n" + _("subtitles found") + " (%d)"% 0
  3664. progressMessage +="\n\n" + _("Press OK to Stop")
  3665. self.message.info(progressMessage)
  3666. self.__searching = True
  3667. self.updateActionMaps()
  3668. self.updateSubsList()
  3669. self.updateBottomMenu()
  3670. SubsSearchProcess().start(params, searchSubsUpdate, self.searchSubsSuccess, self.searchSubsError)
  3671. def cancelSearchSubs(self):
  3672. self.stopSearchSubs()
  3673. self.searchSubsSuccess(self.__finished)
  3674. def stopSearchSubs(self):
  3675. for p in SubsSearchProcess.processes:
  3676. p.stop()
  3677. print len(SubsSearchProcess.processes), 'processes still running'
  3678. def searchSubsSuccess(self, subtitles):
  3679. print '[SubsSearch] search success'
  3680. self.message.hide()
  3681. self.subtitlesDict = subtitles
  3682. subtitlesList = self.seeker.getSubtitlesList(subtitles)
  3683. subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, sort_sync=True)
  3684. langs = [self.searchSettings.lang1.value,
  3685. self.searchSettings.lang2.value,
  3686. self.searchSettings.lang3.value]
  3687. if self.searchSettings.defaultSort.value == 'lang':
  3688. subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, langs, sort_langs=True)
  3689. elif self.searchSettings.defaultSort.value == 'provider':
  3690. subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, langs, sort_provider=True)
  3691. self.subtitlesList = subtitlesList
  3692. if len(self.subtitlesList) == 0:
  3693. noSubtitlesMessage = _("No subtitles found :(")
  3694. noSubtitlesMessage += "\n" + _("Try update(simplify) search expression and try again..")
  3695. self.message.info(noSubtitlesMessage)
  3696. self.__searching = False
  3697. self.updateSubsList()
  3698. self.updateBottomMenu()
  3699. self.updateActionMaps()
  3700. def searchSubsError(self, error):
  3701. print '[SubsSearch] search error', str(error)
  3702. self.message.error(error.message, 4000)
  3703. self.subtitlesList = []
  3704. self.subtitlesDict = {}
  3705. self.updateSubsList()
  3706. self.__searching = False
  3707. self.updateBottomMenu()
  3708. self.updateActionMaps()
  3709. def downloadSubs(self, subtitle, downloadDir=None, fName=None, saveAs=None,
  3710. saveTo=None, langToFilename=None, askOverwrite=None, closeOnSuccess = None):
  3711. if saveAs is None:
  3712. saveAs = self.searchSettings.saveAs.value
  3713. if saveAs == 'video' and not self.isLocalFilepath:
  3714. saveAs = self.searchSettings.saveAsFallback.value
  3715. if langToFilename is None:
  3716. langToFilename = self.searchSettings.addLangToSubsFilename.value
  3717. if askOverwrite is None:
  3718. askOverwrite = self.searchSettings.askOverwriteExistingSubs.value
  3719. if closeOnSuccess is None:
  3720. closeOnSuccess = self.searchSettings.loadSubtitlesAfterDownload.value
  3721. if downloadDir is None:
  3722. if saveTo is None:
  3723. saveTo = self.searchSettings.saveTo.value
  3724. if saveTo == 'video' and self.isLocalFilepath:
  3725. downloadDir = os.path.dirname(self.filepath)
  3726. else:
  3727. downloadDir = self.searchSettings.downloadPath.value
  3728. self.__downloading = True
  3729. self.__downloadingSubtitle = subtitle
  3730. self.updateActionMaps()
  3731. self.updateBottomMenu()
  3732. self.message.info(_('downloading subtitles...'))
  3733. self.__closeOnSuccess = closeOnSuccess
  3734. settings = {
  3735. "save_as": saveAs,
  3736. "lang_to_filename":langToFilename,
  3737. "ask_overwrite":askOverwrite
  3738. }
  3739. self.seeker.downloadSubtitle(self.downloadSubsSuccess, self.downloadSubsError, self.downloadSubsCancel, subtitle, self.subtitlesDict, settings, downloadDir, fName)
  3740. def downloadSubsSuccess(self, subFile):
  3741. print '[SubsSearch] download success %s' % toString(subFile)
  3742. dsubtitle = {
  3743. "name":toUnicode(os.path.basename(subFile)),
  3744. "country": toUnicode(self.__downloadingSubtitle['country']),
  3745. "provider":toUnicode(self.__downloadingSubtitle['provider']),
  3746. "fpath": toUnicode(subFile),
  3747. }
  3748. if self.searchSettings.downloadHistory.enabled.value:
  3749. fpath = os.path.join(self.searchSettings.downloadHistory.path.value, 'hsubtitles.json')
  3750. try:
  3751. subtitles = json.load(open(fpath,"r"))
  3752. except Exception as e:
  3753. print '[SubsSearch] cannot load download history:', e
  3754. subtitles = []
  3755. limit = int(self.searchSettings.downloadHistory.limit.value)
  3756. if dsubtitle in subtitles:
  3757. subtitles.remove(dsubtitle)
  3758. if len(subtitles) >= limit:
  3759. print '[SubsSearch] download history limit reached!, removing oldest entries'
  3760. del subtitles[-(len(subtitles)-limit):]
  3761. subtitles.insert(0, dsubtitle)
  3762. try:
  3763. json.dump(subtitles, open(fpath,'w'))
  3764. except Exception as e:
  3765. print '[SubsSearch] cannot save download history:', e
  3766. self.__downloadedSubtitles.append(dsubtitle)
  3767. self.afterDownloadSuccess(dsubtitle)
  3768. self.message.hide()
  3769. self.__downloading = False
  3770. del self.__downloadingSubtitle
  3771. self.updateBottomMenu()
  3772. self.updateActionMaps()
  3773. def downloadSubsError(self, e):
  3774. print '[SubsSearch] download error', str(e)
  3775. self.__downloading = False
  3776. del self.__downloadingSubtitle
  3777. self.updateBottomMenu()
  3778. self.updateActionMaps()
  3779. errorMessageFormat = "[{0}]: {1}"
  3780. if isinstance(e, SubtitlesDownloadError):
  3781. if e.code == SubtitlesErrors.CAPTCHA_RETYPE_ERROR:
  3782. self.message.error(errorMessageFormat.format(e.provider, _("captcha doesn't match, try again...")), 4000)
  3783. elif e.code == SubtitlesErrors.INVALID_CREDENTIALS_ERROR:
  3784. self.message.error(errorMessageFormat.format(e.provider, _("invalid credentials provided, correct them and try again")), 4000)
  3785. elif e.code == SubtitlesErrors.NO_CREDENTIALS_ERROR:
  3786. self.message.error(errorMessageFormat.format(e.provider, _("no credentials provided, set them and try again")), 4000)
  3787. else:
  3788. self.message.error(str(e), 4000)
  3789. else:
  3790. print "".join(traceback.format_tb(e.tb))
  3791. self.message.error(str(e), 4000)
  3792. def downloadSubsCancel(self):
  3793. print '[SubsSearch] download cancelled'
  3794. self.__downloading = False
  3795. del self.__downloadingSubtitle
  3796. self.updateBottomMenu()
  3797. self.updateActionMaps()
  3798. self.message.hide()
  3799. def afterDownloadSuccess(self, subtitle):
  3800. if not self.standAlone and self.__closeOnSuccess:
  3801. self.close(subtitle['fpath'])
  3802. self.__closeOnSuccess = None
  3803. def openContextMenu(self):
  3804. if not self["subtitles"].count() > 0:
  3805. return
  3806. downloadOptions = [
  3807. (_("Download (user defined)"), "d_custom"),
  3808. (_("Download (next to video)"), "d_video"),
  3809. (_("Download (more...)"), "d_more"),
  3810. ]
  3811. downloadAndLoadOptions = [
  3812. (_("Download and Load (user defined) "), "do_custom"),
  3813. (_("Download and Load (next to video)"), "do_video"),
  3814. (_("Download and Load (more...)"), "do_more"),
  3815. ]
  3816. if not self.isLocalFilepath or self.standAlone:
  3817. downloadOptions.remove(downloadOptions[1])
  3818. downloadAndLoadOptions.remove(downloadAndLoadOptions[1])
  3819. if self.standAlone:
  3820. del downloadAndLoadOptions[:]
  3821. options = []
  3822. if not self.searchSettings.loadSubtitlesAfterDownload.value or self.standAlone:
  3823. if len(downloadOptions) > 0:
  3824. if self.searchSettings.saveTo.value == "custom":
  3825. options.append(downloadOptions[0])
  3826. elif self.searchSettings.saveTo.value == "video" and self.isLocalFilepath:
  3827. options.append(downloadOptions[1])
  3828. elif self.searchSettings.saveTo.value == "more":
  3829. options.append(downloadOptions[-1])
  3830. for o in downloadOptions:
  3831. if o not in options:
  3832. options.append(o)
  3833. options.extend(downloadAndLoadOptions)
  3834. else:
  3835. if len(downloadAndLoadOptions) > 0:
  3836. if self.searchSettings.saveTo.value == "custom":
  3837. options.append(downloadAndLoadOptions[0])
  3838. elif self.searchSettings.saveTo.value == "video" and self.isLocalFilepath:
  3839. options.append(downloadAndLoadOptions[1])
  3840. elif self.searchSettings.saveTo.value == "more":
  3841. options.append(downloadAndLoadOptions[-1])
  3842. for o in downloadAndLoadOptions:
  3843. if o not in options:
  3844. options.append(o)
  3845. options.extend(downloadOptions)
  3846. subtitle = self.subtitlesList[self["subtitles"].index]
  3847. self.__contextMenu.updateGUI(subtitle, options)
  3848. self.__contextMenu.show()
  3849. self.updateActionMaps()
  3850. def contextMenuOk(self):
  3851. def downloadMoreCB(dPath, fName):
  3852. if dPath and fName:
  3853. self.downloadSubs(subtitle, downloadDir=dPath, fName=fName, closeOnSuccess=closeOnSuccess)
  3854. answer = self.__contextMenu.getSelection()
  3855. self.__contextMenu.hide()
  3856. self.updateActionMaps()
  3857. subtitle = self.subtitlesList[self["subtitles"].index]
  3858. if answer == "d_custom":
  3859. self.downloadSubs(subtitle, saveTo='custom', closeOnSuccess=False)
  3860. elif answer == 'd_video':
  3861. self.downloadSubs(subtitle, saveTo='video', closeOnSuccess=False)
  3862. elif answer == 'do_custom':
  3863. self.downloadSubs(subtitle, saveTo='custom', closeOnSuccess=True)
  3864. elif answer == 'do_video':
  3865. self.downloadSubs(subtitle, saveTo='video', closeOnSuccess=True)
  3866. elif answer == 'do_more':
  3867. closeOnSuccess = True
  3868. elif answer == 'd_more':
  3869. closeOnSuccess = False
  3870. if answer == 'd_more' or answer == 'do_more':
  3871. saveAs = self.searchSettings.saveAs.value
  3872. saveTo = self.searchSettings.saveTo.value
  3873. addLang = self.searchSettings.addLangToSubsFilename.value
  3874. dPath = self.searchSettings.downloadPath.value
  3875. vPath = self.filepath
  3876. self.session.openWithCallback(downloadMoreCB, SubsSearchDownloadOptions,
  3877. subtitle, saveAs, saveTo, addLang, dPath, vPath)
  3878. def contextMenuCancel(self):
  3879. self.__contextMenu.hide()
  3880. self.updateActionMaps()
  3881. def updateSearchParams(self):
  3882. def updateSearchParamsCB(callback=None):
  3883. if callback:
  3884. self.updateSearchInfoList()
  3885. self.updateBottomMenu()
  3886. if not self.searchSettings.manualSearch.value:
  3887. self.searchSubs()
  3888. self.session.openWithCallback(updateSearchParamsCB, SubsSearchParamsMenu, self.seeker, self.searchSettings, self.searchTitles, False)
  3889. def openDownloadHistory(self):
  3890. def openDownloadHistoryCB(subtitles, subtitle=None):
  3891. if len(subtitles) > 0:
  3892. if not self.searchSettings.downloadHistory.enabled.value:
  3893. for i in self.__downloadedSubtitles:
  3894. if i in subtitles:
  3895. subtitles.remove(i)
  3896. try:
  3897. json.dump(subtitles, open(fpath,"w"))
  3898. except Exception as e:
  3899. print '[SubsSearch] save download history:', e
  3900. if subtitle is not None:
  3901. if not self.standAlone:
  3902. self.close(subtitle)
  3903. fpath = os.path.join(self.searchSettings.downloadHistory.path.value, 'hsubtitles.json')
  3904. try:
  3905. subtitles = json.load(open(fpath,"r"))
  3906. except Exception as e:
  3907. print '[SubsSearch] cannot load download history:', e
  3908. subtitles = []
  3909. self.session.openWithCallback(openDownloadHistoryCB, SubsDownloadedSelection,
  3910. subtitles, self.searchSettings.downloadHistory, self.__downloadedSubtitles)
  3911. def openSettings(self):
  3912. def openSettingsCB(langChanged = False):
  3913. self.seeker.tmp_path = self.searchSettings.tmpPath.value
  3914. self.seeker.download_path = self.searchSettings.downloadPath.value
  3915. self.searchParamsHelper.updateProviders()
  3916. self.updateSearchInfoList()
  3917. self.updateBottomMenu()
  3918. if langChanged and not self.searchSettings.manualSearch.value:
  3919. self.searchSubs()
  3920. self.session.openWithCallback(openSettingsCB, SubsSearchSettings, self.searchSettings, self.seeker, self.isLocalFilepath)
  3921. class SubsSearchSettings(Screen, ConfigListScreen):
  3922. @staticmethod
  3923. def getConfigList(searchSettings):
  3924. configList = []
  3925. configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 1', searchSettings.lang1))
  3926. configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 2', searchSettings.lang2))
  3927. configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 3', searchSettings.lang3))
  3928. configList.append(getConfigListEntry(_("Preferred Movie provider"), searchSettings.movieProvider))
  3929. configList.append(getConfigListEntry(_("Preferred TV show provider"), searchSettings.tvshowProvider))
  3930. configList.append(getConfigListEntry(_("Manual search"), searchSettings.manualSearch))
  3931. configList.append(getConfigListEntry(_("Subtitles provider timeout"), searchSettings.timeout))
  3932. configList.append(getConfigListEntry(_("Check search parameters before subtitles search"), searchSettings.openParamsDialogOnSearch))
  3933. configList.append(getConfigListEntry(_("Sort subtitles list by"), searchSettings.defaultSort))
  3934. configList.append(getConfigListEntry(_("Save subtitles as"), searchSettings.saveAs))
  3935. configList.append(getConfigListEntry(_("Save subtitles as (fallback)"), searchSettings.saveAsFallback))
  3936. configList.append(getConfigListEntry(_("Add subtitle's language to filename"), searchSettings.addLangToSubsFilename))
  3937. configList.append(getConfigListEntry(_("Save subtitles to"), searchSettings.saveTo))
  3938. configList.append(getConfigListEntry(_("Subtitles download path"), searchSettings.downloadPath))
  3939. configList.append(getConfigListEntry(_("Subtitles temp path"), searchSettings.tmpPath))
  3940. configList.append(getConfigListEntry(_("Always ask before overwriting existing subtitles"), searchSettings.askOverwriteExistingSubs))
  3941. configList.append(getConfigListEntry(_("Load subtitles after download"), searchSettings.loadSubtitlesAfterDownload))
  3942. historySettings = searchSettings.downloadHistory
  3943. configList.append(getConfigListEntry(_("Save downloaded subtitles to history"), historySettings.enabled))
  3944. if historySettings.enabled.value:
  3945. configList.append(getConfigListEntry(_("Load/Save download history directory"), historySettings.path))
  3946. return configList
  3947. skin = """
  3948. <screen name="SubsSearch" position="center,center" size="650,500" zPosition="3" >
  3949. <widget name="key_red" position="10,5" zPosition="1" size="150,45" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" shadowOffset="-2,-2" shadowColor="black" />
  3950. <widget name="key_green" position="170,5" zPosition="1" size="150,45" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" shadowOffset="-2,-2" shadowColor="black" />
  3951. <widget name="key_yellow" position="330,5" zPosition="1" size="150,45" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" shadowOffset="-2,-2" shadowColor="black" />
  3952. <widget name="key_blue" position="490,5" zPosition="1" size="150,45" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" shadowOffset="-2,-2" shadowColor="black" />
  3953. <eLabel position="-1,55" size="650,1" backgroundColor="#999999" />
  3954. <widget name="config" position="10,75" size="630,178" scrollbarMode="showOnDemand" />
  3955. <!-- <eLabel position="5,245" size="640,1" backgroundColor="#999999" /> -->
  3956. <widget source="header_name" render="Label" position = "10,265" size="200,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3957. <widget source="header_lang" render="Label" position = "220,265" size="180,25" font="Regular;18" halign="left" foregroundColor="#0xcccccc" />
  3958. <widget source="header_state" render="Label" position = "410, 265" size="200,25" font="Regular;18" halign="right" foregroundColor="#0xcccccc" />
  3959. <eLabel position="5,295" size="640,1" backgroundColor="#999999" />
  3960. <widget source="providers" render="Listbox" scrollbarMode="showOnDemand" position="10,305" size="630,185" zPosition="3" transparent="1" >
  3961. <convert type="TemplatedMultiContent">
  3962. {"templates":
  3963. {"default": (23, [
  3964. MultiContentEntryText(pos = (0, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # name,
  3965. MultiContentEntryText(pos = (210, 0), size = (180, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # lang,
  3966. MultiContentEntryText(pos = (400, 0), size = (200, 25), font = 0, flags = RT_HALIGN_RIGHT, text = 2, color=0xFF000003) # enabled,
  3967. ], True, "showOnDemand"),
  3968. "notselected": (23, [
  3969. MultiContentEntryText(pos = (0, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # name,
  3970. MultiContentEntryText(pos = (210, 0), size = (180, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # lang,
  3971. MultiContentEntryText(pos = (400, 0), size = (200, 25), font = 0, flags = RT_HALIGN_RIGHT, text = 2, color=0xFF000003) # enabled,
  3972. ], False, "showOnDemand")
  3973. },
  3974. "fonts": [gFont("Regular", 18), gFont("Regular", 16)],
  3975. "itemHeight": 23
  3976. }
  3977. </convert>
  3978. </widget>
  3979. </screen> """
  3980. FOCUS_CONFIG, FOCUS_PROVIDERS = range(2)
  3981. def __init__(self, session, searchSettings, seeker, isLocalFilepath=True):
  3982. Screen.__init__(self, session)
  3983. ConfigListScreen.__init__(self, [], session=session)
  3984. self.searchSettings = searchSettings
  3985. self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings)
  3986. self.providers = seeker.seekers
  3987. self.isLocalFilepath = isLocalFilepath
  3988. self.focus = self.FOCUS_CONFIG
  3989. self['providers'] = List([])
  3990. self["header_name"] = StaticText(_("Provider name"))
  3991. self["header_lang"] = StaticText(_("Supported languages"))
  3992. self["header_state"] = StaticText(_("State"))
  3993. self["key_green"] = Label(_("Save"))
  3994. self["key_red"] = Label(_("Cancel"))
  3995. self["key_blue"] = Label(_("Reset Defaults"))
  3996. self["key_yellow"] = Label(_("Switch List"))
  3997. self["actions"] = ActionMap(["DirectionActions", "SetupActions", "OkCancelActions", "ColorActions", "ListboxActions"],
  3998. {
  3999. "ok": self.keyOk,
  4000. "cancel": self.keyCancel,
  4001. "save":self.keySave,
  4002. "up": self.keyUp,
  4003. "down": self.keyDown,
  4004. "right":self.keyRight,
  4005. "left":self.keyLeft,
  4006. "blue":self.resetDefaults,
  4007. "yellow":self.switchList,
  4008. "pageUp":self.switchList,
  4009. "pageDown":self.switchList,
  4010. }, -2)
  4011. self.onLayoutFinish.append(self.setWindowTitle)
  4012. self.onLayoutFinish.append(self.buildMenu)
  4013. self.onLayoutFinish.append(self.updateProvidersList)
  4014. self.onLayoutFinish.append(self.setConfigFocus)
  4015. def setWindowTitle(self):
  4016. self.setTitle(_("Subtitles search settings"))
  4017. def buildMenu(self):
  4018. self["config"].setList(self.getConfigList(self.searchSettings))
  4019. def updateProvidersList(self):
  4020. providerListGUI = []
  4021. for provider in self.providers:
  4022. providerName = provider.provider_name
  4023. providerLangs = ','.join(provider.supported_langs)
  4024. if provider.error is not None:
  4025. providerState = _("error")
  4026. providerStateColor = 0xff0000
  4027. elif provider.settings_provider.getSetting('enabled'):
  4028. providerState = _("enabled")
  4029. providerStateColor = 0x00ff00
  4030. else:
  4031. providerState = _("disabled")
  4032. providerStateColor = 0xffff00
  4033. providerListGUI.append((toString(providerName), providerLangs, providerState, providerStateColor))
  4034. self['providers'].list = providerListGUI
  4035. def setConfigFocus(self):
  4036. self.focus = self.FOCUS_CONFIG
  4037. self['config'].instance.setSelectionEnable(True)
  4038. self['providers'].style = 'notselected'
  4039. def switchList(self):
  4040. if self.focus == self.FOCUS_PROVIDERS:
  4041. self.focus = self.FOCUS_CONFIG
  4042. self['providers'].style = "notselected"
  4043. self['config'].instance.setSelectionEnable(True)
  4044. else:
  4045. self.focus = self.FOCUS_PROVIDERS
  4046. self['config'].instance.setSelectionEnable(False)
  4047. self['providers'].style = 'default'
  4048. def keyOk(self):
  4049. if self.focus == self.FOCUS_PROVIDERS:
  4050. provider = self.providers[self['providers'].index]
  4051. if provider.error:
  4052. self.showProviderError(provider)
  4053. else:
  4054. self.openProviderSettings(provider)
  4055. else:
  4056. current = self['config'].getCurrent()[1]
  4057. if current == self.searchSettings.downloadPath:
  4058. currentPath = self.searchSettings.downloadPath.value
  4059. self.session.openWithCallback(self.setDownloadPath, LocationBox, "", "", currentPath)
  4060. elif current == self.searchSettings.tmpPath:
  4061. currentPath = self.searchSettings.tmpPath.value
  4062. self.session.openWithCallback(self.setTmpPath, LocationBox, "", "", currentPath)
  4063. elif current == self.searchSettings.downloadHistory.path:
  4064. currentPath = self.searchSettings.downloadHistory.path.value
  4065. self.session.openWithCallback(self.setHistoryPath, LocationBox, "", "", currentPath)
  4066. elif current in [self.searchSettings.lang1,
  4067. self.searchSettings.lang2,
  4068. self.searchSettings.lang3]:
  4069. self.session.openWithCallback(self.setLanguage, MyLanguageSelection, current.value)
  4070. def setLanguage(self, language=None):
  4071. if language:
  4072. self['config'].getCurrent()[1].value = language
  4073. self.buildMenu()
  4074. def setDownloadPath(self, downloadPath=None):
  4075. if downloadPath:
  4076. self.searchSettings.downloadPath.value = downloadPath
  4077. self.buildMenu()
  4078. def setTmpPath(self, tmpPath=None):
  4079. if tmpPath:
  4080. self.searchSettings.tmpPath.value = tmpPath
  4081. self.buildMenu()
  4082. def setHistoryPath(self, historyPath=None):
  4083. if historyPath:
  4084. self.searchSettings.downloadHistory.path.value = historyPath
  4085. self.buildMenu()
  4086. def keySave(self):
  4087. langChanged = (self.searchSettings.lang1.isChanged() or
  4088. self.searchSettings.lang2.isChanged() or
  4089. self.searchSettings.lang3.isChanged())
  4090. for x in self["config"].list:
  4091. x[1].save()
  4092. self.close(langChanged)
  4093. def keyCancel(self):
  4094. for x in self["config"].list:
  4095. x[1].cancel()
  4096. self.close()
  4097. def keyUp(self):
  4098. if self.focus == self.FOCUS_CONFIG:
  4099. self['config'].instance.moveSelection(self["config"].instance.moveUp)
  4100. else:
  4101. if self['providers'].index == 0:
  4102. self['providers'].index = len(self['providers'].list) - 1
  4103. else:
  4104. self['providers'].selectPrevious()
  4105. def keyDown(self):
  4106. if self.focus == self.FOCUS_CONFIG:
  4107. self['config'].instance.moveSelection(self["config"].instance.moveDown)
  4108. else:
  4109. if self['providers'].index == len(self['providers'].list) -1:
  4110. self['providers'].index = 0
  4111. else:
  4112. self['providers'].selectNext()
  4113. def keyRight(self):
  4114. if self.focus == self.FOCUS_CONFIG:
  4115. ConfigListScreen.keyRight(self)
  4116. if self['config'].getCurrent()[1] in [self.searchSettings.saveTo,
  4117. self.searchSettings.downloadHistory.enabled]:
  4118. self.buildMenu()
  4119. def keyLeft(self):
  4120. if self.focus == self.FOCUS_CONFIG:
  4121. ConfigListScreen.keyLeft(self)
  4122. if self['config'].getCurrent()[1] in [self.searchSettings.saveTo,
  4123. self.searchSettings.downloadHistory.enabled]:
  4124. self.buildMenu()
  4125. def resetDefaults(self):
  4126. for x in self["config"].list:
  4127. x[1].value = x[1].default
  4128. self.buildMenu()
  4129. def showProviderError(self, provider):
  4130. providerError = provider.error
  4131. if isinstance(providerError, tuple):
  4132. err_msg = providerError[1]
  4133. else:
  4134. err_msg = "unknown error"
  4135. if isinstance(providerError,Exception):
  4136. if isinstance(providerError, ImportError):
  4137. # No module named ...
  4138. err_msg= _("missing") + " python-%s "% (providerError.message.split()[-1]) + _("library")
  4139. else:
  4140. err_msg = providerError.message
  4141. msg = "%s: %s"%(provider.provider_name, err_msg)
  4142. self.session.open(MessageBox, msg, MessageBox.TYPE_WARNING, timeout = 5)
  4143. def openProviderSettings(self, provider):
  4144. self.session.openWithCallback(self.openProviderSettingsCB, SubsSearchProviderMenu, provider)
  4145. def openProviderSettingsCB(self, changed=False):
  4146. if changed:
  4147. configIndex = self['config'].getCurrentIndex()
  4148. providersIndex = self['providers'].index
  4149. self.updateProvidersList()
  4150. self.searchParamsHelper.updateProviders()
  4151. self.buildMenu()
  4152. self['config'].setCurrentIndex(configIndex)
  4153. self['providers'].index = providersIndex
  4154. class SubsSearchParamsMenu(Screen, ConfigListScreen):
  4155. LIST_CONFIG = 0
  4156. LIST_SUGGESTIONS = 1
  4157. LIST_HISTORY = 2
  4158. def __init__(self, session, seeker, searchSettings, titleList=None, resetSearchParams=True, enabledList=True, windowTitle=None):
  4159. s = getDesktop(0).size()
  4160. desktopSize = (s.width(), s.height())
  4161. windowSize = (550, 330)
  4162. self.skin = """
  4163. <screen position="center,%d" size="550,330" >
  4164. <widget source="sourceTitleInfo" render="Label" position="10,10" size="530,25" halign="center" font="Regular;22" foregroundColor="#66BFFF" />
  4165. <widget source="sourceTitle" render="Label" position="10,35" size="530,55" halign="center" valign="center" font="Regular;21" />
  4166. <widget source="searchTitleParams" render="Label" position="10,90" size="530,25" halign="center" font="Regular;22" foregroundColor="#ffbb00"/>
  4167. <eLabel position="10,120" size="530,1" backgroundColor="#999999" />
  4168. <widget name="config" position="10,130" size="530,160" scrollbarMode="showOnDemand" />
  4169. <eLabel position="10,285" size="530,1" backgroundColor="#999999" />
  4170. <widget source="pressOkTitleInfo" render="Label" position="10,295" size="530,25" halign="center" font="Regular;21" />
  4171. </screen>""" % ((desktopSize[1] / 2) - (windowSize[1] / 2) + 50)
  4172. Screen.__init__(self, session)
  4173. ConfigListScreen.__init__(self, [], session=session)
  4174. self["config"] = MyConfigList([], session, enabledList)
  4175. if not self.handleInputHelpers in self["config"].onSelectionChanged:
  4176. self["config"].onSelectionChanged.append(self.handleInputHelpers)
  4177. self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings)
  4178. self.searchSettings = searchSettings
  4179. if titleList is None:
  4180. titleList = []
  4181. self.sourceTitleList = titleList
  4182. self.sourceTitle = titleList and titleList[0] or ""
  4183. self.currentList = self.LIST_CONFIG
  4184. self.windowTitle = windowTitle
  4185. if len(self.sourceTitleList) == 0:
  4186. self['sourceTitleInfo'] = StaticText(_("Source title not provided"))
  4187. else:
  4188. self['sourceTitleInfo'] = StaticText("%s [%d/%d]" % (_("Source title"), 1, len(self.sourceTitleList)))
  4189. self['sourceTitle'] = StaticText(self.sourceTitle)
  4190. self['searchTitleParams'] = StaticText(_("Search parameters"))
  4191. self['pressOkTitleInfo'] = StaticText(_("Press OK to confirm"))
  4192. self["suggestionActions"] = ActionMap(["DirectionActions", "ColorActions", "OkCancelActions"],
  4193. {
  4194. "red": self.cancelToHistoryList,
  4195. "green": self.cancelToSuggestionsList,
  4196. "ok": self.switchToConfigList,
  4197. "cancel":self.cancelToConfigList,
  4198. "right": self.keyRight,
  4199. "rightRepeated": self.keyRight,
  4200. "left": self.keyLeft,
  4201. "leftRepeated": self.keyLeft,
  4202. "up": self.keyUp,
  4203. "upRepeated": self.keyUp,
  4204. "down": self.keyDown,
  4205. "downRepeated": self.keyDown
  4206. }, -2)
  4207. self["configActions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions"],
  4208. {
  4209. "ok": self.keyOK,
  4210. "cancel": self.keyCancel,
  4211. "green": self.switchToSuggestionsList,
  4212. "red": self.switchToHistoryList,
  4213. "blue": self.toggleSourceTitle,
  4214. "right": self.keyRight,
  4215. "rightRepeated": self.keyRight,
  4216. "left": self.keyLeft,
  4217. "leftRepeated": self.keyLeft,
  4218. "up": self.keyUp,
  4219. "upRepeated": self.keyUp,
  4220. "down": self.keyDown,
  4221. "downRepeated": self.keyDown
  4222. }, -2)
  4223. self['suggestionActions'].setEnabled(False)
  4224. if resetSearchParams and titleList is not None:
  4225. self.onLayoutFinish.append(self.detectSearchParams)
  4226. self.onLayoutFinish.append(self.buildMenu)
  4227. self.onLayoutFinish.append(self.setWindowTitle)
  4228. self.onLayoutFinish.append(self.saveAll)
  4229. self.onClose.append(self.removeSuggestionWindows)
  4230. def setWindowTitle(self):
  4231. if self.windowTitle is not None:
  4232. self.setTitle(self.windowTitle)
  4233. else: self.setTitle(_("Update Search params"))
  4234. def buildMenu(self):
  4235. menuList = []
  4236. menuList.append(getConfigListEntry(_("Title") , self.searchSettings.title))
  4237. menuList.append(getConfigListEntry(_("Type") , self.searchSettings.type))
  4238. if self.searchSettings.type.value == "movie":
  4239. menuList.append(getConfigListEntry(_("Year") , self.searchSettings.year))
  4240. else:
  4241. menuList.append(getConfigListEntry(_("Season") , self.searchSettings.season))
  4242. menuList.append(getConfigListEntry(_("Episode") , self.searchSettings.episode))
  4243. menuList.append(getConfigListEntry(_("Provider") , self.searchSettings.provider))
  4244. menuList.append(getConfigListEntry(_("Use File path"), self.searchSettings.useFilePath))
  4245. self["config"].list = menuList
  4246. self["config"].setList(menuList)
  4247. def detectSearchParams(self):
  4248. self.searchParamsHelper.detectSearchParams(self.sourceTitle)
  4249. self.searchParamsHelper.updateProviders()
  4250. def toggleSourceTitle(self):
  4251. if len(self.sourceTitleList) == 0:
  4252. return
  4253. currIdx = self.sourceTitleList.index(self.sourceTitle)
  4254. if self.sourceTitle == self.sourceTitleList[-1]:
  4255. currIdx = 0
  4256. else: currIdx += 1
  4257. self.sourceTitle = self.sourceTitleList[currIdx]
  4258. self['sourceTitle'].text = self.sourceTitle
  4259. self['sourceTitleInfo'].text = "%s [%d/%d]" % (_("Source title"), currIdx + 1, len(self.sourceTitleList))
  4260. self.detectSearchParams()
  4261. self.buildMenu()
  4262. def switchToSuggestionsList(self):
  4263. if not self['config'].enabled:
  4264. return
  4265. if self["config"].getCurrent()[1] == self.searchSettings.title:
  4266. self["configActions"].setEnabled(False)
  4267. self["suggestionActions"].setEnabled(True)
  4268. self["config"].invalidateCurrent()
  4269. self["config"].getCurrent()[1].enableHistory(False)
  4270. if self["config"].getCurrent()[1].enableSuggestions(True):
  4271. self.currentList = self.LIST_SUGGESTIONS
  4272. else:
  4273. self.cancelToConfigList()
  4274. def switchToHistoryList(self):
  4275. if not self['config'].enabled:
  4276. return
  4277. if self["config"].getCurrent()[1] == self.searchSettings.title:
  4278. self["configActions"].setEnabled(False)
  4279. self["suggestionActions"].setEnabled(True)
  4280. self["config"].invalidateCurrent()
  4281. self["config"].getCurrent()[1].enableSuggestions(False)
  4282. if self["config"].getCurrent()[1].enableHistory(True):
  4283. self.currentList = self.LIST_HISTORY
  4284. else:
  4285. self.cancelToConfigList()
  4286. def switchToConfigList(self):
  4287. self["config"].getCurrent()[1].enableSuggestions(False)
  4288. self["config"].getCurrent()[1].enableHistory(False)
  4289. self["suggestionActions"].setEnabled(False)
  4290. self["configActions"].setEnabled(True)
  4291. self.currentList = self.LIST_CONFIG
  4292. def cancelToHistoryList(self):
  4293. self["config"].invalidateCurrent()
  4294. self["config"].getCurrent()[1].cancelSuggestions()
  4295. self.switchToHistoryList()
  4296. def cancelToSuggestionsList(self):
  4297. self["config"].invalidateCurrent()
  4298. self["config"].getCurrent()[1].cancelSuggestions()
  4299. self.switchToSuggestionsList()
  4300. def cancelToConfigList(self):
  4301. self["config"].invalidateCurrent()
  4302. self["config"].getCurrent()[1].cancelSuggestions()
  4303. self.switchToConfigList()
  4304. def addToHistory(self):
  4305. history = self.searchSettings.history.value.split(',')
  4306. if history[0] == '':
  4307. del history[0]
  4308. if self.searchSettings.title.value in history:
  4309. history.remove((self.searchSettings.title.value))
  4310. history.insert(0, (self.searchSettings.title.value))
  4311. if len(history) == 30:
  4312. history.pop()
  4313. self.searchSettings.history.value = ",".join(history)
  4314. self.searchSettings.history.save()
  4315. def keySave(self):
  4316. for x in self["config"].list:
  4317. x[1].save()
  4318. self.close(True)
  4319. def keyCancel(self):
  4320. for x in self["config"].list:
  4321. x[1].cancel()
  4322. self.close()
  4323. def keyOK(self):
  4324. if self.currentList == self.LIST_CONFIG:
  4325. self.addToHistory()
  4326. self.saveAll()
  4327. self.close(True)
  4328. elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY):
  4329. self.switchToConfigList()
  4330. def keyDown(self):
  4331. if not self['config'].enabled:
  4332. self['config'].enableList()
  4333. elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY):
  4334. self["config"].getCurrent()[1].currentListDown()
  4335. self["config"].invalidateCurrent()
  4336. elif self.currentList == self.LIST_CONFIG:
  4337. self['config'].instance.moveSelection(self["config"].instance.moveDown)
  4338. def keyUp(self):
  4339. if not self['config'].enabled:
  4340. self['config'].enableList()
  4341. elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY):
  4342. self["config"].getCurrent()[1].currentListUp()
  4343. self["config"].invalidateCurrent()
  4344. elif self.currentList == self.LIST_CONFIG:
  4345. self['config'].instance.moveSelection(self["config"].instance.moveUp)
  4346. def keyLeft(self):
  4347. if not self['config'].enabled:
  4348. self['config'].enableList()
  4349. elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY):
  4350. self["config"].getCurrent()[1].currentListPageUp()
  4351. self["config"].invalidateCurrent()
  4352. elif self.currentList == self.LIST_CONFIG:
  4353. ConfigListScreen.keyLeft(self)
  4354. if self['config'].getCurrent()[1] == self.searchSettings.type:
  4355. self.searchParamsHelper.updateProviders()
  4356. self.buildMenu()
  4357. def keyRight(self):
  4358. if not self['config'].enabled:
  4359. self['config'].enableList()
  4360. elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY):
  4361. self["config"].getCurrent()[1].currentListPageDown()
  4362. self["config"].invalidateCurrent()
  4363. elif self.currentList == self.LIST_CONFIG:
  4364. ConfigListScreen.keyRight(self)
  4365. if self['config'].getCurrent()[1] == self.searchSettings.type:
  4366. self.searchParamsHelper.updateProviders()
  4367. self.buildMenu()
  4368. def removeSuggestionWindows(self):
  4369. if hasattr(self.searchSettings.title, 'suggestionsWindow'):
  4370. suggestionsWindow = self.searchSettings.title.suggestionsWindow
  4371. if suggestionsWindow is not None:
  4372. self.session.deleteDialog(suggestionsWindow)
  4373. self.searchSettings.title.suggestionsWindow = None
  4374. if hasattr(self.searchSettings.title, 'historyWindow'):
  4375. historyWindow = self.searchSettings.title.historyWindow
  4376. if historyWindow is not None:
  4377. self.session.deleteDialog(historyWindow)
  4378. self.searchSettings.title.historyWindow = None
  4379. class SubsSearchProviderMenu(BaseMenuScreen):
  4380. def __init__(self, session, provider):
  4381. title = toString(provider.provider_name) +" " + _("settings")
  4382. BaseMenuScreen.__init__(self, session, title)
  4383. self.provider = provider
  4384. def buildMenu(self):
  4385. settingsProvider = self.provider.settings_provider
  4386. self["config"].setList(settingsProvider.getE2Settings())